Hi, I have added two features to the TaskTimerPlugin.
*Button Styling*
I have added config.macros.taskTimer.class to allow a class name to be
specified for the input button.
When using the specified class name the plugin appends "_TimerOn" or
"_TimerOff" depending on the status of the timer.
This can be used to apply state relevant CSS styles to the timer
buttons.
For example,
Tiddler - TaskTimerPluginConfig add,
---
// e.g. class="TaskTimer_TimerOn". Use this in congunction with a
style to change the look of the timer button.
config.macros.taskTimer.class="TaskTimer";
---
In an active stylesheet add:
---
.TaskTimer_TimerOff {
background-color:none;
}
.TaskTimer_TimerOn {
background-color:lightgreen;
}
---
this will turn the button light green when the timer is running.
*Field parsing in record format*
You can now place in the format string a Field value from any
tiddler. The field is retrieved just before the record is written.
For example,
<<taskTimer today:YYYY-wWW "|%4|[[billCode@Client Project Alpha]] |%0|
%1|%2|%3|\n" "Activity: " " " | ">>
This useful when used in conjunction with the ListBoxPlugin. Two
scenarios in particular,
1. You need change a part of the format on a active timer without
stopping it.
2. Your timers are all defined in a infrequently refreshed tiddler
(e.g. MainMenu) and you require the ability to change the format
without reloading all the time.
Code below has only been tested on Firefox 6.0.2, Fedora 15 Linux
6.2.40.4
@Eric - If you think this might be useful to others and it is suitable
for inclusion into your main plugin please feel free to merge the
changes.
--- code start ---
/***
|Name|TaskTimerPlugin|
|Source|
http://www.TiddlyTools.com/#TaskTimerPlugin|
|Documentation|
http://www.TiddlyTools.com/#TaskTimerPluginInfo|
|Version|1.4.2|
|Author|Eric Shulman - ELS Design Studios|
|License|
http://www.TiddlyTools.com/#LegalStatements <br>and
[[Creative Commons Attribution-ShareAlike 2.5 License|http://
creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Options|##Configuration|
|Description|'timer' button automatically writes start/end/elapsed
time into tiddler content|
Quickly generate 'timed task' logs that can be used for status
reports, billing purposes, etc.
!!!!!Documentation
> see [[TaskTimerPluginInfo]]
!!!!!Configuration
> see [[TaskTimerPluginConfig]]
!!!!!Revisions
<<<
2011.09.25 [1.4.2] jm - Added ability to style the button. Added field
(format {{{field@tiddler}}} or {{{[[field@tiddler with more
words]]}}} ) parsing in the record format.
2008.11.10 [1.4.1] in elapsed time calculation, truncate start/stop
times to nearest second (avoids 1-second 'round-down' error)
|please see [[TaskTimerPluginInfo]] for additional revision details|
2007.03.14 [0.5.0] converted from inline script
<<<
!!!!!Code
***/
//{{{
version.extensions.TaskTimerPlugin= {major: 1, minor:4, revision: 1,
date: new Date(2008,11,10)};
config.macros.taskTimer = {
label: "start timer",
title: "press to start the task timer",
format: "|%4|%0|%1|%2|%3|\\n", // note: double-backslash-en, also
date is %4 (for backward compatibility)
defText: " ", // default description text
todayKeyword: "today",
todayFormat: "0MM/0DD/YYYY", // default format - superceded by
CalendarPlugin, DatePlugin, or DatePluginConfig
datestampFormat: "YYYY-0MM-0DD", // date stamp format
buttonFormat: "%0 - %2", // timer button formats: %0=current time,
%1=start time, %2=elapsed time
defHeader: "|//Date//|//Description//|//Started//|//Stopped//|//
Elapsed//|\n",
defTarget: "ActivityReport",
descrMsg: "Enter a short description for this activity. Press
[cancel] to continue timer.",
askMsg: "Enter the title of a tiddler in which to record this
activity. Press [cancel] to continue timer.",
errMsg: "'%0' is not a valid tiddler title. Please try again...\n
\n",
createdMsg: "'%0' has been created",
updatedMsg: "'%0' has been updated",
marker: "/%"+"tasktimer"+"%/",
tag: "task",
handler:
function(place,macroName,params,wikifier,paramString,tiddler) {
var target=params.shift(); // get optional target tiddler title
if (!target) target="";
var format=this.format; if (params[0]) format=params.shift(); // get
optional output format
var descrMsg=this.descrMsg; if (params[0])
descrMsg=params.shift(); // get optional message text
var defText=this.defText; if (params[0]) defText=params.shift(); //
get optional default text
var onclick="config.macros.taskTimer.toggle(this,'"+target
+"','"+format+"','"+descrMsg+"','"+defText+"')";
createTiddlyElement(place,"span").innerHTML =
'<input type="button" value="start timer" class="'+
config.macros.taskTimer.class+"_TimerOff" +'" title="'+this.title+'"
onclick="'+onclick+'">';
},
toggle: function(here,target,format,msg,defText) {
if (!target || !target.length || target=="here") {
var tid=story.findContainingTiddler(here);
target=tid?tid.getAttribute("tiddler"):"ask";
}
if (!here.running) { // not running... start timer...
here.startTime=new Date();
var now=here.startTime.formatString("0hh:0mm:0ss");
here.title=(here.target||target)+" - started at "+now;
here.value=this.buttonFormat.format([now,now,"00:00:00"]);
here.id=new Date().getTime()+Math.random().toString(); // unique ID
here.className=config.macros.taskTimer.class+"_TimerOn";
here.ticker=setTimeout("config.macros.taskTimer.tick('"+
here.id
+"')",500);
here.running=true;
} else {
if (target=="ask") {
target=prompt(this.askMsg,here.target||this.defTarget);
while (target && !target.trim().length)
target=prompt(this.errMsg.format([target])
+this.askMsg,here.target||this.defTarget);
if (!target) return; // user cancelled input... continue timer
}
var txt=prompt(msg,defText); // get description from user
if (!txt) return; // user cancelled input... continue timer
if (target==this.todayKeyword ||
target.substr(0,this.todayKeyword.length+1)==this.todayKeyword+":")
target=(new Date()).formatString(this.getJournalFormat(target));
here=document.getElementById(
here.id); // RE-get button element
after timer has stopped...
here.className=config.macros.taskTimer.class+"_TimerOff";
clearTimeout(here.ticker);
here.target=target;
var before=this.defHeader;
var after=this.marker+"\n";
var tiddler=store.getTiddler(here.target);
if (tiddler && tiddler.text.length) {
var pos=tiddler.text.indexOf(this.marker);
if (pos==-1) pos=tiddler.text.length; // no marker, append content
to end
var before=tiddler.text.substr(0,pos); // everything up to marker
if (before.length&&before.substr(before.length-1)!="\n") before
+="\n"; // start on a new line
var after=tiddler.text.substr(pos); // marker+everything else
}
var now=new Date(Math.floor(new Date()/1000)*1000);
var then=new Date(Math.floor(here.startTime/1000)*1000);
var diff=new Date(now-then);
var s=diff.getUTCSeconds(); if (s<10) s="0"+s;
var m=diff.getUTCMinutes(); if (m<10) m="0"+m;
var h=diff.getUTCHours(); if (h<10) h="0"+h;
var start=then.formatString("0hh:0mm:0ss");
var stop=now.formatString("0hh:0mm:0ss");
var elapsed=h+":"+m+":"+s;
var
dateStamp=now.formatString(config.macros.taskTimer.datestampFormat);
var fieldPatternRe =new RegExp('(\\w+@.*? )|(\\[\\[\\w+@[\\w\\W ]+?
\\]{2})', 'gi');
var newFormat="";
var currentIndex=0;
var arrMatch;
while (arrMatch = fieldPatternRe.exec( format )){
fieldSplit=arrMatch[0].replace(/[\[\]]{2}/g,"").split("@");
newFormat+=format.substring(currentIndex, arrMatch.index);
if (field=store.getValue(fieldSplit[1], fieldSplit[0])) {
newFormat+=field;
} else {
newFormat+=format.substring(arrMatch.index,
fieldPatternRe.lastIndex);
}
currentIndex=fieldPatternRe.lastIndex;
}
newFormat+=format.substring(currentIndex);
format=newFormat;
var newtxt=before+format.format([txt,start,stop,elapsed,dateStamp])
+after;
var newtags=(tiddler?tiddler.tags:['task']); // include 'task' tag
when creating new tiddlers
store.saveTiddler(here.target,here.target,newtxt,config.options.txtUserName,new
Date(),newtags,tiddler?tiddler.fields:null);
if (!tiddler)
displayMessage(this.createdMsg.format([here.target]));
else displayMessage(this.updatedMsg.format([here.target]));
here.running=false;
here.value=this.label;
here.title=this.title;
var tid=story.findContainingTiddler(here);
if (!tid || tid.getAttribute("tiddler")!=target) // display target
tiddler, but only when button is not IN the target tiddler
{ story.displayTiddler(story.findContainingTiddler(here),here.target);
story.refreshTiddler(here.target,1,true); }
}
},
tick: function(id) {
var here=document.getElementById(id); if (!here) return;
var now=new Date();
var diff=new Date(now-here.startTime);
var s=diff.getUTCSeconds(); if (s<10) s="0"+s;
var m=diff.getUTCMinutes(); if (m<10) m="0"+m;
var h=diff.getUTCHours(); if (h<10) h="0"+h;
var elapsed=h+":"+m+":"+s;
now=now.formatString("0hh:0mm:0ss");
var start=here.startTime.formatString("0hh:0mm:0ss");
here.value=this.buttonFormat.format([now,start,elapsed]);
here.ticker=setTimeout("config.macros.taskTimer.tick('"+id+"')",
500);
},
getJournalFormat: function(target) {
var fmt=target.split(":"); fmt.shift(); fmt=fmt.join(":");
if (!fmt || !fmt.length) { // if date format was not specified
if (config.macros.date) // if installed, use default from
DatePlugin
fmt=config.macros.date.linkformat;
if (config.macros.calendar) { // if installed, use default from
CalendarPlugin
if (!config.macros.date) // hard-coded calendar fallback if no
DatePlugin
fmt=config.macros.calendar.tiddlerformat;
else // journalDateFmt is set when calendar is rendered with
DatePlugin
fmt=config.macros.calendar.journalDateFmt;
}
}
if (!fmt) { // if not specified and no DatePlugin/CalendarPlugin
// get format from <<newJournal>> in SideBarOptions
var text = store.getTiddlerText("SideBarOptions");
var re=new RegExp("<<(?:newJournal)([^>]*)>>","mg"); var
fm=re.exec(text);
if (fm && fm[1]!=null) { var pa=fm[1].readMacroParams(); if (pa[0])
fmt = pa[0]; }
}
if (!fmt) var fmt=this.todayFormat; // no "newJournal"... final
fallback.
return fmt;
}
}
//}}}
--- code end ---