A task has a description, an estimate of how long it will take, and a record of how much time you have spent on it so far. Here's an example, which shows a task estimated at 3 hours, with 1 hour spent on it, and ''2'' hours remaining:\n<<<\n<<task 3 3 1>> Add a double-click handler to the description cell that opens the editor and selects the text\n<<<\nIf you hover the mouse over any part of the task -- the bullet, the description, or any of the numeric cells -- a tip will appear explaining it.\n\nTry modifying the time spent. Suppose you've just spent one more hour and want to record it. Just click on the second yellow cell, and enter "+1" (sans the quote marks, of course) in the popup window. Watch the time remaining go down to 1 hour.\n\nIn reality, I originally estimated this task at a half-hour, but it ended up taking 3.5 hours. The macro also tracks your original estimate, if it is different from the current estimate, in a fourth cell like this:\n<<<\n<<task 0.5 2 1>> Add a double-click handler to the description cell that opens the editor and selects the text\n<<<\nYou can adjust the current estimate in the same way as you adjusted the time spent. Click on the current estimate cell (the first yellow cell), and change it to 2.5 hours by typing "2.5" or "+.5".\n\nYou can also adjust the time remaining, which will modify either the estimate (if the time remaining increases) or the time spent (if it decreases). Click on the time remaining and add an hour by typing "+1".\n\nWhen the time remaining goes to zero, the task is considered complete:\n<<<\n<<task 0.5 3.5 3.5>> Add a double-click handler to the description cell that opens the editor and selects the text\n<<<\nIf you haven't already done so, try double-clicking the description. Yes, it really does open up the editor and select just the text of the description.\n\n----\nTo continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
A task's description is a single wikified line, so it can contain any formatting that can be specified on one line:\n<<<\n<<task 1>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).\n<<task 0.5>> Put tasksum on the ViewTemplate.\n<<<\nYou can specify just the description of a task, and leave it unestimated. Click the question mark to enter the estimate:\n<<<\n<<task>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).\n<<<\nAs this task implies, you can enter two values in the popup when you click on any of the time cells. Separate them with spaces and/or a comma. Experiment:\n<<<\n<<task 1>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).\n<<<\nFinally, if you haven't already figured this out, you can double-click on a task's bullet to mark it complete, with the current estimate entered as the time spent.\n\n----\nTo continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
If you've been paying attention, you've noticed that I haven't discussed the actual adding of calls to the task macro within your tiddlers -- it's all been about modifying tasks that were already there. That's because adding tasks via the taskadder macro is much easier and more intuitive than adding them by hand.\n\nAnd setting up a taskadder is simplicity itself. Just add {{{<<taskadder>>}}} to your tiddler. You will see this:\n<<<\n<<taskadder>>\n<<<\nJust type a task description into the first field, and your initial estimate for how long it will take into the second field. Click the "add task" button, or just hit Enter in either of the fields, to add the new task into the tiddler. Notice that you can just start typing a new task as soon as you're done entering the first one.\n\nYou can have as many taskadders as you like in any tiddler. The last one you used will capture the keyboard focus when it is redisplayed, meaning you can type a series of tasks without using the mouse. Try adding some tasks here and in the above adder:\n<<<\n<<taskadder>>\n<<<\nNotice that the one you just used takes focus when this tiddler is redisplayed.\n\nA taskadder by default adds tasks above itself. You can make it add them below by adding a {{{below}}} argument to the macro call:\n<<<\n<<taskadder below>>\n<<<\n\n----\nTo continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
In this tutorial, we've been looking mostly at individual tasks. In real life, though, you'll typically have a series of them, or even several series of them in the same tiddler. In these cases you want a summary that tells you -- at a minimum -- how much time you still expect to spend on these tasks.\n\nTo get such a summary, just add {{{<<tasksum start>>}}} before the tasks and {{{<<tasksum end>>}}} after them. Here's an example:\n<<<\n<<tasksum start>>\n<<task 0.25 0.25 0.25>> Add tooltips to the various cells\n<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells\n<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values\n<<task 1 3.5 2.5>> Add a double-click handler to the desc cell that opens the editor and selects the text\n<<task 1 1 0>> Beef up the time click handlers to allow entry of two values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).\n<<task 1 1 0>> Beef up the time click handlers to handle leading + or -\n<<task 1 1 0>> Add a double-click handler to the status cell that functions like typing 0 into the rem cell\n<<tasksum end>>\n<<<\nIf you'd rather have the summary at the top, just add {{{here}}} to the start call, ie {{{<<tasksum start here>>}}}.\n<<<\n<<tasksum start here>>\n<<task 0.25 0.25 0.25>> Add tooltips to the various cells\n<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells\n<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values\n<<tasksum end>>\n<<<\nYou can nest these things if you like, just be sure to match starts and ends:\n<<<\n<<tasksum start here>>\n* Time cell manipulation:<<tasksum start>>\n<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells\n<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values\n<<task 1 1 0>> Beef up the time click handlers to allow entry of two values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).\n<<task 1 1 0>> Beef up the time click handlers to handle leading + or -\n<<tasksum end "Cell manipulation:">>\n<<br>>\n* Double-click handling:<<tasksum start>>\n<<task 1 3.5 2.5>> Add a double-click handler to the desc cell that opens the editor and selects the text\n<<task 1 1 0>> Add a double-click handler to the status cell that functions like typing 0 into the rem cell\n<<tasksum end "Double-clicks:">>\n\n<<tasksum end>>\n<<<\nFinally, the simplest way to use tasksum is to add it to your view template. See TaskSummaryViewTemplate for an example template. Note that if no tasks are present between the start and end, nothing is displayed.\n\n----\nTo continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
The TaskMacroPlugin can be installed like any other TiddlyWiki plugin, and used without further effort. However, there are two issues that may affect you. (To get started with a brand new wiki that does not have these issues, consider downloading the [[empty LabWiki|empty_labwiki.html]].)\n# The task macros don't play nicely with the default TiddlyWiki display of tags. In the default view template, a tiddler's list of tags is shown in a little box that floats in the upper right corner of the tiddler. However, this little box may interfere with the tables used by the task macros. In Firefox, the tables are drawn right over the top of the tag box, rendering both of them illegible. In Internet Explorer, the tag box forces the tables to be pushed down below the box, which can waste a lot of space.<<br>><<br>>Thus, I recommend changing your view template to eliminate the little box. If you use Simon Baird's [[TagglyTagging|http://simonbaird.com/mptw/#TagglyTagging]] (as LabWiki does), then my TaskSummaryViewTemplate might be a good alternative. Simply import it into your wiki and rename it to ViewTemplate. This template also demonstrates how to incorporate the tasksum macro into every tiddler so any tiddler with tasks has a summary at the top.<<br>><<br>>\n# Most view templates also add a minus sign ("-") before the "close" command. TiddlyWiki interprets this to mean that you want the close command to be executed if you hit the Escape key from within the tiddler.<<br>><<br>>However, most tiddlers never have focus, and so never give you the opportunity to try it out. But if you have a taskadder in your tiddler, then you suddenly enable this feature -- and you probably don't want it. It means that if you type a nice long task description and then hit Escape, that description will be lost and the tiddler will be closed. So I recommend that you remove the minus sign from the view template's menu altogether, as I have done in LabWiki's own ViewTemplate.\n\n----\nThis ends the tutorial. To go back to any previous section, click the down-arrow and choose it: <<tag TaskMacroTutorial>>
I'd like to thank:\n* Jeremy Ruston for TiddlyWiki\n* Simon Baird for ~TagglyTaggling and most of the look and feel of this wiki (see [[MPTW|http://simonbaird.com/mptw/]])\n* Eric Shulman for ~ImportTiddlers (see [[TiddlyTools|http://tiddlytools.com/]])\n...and the cast of thousands from the various Google groups.
A TiddlyWiki naturally combines works from many different authors. The core platform is written by Jeremy Ruston; you can see his copyright and license by viewing the source of this page in your browser. Various tiddlers included here in LabWiki are written and copyright by others, including Simon Baird and Eric Shulman (see [[Acknowledgements]] for links to their sites).\n\nThe portions of LabWiki written by me are (unless otherwise indicated) licensed under the following variant of the Fair License:\n<<<\nCopyright &copy; by Luke Blanshard, 2006\n\nUse of the works is permitted provided that this instrument is retained with the works, so that any entity that uses the works is notified of this instrument. The instrument may be retained as a literal copy or as this hyperlink: http://labwiki.sourceforge.net/#CopyrightAndLicense.\n\n''DISCLAIMER:'' THE WORKS ARE WITHOUT WARRANTY.\n<<<
LabWiki TaskMacro
<!---\nIncludes portions of ~TagglyTaggingEditTemplate v1.1 (12-Jan-2006).\nModified to move the tags entry box below the content text area.\n--->\n<!--{{{-->\n<div class="toolbar" macro="toolbar +saveTiddler closeOthers -cancelTiddler deleteTiddler"></div>\n<div class="title" macro="view title"></div>\n<div class="editLabel">Title</div><div class="editor" macro="edit title"></div>\n<div class="editor" macro="edit text"></div>\n<div class="editLabel">Tags</div><div class="editor" macro="edit tags"></div>\n<div class="editorFooter"><span macro="message views.editor.tagPrompt"></span><span macro="tagChooser"></span></div>\n<br/>\n<!--}}}-->
/***\nTo use, add {{{[[HorizontalMainMenuStyles]]}}} to your StyleSheet tiddler, or you can just paste the CSS in directly. See also HorizontalMainMenu and PageTemplate.\n***/\n/*{{{*/\n\n#topMenu br {display:none; }\n#topMenu { background: #39a; }\n#topMenu { padding:2px; }\n#topMenu .button, #topMenu .tiddlyLink {\n margin-left:0.5em; margin-right:0.5em;\n padding-left:3px; padding-right:3px;\n color:white; font-size:115%;\n}\n#topMenu .button:hover, #topMenu .tiddlyLink:hover { background:#178;}\n\n#displayArea { margin: 1em 15.7em 0em 1em; } /* so we use the freed up space */\n\n/* just in case want some QuickOpenTags in your topMenu */\n#topMenu .quickopentag { padding:0px; margin:0px; border:0px; }\n#topMenu .quickopentag .tiddlyLink { padding-right:1px; margin-right:0px; }\n#topMenu .quickopentag .button { padding-left:1px; margin-left:0px; border:0px; }\n\n\n/*}}}*/
/***\n''Import Tiddlers Plugin for TiddlyWiki version 1.2.x, 2.0 and 2.1beta''\n^^author: Eric Shulman - ELS Design Studios\nsource: http://www.TiddlyTools.com/#ImportTiddlersPlugin\nlicense: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^\n\nWhen many people share and edit copies of the same TiddlyWiki document, the ability to quickly collect all these changes back into a single, updated document that can then be redistributed to the entire group is very important. This plugin lets you selectively combine tiddlers from any two TiddlyWiki documents. It can also be very useful when moving your own tiddlers from document to document (e.g., when upgrading to the latest version of TiddlyWiki, or 'pre-loading' your favorite stylesheets into a new 'empty' TiddlyWiki document.)\n\n!!!!!Interactive interface\n<<<\n{{{<<importTiddlers>>}}}\ncreates "import tiddlers" link. click to show/hide import control panel\n\n{{{<<importTiddlers inline>>}}}\ncreates import control panel directly in tiddler content\n\n<<importTiddlers inline>>\n\nPress ''[browse]'' to select a TiddlyWiki document file to import. You can also type in the path/filename or a remote document URL (starting with http://)and press ''[open]''. //Note: There may be some delay to permit the browser time to access and load the document before updating the listbox with the titles of all tiddlers that are available to be imported.//\n\nSelect one or more titles from the listbox (hold CTRL or SHIFT while clicking to add/remove the highlight from individual list items). You can press ''[select all]'' to quickly highlight all tiddler titles in the list. Use the ''[-]'', ''[+]'', or ''[=]'' links to adjust the listbox size so you can view more (or less) tiddler titles at one time. When you have chosen the tiddlers you want to import and entered any extra tags, press ''[import]'' to begin copying them to the current TiddlyWiki document.\n\n''select: all, new, changes, or differences''\n\nYou can click on ''all'', ''new'', ''changes'', or ''differences'' to automatically select a subset of tiddlers from the list. This makes it very quick and easy to find and import just the updated tiddlers you are interested in:\n>''"all"'' selects ALL tiddlers from the import source document, even if they have not been changed.\n>''"new"'' selects only tiddlers that are found in the import source document, but do not yet exist in the destination document\n>''"changes"'' selects only tiddlers that exist in both documents but that are newer in the source document\n>''"differences"'' selects all new and existing tiddlers that are different from the destination document (even if destination tiddler is newer)\n\n''Import Tagging:''\n\nTiddlers that have been imported can be automatically tagged, so they will be easier to find later on, after they have been added to your document. New tags are entered into the "add tags" input field, and then //added// to the existing tags for each tiddler as it is imported.\n\n''Skip, Rename, Merge, or Replace:''\n\nWhen importing a tiddler whose title is identical to one that already exists, the import process pauses and the tiddler title is displayed in an input field, along with four push buttons: ''[skip]'', ''[rename]'', ''[merge]'' and ''[replace]''.\n\nTo bypass importing this tiddler, press ''[skip]''. To import the tiddler with a different name (so that both the tiddlers will exist when the import is done), enter a new title in the input field and then press ''[rename]''. Press ''[merge]'' to combine the content from both tiddlers into a single tiddler. Press ''[replace]'' to overwrite the existing tiddler with the imported one, discarding the previous tiddler content.\n\n//Note: if both the title ''and'' modification date/////time match, the imported tiddler is assumed to be identical to the existing one, and will be automatically skipped (i.e., not imported) without asking.//\n\n''Import Report History''\n\nWhen tiddlers are imported, a report is generated into ImportedTiddlers, indicating when the latest import was performed, the number of tiddlers successfully imported, from what location, and by whom. It also includes a list with the title, date and author of each tiddler that was imported.\n\nWhen the import process is completed, the ImportedTiddlers report is automatically displayed for your review. If more tiddlers are subsequently imported, a new report is //added// to ImportedTiddlers, above the previous report (i.e., at the top of the tiddler), so that a reverse-chronological history of imports is maintained.\n\nIf a cumulative record is not desired, the ImportedTiddlers report may be deleted at any time. A new ImportedTiddlers report will be created the next time tiddlers are imported.\n\nNote: You can prevent the ImportedTiddlers report from being generated for any given import activity by clearing the "create a report" checkbox before beginning the import processing.\n\n<<<\n!!!!!non-interactive 'load tiddlers' macro\n<<<\nUseful for automated installation/update of plugins and other tiddler content.\n\n{{{<<loadTiddlers "label:load tiddlers from %0" http://www.tiddlytools.com/example.html confirm>>}}}\n<<loadTiddlers "label:load tiddlers from %0" http://www.tiddlytools.com/example.html confirm>>\n\nSyntax:\n{{{<<loadTiddlers label:text prompt:text filter source quiet confirm>>}}}\n\n''label:text'' and ''prompt:text''\n>defines link text and tooltip (prompt) that can be clicked to trigger the load tiddler processing. If a label is NOT provided, then no link is created and loadTiddlers() is executed whenever the containing tiddler is rendered.\n''filter'' (optional) determines which tiddlers will be automatically selected for importing. Use one of the following keywords:\n>''"all"'' retrieves ALL tiddlers from the import source document, even if they have not been changed.\n>''"new"'' retrieves only tiddlers that are found in the import source document, but do not yet exist in the destination document\n>''"changes"'' retrieves only tiddlers that exist in both documents for which the import source tiddler is newer than the existing tiddler\n>''"updates"'' retrieves both ''new'' and ''changed'' tiddlers (this is the default action when none is specified)\n>''"tiddler:~TiddlerName"'' retrieves only the specific tiddler named in the parameter.\n>''"tag:text"'' retrieves only the tiddlers tagged with the indicated text.\n''source'' (required) is the location of the imported document. It can be either a local document path/filename in whatever format your system requires, or a remote web location (starting with "http://" or "https://")\n>use the keyword ''ask'' to prompt for a source location whenever the macro is invoked\n''"quiet"'' (optional)\n>supresses all status message during the import processing (e.g., "opening local file...", "found NN tiddlers..." etc). Note that if ANY tiddlers are actualy imported, a final information message will still be displayed (along with the ImportedTiddlers report), even when 'quiet' is specified. This ensures that changes to your document cannot occur without any visible indication at all.\n''"confirm"'' (optional)\n>adds interactive confirmation. A browser message box (OK/Cancel) is displayed for each tiddler that will be imported, so that you can manually bypass any tiddlers that you do not want to import.\n<<<\n!!!!!Installation\n<<<\ncopy/paste the following tiddlers into your document:\n''ImportTiddlersPlugin'' (tagged with <<tag systemConfig>>)\n\ncreate/edit ''SideBarOptions'': (sidebar menu items) \n^^Add "< < ImportTiddlers > >" macro^^\n\n''Quick Installation Tip #1:''\nIf you are using an unmodified version of TiddlyWiki (core release version <<version>>), you can get a new, empty TiddlyWiki with the Import Tiddlers plugin pre-installed (''[[download from here|TW+ImportExport.html]]''), and then simply import all your content from your old document into this new, empty document.\n<<<\n!!!!!Revision History\n<<<\n''2006.04.04 [3.0.1]''\nin refreshImportList(), when using [by tags], tiddlers without tags are now included in a new "untagged" psuedo-tag list section\n''2006.04.04 [3.0.0]''\nSeparate non-interactive {{{<<importTiddlers...>>}}} macro functionality for incorporation into TW2.1 core and renamed as {{{<<loadTiddlers>>}}} macro. New parameters for loadTiddlers: ''label:text'' and ''prompt:text'' for link creation, ''ask'' for filename/URL, ''tag:text'' for filtering, "confirm" for accept/reject of individual inbound tiddlers. Also, ImportedTiddlers report generator output has been simplified and "importReplace/importPublic" tags and associated "force" param (which were rarely, if ever, used) has been dropped.\n''2006.03.30 [2.9.1]''\nwhen extracting store area from remote URL, look for "</body>" instead of "</body>\sn</html>" so it will match even if the "\sn" is absent from the source.\n''2006.03.30 [2.9.0]''\nadded optional 'force' macro param. When present, autoImportTiddlers() bypasses the checks for importPublic and importReplace. Based on a request from Tom Otvos.\n''2006.03.28 [2.8.1]''\nin loadImportFile(), added checks to see if 'netscape' and 'x.overrideMimeType()' are defined (IE does *not* define these values, so we bypass this code)\nAlso, when extracting store area from remote URL, explicitly look for "</body>\sn</html>" to exclude any extra content that may have been added to the end of the file by hosting environments such as GeoCities. Thanks to Tom Otvos for finding these bugs and suggesting some fixes.\n''2006.02.21 [2.8.0]''\nadded support for "tiddler:TiddlerName" filtering parameter in auto-import processing\n''2006.02.21 [2.7.1]''\nClean up layout problems with IE. (Use tables for alignment instead of SPANs styled with float:left and float:right)\n''2006.02.21 [2.7.0]''\nAdded "local file" and "web server" radio buttons for selecting dynamic import source controls in ImportPanel. Default file control is replaced with URL text input field when "web server" is selected. Default remote document URL is defined in SiteURL tiddler. Also, added option for prepending SiteProxy URL as prefix to remote URL to mask cross-domain document access (requires compatible server-side script)\n''2006.02.17 [2.6.0]''\nRemoved "differences only" listbox display mode, replaced with selection filter 'presets': all/new/changes/differences. Also fixed initialization handling for "add new tags" so that checkbox state is correctly tracked when panel is first displayed.\n''2006.02.16 [2.5.4]''\nadded checkbox options to control "import remote tags" and "keep existing tags" behavior, in addition to existing "add new tags" functionality.\n''2006.02.14 [2.5.3]''\nFF1501 corrected unintended global 't' (loop index) in importReport() and autoImportTiddlers()\n''2006.02.10 [2.5.2]''\ncorrected unintended global variable in importReport().\n''2006.02.05 [2.5.1]''\nmoved globals from window.* to config.macros.importTiddlers.* to avoid FireFox 1.5.0.1 crash bug when referencing globals\n''2006.01.18 [2.5.0]''\nadded checkbox for "create a report". Default is to create/update the ImportedTiddlers report. Clear the checkbox to skip this step.\n''2006.01.15 [2.4.1]''\nadded "importPublic" tag and inverted default so that auto sharing is NOT done unless tagged with importPublic\n''2006.01.15 [2.4.0]''\nAdded support for tagging individual tiddlers with importSkip, importReplace, and/or importPrivate to control which tiddlers can be overwritten or shared with others when using auto-import macro syntax. Defaults are to SKIP overwriting existing tiddlers with imported tiddlers, and ALLOW your tiddlers to be auto-imported by others.\n''2006.01.15 [2.3.2]''\nAdded "ask" parameter to confirm each tiddler before importing (for use with auto-importing)\n''2006.01.15 [2.3.1]''\nStrip TW core scripts from import source content and load just the storeArea into the hidden IFRAME. Makes loading more efficient by reducing the document size and by preventing the import document from executing its TW initialization (including plugins). Seems to resolve the "Found 0 tiddlers" problem. Also, when importing local documents, use convertUTF8ToUnicode() to convert the file contents so support international characters sets.\n''2006.01.12 [2.3.0]''\nReorganized code to use callback function for loading import files to support event-driven I/O via an ASYNCHRONOUS XMLHttpRequest. Let's processing continue while waiting for remote hosts to respond to URL requests. Added non-interactive 'batch' macro mode, using parameters to specify which tiddlers to import, and from what document source. Improved error messages and diagnostics, plus an optional 'quiet' switch for batch mode to eliminate //most// feedback.\n''2006.01.11 [2.2.0]''\nAdded "[by tags]" to list of tiddlers, based on code submitted by BradleyMeck\n''2006.01.09 [2.1.1]''\nWhen a URL is typed in, and then the "open" button is pressed, it generates both an onChange event for the file input and a click event for open button. This results in multiple XMLHttpRequest()'s which seem to jam things up quite a bit. I removed the onChange handling for file input field. To open a file (local or URL), you must now explicitly press the "open" button in the control panel.\n''2006.01.08 [2.1.0]''\nIMPORT FROM ANYWHERE!!! re-write getImportedTiddlers() logic to either read a local file (using local I/O), OR... read a remote file, using a combination of XML and an iframe to permit cross-domain reading of DOM elements. Adapted from example code and techniques courtesy of Jonny LeRoy.\n''2006.01.06 [2.0.2]''\nWhen refreshing list contents, fixed check for tiddlerExists() when "show differences only" is selected, so that imported tiddlers that don't exist in the current file will be recognized as differences and included in the list.\n''2006.01.04 [2.0.1]''\nWhen "show differences only" is NOT checked, import all tiddlers that have been selected even when they have a matching title and date.\n''2005.12.27 [2.0.0]''\nUpdate for TW2.0\nDefer initial panel creation and only register a notification function when panel first is created\n''2005.12.22 [1.3.1]''\ntweak formatting in importReport() and add 'discard report' link to output\n''2005.12.03 [1.3.0]''\nDynamically create/remove importPanel as needed to ensure only one instance of interface elements exists, even if there are multiple instances of macro embedding. Also, dynamically create/recreate importFrame each time an external TW document is loaded for importation (reduces DOM overhead and ensures a 'fresh' frame for each document)\n''2005.11.29 [1.2.1]''\nfixed formatting of 'detail info' in importReport()\n''2005.11.11 [1.2.0]''\nadded 'inline' param to embed controls in a tiddler\n''2005.11.09 [1.1.0]''\nonly load HTML and CSS the first time the macro handler is called. Allows for redundant placement of the macro without creating multiple instances of controls with the same ID's.\n''2005.10.25 [1.0.5]''\nfixed typo in importReport() that prevented reports from being generated\n''2005.10.09 [1.0.4]''\ncombined documentation with plugin code instead of using separate tiddlers\n''2005.08.05 [1.0.3]''\nmoved CSS and HTML definitions into plugin code instead of using separate tiddlers\n''2005.07.27 [1.0.2]''\ncore update 1.2.29: custom overlayStyleSheet() replaced with new core setStylesheet()\n''2005.07.23 [1.0.1]''\nadded parameter checks and corrected addNotification() usage\n''2005.07.20 [1.0.0]''\nInitial Release\n<<<\n!!!!!Credits\n<<<\nThis feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]\n<<<\n!!!!!Code\n***/\n// // ''MACRO DEFINITION''\n//{{{\n// Version\nversion.extensions.importTiddlers = {major: 3, minor: 0, revision: 1, date: new Date(2006,4,6)};\n\n// IE needs explicit global scoping for functions/vars called from browser events\nwindow.onClickImportButton=onClickImportButton;\nwindow.refreshImportList=refreshImportList;\n\n// default cookie/option values\nif (!config.options.chkImportReport) config.options.chkImportReport=true;\n\nconfig.macros.importTiddlers = { };\nconfig.macros.importTiddlers = {\n label: "import tiddlers",\n prompt: "Copy tiddlers from another document",\n countMsg: "%0 tiddlers selected for import",\n src: "", // path/filename or URL of document to import (retrieved from SiteUrl tiddler)\n proxy: "", // URL for remote proxy script (retrieved from SiteProxy tiddler)\n useProxy: false, // use specific proxy script in front of remote URL\n inbound: null, // hash-indexed array of tiddlers from other document\n newTags: "", // text of tags added to imported tiddlers\n addTags: true, // add new tags to imported tiddlers\n listsize: 8, // # of lines to show in imported tiddler list\n importTags: true, // include tags from remote source document when importing a tiddler\n keepTags: true, // retain existing tags when replacing a tiddler\n index: 0, // current processing index in import list\n sort: "" // sort order for imported tiddler listbox\n};\n\nconfig.macros.importTiddlers.handler = function(place,macroName,params) {\n if (!config.macros.loadTiddlers.handler)\n { alert("importTiddlers error: this plugin requires LoadTiddlersPlugin or TiddlyWiki 2.1+"); return; }\n if (!params[0]) // LINK TO FLOATING PANEL\n createTiddlyButton(place,this.label,this.prompt,onClickImportMenu);\n else if (params[0]=="inline") {// // INLINE TIDDLER CONTENT\n createImportPanel(place);\n document.getElementById("importPanel").style.position="static";\n document.getElementById("importPanel").style.display="block";\n }\n else config.macros.loadTiddlers.handler(place,macroName,params); // FALLBACK: PASS TO LOADTIDDLERS\n}\n//}}}\n\n// // ''INTERFACE DEFINITION''\n\n// // Handle link click to create/show/hide control panel\n//{{{\nfunction onClickImportMenu(e)\n{\n if (!e) var e = window.event;\n var parent=resolveTarget(e).parentNode;\n var panel = document.getElementById("importPanel");\n if (panel==undefined || panel.parentNode!=parent)\n panel=createImportPanel(parent);\n var isOpen = panel.style.display=="block";\n if(config.options.chkAnimate)\n anim.startAnimating(new Slider(panel,!isOpen,e.shiftKey || e.altKey,"none"));\n else\n panel.style.display = isOpen ? "none" : "block" ;\n e.cancelBubble = true;\n if (e.stopPropagation) e.stopPropagation();\n return(false);\n}\n//}}}\n\n// // Create control panel: HTML, CSS, register for notification\n//{{{\nfunction createImportPanel(place) {\n var panel=document.getElementById("importPanel");\n if (panel) { panel.parentNode.removeChild(panel); }\n setStylesheet(config.macros.importTiddlers.css,"importTiddlers");\n panel=createTiddlyElement(place,"span","importPanel",null,null)\n panel.innerHTML=config.macros.importTiddlers.html;\n store.addNotification(null,refreshImportList); // refresh listbox after every tiddler change\n refreshImportList();\n var siteURL=store.getTiddlerText("SiteUrl"); if (!siteURL) siteURL="";\n document.getElementById("importSourceURL").value=siteURL;\n config.macros.importTiddlers.src=siteURL;\n var siteProxy=store.getTiddlerText("SiteProxy"); if (!siteProxy) siteProxy="SiteProxy";\n document.getElementById("importSiteProxy").value=siteProxy;\n config.macros.importTiddlers.proxy=siteProxy;\n return panel;\n}\n//}}}\n\n// // CSS\n//{{{\nconfig.macros.importTiddlers.css = '\s\n#importPanel {\s\n display: none; position:absolute; z-index:11; width:35em; right:105%; top:3em;\s\n background-color: #eee; color:#000; font-size: 8pt; line-height:110%;\s\n border:1px solid black; border-bottom-width: 3px; border-right-width: 3px;\s\n padding: 0.5em; margin:0em; -moz-border-radius:1em;\s\n}\s\n#importPanel a, #importPanel td a { color:#009; display:inline; margin:0px; padding:1px; }\s\n#importPanel table { width:100%; border:0px; padding:0px; margin:0px; font-size:8pt; line-height:110%; background:transparent; }\s\n#importPanel tr { border:0px;padding:0px;margin:0px; background:transparent; }\s\n#importPanel td { color:#000; border:0px;padding:0px;margin:0px; background:transparent; }\s\n#importPanel select { width:98%;margin:0px;font-size:8pt;line-height:110%;}\s\n#importPanel input { width:98%;padding:0px;margin:0px;font-size:8pt;line-height:110%}\s\n#importPanel .box { border:1px solid black; padding:3px; margin-bottom:5px; background:#f8f8f8; -moz-border-radius:5px;}\s\n#importPanel .topline { border-top:2px solid black; padding-top:3px; margin-bottom:5px; }\s\n#importPanel .rad { width:auto; }\s\n#importPanel .chk { width:auto; margin:1px; }\s\n#importPanel .btn { width:auto; }\s\n#importPanel .btn1 { width:98%; }\s\n#importPanel .btn2 { width:48%; }\s\n#importPanel .btn3 { width:32%; }\s\n#importPanel .btn4 { width:24%; }\s\n#importPanel .btn5 { width:19%; }\s\n#importPanel .importButton { padding: 0em; margin: 0px; font-size:8pt; }\s\n#importPanel .importListButton { padding:0em 0.25em 0em 0.25em; color: #000000; display:inline }\s\n#importCollisionPanel { display:none; margin:0.5em 0em 0em 0em; }\s\n';\n//}}}\n\n// // HTML \n//{{{\nconfig.macros.importTiddlers.html = '\s\n<!-- source and report -->\s\n<table><tr><td align=left>\s\n import from\s\n <input type="radio" class="rad" name="importFrom" value="file" CHECKED\s\n onClick="document.getElementById(\s'importLocalPanel\s').style.display=this.checked?\s'block\s':\s'none\s';\s\n document.getElementById(\s'importHTTPPanel\s').style.display=!this.checked?\s'block\s':\s'none\s'"> local file\s\n <input type="radio" class="rad" name="importFrom" value="http"\s\n onClick="document.getElementById(\s'importLocalPanel\s').style.display=!this.checked?\s'block\s':\s'none\s';\s\n document.getElementById(\s'importHTTPPanel\s').style.display=this.checked?\s'block\s':\s'none\s'"> web server\s\n</td><td align=right>\s\n <input type=checkbox class="chk" id="chkImportReport" checked\s\n onClick="config.options[\s'chkImportReport\s']=this.checked;"> create a report\s\n</td></tr></table>\s\n<!-- import from local file -->\s\n<div id="importLocalPanel" style="display:block;margin-bottom:5px;margin-top:5px;padding-top:3px;border-top:1px solid #999">\s\nlocal document path/filename:<br>\s\n<input type="file" id="fileImportSource" size=57 style="width:100%"\s\n onKeyUp="config.macros.importTiddlers.src=this.value"\s\n onChange="config.macros.importTiddlers.src=this.value;">\s\n</div><!--panel-->\s\n\s\n<!-- import from http server -->\s\n<div id="importHTTPPanel" style="display:none;margin-bottom:5px;margin-top:5px;padding-top:3px;border-top:1px solid #999">\s\n<table><tr><td align=left>\s\n remote document URL:<br>\s\n</td><td align=right>\s\n <input type="checkbox" class="chk" id="importUseProxy"\s\n onClick="config.macros.importTiddlers.useProxy=this.checked;\s\n document.getElementById(\s'importSiteProxy\s').style.display=this.checked?\s'block\s':\s'none\s'"> use a proxy script\s\n</td></tr></table>\s\n<input type="text" id="importSiteProxy" style="display:none;margin-bottom:1px" onfocus="this.select()" value="SiteProxy"\s\n onKeyUp="config.macros.importTiddlers.proxy=this.value"\s\n onChange="config.macros.importTiddlers.proxy=this.value;">\s\n<input type="text" id="importSourceURL" onfocus="this.select()" value="SiteUrl"\s\n onKeyUp="config.macros.importTiddlers.src=this.value"\s\n onChange="config.macros.importTiddlers.src=this.value;">\s\n</div><!--panel-->\s\n\s\n<table><tr><td align=left>\s\n select:\s\n <a href="JavaScript:;" id="importSelectAll"\s\n onclick="onClickImportButton(this)" title="select all tiddlers">\s\n &nbsp;all&nbsp;</a>\s\n <a href="JavaScript:;" id="importSelectNew"\s\n onclick="onClickImportButton(this)" title="select tiddlers not already in destination document">\s\n &nbsp;added&nbsp;</a> \s\n <a href="JavaScript:;" id="importSelectChanges"\s\n onclick="onClickImportButton(this)" title="select tiddlers that have been updated in source document">\s\n &nbsp;changes&nbsp;</a> \s\n <a href="JavaScript:;" id="importSelectDifferences"\s\n onclick="onClickImportButton(this)" title="select tiddlers that have been added or are different from existing tiddlers">\s\n &nbsp;differences&nbsp;</a> \s\n <a href="JavaScript:;" id="importToggleFilter"\s\n onclick="onClickImportButton(this)" title="show/hide selection filter">\s\n &nbsp;filter&nbsp;</a> \s\n</td><td align=right>\s\n <a href="JavaScript:;" id="importListSmaller"\s\n onclick="onClickImportButton(this)" title="reduce list size">\s\n &nbsp;&#150;&nbsp;</a>\s\n <a href="JavaScript:;" id="importListLarger"\s\n onclick="onClickImportButton(this)" title="increase list size">\s\n &nbsp;+&nbsp;</a>\s\n <a href="JavaScript:;" id="importListMaximize"\s\n onclick="onClickImportButton(this)" title="maximize/restore list size">\s\n &nbsp;=&nbsp;</a>\s\n</td></tr></table>\s\n<select id="importList" size=8 multiple\s\n onchange="setTimeout(\s'refreshImportList(\s'+this.selectedIndex+\s')\s',1)">\s\n <!-- NOTE: delay refresh so list is updated AFTER onchange event is handled -->\s\n</select>\s\n<input type=checkbox class="chk" id="chkAddTags" checked\s\n onClick="config.macros.importTiddlers.addTags=this.checked;">add new tags &nbsp;\s\n<input type=checkbox class="chk" id="chkImportTags" checked\s\n onClick="config.macros.importTiddlers.importTags=this.checked;">import source tags &nbsp;\s\n<input type=checkbox class="chk" id="chkKeepTags" checked\s\n onClick="config.macros.importTiddlers.keepTags=this.checked;">keep existing tags<br>\s\n<input type=text id="txtNewTags" size=15 onKeyUp="config.macros.importTiddlers.newTags=this.value" autocomplete=off>\s\n<div align=center>\s\n <input type=button id="importOpen" class="importButton" style="width:32%" value="open"\s\n onclick="onClickImportButton(this)">\s\n <input type=button id="importStart" class="importButton" style="width:32%" value="import"\s\n onclick="onClickImportButton(this)">\s\n <input type=button id="importClose" class="importButton" style="width:32%" value="close"\s\n onclick="onClickImportButton(this)">\s\n</div>\s\n<div id="importCollisionPanel">\s\n tiddler already exists:\s\n <input type=text id="importNewTitle" size=15 autocomplete=off">\s\n <div align=center>\s\n <input type=button id="importSkip" class="importButton" style="width:23%" value="skip"\s\n onclick="onClickImportButton(this)">\s\n <input type=button id="importRename" class="importButton" style="width:23%" value="rename"\s\n onclick="onClickImportButton(this)">\s\n <input type=button id="importMerge" class="importButton" style="width:23%" value="merge"\s\n onclick="onClickImportButton(this)">\s\n <input type=button id="importReplace" class="importButton" style="width:23%" value="replace"\s\n onclick="onClickImportButton(this)">\s\n </div>\s\n</div>\s\n';\n//}}}\n\n// // Control interactions\n//{{{\nfunction onClickImportButton(which)\n{\n // DEBUG alert(which.id);\n var theList = document.getElementById('importList');\n if (!theList) return;\n var thePanel = document.getElementById('importPanel');\n var theCollisionPanel = document.getElementById('importCollisionPanel');\n var theNewTitle = document.getElementById('importNewTitle');\n var count=0;\n switch (which.id)\n {\n case 'fileImportSource':\n case 'importOpen': // load import source into hidden frame\n importReport(); // if an import was in progress, generate a report\n config.macros.importTiddlers.inbound=null; // clear the imported tiddler buffer\n refreshImportList(); // reset/resize the listbox\n if (config.macros.importTiddlers.src=="") break;\n // Load document into hidden iframe so we can read it's DOM and fill the list\n loadTiddlers(config.macros.importTiddlers.src,"all",false,false,\n function(src,tiddlers){config.macros.importTiddlers.inbound=tiddlers;window.refreshImportList(0);});\n break;\n case 'importSelectAll': // select all tiddler list items (i.e., not headings)\n importReport(); // if an import was in progress, generate a report\n for (var t=0,count=0; t < theList.options.length; t++) {\n if (theList.options[t].value=="") continue;\n theList.options[t].selected=true;\n count++;\n }\n clearMessage(); displayMessage(config.macros.importTiddlers.countMsg.format([count]));\n break;\n case 'importSelectNew': // select tiddlers not in current document\n importReport(); // if an import was in progress, generate a report\n for (var t=0,count=0; t < theList.options.length; t++) {\n theList.options[t].selected=false;\n if (theList.options[t].value=="") continue;\n theList.options[t].selected=!store.tiddlerExists(theList.options[t].value);\n count+=theList.options[t].selected?1:0;\n }\n clearMessage(); displayMessage(config.macros.importTiddlers.countMsg.format([count]));\n break;\n case 'importSelectChanges': // select tiddlers that are updated from existing tiddlers\n importReport(); // if an import was in progress, generate a report\n for (var t=0,count=0; t < theList.options.length; t++) {\n theList.options[t].selected=false;\n if (theList.options[t].value==""||!store.tiddlerExists(theList.options[t].value)) continue;\n for (var i=0; i<config.macros.importTiddlers.inbound.length; i++) // find matching inbound tiddler\n { var inbound=config.macros.importTiddlers.inbound[i]; if (inbound.title==theList.options[t].value) break; }\n theList.options[t].selected=(inbound.modified-store.getTiddler(theList.options[t].value).modified>0); // updated tiddler\n count+=theList.options[t].selected?1:0;\n }\n clearMessage(); displayMessage(config.macros.importTiddlers.countMsg.format([count]));\n break;\n case 'importSelectDifferences': // select tiddlers that are new or different from existing tiddlers\n importReport(); // if an import was in progress, generate a report\n for (var t=0,count=0; t < theList.options.length; t++) {\n theList.options[t].selected=false;\n if (theList.options[t].value=="") continue;\n if (!store.tiddlerExists(theList.options[t].value)) { theList.options[t].selected=true; count++; continue; }\n for (var i=0; i<config.macros.importTiddlers.inbound.length; i++) // find matching inbound tiddler\n { var inbound=config.macros.importTiddlers.inbound[i]; if (inbound.title==theList.options[t].value) break; }\n theList.options[t].selected=(inbound.modified-store.getTiddler(theList.options[t].value).modified!=0); // changed tiddler\n count+=theList.options[t].selected?1:0;\n }\n clearMessage(); displayMessage(config.macros.importTiddlers.countMsg.format([count]));\n break;\n case 'importToggleFilter': // show/hide filter\n case 'importFilter': // apply filter\n alert("coming soon!");\n break;\n case 'importStart': // initiate the import processing\n importReport(); // if an import was in progress, generate a report\n config.macros.importTiddlers.index=0;\n config.macros.importTiddlers.index=importTiddlers(0);\n importStopped();\n break;\n case 'importClose': // unload imported tiddlers or hide the import control panel\n // if imported tiddlers not loaded, close the import control panel\n if (!config.macros.importTiddlers.inbound) { thePanel.style.display='none'; break; }\n importReport(); // if an import was in progress, generate a report\n config.macros.importTiddlers.inbound=null; // clear the imported tiddler buffer\n refreshImportList(); // reset/resize the listbox\n break;\n case 'importSkip': // don't import the tiddler\n var theItem = theList.options[config.macros.importTiddlers.index];\n for (var j=0;j<config.macros.importTiddlers.inbound.length;j++)\n if (config.macros.importTiddlers.inbound[j].title==theItem.value) break;\n var theImported = config.macros.importTiddlers.inbound[j];\n theImported.status='skipped after asking'; // mark item as skipped\n theCollisionPanel.style.display='none';\n config.macros.importTiddlers.index=importTiddlers(config.macros.importTiddlers.index+1); // resume with NEXT item\n importStopped();\n break;\n case 'importRename': // change name of imported tiddler\n var theItem = theList.options[config.macros.importTiddlers.index];\n for (var j=0;j<config.macros.importTiddlers.inbound.length;j++)\n if (config.macros.importTiddlers.inbound[j].title==theItem.value) break;\n var theImported = config.macros.importTiddlers.inbound[j];\n theImported.status = 'renamed from '+theImported.title; // mark item as renamed\n theImported.set(theNewTitle.value,null,null,null,null); // change the tiddler title\n theItem.value = theNewTitle.value; // change the listbox item text\n theItem.text = theNewTitle.value; // change the listbox item text\n theCollisionPanel.style.display='none';\n config.macros.importTiddlers.index=importTiddlers(config.macros.importTiddlers.index); // resume with THIS item\n importStopped();\n break;\n case 'importMerge': // join existing and imported tiddler content\n var theItem = theList.options[config.macros.importTiddlers.index];\n for (var j=0;j<config.macros.importTiddlers.inbound.length;j++)\n if (config.macros.importTiddlers.inbound[j].title==theItem.value) break;\n var theImported = config.macros.importTiddlers.inbound[j];\n var theExisting = store.getTiddler(theItem.value);\n var theText = theExisting.text+'\sn----\sn^^merged from: ';\n theText +='[['+config.macros.importTiddlers.src+'#'+theItem.value+'|'+config.macros.importTiddlers.src+'#'+theItem.value+']]^^\sn';\n theText +='^^'+theImported.modified.toLocaleString()+' by '+theImported.modifier+'^^\sn'+theImported.text;\n var theDate = new Date();\n var theTags = theExisting.getTags()+' '+theImported.getTags();\n theImported.set(null,theText,null,theDate,theTags);\n theImported.status = 'merged with '+theExisting.title; // mark item as merged\n theImported.status += ' - '+theExisting.modified.formatString("MM/DD/YYYY 0hh:0mm:0ss");\n theImported.status += ' by '+theExisting.modifier;\n theCollisionPanel.style.display='none';\n config.macros.importTiddlers.index=importTiddlers(config.macros.importTiddlers.index); // resume with this item\n importStopped();\n break;\n case 'importReplace': // substitute imported tiddler for existing tiddler\n var theItem = theList.options[config.macros.importTiddlers.index];\n for (var j=0;j<config.macros.importTiddlers.inbound.length;j++)\n if (config.macros.importTiddlers.inbound[j].title==theItem.value) break;\n var theImported = config.macros.importTiddlers.inbound[j];\n var theExisting = store.getTiddler(theItem.value);\n theImported.status = 'replaces '+theExisting.title; // mark item for replace\n theImported.status += ' - '+theExisting.modified.formatString("MM/DD/YYYY 0hh:0mm:0ss");\n theImported.status += ' by '+theExisting.modifier;\n theCollisionPanel.style.display='none';\n config.macros.importTiddlers.index=importTiddlers(config.macros.importTiddlers.index); // resume with THIS item\n importStopped();\n break;\n case 'importListSmaller': // decrease current listbox size, minimum=5\n if (theList.options.length==1) break;\n theList.size-=(theList.size>5)?1:0;\n config.macros.importTiddlers.listsize=theList.size;\n break;\n case 'importListLarger': // increase current listbox size, maximum=number of items in list\n if (theList.options.length==1) break;\n theList.size+=(theList.size<theList.options.length)?1:0;\n config.macros.importTiddlers.listsize=theList.size;\n break;\n case 'importListMaximize': // toggle listbox size between current and maximum\n if (theList.options.length==1) break;\n theList.size=(theList.size==theList.options.length)?config.macros.importTiddlers.listsize:theList.options.length;\n break;\n }\n}\n//}}}\n\n// // refresh listbox\n//{{{\nfunction refreshImportList(selectedIndex)\n{\n var theList = document.getElementById("importList");\n if (!theList) return;\n // if nothing to show, reset list content and size\n if (!config.macros.importTiddlers.inbound) \n {\n while (theList.length > 0) { theList.options[0] = null; }\n theList.options[0]=new Option('please open a document...',"",false,false);\n theList.size=config.macros.importTiddlers.listsize;\n return;\n }\n // get the sort order\n if (!selectedIndex) selectedIndex=0;\n if (selectedIndex==0) config.macros.importTiddlers.sort='title'; // heading\n if (selectedIndex==1) config.macros.importTiddlers.sort='title';\n if (selectedIndex==2) config.macros.importTiddlers.sort='modified';\n if (selectedIndex==3) config.macros.importTiddlers.sort='tags';\n if (selectedIndex>3) {\n // display selected tiddler count\n for (var t=0,count=0; t < theList.options.length; t++) count+=(theList.options[t].selected&&theList.options[t].value!="")?1:0;\n clearMessage(); displayMessage(config.macros.importTiddlers.countMsg.format([count]));\n return; // no refresh needed\n }\n\n // get the alphasorted list of tiddlers (optionally, filter out unchanged tiddlers)\n var tiddlers=config.macros.importTiddlers.inbound;\n tiddlers.sort(function (a,b) {if(a['title'] == b['title']) return(0); else return (a['title'] < b['title']) ? -1 : +1; });\n // clear current list contents\n while (theList.length > 0) { theList.options[0] = null; }\n // add heading and control items to list\n var i=0;\n var indent=String.fromCharCode(160)+String.fromCharCode(160);\n theList.options[i++]=new Option(tiddlers.length+' tiddler'+((tiddlers.length!=1)?'s are':' is')+' in the document',"",false,false);\n theList.options[i++]=new Option(((config.macros.importTiddlers.sort=="title" )?">":indent)+' [by title]',"",false,false);\n theList.options[i++]=new Option(((config.macros.importTiddlers.sort=="modified")?">":indent)+' [by date]',"",false,false);\n theList.options[i++]=new Option(((config.macros.importTiddlers.sort=="tags")?">":indent)+' [by tags]',"",false,false);\n // output the tiddler list\n switch(config.macros.importTiddlers.sort)\n {\n case "title":\n for(var t = 0; t < tiddlers.length; t++)\n theList.options[i++] = new Option(tiddlers[t].title,tiddlers[t].title,false,false);\n break;\n case "modified":\n // sort descending for newest date first\n tiddlers.sort(function (a,b) {if(a['modified'] == b['modified']) return(0); else return (a['modified'] > b['modified']) ? -1 : +1; });\n var lastSection = "";\n for(var t = 0; t < tiddlers.length; t++) {\n var tiddler = tiddlers[t];\n var theSection = tiddler.modified.toLocaleDateString();\n if (theSection != lastSection) {\n theList.options[i++] = new Option(theSection,"",false,false);\n lastSection = theSection;\n }\n theList.options[i++] = new Option(indent+indent+tiddler.title,tiddler.title,false,false);\n }\n break;\n case "tags":\n var theTitles = {}; // all tiddler titles, hash indexed by tag value\n var theTags = new Array();\n for(var t=0; t<tiddlers.length; t++) {\n var title=tiddlers[t].title;\n var tags=tiddlers[t].tags;\n if (!tags || !tags.length) {\n if (theTitles["untagged"]==undefined) { theTags.push("untagged"); theTitles["untagged"]=new Array(); }\n theTitles["untagged"].push(title);\n }\n else for(var s=0; s<tags.length; s++) {\n if (theTitles[tags[s]]==undefined) { theTags.push(tags[s]); theTitles[tags[s]]=new Array(); }\n theTitles[tags[s]].push(title);\n }\n }\n theTags.sort();\n for(var tagindex=0; tagindex<theTags.length; tagindex++) {\n var theTag=theTags[tagindex];\n theList.options[i++]=new Option(theTag,"",false,false);\n for(var t=0; t<theTitles[theTag].length; t++)\n theList.options[i++]=new Option(indent+indent+theTitles[theTag][t],theTitles[theTag][t],false,false);\n }\n break;\n }\n theList.selectedIndex=selectedIndex; // select current control item\n if (theList.size<config.macros.importTiddlers.listsize) theList.size=config.macros.importTiddlers.listsize;\n if (theList.size>theList.options.length) theList.size=theList.options.length;\n}\n//}}}\n\n// // re-entrant processing for handling import with interactive collision prompting\n//{{{\nfunction importTiddlers(startIndex)\n{\n if (!config.macros.importTiddlers.inbound) return -1;\n\n var theList = document.getElementById('importList');\n if (!theList) return;\n var t;\n // if starting new import, reset import status flags\n if (startIndex==0)\n for (var t=0;t<config.macros.importTiddlers.inbound.length;t++)\n config.macros.importTiddlers.inbound[t].status="";\n for (var i=startIndex; i<theList.options.length; i++)\n {\n // if list item is not selected or is a heading (i.e., has no value), skip it\n if ((!theList.options[i].selected) || ((t=theList.options[i].value)==""))\n continue;\n for (var j=0;j<config.macros.importTiddlers.inbound.length;j++)\n if (config.macros.importTiddlers.inbound[j].title==t) break;\n var theImported = config.macros.importTiddlers.inbound[j];\n var theExisting = store.getTiddler(theImported.title);\n // avoid redundant import for tiddlers that are listed multiple times (when 'by tags')\n if (theImported.status=="added")\n continue;\n // don't import the "ImportedTiddlers" history from the other document...\n if (theImported.title=='ImportedTiddlers')\n continue;\n // if tiddler exists and import not marked for replace or merge, stop importing\n if (theExisting && (theImported.status.substr(0,7)!="replace") && (theImported.status.substr(0,5)!="merge"))\n return i;\n // assemble tags (remote + existing + added)\n var newTags = "";\n if (config.macros.importTiddlers.importTags)\n newTags+=theImported.getTags() // import remote tags\n if (config.macros.importTiddlers.keepTags && theExisting)\n newTags+=" "+theExisting.getTags(); // keep existing tags\n if (config.macros.importTiddlers.addTags && config.macros.importTiddlers.newTags.trim().length)\n newTags+=" "+config.macros.importTiddlers.newTags; // add new tags\n theImported.set(null,null,null,null,newTags.trim());\n // set the status to 'added' (if not already set by the 'ask the user' UI)\n theImported.status=(theImported.status=="")?'added':theImported.status;\n // do the import!\n store.addTiddler(theImported);\n store.setDirty(true);\n }\n return(-1); // signals that we really finished the entire list\n}\n//}}}\n\n//{{{\nfunction importStopped()\n{\n var theList = document.getElementById('importList');\n var theNewTitle = document.getElementById('importNewTitle');\n if (!theList) return;\n if (config.macros.importTiddlers.index==-1)\n importReport(); // import finished... generate the report\n else\n {\n // DEBUG alert('import stopped at: '+config.macros.importTiddlers.index);\n // import collision... show the collision panel and set the title edit field\n document.getElementById('importCollisionPanel').style.display='block';\n theNewTitle.value=theList.options[config.macros.importTiddlers.index].value;\n }\n}\n//}}}\n\n// // ''REPORT GENERATOR''\n//{{{\nfunction importReport(quiet)\n{\n if (!config.macros.importTiddlers.inbound) return;\n // DEBUG alert('importReport: start');\n\n // if import was not completed, the collision panel will still be open... close it now.\n var panel=document.getElementById('importCollisionPanel'); if (panel) panel.style.display='none';\n\n // get the alphasorted list of tiddlers\n var tiddlers = config.macros.importTiddlers.inbound;\n // gather the statistics\n var count=0;\n for (var t=0; t<tiddlers.length; t++)\n if (tiddlers[t].status && tiddlers[t].status.trim().length && tiddlers[t].status.substr(0,7)!="skipped") count++;\n\n // generate a report\n if (count && config.options.chkImportReport) {\n // get/create the report tiddler\n var theReport = store.getTiddler('ImportedTiddlers');\n if (!theReport) { theReport= new Tiddler(); theReport.title = 'ImportedTiddlers'; theReport.text = ""; }\n // format the report content\n var now = new Date();\n var newText = "On "+now.toLocaleString()+", "+config.options.txtUserName\n newText +=" loaded "+count+" tiddlers from\sn[["+config.macros.importTiddlers.src+"|"+config.macros.importTiddlers.src+"]]:\sn";\n if (config.macros.importTiddlers.addTags && config.macros.importTiddlers.newTags.trim().length)\n newText += "imported tiddlers were tagged with: \s""+config.macros.importTiddlers.newTags+"\s"\sn";\n newText += "<<<\sn";\n for (var t=0; t<tiddlers.length; t++) if (tiddlers[t].status) newText += "#[["+tiddlers[t].title+"]] - "+tiddlers[t].status+"\sn";\n newText += "<<<\sn";\n newText += "<html><input type=\s"button\s" href=\s"javascript:;\s" ";\n newText += "onclick=\s"story.closeTiddler('"+theReport.title+"'); store.deleteTiddler('"+theReport.title+"');\s" ";\n newText += "value=\s"discard report\s"></html>";\n // update the ImportedTiddlers content and show the tiddler\n theReport.text = newText+((theReport.text!="")?'\sn----\sn':"")+theReport.text;\n theReport.modifier = config.options.txtUserName;\n theReport.modified = new Date();\n store.addTiddler(theReport);\n if (!quiet) { story.displayTiddler(null,theReport.title,1,null,null,false); story.refreshTiddler(theReport.title,1,true); }\n }\n\n // reset status flags\n for (var t=0; t<config.macros.importTiddlers.inbound.length; t++) config.macros.importTiddlers.inbound[t].status="";\n\n // refresh display if tiddlers have been loaded\n if (count) { store.setDirty(true); store.notifyAll(); }\n\n // always show final message when tiddlers were actually loaded\n if (count) displayMessage("Imported "+count+" of "+tiddlers.length+" tiddlers from "+config.macros.importTiddlers.src);\n}\n//}}}\n\n/***\n!!!!!TW 2.1beta Core Code Candidate\n//The following section is a preliminary 'code candidate' for incorporation of non-interactive 'load tiddlers' functionality into TW2.1beta. //\n***/\n//{{{\n// default cookie/option values\nif (!config.options.chkImportReport) config.options.chkImportReport=true;\n\nconfig.macros.loadTiddlers = { label:"", prompt:"add/update tiddlers from '%0'" };\nconfig.macros.loadTiddlers.handler = function(place,macroName,params) {\n var label=(params[0].substr(0,6)=='label:')?params.shift().substr(6):this.label;\n var prompt=(params[0].substr(0,7)=='prompt:')?params.shift().substr(6):this.prompt;\n var filter="updates";\n if (params[0]=='all' || params[0]=='new' || params[0]=='changes' || params[0]=='updates'\n || params[0].substr(0,8)=='tiddler:' || params[0].substr(0,4)=='tag:')\n filter=params.shift();\n var src=params.shift(); if (!src || !src.length) return; // filename is required\n var quiet=(params[0]=="quiet"); if (quiet) params.shift();\n var ask=(params[0]=="confirm"); if (ask) params.shift();\n if (label.trim().length) {\n // link triggers load tiddlers from another file/URL and then applies filtering rules to add/replace tiddlers in the store\n createTiddlyButton(place,label.format([src]),prompt.format([src]),function(){loadTiddlers(src,filter,quiet,ask,copyTiddlersToStore);})\n }\n else {\n // load tiddlers from another file/URL and then apply filtering rules to add/replace tiddlers in the store\n loadTiddlers(src,filter,quiet,ask,copyTiddlersToStore);\n }\n}\n\nfunction copyTiddlersToStore(src,tiddlers,filter,quiet,ask)\n{\n var count=0;\n if (tiddlers) for (var t=0;t<tiddlers.length;t++) {\n var theInbound = tiddlers[t];\n var theExisting = store.getTiddler(theInbound.title);\n if (theInbound.title=='ImportedTiddlers')\n continue; // skip "ImportedTiddlers" history from the other document...\n\n // apply the all/new/changes/updates filter (if any)\n if (filter && filter!="all") {\n if ((filter=="new") && theExisting) // skip existing tiddlers\n { tiddlers[t].status="skipped - tiddler already exists"; continue; }\n if ((filter=="changes") && !theExisting) // skip new tiddlers\n { tiddlers[t].status="skipped - no existing tiddler"; continue; }\n if ((filter.substr(0,4)=="tag:") && theInbound.tags.find(filter.substr(4))==null) // must match specific tag value\n { tiddlers[t].status="skipped - does not match tag"; continue; }\n if ((filter.substr(0,8)=="tiddler:") && theInbound.title!=filter.substr(8)) // must match specific tiddler name\n { continue; } // no status means don't show in report\n if (store.tiddlerExists(theInbound.title) && ((theExisting.modified.getTime()-theInbound.modified.getTime())>=0)) // tiddler is unchanged\n { tiddlers[t].status="skipped - tiddler is unchanged"; continue; }\n }\n // get confirmation if required\n if (ask && !confirm((theExisting?"Update":"Add")+" tiddler '"+theInbound.title+"'\snfrom "+src))\n { tiddlers[t].status="skipped - cancelled by user"; continue; }\n // DO IT!\n store.addTiddler(theInbound);\n tiddlers[t].status=theExisting?"updated":"added"\n count++;\n }\n\n // generate a report\n if (count && config.options.chkImportReport) {\n // get/create the report tiddler\n var theReport = store.getTiddler('ImportedTiddlers');\n if (!theReport) { theReport= new Tiddler(); theReport.title = 'ImportedTiddlers'; theReport.text = ""; }\n // format the report content\n var now = new Date();\n var newText = "On "+now.toLocaleString()+", "+config.options.txtUserName+" loaded "+count+" tiddlers from\sn[["+src+"|"+src+"]]:\sn";\n newText += "<<<\sn";\n for (var t=0; t<tiddlers.length; t++) if (tiddlers[t].status) newText += "#[["+tiddlers[t].title+"]] - "+tiddlers[t].status+"\sn";\n newText += "<<<\sn";\n newText += "<html><input type=\s"button\s" href=\s"javascript:;\s" ";\n newText += "onclick=\s"story.closeTiddler('"+theReport.title+"'); store.deleteTiddler('"+theReport.title+"');\s" ";\n newText += "value=\s"discard report\s"></html>";\n // update the ImportedTiddlers content and show the tiddler\n theReport.text = newText+((theReport.text!="")?'\sn----\sn':"")+theReport.text;\n theReport.modifier = config.options.txtUserName;\n theReport.modified = new Date();\n store.addTiddler(theReport);\n if (!quiet) { story.displayTiddler(null,theReport.title,1,null,null,false); story.refreshTiddler(theReport.title,1,true); }\n }\n\n // refresh display if tiddlers have been loaded\n if (count) { store.setDirty(true); store.notifyAll(); }\n\n // always show final message when tiddlers were actually loaded\n if (!quiet||count) displayMessage("Loaded "+count+" of "+tiddlers.length+" tiddlers from "+src);\n}\n\nfunction loadTiddlers(src,filter,quiet,ask,callback) {\n if (!quiet) clearMessage();\n if (src=="ask") src=prompt("Enter a local path/filename or a remote URL");\n if (!src || !src.length) return; // filename is required\n if (src.substr(0,4)!="http") {\n if (!quiet) displayMessage("Opening local document: "+ src);\n var txt=loadFile(src);\n if(!txt) { if (!quiet) displayMessage("Could not open local document: "+src); }\n else {\n var start=txt.indexOf('<div id="storeArea">');\n var end=txt.indexOf('</body>');\n var sa="<html><body>"+txt.substring(start,end)+"</body></html>";\n if (!quiet) displayMessage(txt.length+" bytes in document. ("+sa.length+" bytes used for tiddler storage)");\n var tiddlers = getTiddlersFromHTML(convertUTF8ToUnicode(sa));\n var count=tiddlers?tiddlers.length:0;\n if (!quiet) displayMessage("Found "+count+" tiddlers in "+src);\n if (callback) callback(src,tiddlers,filter,quiet,ask);\n }\n }\n else {\n if (!quiet) displayMessage("Opening remote document: "+ src);\n var x; // XML object\n try {x = new XMLHttpRequest()}\n catch(e) {\n try {x = new ActiveXObject("Msxml2.XMLHTTP")}\n catch (e) {\n try {x = new ActiveXObject("Microsoft.XMLHTTP")}\n catch (e) { return }\n }\n }\n x.onreadystatechange = function() {\n if (x.readyState == 4) {\n if (x.status == 200) {\n var start=x.responseText.indexOf('<div id="storeArea">');\n var end=x.responseText.indexOf('</body>',start);\n var sa="<html><body>"+x.responseText.substring(start,end)+"</body></html>";\n if (!quiet) displayMessage(x.responseText.length+" bytes in document. ("+sa.length+" bytes used for tiddler storage)");\n var tiddlers = getTiddlersFromHTML(sa);\n var count=tiddlers?tiddlers.length:0;\n if (!quiet) displayMessage("Found "+count+" tiddlers in "+src);\n if (callback) callback(src,tiddlers,filter,quiet,ask);\n }\n else\n if (!quiet) displayMessage("Could not open remote document:"+ src+" (error="+x.status+")");\n }\n }\n if ((document.location.protocol=="file:") && (typeof(netscape)!="undefined")) { // UniversalBrowserRead only works from a local file context\n try { netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserRead')}\n catch (e) { if (!quiet) displayMessage(e.description?e.description:e.toString()); }\n }\n try {\n var url=src+(src.indexOf('?')<0?'?':'&')+'nocache='+Math.random();\n x.open("GET",url,true);\n if (x.overrideMimeType) x.overrideMimeType('text/html');\n x.send(null);\n }\n catch (e) {\n if (!quiet) {\n displayMessage("Could not open remote document: "+src);\n displayMessage(e.description?e.description:e.toString());\n }\n }\n }\n}\n\nfunction getTiddlersFromHTML(html)\n{\n // load html into iframe document\n var f=document.getElementById("loaderFrame"); if (f) document.body.removeChild(f);\n f=document.createElement("iframe"); f.id="loaderFrame";\n f.style.width="0px"; f.style.height="0px"; f.style.border="0px";\n document.body.appendChild(f);\n var d=f.document;\n if (f.contentDocument) d=f.contentDocument; // For NS6\n else if (f.contentWindow) d=f.contentWindow.document; // For IE5.5 and IE6\n d.open(); d.writeln(html); d.close();\n\n // read tiddler DIVs from storeArea DOM element \n var sa = d.getElementById("storeArea");\n if (!sa) return null;\n sa.normalize();\n var nodes = sa.childNodes;\n if (!nodes || !nodes.length) return null;\n var tiddlers = [];\n for(var t = 0; t < nodes.length; t++) {\n var title = null;\n if(nodes[t].getAttribute)\n title = nodes[t].getAttribute("tiddler");\n if(!title && nodes[t].id && (nodes[t].id.substr(0,5) == "store"))\n title = nodes[t].id.substr(5);\n if(title && title != "")\n tiddlers.push((new Tiddler()).loadFromDiv(nodes[t],title));\n }\n return tiddlers;\n}\n//}}}
~LabWiki is [[my|LukeBlanshard]] laboratory for ~TiddlyWiki [[plugins|Plugin]]. (LAB is also my initials.)\n\n~LabWiki is currently running against ~TiddlyWiki version <<version>>.\n\nThe official release page for ~LabWiki is [[here|http://sourceforge.net/project/showfiles.php?group_id=163155]]. ~SourceForge would prefer that you download everything from there, but I don't really care -- I personally find it a lot easier to download from the web pages.\n\n
I am a software developer living in Chicago. I've been using TiddlyWiki since spring of 2005.
[[Home|LabWiki]]\n<<tag Plugin>>
/***\n|Name|NewHereCommand|\n|Source|http://simonbaird.com/mptw/#NewHereCommand|\n|Version|1.0|\n\nCode originally by ArphenLin. Small tweak by SimonBaird\nhttp://aiddlywiki.sourceforge.net/NewHere_demo.html#NewHereCommand\nTo use this you must edit your ViewTemplate and add newHere to the toolbar div, eg\n{{{<div class='toolbar' macro='toolbar ... newHere'></div>}}}\n***/\n\n//{{{\n\nconfig.commands.newHere = {\n text: 'new here',\n tooltip: 'Create a new tiddler tagged as this tiddler',\n handler: function(e,src,title) {\n if (!readOnly) {\n clearMessage();\n var t=document.getElementById('tiddler'+title);\n story.displayTiddler(t,config.macros.newTiddler.title,DEFAULT_EDIT_TEMPLATE);\n story.setTiddlerTag(config.macros.newTiddler.title, title, 0);\n story.focusTiddler(config.macros.newTiddler.title,"title");\n return false;\n }\n }\n};\n\n//}}}
<!---\nContains modifications to the base by Simon Baird; see http://simonbaird.com/mptw/#HorizontalMainMenu.\nPortions by Luke Blanshard are hereby released into the public domain.\n--->\n<!--{{{-->\n<div class='header'>\n<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;\n<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>\n<div id='topMenu' refresh='content' tiddler='MainMenu'></div>\n</div>\n<div id='sidebar'>\n<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>\n<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>\n</div>\n<div id='displayArea'>\n<div id='messageArea'></div>\n<div id='tiddlerDisplay'></div>\n</div>\n<!--}}}-->\n
Tiddlers tagged as Plugin are TiddlyWiki extensions I've written.
/***\n| Name:|QuickOpenTagPlugin|\n| Purpose:|Makes tag links into a Taggly style open tag plus a normal style drop down menu|\n| Creator:|SimonBaird|\n| Source:|http://simonbaird.com/mptw/#QuickOpenTagPlugin|\n| Requires:|TW 2.x|\n| Version|1.1 (7-Feb-06)|\n\n!History\n* Version 1.1 (07/02/2006)\n** Fix Firefox 1.5.0.1 crashes\n** Updated by ~BidiX[at]~BidiX.info\n* Version 1.0 (?/01/2006)\n** First release\n\n***/\n//{{{\n\n//⊻ ⊽ ⋁ ▼ \n\nwindow.createTagButton_orig_mptw = createTagButton;\nwindow.createTagButton = function(place,tag,excludeTiddler) {\n var sp = createTiddlyElement(place,"span",null,"quickopentag");\n createTiddlyLink(sp,tag,true,"button");\n var theTag = createTiddlyButton(sp,config.macros.miniTag.dropdownchar,config.views.wikified.tag.tooltip.format([tag]),onClickTag);\n theTag.setAttribute("tag",tag);\n if(excludeTiddler)\n theTag.setAttribute("tiddler",excludeTiddler);\n return(theTag);\n};\n\nconfig.macros.miniTag = {handler:function(place,macroName,params,wikifier,paramString,tiddler) {\n var tagged = store.getTaggedTiddlers(tiddler.title);\n if (tagged.length > 0) {\n var theTag = createTiddlyButton(place,config.macros.miniTag.dropdownchar,config.views.wikified.tag.tooltip.format([tiddler.title]),onClickTag);\n theTag.setAttribute("tag",tiddler.title);\n theTag.className = "miniTag";\n }\n}};\n\nconfig.macros.miniTag.dropdownchar = (document.all?"▼":"▾"); // the fat one is the only one that works in IE\n\nconfig.macros.allTags.handler = function(place,macroName,params)\n{\n var tags = store.getTags();\n var theDateList = createTiddlyElement(place,"ul",null,null,null);\n if(tags.length === 0)\n createTiddlyElement(theDateList,"li",null,"listTitle",this.noTags);\n for (var t=0; t<tags.length; t++)\n {\n var theListItem =createTiddlyElement(theDateList,"li",null,null,null);\n var theLink = createTiddlyLink(theListItem,tags[t][0],true);\n var theCount = " (" + tags[t][1] + ")";\n theLink.appendChild(document.createTextNode(theCount));\n\n var theDropDownBtn = createTiddlyButton(theListItem," "+config.macros.miniTag.dropdownchar,this.tooltip.format([tags[t][0]]),onClickTag);\n theDropDownBtn.setAttribute("tag",tags[t][0]);\n }\n};\n\n\nsetStylesheet(\n ".quickopentag { margin-right:1.2em; border:1px solid #eee; padding:2px; padding-right:0px; padding-left:1px; }\sn"+\n ".quickopentag .tiddlyLink { padding:2px; padding-left:3px; }\sn"+\n ".quickopentag a.button { padding:1px; padding-left:2px; padding-right:2px;}\sn"+\n "a.miniTag {font-size:150%;}\sn"+\n "",\n"QuickOpenTagStyles");\n\n//}}}\n\n/***\n<html>&#x22bb; &#x22bd; &#x22c1; &#x25bc; &#x25be;</html>\n***/\n
<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal 'DD MMM YYYY'>><<saveChanges>><<importTiddlers>><<slider chkSliderOptionsPanel OptionsPanel 'options »' 'Change TiddlyWiki advanced options'>>
/***\nThis CSS by DaveBirss.\n***/\n/*{{{*/\n\n.tabSelected {\n background: #fff;\n}\n\n.tabUnselected {\n background: #eee;\n}\n\n#sidebar {\n color: #000;\n background: transparent; \n}\n\n#sidebarOptions {\n background: #fff;\n}\n\n#sidebarOptions .button {\n color: #999;\n}\n\n#sidebarOptions .button:hover {\n color: #000;\n background: #fff;\n border-color:white;\n}\n\n#sidebarOptions .button:active {\n color: #000;\n background: #fff;\n}\n\n#sidebarOptions .sliderPanel {\n background: transparent;\n}\n\n#sidebarOptions .sliderPanel A {\n color: #999;\n}\n\n#sidebarOptions .sliderPanel A:hover {\n color: #000;\n background: #fff;\n}\n\n#sidebarOptions .sliderPanel A:active {\n color: #000;\n background: #fff;\n}\n\n.sidebarSubHeading {\n color: #000;\n}\n\n#sidebarTabs {\n background: #fff\n}\n\n#sidebarTabs .tabSelected {\n color: #000;\n background: #fff;\n border-top: solid 1px #ccc;\n border-left: solid 1px #ccc;\n border-right: solid 1px #ccc;\n border-bottom: none;\n}\n\n#sidebarTabs .tabUnselected {\n color: #999;\n background: #eee;\n border-top: solid 1px #ccc;\n border-left: solid 1px #ccc;\n border-right: solid 1px #ccc;\n border-bottom: none;\n}\n\n#sidebarTabs .tabContents {\n background: #fff;\n}\n\n\n#sidebarTabs .txtMoreTab .tabSelected {\n background: #fff;\n}\n\n#sidebarTabs .txtMoreTab .tabUnselected {\n background: #eee;\n}\n\n#sidebarTabs .txtMoreTab .tabContents {\n background: #fff;\n}\n\n#sidebarTabs .tabContents .tiddlyLink {\n color: #999;\n}\n\n#sidebarTabs .tabContents .tiddlyLink:hover {\n background: #fff;\n color: #000;\n}\n\n#sidebarTabs .tabContents {\n color: #000;\n}\n\n#sidebarTabs .button {\n color: #666;\n}\n\n#sidebarTabs .tabContents .button:hover {\n color: #000;\n background: #fff;\n}\n\n\n/*}}}*/
LukeBlanshard's plugins for TiddlyWiki
LabWiki
http://labwiki.sourceforge.net/
/***\nMost of this comes from Simon Baird. Portions by Luke Blanshard are hereby released to the public domain.\n\nImportant stuff. See TagglyTaggingStyles and HorizontalMainMenuStyles\n***/\n/*{{{*/\n[[TagglyTaggingStyles]]\n[[HorizontalMainMenuStyles]]\n/*}}}*/\n/***\nChanges to the header background colors\n***/\n/*{{{*/\n.header { background-color: #db8 }\n.header a:hover { color: #178 }\n.siteTitle { margin-left: 10px }\n.header { padding-top: 25px }\n#topMenu { background-color: #bbb }\n/*}}}*/\n/***\nClint's fix for weird IE behavious\n***/\n/*{{{*/\nbody {position:static;}\n.tagClear{margin-top:1em;clear:both;}\n/*}}}*/\n/***\nJust colours, fonts, tweaks etc. See SideBarWhiteAndGrey\n***/\n/*{{{*/\nbody {background:#eee; }\n.headerForeground a { color: #6fc;}\n.headerShadow { left: 2px; top: 2px; }\n.siteSubtitle { padding-left:1.5em; }\n.subtitle { font-size:90%; color:#ccc; padding-left:0.25em; }\nh1,h2,h3,h4,h5 { color: #000; background: transparent; }\n.title {color:black; font-size:2em;}\n.shadow .title {color:#999; }\n.viewer pre { background-color:#f8f8ff; border-color:#ddf}\n.viewer hr { border-top: solid 1px #ccc; }\n#sidebarOptions { border:1px #ccc solid; }\n.tiddler {\n border-bottom:1px solid #ccc; border-right:1px solid #ccc; padding-bottom:1em; margin-bottom:1em; \n background:#fff; padding-right:1.5em; }\n#messageArea { background-color:#bde; border-color:#8ab; border-width:4px; border-style:dotted; font-size:90%; }\n#messageArea .button { text-decoration:none; font-weight:bold; background:transparent; border:0px; }\n#messageArea .button:hover {background: #acd; }\n[[SideBarWhiteAndGrey]]\n/*}}}*/\n
/***\n|Name|TagglyListPlugin|\n|Created by|SimonBaird|\n|Location|http://simonbaird.com/mptw/#TagglyListPlugin|\n|Version|1.1.1 6-Mar-06|\n|Requires|See TagglyTagging|\n\n!History\n* 1.1.1 (6-Mar-2006) fixed bug with refreshAllVisible closing tiddlers being edited. Thanks Luke Blanshard.\n\n***/\n\n/***\n!Setup and config\n***/\n//{{{\n\nversion.extensions.TagglyListPlugin = {\n major: 1, minor: 1, revision: 1,\n date: new Date(2006,3,6),\n source: "http://simonbaird.com/mptw/#TagglyListPlugin"\n};\n\nconfig.macros.tagglyList = {};\nconfig.macros.tagglyListByTag = {};\nconfig.macros.tagglyListControl = {};\nconfig.macros.tagglyListWithSort = {};\nconfig.macros.hideSomeTags = {};\n\n// change this to your preference\nconfig.macros.tagglyListWithSort.maxCols = 6;\n\nconfig.macros.tagglyList.label = "Tagged as %0:";\n\n// the default sort options. set these to your preference\nconfig.macros.tagglyListWithSort.defaults = {\n sortBy:"title", // title|created|modified\n sortOrder: "asc", // asc|desc\n hideState: "show", // show|hide\n groupState: "nogroup", // nogroup|group\n numCols: 1\n};\n\n// these tags will be ignored by the grouped view\nconfig.macros.tagglyListByTag.excludeTheseTags = [\n "systemConfig",\n "TiddlerTemplates"\n];\n\nconfig.macros.tagglyListControl.tags = {\n title:"sortByTitle", \n modified: "sortByModified", \n created: "sortByCreated",\n asc:"sortAsc", \n desc:"sortDesc",\n hide:"hideTagged", \n show:"showTagged",\n nogroup:"noGroupByTag",\n group:"groupByTag",\n cols1:"list1Cols",\n cols2:"list2Cols",\n cols3:"list3Cols",\n cols4:"list4Cols",\n cols5:"list5Cols",\n cols6:"list6Cols",\n cols7:"list7Cols",\n cols8:"list8Cols",\n cols9:"list9Cols" \n}\n\n// note: should match config.macros.tagglyListControl.tags\nconfig.macros.hideSomeTags.tagsToHide = [\n "sortByTitle",\n "sortByCreated",\n "sortByModified",\n "sortDesc",\n "sortAsc",\n "hideTagged",\n "showTagged",\n "noGroupByTag",\n "groupByTag",\n "list1Cols",\n "list2Cols",\n "list3Cols",\n "list4Cols",\n "list5Cols",\n "list6Cols",\n "list7Cols",\n "list8Cols",\n "list9Cols"\n];\n\n\n//}}}\n/***\n\n!Utils\n***/\n//{{{\n// from Eric\nfunction isTagged(title,tag) {\n var t=store.getTiddler(title); if (!t) return false;\n return (t.tags.find(tag)!=null);\n}\n\n// from Eric\nfunction toggleTag(title,tag) {\n var t=store.getTiddler(title); if (!t || !t.tags) return;\n if (t.tags.find(tag)==null) t.tags.push(tag);\n else t.tags.splice(t.tags.find(tag),1);\n}\n\nfunction addTag(title,tag) {\n var t=store.getTiddler(title); if (!t || !t.tags) return;\n t.tags.push(tag);\n}\n\nfunction removeTag(title,tag) {\n var t=store.getTiddler(title); if (!t || !t.tags) return;\n if (t.tags.find(tag)!=null) t.tags.splice(t.tags.find(tag),1);\n}\n\n// from Udo\nArray.prototype.indexOf = function(item) {\n for (var i = 0; i < this.length; i++) {\n if (this[i] == item) {\n return i;\n }\n }\n return -1;\n};\nArray.prototype.contains = function(item) {\n return (this.indexOf(item) >= 0);\n}\n//}}}\n/***\n\n!tagglyList\ndisplays a list of tagged tiddlers. \nparameters are sortField and sortOrder\n***/\n//{{{\n\n// not used at the moment...\nfunction sortedListOfOtherTags(tiddler,thisTag) {\n var list = tiddler.tags.concat(); // so we are working on a clone..\n for (var i=0;i<config.macros.hideSomeTags.tagsToHide.length;i++) {\n if (list.find(config.macros.hideSomeTags.tagsToHide[i]) != null)\n list.splice(list.find(config.macros.hideSomeTags.tagsToHide[i]),1); // remove hidden ones\n }\n for (var i=0;i<config.macros.tagglyListByTag.excludeTheseTags.length;i++) {\n if (list.find(config.macros.tagglyListByTag.excludeTheseTags[i]) != null)\n list.splice(list.find(config.macros.tagglyListByTag.excludeTheseTags[i]),1); // remove excluded ones\n }\n list.splice(list.find(thisTag),1); // remove thisTag\n return '[[' + list.sort().join("]] [[") + ']]';\n}\n\nfunction sortHelper(a,b) {\n if (a == b) return 0;\n else if (a < b) return -1;\n else return +1;\n}\n\nconfig.macros.tagglyListByTag.handler = function (place,macroName,params,wikifier,paramString,tiddler) {\n\n var sortBy = params[0] ? params[0] : "title"; \n var sortOrder = params[1] ? params[1] : "asc";\n\n var result = store.getTaggedTiddlers(tiddler.title,sortBy);\n\n if (sortOrder == "desc")\n result = result.reverse();\n\n var leftOvers = []\n for (var i=0;i<result.length;i++) {\n leftOvers.push(result[i].title);\n }\n\n var allTagsHolder = {};\n for (var i=0;i<result.length;i++) {\n for (var j=0;j<result[i].tags.length;j++) {\n\n if ( \n result[i].tags[j] != tiddler.title // not this tiddler\n && config.macros.hideSomeTags.tagsToHide.find(result[i].tags[j]) == null // not a hidden one\n && config.macros.tagglyListByTag.excludeTheseTags.find(result[i].tags[j]) == null // not excluded\n ) {\n if (!allTagsHolder[result[i].tags[j]])\n allTagsHolder[result[i].tags[j]] = "";\n allTagsHolder[result[i].tags[j]] += "**[["+result[i].title+"]]\sn";\n\n if (leftOvers.find(result[i].title) != null)\n leftOvers.splice(leftOvers.find(result[i].title),1); // remove from leftovers. at the end it will contain the leftovers...\n }\n }\n }\n\n\n var allTags = [];\n for (var t in allTagsHolder)\n allTags.push(t);\n\n allTags.sort(function(a,b) {\n var tidA = store.getTiddler(a);\n var tidB = store.getTiddler(b);\n if (sortBy == "title") return sortHelper(a,b);\n else if (!tidA && !tidB) return 0;\n else if (!tidA) return -1;\n else if (!tidB) return +1;\n else return sortHelper(tidA[sortBy],tidB[sortBy]);\n });\n\n var markup = "";\n\n if (sortOrder == "desc") {\n allTags.reverse();\n }\n else {\n // leftovers first...\n for (var i=0;i<leftOvers.length;i++)\n markup += "*[["+leftOvers[i]+"]]\sn";\n } \n\n for (var i=0;i<allTags.length;i++)\n markup += "*[["+allTags[i]+"]]\sn" + allTagsHolder[allTags[i]];\n\n if (sortOrder == "desc") {\n // leftovers last...\n for (var i=0;i<leftOvers.length;i++)\n markup += "*[["+leftOvers[i]+"]]\sn";\n }\n\n wikify(markup,place);\n}\n\nconfig.macros.tagglyList.handler = function (place,macroName,params,wikifier,paramString,tiddler) {\n var sortBy = params[0] ? params[0] : "title"; \n var sortOrder = params[1] ? params[1] : "asc";\n var numCols = params[2] ? params[2] : 1;\n\n var result = store.getTaggedTiddlers(tiddler.title,sortBy);\n if (sortOrder == "desc")\n result = result.reverse();\n\n var listSize = result.length;\n var colSize = listSize/numCols;\n var remainder = listSize % numCols;\n\n var upperColsize;\n var lowerColsize;\n if (colSize != Math.floor(colSize)) {\n // it's not an exact fit so..\n lowerColsize = Math.floor(colSize);\n upperColsize = Math.floor(colSize) + 1;\n }\n else {\n lowerColsize = colSize;\n upperColsize = colSize;\n }\n\n var markup = "";\n var c=0;\n\n var newTaggedTable = createTiddlyElement(place,"table");\n var newTaggedBody = createTiddlyElement(newTaggedTable,"tbody");\n var newTaggedTr = createTiddlyElement(newTaggedBody,"tr");\n\n for (var j=0;j<numCols;j++) {\n var foo = "";\n var thisSize;\n\n if (j<remainder)\n thisSize = upperColsize;\n else\n thisSize = lowerColsize;\n\n for (var i=0;i<thisSize;i++) \n foo += ( "*[[" + result[c++].title + "]]\sn"); // was using splitList.shift() but didn't work in IE;\n\n var newTd = createTiddlyElement(newTaggedTr,"td",null,"tagglyTagging");\n wikify(foo,newTd);\n\n }\n\n};\n\n/* snip for later.....\n //var groupBy = params[3] ? params[3] : "t.title.substr(0,1)";\n //var groupBy = params[3] ? params[3] : "sortedListOfOtherTags(t,tiddler.title)";\n //var groupBy = params[3] ? params[3] : "t.modified";\n var groupBy = null; // for now. groupBy here is working but disabled for now.\n\n var prevGroup = "";\n var thisGroup = "";\n\n if (groupBy) {\n result.sort(function(a,b) {\n var t = a; var aSortVal = eval(groupBy); var aSortVal2 = eval("t".sortBy);\n var t = b; var bSortVal = eval(groupBy); var bSortVal2 = eval("t".sortBy);\n var t = b; var bSortVal2 = eval(groupBy);\n return (aSortVal == bSortVal ?\n (aSortVal2 == bSortVal2 ? 0 : (aSortVal2 < bSortVal2 ? -1 : +1)) // yuck\n : (aSortVal < bSortVal ? -1 : +1));\n });\n }\n\n if (groupBy) {\n thisGroup = eval(groupBy);\n if (thisGroup != prevGroup)\n markup += "*[["+thisGroup+']]\sn';\n markup += "**[["+t.title+']]\sn';\n prevGroup = thisGroup;\n }\n\n\n\n*/\n\n\n//}}}\n\n/***\n\n!tagglyListControl\nUse to make the sort control buttons\n***/\n//{{{\n\nfunction getSortBy(title) {\n var tiddler = store.getTiddler(title);\n var defaultVal = config.macros.tagglyListWithSort.defaults.sortBy;\n if (!tiddler) return defaultVal;\n var usetags = config.macros.tagglyListControl.tags;\n if (tiddler.tags.contains(usetags["title"])) return "title";\n else if (tiddler.tags.contains(usetags["modified"])) return "modified";\n else if (tiddler.tags.contains(usetags["created"])) return "created";\n else return defaultVal;\n}\n\nfunction getSortOrder(title) {\n var tiddler = store.getTiddler(title);\n var defaultVal = config.macros.tagglyListWithSort.defaults.sortOrder;\n if (!tiddler) return defaultVal;\n var usetags = config.macros.tagglyListControl.tags;\n if (tiddler.tags.contains(usetags["asc"])) return "asc";\n else if (tiddler.tags.contains(usetags["desc"])) return "desc";\n else return defaultVal;\n}\n\nfunction getHideState(title) {\n var tiddler = store.getTiddler(title);\n var defaultVal = config.macros.tagglyListWithSort.defaults.hideState;\n if (!tiddler) return defaultVal;\n var usetags = config.macros.tagglyListControl.tags;\n if (tiddler.tags.contains(usetags["hide"])) return "hide";\n else if (tiddler.tags.contains(usetags["show"])) return "show";\n else return defaultVal;\n}\n\nfunction getGroupState(title) {\n var tiddler = store.getTiddler(title);\n var defaultVal = config.macros.tagglyListWithSort.defaults.groupState;\n if (!tiddler) return defaultVal;\n var usetags = config.macros.tagglyListControl.tags;\n if (tiddler.tags.contains(usetags["group"])) return "group";\n else if (tiddler.tags.contains(usetags["nogroup"])) return "nogroup";\n else return defaultVal;\n}\n\nfunction getNumCols(title) {\n var tiddler = store.getTiddler(title);\n var defaultVal = config.macros.tagglyListWithSort.defaults.numCols; // an int\n if (!tiddler) return defaultVal;\n var usetags = config.macros.tagglyListControl.tags;\n for (var i=1;i<=config.macros.tagglyListWithSort.maxCols;i++)\n if (tiddler.tags.contains(usetags["cols"+i])) return i;\n return defaultVal;\n}\n\n\nfunction getSortLabel(title,which) {\n // TODO. the strings here should be definable in config\n var by = getSortBy(title);\n var order = getSortOrder(title);\n var hide = getHideState(title);\n var group = getGroupState(title);\n if (which == "hide") return (hide == "show" ? "−" : "+"); // 0x25b8;\n else if (which == "group") return (group == "group" ? "normal" : "grouped");\n else if (which == "cols") return "cols±"; // &plusmn;\n else if (by == which) return which + (order == "asc" ? "↓" : "↑"); // &uarr; &darr;\n else return which;\n}\n\nfunction handleSortClick(title,which) {\n var currentSortBy = getSortBy(title);\n var currentSortOrder = getSortOrder(title);\n var currentHideState = getHideState(title);\n var currentGroupState = getGroupState(title);\n var currentNumCols = getNumCols(title);\n\n var tags = config.macros.tagglyListControl.tags;\n\n // if it doesn't exist, lets create it..\n if (!store.getTiddler(title))\n store.saveTiddler(title,title,"",config.options.txtUserName,new Date(),null);\n\n if (which == "hide") {\n // toggle hide state\n var newHideState = (currentHideState == "hide" ? "show" : "hide");\n removeTag(title,tags[currentHideState]);\n if (newHideState != config.macros.tagglyListWithSort.defaults.hideState)\n toggleTag(title,tags[newHideState]);\n }\n else if (which == "group") {\n // toggle hide state\n var newGroupState = (currentGroupState == "group" ? "nogroup" : "group");\n removeTag(title,tags[currentGroupState]);\n if (newGroupState != config.macros.tagglyListWithSort.defaults.groupState)\n toggleTag(title,tags[newGroupState]);\n }\n else if (which == "cols") {\n // toggle num cols\n var newNumCols = currentNumCols + 1; // confusing. currentNumCols is an int\n if (newNumCols > config.macros.tagglyListWithSort.maxCols || newNumCols > store.getTaggedTiddlers(title).length)\n newNumCols = 1;\n removeTag(title,tags["cols"+currentNumCols]);\n if (("cols"+newNumCols) != config.macros.tagglyListWithSort.defaults.groupState)\n toggleTag(title,tags["cols"+newNumCols]);\n }\n else if (currentSortBy == which) {\n // toggle sort order\n var newSortOrder = (currentSortOrder == "asc" ? "desc" : "asc");\n removeTag(title,tags[currentSortOrder]);\n if (newSortOrder != config.macros.tagglyListWithSort.defaults.sortOrder)\n toggleTag(title,tags[newSortOrder]);\n }\n else {\n // change sortBy only\n removeTag(title,tags["title"]);\n removeTag(title,tags["created"]);\n removeTag(title,tags["modified"]);\n\n if (which != config.macros.tagglyListWithSort.defaults.sortBy)\n toggleTag(title,tags[which]);\n }\n\n store.setDirty(true); // save is required now.\n story.refreshTiddler(title,false,true); // force=true\n}\n\nconfig.macros.tagglyListControl.handler = function (place,macroName,params,wikifier,paramString,tiddler) {\n var onclick = function(e) {\n if (!e) var e = window.event;\n handleSortClick(tiddler.title,params[0]);\n e.cancelBubble = true;\n if (e.stopPropagation) e.stopPropagation();\n return false;\n };\n createTiddlyButton(place,getSortLabel(tiddler.title,params[0]),"Click to change sort options",onclick,params[0]=="hide"?"hidebutton":"button");\n}\n//}}}\n/***\n\n!tagglyListWithSort\nput it all together..\n***/\n//{{{\nconfig.macros.tagglyListWithSort.handler = function (place,macroName,params,wikifier,paramString,tiddler) {\n if (tiddler && store.getTaggedTiddlers(tiddler.title).length > 0)\n // todo make this readable\n wikify(\n "<<tagglyListControl hide>>"+\n (getHideState(tiddler.title) != "hide" ? \n '<html><span class="tagglyLabel">'+config.macros.tagglyList.label.format([tiddler.title])+' </span></html>'+\n "<<tagglyListControl title>><<tagglyListControl modified>><<tagglyListControl created>><<tagglyListControl group>>"+(getGroupState(tiddler.title)=="group"?"":"<<tagglyListControl cols>>")+"\sn" + \n "<<tagglyList" + (getGroupState(tiddler.title)=="group"?"ByTag ":" ") + getSortBy(tiddler.title)+" "+getSortOrder(tiddler.title)+" "+getNumCols(tiddler.title)+">>" // hacky\n // + \sn----\sn" +\n //"<<tagglyList "+getSortBy(tiddler.title)+" "+getSortOrder(tiddler.title)+">>"\n : ""),\n place,null,tiddler);\n}\n\nconfig.macros.tagglyTagging = { handler: config.macros.tagglyListWithSort.handler };\n\n\n//}}}\n/***\n\n!hideSomeTags\nSo we don't see the sort tags.\n(note, they are still there when you edit. Will that be too annoying?\n***/\n//{{{\n\n// based on tags.handler\nconfig.macros.hideSomeTags.handler = function(place,macroName,params,wikifier,paramString,tiddler) {\n var theList = createTiddlyElement(place,"ul");\n if(params[0] && store.tiddlerExists[params[0]])\n tiddler = store.getTiddler(params[0]);\n var lingo = config.views.wikified.tag;\n var prompt = tiddler.tags.length == 0 ? lingo.labelNoTags : lingo.labelTags;\n createTiddlyElement(theList,"li",null,"listTitle",prompt.format([tiddler.title]));\n for(var t=0; t<tiddler.tags.length; t++)\n if (!this.tagsToHide.contains(tiddler.tags[t])) // this is the only difference from tags.handler...\n createTagButton(createTiddlyElement(theList,"li"),tiddler.tags[t],tiddler.title);\n\n}\n\n//}}}\n/***\n\n!Refresh everything when we save a tiddler. So the tagged lists never get stale. Is this too slow???\n***/\n//{{{\n\nfunction refreshAllVisible() {\n story.forEachTiddler(function(title,element) {\n if (element.getAttribute("dirty") != "true") \n story.refreshTiddler(title,false,true);\n });\n}\n\nstory.saveTiddler_orig_mptw = story.saveTiddler;\nstory.saveTiddler = function(title,minorUpdate) {\n var result = this.saveTiddler_orig_mptw(title,minorUpdate);\n refreshAllVisible();\n return result;\n}\n\nstore.removeTiddler_orig_mptw = store.removeTiddler;\nstore.removeTiddler = function(title) {\n this.removeTiddler_orig_mptw(title);\n refreshAllVisible();\n}\n\n//}}}\n\n// // <html>&#x25b8;&#x25be;&minus;&plusmn;</html>
/***\nTo use, add {{{[[TagglyTaggingStyles]]}}} to your StyleSheet tiddler, or you can just paste the CSS in directly. See also ViewTemplate, EditTemplate and TagglyTagging.\n***/\n/*{{{*/\n.tagglyTagged li.listTitle { display:none;}\n.tagglyTagged li { display: inline; font-size:90%; }\n.tagglyTagged ul { margin:0px; padding:0px; }\n.tagglyTagging { padding-top:0.5em; }\n.tagglyTagging li.listTitle { display:none;}\n.tagglyTagging ul { margin-top:0px; padding-top:0.5em; padding-left:2em; margin-bottom:0px; padding-bottom:0px; }\n\n/* .tagglyTagging .tghide { display:inline; } */\n\n.tagglyTagging { vertical-align: top; margin:0px; padding:0px; }\n.tagglyTagging table { margin:0px; padding:0px; }\n\n\n.tagglyTagging .button { display:none; margin-left:3px; margin-right:3px; }\n.tagglyTagging .button, .tagglyTagging .hidebutton { color:#aaa; font-size:90%; border:0px; padding-left:0.3em;padding-right:0.3em;}\n.tagglyTagging .button:hover, .hidebutton:hover { background:#eee; color:#888; }\n.selected .tagglyTagging .button { display:inline; }\n\n.tagglyTagging .hidebutton { color:white; } /* has to be there so it takes up space. tweak if you're not using a white tiddler bg */\n.selected .tagglyTagging .hidebutton { color:#aaa }\n\n.tagglyLabel { color:#aaa; font-size:90%; }\n\n.tagglyTagging ul {padding-top:0px; padding-bottom:0.5em; margin-left:1em; }\n.tagglyTagging ul ul {list-style-type:disc; margin-left:-1em;}\n.tagglyTagging ul ul li {margin-left:0.5em; }\n\n.editLabel { font-size:90%; padding-top:0.5em; }\n/*}}}*/\n
The TaskMacroPlugin is a ~TiddlyWiki plugin that contains macros for managing detailed lists of tasks, especially helpful for work like developing software.\n* The {{{<<task>>}}} macro describes a single task and tracks the time estimated and spent on it.\n* The {{{<<taskadder>>}}} macro provides a box to type into, to simplify the adding of new tasks.\n* The {{{<<tasksum>>}}} macro summarizes a set of tasks to show how much time has been spent and how much time remains to complete them.\nSee TaskMacroTutorial for an introduction to the macros. You may also wish to download the [[empty LabWiki|empty_labwiki.html]] to experiment with them in your own environment, or simply import the TaskMacroPlugin tiddler to your own ~TiddlyWiki. (Please be sure to read about known [[issues|5. Task macro installation issues]] if you import it into your own wiki.)\n\nThere is an optional [[translation|TaskMacroPlugin_dollars]] available that makes your task estimates be in terms of money (dollars) instead of time.\n\nSee TaskMacroReleaseNotes for changes from prior versions of this plugin.\n\n
/***\n|Name|TaskMacroPlugin|\n|Author|<<extension TaskMacroPlugin author>>|\n|Location|<<extension TaskMacroPlugin source>>|\n|License|<<extension TaskMacroPlugin license>>|\n|Version|<<extension TaskMacroPlugin versionAndDate>>|\n!Description\nA set of macros to help you keep track of time estimates for tasks.\n\nMacros defined:\n* {{{task}}}: Displays a task description and makes it easy to estimate and track the time spent on the task.\n* {{{taskadder}}}: Displays text entry field to simplify the adding of tasks.\n* {{{tasksum}}}: Displays a summary of tasks sandwiched between two calls to this macro.\n* {{{extension}}}: A simple little macro that displays information about a TiddlyWiki plugin, and that will hopefully someday migrate to the TW core in some form.\nCore overrides:\n* {{{wikify}}}: when wikifying a tiddler's complete text, adds refresh information so the tiddler will be refreshed when it changes\n* {{{config.refreshers}}}: have the built-in refreshers return true; also, add a new refresher ("fullContent") that redisplays a full tiddler whenever it or any nested tiddlers it shows are changed\n* {{{refreshElements}}}: now checks the return value from the refresher and only short-circuits the recursion if the refresher returns true\n!Plugin Information\n***/\n//{{{\nversion.extensions.TaskMacroPlugin = {\n major: 1, minor: 1, revision: 0,\n date: new Date(2006,5-1,13),\n author: "LukeBlanshard",\n source: "http://labwiki.sourceforge.net/#TaskMacroPlugin",\n license: "http://labwiki.sourceforge.net/#CopyrightAndLicense"\n}\n//}}}\n/***\nA little macro for pulling out extension info. Use like {{{<<extension PluginName datum>>}}}, where {{{PluginName}}} is the name you used for {{{version.extensions}}} and {{{datum}}} is either {{{versionAndDate}}} or a property of the extension description object, such as {{{source}}}.\n***/\n//{{{\nconfig.macros.extension = {\n handler: function( place, macroName, params, wikifier, paramString, tiddler ) {\n var info = version.extensions[params[0]]\n var datum = params[1]\n switch (params[1]) {\n case 'versionAndDate':\n createTiddlyElement( place, "span", null, null,\n info.major+'.'+info.minor+'.'+info.revision+', '+info.date.formatString('DD MMM YYYY') )\n break;\n default:\n wikify( info[datum], place )\n break;\n }\n }\n}\n//}}}\n/***\n!Core Overrides\n***/\n//{{{\nwindow.wikify_orig_TaskMacroPlugin = window.wikify\nwindow.wikify = function(source,output,highlightRegExp,tiddler)\n{\n if ( tiddler && tiddler.text === source )\n addDisplayDependency( output, tiddler.title )\n wikify_orig_TaskMacroPlugin.apply( this, arguments )\n}\nconfig.refreshers_orig_TaskMacroPlugin = config.refreshers\nconfig.refreshers = {\n link: function() {\n config.refreshers_orig_TaskMacroPlugin.link.apply( this, arguments )\n return true\n },\n content: function() {\n config.refreshers_orig_TaskMacroPlugin.content.apply( this, arguments )\n return true\n },\n fullContent: function( e, changeList ) {\n var tiddlers = e.refreshTiddlers\n if ( changeList == null || tiddlers == null )\n return false\n for ( var i=0; i < tiddlers.length; ++i )\n if ( changeList.find(tiddlers[i]) != null ) {\n var title = tiddlers[0]\n story.refreshTiddler( title, null, true )\n return true\n }\n return false\n }\n}\nfunction refreshElements(root,changeList)\n{\n var nodes = root.childNodes;\n for(var c=0; c<nodes.length; c++)\n {\n var e = nodes[c],type;\n if(e.getAttribute)\n type = e.getAttribute("refresh");\n else\n type = null;\n var refresher = config.refreshers[type];\n if ( ! refresher || ! refresher(e, changeList) )\n {\n if(e.hasChildNodes())\n refreshElements(e,changeList);\n }\n }\n}\n//}}}\n/***\n!Global Functions\n***/\n//{{{\n// Add the tiddler whose title is given to the list of tiddlers whose\n// changing will cause a refresh of the tiddler containing the given element.\nfunction addDisplayDependency( element, title ) {\n while ( element && element.getAttribute ) {\n var idAttr = element.getAttribute("id"), tiddlerAttr = element.getAttribute("tiddler")\n if ( idAttr && tiddlerAttr && idAttr == story.idPrefix+tiddlerAttr ) {\n var list = element.refreshTiddlers\n if ( list == null ) {\n list = [tiddlerAttr]\n element.refreshTiddlers = list\n element.setAttribute( "refresh", "fullContent" )\n }\n list.pushUnique( title )\n return\n }\n element = element.parentNode\n }\n}\n\n// Lifted from Story.prototype.focusTiddler: just return the field instead of focusing it.\nStory.prototype.findEditField = function( title, field )\n{\n var tiddler = document.getElementById(this.idPrefix + title);\n if(tiddler != null)\n {\n var children = tiddler.getElementsByTagName("*")\n var e = null;\n for (var t=0; t<children.length; t++)\n {\n var c = children[t];\n if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")\n {\n if(!e)\n e = c;\n if(c.getAttribute("edit") == field)\n e = c;\n }\n }\n return e\n }\n}\n\n// Wraps the given event function in another function that handles the\n// event in a standard way.\nfunction wrapEventHandler( otherHandler ) {\n return function(e) {\n if (!e) var e = window.event\n e.cancelBubble = true\n if (e.stopPropagation) e.stopPropagation()\n return otherHandler( e )\n }\n}\n//}}}\n/***\n!Task Macro\nUsage:\n> {{{<<task orig cur spent>>description}}}\nAll of orig, cur, and spent are optional numbers of hours. The description goes through the end of the line, and is wikified.\n***/\n//{{{\nconfig.macros.task = {\n NASCENT: 0, // Task not yet estimated\n LIVE: 1, // Estimated but with time remaining\n DONE: 2, // Completed: no time remaining\n bullets: ["\su25cb", // nascent (open circle)\n "\su25ba", // live (right arrow)\n "\su25a0"],// done (black square)\n styles: ["nascent", "live", "done"],\n\n // Translatable text:\n lingo: {\n spentTooBig: "Spent time %0 can't exceed current estimate %1",\n noNegative: "Times may not be negative numbers",\n statusTips: ["Not yet estimated", "To do", "Done"], // Array indexed by state (NASCENT/LIVE/DONE)\n descClickTip: " -- Double-click to edit task description",\n statusClickTip: " -- Double-click to mark task complete",\n statusDoneTip: " -- Double-click to adjust the time spent, to revive the task",\n origTip: "Original estimate in hours",\n curTip: "Current estimate in hours",\n curTip2: "Estimate in hours", // For when orig == cur\n clickTip: " -- Click to adjust",\n spentTip: "Hours spent on this task",\n remTip: "Hours remaining",\n curPrompt: "Estimate this task in hours, or adjust the current estimate by starting with + or -.\sn\snYou may optionally also set or adjust the time spent by putting a second number after the first.",\n spentPrompt: "Enter the number of hours you've spent on this task, or adjust the current number by starting with + or -.\sn\snYou may optionally also set or adjust the time remaining by putting a second number after the first.",\n remPrompt: "Enter the number of hours it will take to finish this task, or adjust the current estimate by starting with + or -.\sn\snYou may optionally also set or adjust the time spent by putting a second number after the first.",\n numbersOnly: "Enter numbers only, please",\n notCurrent: "The tiddler has been modified since it was displayed, please redisplay it before doing this."\n },\n\n // The macro handler\n handler: function( place, macroName, params, wikifier, paramString, tiddler )\n {\n var start = wikifier.matchStart, end = wikifier.nextMatch\n\n var origStr = params.length > 0? params.shift() : "?"\n var orig = +origStr // as a number\n var cur = params.length > 1? +params.shift() : orig\n var spent = params.length > 0? +params.shift() : 0\n if ( spent > cur )\n throw Error( this.lingo.spentTooBig.format([spent, cur]) )\n if ( orig < 0 || cur < 0 || spent < 0 )\n throw Error( this.lingo.noNegative )\n var rem = cur - spent\n var state = isNaN(orig+rem)? this.NASCENT : rem > 0? this.LIVE : this.DONE\n var table = createTiddlyElement( place, "table", null, "task "+this.styles[state] )\n var tbody = createTiddlyElement( table, "tbody" )\n var row = createTiddlyElement( tbody, "tr" )\n var statusCell = createTiddlyElement( row, "td", null, "status", this.bullets[state] )\n var descCell = createTiddlyElement( row, "td", null, "description" )\n\n var origCell = state==this.NASCENT || orig==cur? null\n : createTiddlyElement( row, "td", null, "numeric original" )\n var curCell = createTiddlyElement( row, "td", null, "numeric current" )\n var spentCell = createTiddlyElement( row, "td", null, "numeric spent" )\n var remCell = createTiddlyElement( row, "td", null, "numeric remaining" )\n\n var sums = config.macros.tasksum.tasksums\n if ( sums && sums.length ) {\n var summary = [(state == this.NASCENT? NaN : orig), cur, spent]\n summary.owner = tiddler\n sums[0].push( summary )\n }\n\n // The description goes to the end of the line\n wikifier.subWikify( descCell, "$\s\sn?" )\n var descEnd = wikifier.nextMatch\n\n statusCell.setAttribute( "title", this.lingo.statusTips[state] )\n descCell.setAttribute( "title", this.lingo.statusTips[state]+this.lingo.descClickTip )\n if (origCell) {\n createTiddlyElement( origCell, "div", null, null, orig )\n origCell.setAttribute( "title", this.lingo.origTip )\n curCell.setAttribute( "title", this.lingo.curTip )\n }\n else {\n curCell.setAttribute( "title", this.lingo.curTip2 )\n }\n var curDivContents = (state==this.NASCENT)? "?" : cur\n var curDiv = createTiddlyElement( curCell, "div", null, null, curDivContents )\n spentCell.setAttribute( "title", this.lingo.spentTip )\n var spentDiv = createTiddlyElement( spentCell, "div", null, null, spent )\n remCell.setAttribute( "title", this.lingo.remTip )\n var remDiv = createTiddlyElement( remCell, "div", null, null, rem )\n\n // Handle double-click on the description by going\n // into edit mode and selecting the description\n descCell.ondblclick = this.editDescription( tiddler, end, descEnd )\n\n function appTitle( el, suffix ) {\n el.setAttribute( "title", el.getAttribute("title")+suffix )\n }\n\n // For incomplete tasks, handle double-click on the bullet by marking the task complete\n if ( state != this.DONE ) {\n appTitle( statusCell, this.lingo.statusClickTip )\n statusCell.ondblclick = this.markTaskComplete( tiddler, start, end, macroName, orig, cur, state )\n }\n // For complete ones, handle double-click on the bullet by letting you adjust the time spent\n else {\n appTitle( statusCell, this.lingo.statusDoneTip )\n statusCell.ondblclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )\n }\n\n // Add click handlers for the numeric cells.\n if ( state != this.DONE ) {\n appTitle( curCell, this.lingo.clickTip )\n curDiv.className = "adjustable"\n curDiv.onclick = this.adjustCurrentEstimate( tiddler, start, end, macroName,\n orig, cur, spent, curDivContents )\n }\n appTitle( spentCell, this.lingo.clickTip )\n spentDiv.className = "adjustable"\n spentDiv.onclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )\n if ( state == this.LIVE ) {\n appTitle( remCell, this.lingo.clickTip )\n remDiv.className = "adjustable"\n remDiv.onclick = this.adjustTimeRemaining( tiddler, start, end, macroName, orig, cur, spent )\n }\n },\n\n // Puts the tiddler into edit mode, and selects the range of characters\n // defined by start and end. Separated for leak prevention in IE.\n editDescription: function( tiddler, start, end ) {\n return wrapEventHandler( function(e) {\n story.displayTiddler( null, tiddler.title, DEFAULT_EDIT_TEMPLATE )\n var tiddlerElement = document.getElementById( story.idPrefix + tiddler.title )\n window.scrollTo( 0, ensureVisible(tiddlerElement) )\n var element = story.findEditField( tiddler.title, "text" )\n if ( element && element.tagName.toLowerCase() == "textarea" ) {\n // Back up one char if the last char's a newline\n if ( tiddler.text[end-1] == '\sn' )\n --end\n element.focus()\n if ( element.setSelectionRange != undefined ) { // Mozilla\n element.setSelectionRange( start, end )\n // Damn mozilla doesn't scroll to visible. Approximate.\n var max = 0.0 + element.scrollHeight\n var len = element.textLength\n var top = max*start/len, bot = max*end/len\n element.scrollTop = Math.min( top, (bot+top-element.clientHeight)/2 )\n }\n else if ( element.createTextRange != undefined ) { // IE\n var range = element.createTextRange()\n range.collapse()\n range.moveEnd("character", end)\n range.moveStart("character", start)\n range.select()\n }\n else // Other? Too bad, just select the whole thing.\n element.select()\n return false\n }\n else\n return true\n } )\n },\n\n // Modifies a task macro call such that the task appears complete.\n markTaskComplete: function( tiddler, start, end, macroName, orig, cur, state ) {\n var macro = this, text = tiddler.text\n return wrapEventHandler( function(e) {\n if ( text !== tiddler.text ) {\n alert( macro.lingo.notCurrent )\n return false\n }\n if ( state == macro.NASCENT )\n orig = cur = 0\n // The second "cur" in the call below bumps up the time spent\n // to match the current estimate.\n macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, cur )\n return false\n } )\n },\n\n // Asks the user for an adjustment to the current estimate, modifies the macro call accordingly.\n adjustCurrentEstimate: function( tiddler, start, end, macroName, orig, cur, spent, curDivContents ) {\n var macro = this, text = tiddler.text\n return wrapEventHandler( function(e) {\n if ( text !== tiddler.text ) {\n alert( macro.lingo.notCurrent )\n return false\n }\n var txt = prompt( macro.lingo.curPrompt, curDivContents )\n if ( txt != null ) {\n var a = macro.breakInput( txt )\n cur = macro.offset( cur, a[0] )\n if ( a.length > 1 )\n spent = macro.offset( spent, a[1] )\n macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )\n }\n return false\n } )\n },\n\n // Asks the user for an adjustment to the time spent, modifies the macro call accordingly.\n adjustTimeSpent: function( tiddler, start, end, macroName, orig, cur, spent ) {\n var macro = this, text = tiddler.text\n return wrapEventHandler( function(e) {\n if ( text !== tiddler.text ) {\n alert( macro.lingo.notCurrent )\n return false\n }\n var txt = prompt( macro.lingo.spentPrompt, spent )\n if ( txt != null ) {\n var a = macro.breakInput( txt )\n spent = macro.offset( spent, a[0] )\n var rem = cur - spent\n if ( a.length > 1 ) {\n rem = macro.offset( rem, a[1] )\n cur = spent + rem\n }\n macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )\n }\n return false\n } )\n },\n\n // Asks the user for an adjustment to the time remaining, modifies the macro call accordingly.\n adjustTimeRemaining: function( tiddler, start, end, macroName, orig, cur, spent ) {\n var macro = this\n var text = tiddler.text\n var rem = cur - spent\n return wrapEventHandler( function(e) {\n if ( text !== tiddler.text ) {\n alert( macro.lingo.notCurrent )\n return false\n }\n var txt = prompt( macro.lingo.remPrompt, rem )\n if ( txt != null ) {\n var a = macro.breakInput( txt )\n var newRem = macro.offset( rem, a[0] )\n if ( newRem > rem || a.length > 1 )\n cur += (newRem - rem)\n else\n spent += (rem - newRem)\n if ( a.length > 1 )\n spent = macro.offset( spent, a[1] )\n macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )\n }\n return false\n } )\n },\n\n // Breaks input at spaces & commas, returns array\n breakInput: function( txt ) {\n var a = txt.trim().split( /[\ss,]+/ )\n if ( a.length == 0 )\n a = [NaN]\n return a\n },\n\n // Adds to, subtracts from, or replaces a numeric value\n offset: function( num, txt ) {\n if ( txt == "" || typeof(txt) != "string" )\n return NaN\n if ( txt.match(/^[+-]/) )\n return num + (+txt)\n return +txt\n },\n\n // Does some error checking, then replaces the indicated macro\n // call within the text of the given tiddler.\n replaceMacroCall: function( tiddler, start, end, macroName, orig, cur, spent )\n {\n if ( isNaN(cur+spent) ) {\n alert( this.lingo.numbersOnly )\n return\n }\n if ( spent < 0 || cur < 0 ) {\n alert( this.lingo.noNegative )\n return\n }\n if ( isNaN(orig) )\n orig = cur\n if ( spent > cur )\n cur = spent\n var text = tiddler.text.substring(0,start) + "<<" + macroName + " " +\n orig + " " + cur + " " + spent + ">>" + tiddler.text.substring(end)\n var title = tiddler.title\n store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )\n //story.refreshTiddler( title, null, true )\n if ( config.options.chkAutoSave )\n saveChanges()\n }\n}\n//}}}\n/***\n!Tasksum Macro\nUsage:\n> {{{<<tasksum "start" ["here" [intro]]>>}}}\nor:\n> {{{<<tasksum "end" [intro]>>}}}\nPut one of the {{{<<tasksum start>>}}} lines before the tasks you want to summarize, and an {{{end}}} line after them. By default, the summary goes at the end; if you include {{{here}}} in the start line, the summary will go at the top. The intro argument, if supplied, replaces the default text introducing the summary.\n***/\n//{{{\nconfig.macros.tasksum = {\n\n // Translatable text:\n lingo: {\n unrecVerb: "<<%0>> requires 'start' or 'end' as its first argument",\n mustMatch: "<<%0 end>> must match a preceding <<%0 start>>",\n defIntro: "Task summary:",\n nascentSum: "''%0 not estimated''",\n doneSum: "%0 complete (in %1 hours)",\n liveSum: "%0 ongoing (%1 hours so far, ''%2 hours remaining'')",\n overSum: "Total overestimate: %0%.",\n underSum: "Total underestimate: %0%.",\n descPattern: "%0 %1. %2",\n origTip: "Total original estimates in hours",\n curTip: "Total current estimates in hours",\n spentTip: "Total hours spent on tasks",\n remTip: "Total hours remaining"\n },\n\n // The macro handler\n handler: function( place, macroName, params, wikifier, paramString, tiddler )\n {\n var sums = this.tasksums\n if ( params[0] == "start" ) {\n sums.unshift([])\n if ( params[1] == "here" ) {\n sums[0].intro = params[2] || this.lingo.defIntro\n sums[0].place = place\n sums[0].placement = place.childNodes.length\n }\n }\n else if ( params[0] == "end" ) {\n if ( ! sums.length )\n throw Error( this.lingo.mustMatch.format([macroName]) )\n var list = sums.shift()\n var intro = list.intro || params[1] || this.lingo.defIntro\n var nNascent=0, nLive=0, nDone=0, nMine=0\n var totLiveSpent=0, totDoneSpent=0\n var totOrig=0, totCur=0, totSpent=0\n for ( var i=0; i < list.length; ++i ) {\n var a = list[i]\n if ( a.length > 3 ) {\n nNascent += a[0]\n nLive += a[1]\n nDone += a[2]\n totLiveSpent += a[3]\n totDoneSpent += a[4]\n totOrig += a[5]\n totCur += a[6]\n totSpent += a[7]\n if ( a.owner == tiddler )\n nMine += a[8]\n }\n else {\n if ( a.owner == tiddler )\n ++nMine\n if ( isNaN(a[0]) ) {\n ++nNascent\n }\n else {\n if ( a[1] > a[2] ) {\n ++nLive\n totLiveSpent += a[2]\n }\n else {\n ++nDone\n totDoneSpent += a[2]\n }\n totOrig += a[0]\n totCur += a[1]\n totSpent += a[2]\n }\n }\n }\n\n // If we're nested, push a summary outward\n if ( sums.length ) {\n var summary = [nNascent, nLive, nDone, totLiveSpent, totDoneSpent,\n totOrig, totCur, totSpent, nMine]\n summary.owner = tiddler\n sums[0].push( summary )\n }\n\n var descs = [], styles = []\n if ( nNascent > 0 ) {\n descs.push( this.lingo.nascentSum.format([nNascent]) )\n styles.push( "nascent" )\n }\n if ( nDone > 0 )\n descs.push( this.lingo.doneSum.format([nDone, totDoneSpent]) )\n if ( nLive > 0 ) {\n descs.push( this.lingo.liveSum.format([nLive, totLiveSpent, totCur-totSpent]) )\n styles.push( "live" )\n }\n else\n styles.push( "done" )\n var off = ""\n if ( totOrig > totCur )\n off = this.lingo.overSum.format( [Math.round(100.0*(totOrig-totCur)/totCur)] )\n else if ( totCur > totOrig )\n off = this.lingo.underSum.format( [Math.round(100.0*(totCur-totOrig)/totOrig)] )\n\n var top = (list.intro != undefined)\n var table = createTiddlyElement( null, "table", null, "tasksum "+(top?"top":"bottom") )\n var tbody = createTiddlyElement( table, "tbody" )\n var row = createTiddlyElement( tbody, "tr", null, styles.join(" ") )\n var descCell = createTiddlyElement( row, "td", null, "description" )\n\n var description = this.lingo.descPattern.format( [intro, descs.join(", "), off] )\n wikify( description, descCell, null, tiddler )\n\n var origCell = totOrig == totCur? null\n : createTiddlyElement( row, "td", null, "numeric original", totOrig )\n var curCell = createTiddlyElement( row, "td", null, "numeric current", totCur )\n var spentCell = createTiddlyElement( row, "td", null, "numeric spent", totSpent )\n var remCell = createTiddlyElement( row, "td", null, "numeric remaining", totCur-totSpent )\n\n if ( origCell )\n origCell.setAttribute( "title", this.lingo.origTip )\n curCell .setAttribute( "title", this.lingo.curTip )\n spentCell.setAttribute( "title", this.lingo.spentTip )\n remCell .setAttribute( "title", this.lingo.remTip )\n\n // Discard the table if there are no tasks\n if ( list.length > 0 ) {\n var place = top? list.place : place\n var placement = top? list.placement : place.childNodes.length\n if ( placement >= place.childNodes.length )\n place.appendChild( table )\n else\n place.insertBefore( table, place.childNodes[placement] )\n }\n }\n else\n throw Error( this.lingo.unrecVerb.format([macroName]) )\n\n // If we're wikifying, and are followed by end-of-line, swallow the newline.\n if ( wikifier && wikifier.source.charAt(wikifier.nextMatch) == "\sn" )\n ++wikifier.nextMatch\n },\n\n // This is the stack of pending summaries\n tasksums: []\n}\n//}}}\n/***\n!Taskadder Macro\nUsage:\n> {{{<<taskadder ["above"|"below"|"focus"|"nofocus"]...>>}}}\nCreates a line with text entry fields for a description and an estimate. By default, puts focus in the description field and adds tasks above the entry fields. Use {{{nofocus}}} to not put focus in the description field. Use {{{below}}} to add tasks below the entry fields.\n***/\n//{{{\nconfig.macros.taskadder = {\n\n // Translatable text:\n lingo: {\n unrecParam: "<<%0>> doesn't recognize '%1' as a parameter",\n descTip: "Describe a new task",\n curTip: "Estimate how long in hours the task will take",\n buttonText: "add task",\n buttonTip: "Add a new task with the description and estimate as entered",\n notCurrent: "The tiddler has been modified since it was displayed, please redisplay it before adding a task this way.",\n\n eol: "eol"\n },\n\n // The macro handler\n handler: function( place, macroName, params, wikifier, paramString, tiddler )\n {\n var above = true\n var focus = false\n\n while ( params.length > 0 ) {\n var p = params.shift()\n switch (p) {\n case "above": above = true; break\n case "below": above = false; break\n case "focus": focus = true; break\n case "nofocus": focus = false; break\n default: throw Error( this.lingo.unrecParam.format([macroName, p]) )\n }\n }\n\n // If we're followed by end-of-line, swallow the newline.\n if ( wikifier.source.charAt(wikifier.nextMatch) == "\sn" )\n ++wikifier.nextMatch\n\n var where = above? wikifier.matchStart : wikifier.nextMatch\n\n var table = createTiddlyElement( place, "table", null, "task" )\n var tbody = createTiddlyElement( table, "tbody" )\n var row = createTiddlyElement( tbody, "tr" )\n var statusCell = createTiddlyElement( row, "td", null, "status" )\n var descCell = createTiddlyElement( row, "td", null, "description" )\n var curCell = createTiddlyElement( row, "td", null, "numeric" )\n var addCell = createTiddlyElement( row, "td", null, "addtask" )\n\n var descId = this.generateId()\n var curId = this.generateId()\n var descInput = createTiddlyElement( descCell, "input", descId )\n var curInput = createTiddlyElement( curCell, "input", curId )\n\n descInput.setAttribute( "type", "text" )\n curInput .setAttribute( "type", "text" )\n descInput.setAttribute( "size", "40")\n curInput .setAttribute( "size", "6" )\n descInput.setAttribute( "autocomplete", "off" );\n curInput .setAttribute( "autocomplete", "off" );\n descInput.setAttribute( "title", this.lingo.descTip );\n curInput .setAttribute( "title", this.lingo.curTip );\n\n var addAction = this.addTask( tiddler, where, descId, curId, above )\n var addButton = createTiddlyButton( addCell, this.lingo.buttonText, this.lingo.buttonTip, addAction )\n\n descInput.onkeypress = this.handleEnter(addAction)\n curInput .onkeypress = descInput.onkeypress\n addButton.onkeypress = this.handleSpace(addAction)\n if ( focus || tiddler.taskadderLocation == where ) {\n descInput.focus()\n descInput.select()\n }\n },\n\n // Returns a function that inserts a new task macro into the tiddler.\n addTask: function( tiddler, where, descId, curId, above ) {\n var macro = this, oldText = tiddler.text\n return wrapEventHandler( function(e) {\n if ( oldText !== tiddler.text ) {\n alert( macro.lingo.notCurrent )\n return false\n }\n var desc = document.getElementById(descId).value\n var cur = document.getElementById(curId) .value\n var init = tiddler.text.substring(0,where) + "<<task " + cur + ">> " + desc + "\sn"\n var text = init + tiddler.text.substring(where)\n var title = tiddler.title\n tiddler.taskadderLocation = (above? init.length : where)\n try {\n store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )\n //story.refreshTiddler( title, null, true )\n }\n finally {\n delete tiddler.taskadderLocation\n }\n if ( config.options.chkAutoSave )\n saveChanges()\n } )\n },\n\n // Returns an event handler that delegates to two other functions: "matches" to decide\n // whether to consume the event, and "addTask" to actually perform the work.\n handleGeneric: function( addTask, matches ) {\n return function(e) {\n if (!e) var e = window.event\n var consume = false\n if ( matches(e) ) {\n consume = true\n addTask( e )\n }\n e.cancelBubble = consume;\n if ( consume && e.stopPropagation ) e.stopPropagation();\n return !consume;\n }\n },\n\n // Returns an event handler that handles enter keys by calling another event handler\n handleEnter: function( addTask ) {\n return this.handleGeneric( addTask, function(e){return e.keyCode == 13 || e.keyCode == 10} ) // Different codes for Enter\n },\n\n // Returns an event handler that handles the space key by calling another event handler\n handleSpace: function( addTask ) {\n return this.handleGeneric( addTask, function(e){return (e.charCode||e.keyCode) == 32} )\n },\n\n counter: 0,\n generateId: function() {\n return "taskadder:" + String(this.counter++)\n }\n}\n//}}}\n/***\n!Stylesheet\n***/\n//{{{\nvar stylesheet = '\s\n.viewer table.task, table.tasksum {\s\n width: 100%;\s\n padding: 0;\s\n border-collapse: collapse;\s\n}\s\n.viewer table.task {\s\n border: none;\s\n margin: 0;\s\n}\s\ntable.tasksum, .viewer table.tasksum {\s\n border: solid 2px #999;\s\n margin: 3px 0;\s\n}\s\ntable.tasksum td {\s\n text-align: center;\s\n border: 1px solid #ddd;\s\n background-color: #ffc;\s\n vertical-align: middle;\s\n margin: 0;\s\n padding: 0;\s\n}\s\n.viewer table.task tr {\s\n border: none;\s\n}\s\n.viewer table.task td {\s\n text-align: center;\s\n vertical-align: baseline;\s\n border: 1px solid #fff;\s\n background-color: inherit;\s\n margin: 0;\s\n padding: 0;\s\n}\s\ntd.numeric {\s\n width: 3em;\s\n}\s\ntable.task td.numeric div {\s\n border: 1px solid #ddd;\s\n background-color: #ffc;\s\n margin: 1px 0;\s\n padding: 0;\s\n}\s\ntable.task td.original div {\s\n background-color: #fdd;\s\n}\s\ntable.tasksum td.original {\s\n background-color: #fdd;\s\n}\s\ntable.tasksum td.description {\s\n background-color: #e8e8e8;\s\n}\s\ntable.task td.status {\s\n width: 1.5em;\s\n cursor: default;\s\n}\s\ntable.task td.description, table.tasksum td.description {\s\n width: auto;\s\n text-align: left;\s\n padding: 0 3px;\s\n}\s\ntable.task.done td.status,table.task.done td.description {\s\n color: #ccc;\s\n}\s\ntable.task.done td.current, table.task.done td.remaining {\s\n visibility: hidden;\s\n}\s\ntable.task.done td.spent div, table.tasksum tr.done td.current,\s\ntable.tasksum tr.done td.spent, table.tasksum tr.done td.remaining {\s\n background-color: #eee;\s\n color: #aaa;\s\n}\s\ntable.task.nascent td.description {\s\n color: #844;\s\n}\s\ntable.task.nascent td.current div, table.tasksum tr.nascent td.numeric.current {\s\n font-weight: bold;\s\n color: #c00;\s\n background-color: #def;\s\n}\s\ntable.task.nascent td.spent, table.task.nascent td.remaining {\s\n visibility: hidden;\s\n}\s\ntd.remaining {\s\n font-weight: bold;\s\n}\s\n.adjustable {\s\n cursor: pointer; \s\n}\s\ntable.task input {\s\n display: block;\s\n width: 100%;\s\n font: inherit;\s\n margin: 2px 0;\s\n padding: 0;\s\n border: 1px inset #999;\s\n}\s\ntable.task td.numeric input {\s\n background-color: #ffc;\s\n text-align: center;\s\n}\s\ntable.task td.addtask {\s\n width: 6em;\s\n border-left: 2px solid white;\s\n vertical-align: middle;\s\n}\s\n'\nsetStylesheet( stylesheet, "TaskMacroPluginStylesheet" )\n//}}}\n
/***\n|Name|TaskMacroPlugin_dollars|\n|Author|<<extension TaskMacroPlugin_dollars author>>|\n|Location|<<extension TaskMacroPlugin_dollars source>>|\n|License|<<extension TaskMacroPlugin_dollars license>>|\n|Version|<<extension TaskMacroPlugin_dollars versionAndDate>>|\n!Description\nA "translation" of the TaskMacroPlugin from hours into dollars. Redefines the text strings used by the task macros so that the units you are tracking are dollars. Suggested by Ken Girard (see http://groups.google.com/group/TiddlyWiki/msg/196cea28a16d11b5).\n\n!Note\nThis plugin will fail to load unless the TaskMacroPlugin is loaded first. Its name will ensure that this happens, as long as their names are not changed and they are both tagged as systemConfig.\n\n!Plugin Information\n***/\n//{{{\nversion.extensions.TaskMacroPlugin_dollars = {\n major: 1, minor: 1, revision: 0,\n date: new Date(2006,5-1,13),\n author: "LukeBlanshard",\n source: "http://labwiki.sourceforge.net/#TaskMacroPlugin_dollars",\n license: "http://labwiki.sourceforge.net/#CopyrightAndLicense"\n}\n//}}}\n/***\n!The translations\n***/\n//{{{\nconfig.macros.task.lingo = {\n spentTooBig: "Spent amount %0 can't exceed current estimate %1",\n noNegative: "Amounts may not be negative numbers",\n statusTips: ["Not yet estimated", "To do", "Done"], // Array indexed by state (NASCENT/LIVE/DONE)\n descClickTip: " -- Double-click to edit task description",\n statusDoneTip: " -- Double-click to adjust the amount spent, to revive the task",\n origTip: "Original estimate in dollars",\n curTip: "Current estimate in dollars",\n curTip2: "Estimated cost in dollars",\n clickTip: " -- Click to adjust",\n spentTip: "Dollars spent on this task",\n remTip: "Remaining cost in dollars",\n curPrompt: "Estimate this task's cost in dollars, or adjust the current estimate by starting with + or -.\sn\snYou may optionally also set or adjust the amount spent by putting a second number after the first.",\n spentPrompt: "Enter the number of dollars you've spent on this task, or adjust the current number by starting with + or -.\sn\snYou may optionally also set or adjust the amount remaining by putting a second number after the first.",\n remPrompt: "Enter the number of dollars it will cost to finish this task, or adjust the current estimate by starting with + or -.\sn\snYou may optionally also set or adjust the amount spent by putting a second number after the first.",\n numbersOnly: "Enter numbers only, please",\n notCurrent: "The tiddler has been modified since it was displayed, please redisplay it before doing this."\n}\n\nconfig.macros.tasksum.lingo = {\n unrecVerb: "<<%0>> requires 'start' or 'end' as its first argument",\n mustMatch: "<<%0 end>> must match a preceding <<%0 start>>",\n defIntro: "Task summary:",\n nascentSum: "''%0 not estimated''",\n doneSum: "%0 complete ($%1)",\n liveSum: "%0 ongoing ($%1 so far, ''$%2 remaining'')",\n overSum: "Total overestimate: %0%.",\n underSum: "Total underestimate: %0%.",\n descPattern: "%0 %1. %2",\n origTip: "Total original estimates in dollars",\n curTip: "Total current estimates in dollars",\n spentTip: "Total dollars spent on tasks",\n remTip: "Total remaining cost in dollars"\n}\n\nconfig.macros.taskadder.lingo = {\n unrecParam: "<<%0>> doesn't recognize '%1' as a parameter",\n descTip: "Describe a new task",\n curTip: "Estimate the cost of this task in dollars",\n buttonText: "add task",\n buttonTip: "Add a new task with the description and estimate as entered",\n notCurrent: "The tiddler has been modified since it was displayed, please redisplay it before adding a task this way.",\n\n eol: "eol"\n}\n\n//}}}\n
!!Changes in 1.1.0\n* Made the macros work in nested tiddlers (ie when one tiddler includes another using {{{<<tiddler>>}}} or something similar):\n** Task summaries in the outer tiddler include the tasks from the inner one\n** Using the editing shortcuts on the tasks as displayed in the outer tiddler correctly changes the inner tiddler and also redisplays the outer one\n** Added sanity checks to the editing shortcuts so they will refuse to work if the tiddler has been modified behind their backs\n* Made some small usability fixes:\n** The "add task" button now responds to the Space key (hat tip: Daniel Baird)\n** Double-clicking on a completed task's bullet now does the same thing as clicking on the elapsed time: it lets you adjust the time spent, giving you the option of resurrecting the task (hat tip: ~JackF)\n** Reworked the focus handling of the taskadder macro so it works more intuitively, by refocusing on the same adder you just used\n
The task macro provided by the TaskMacroPlugin is for planning, estimating, and tracking detailed tasks such as those required for writing software. It is inspired by [[Joel Spolsky|http://www.joelonsoftware.com/articles/fog0000000245.html]]'s method for scheduling software development, also popularized by [[Voo2do|http://voo2do.com]] and [[XPlanner|http://xplanner.org]].\n\nFor changes since the previous version, see the TaskMacroReleaseNotes.\n\nThis tutorial leads you through the use of the task macro itself, and supporting macros that summarize lists of tasks and simplify the adding of tasks to a list. Follow along by clicking the links below. Or click the little down-arrow next to this tiddler's title, above, and choose "Open all" to have all the tutorial sections displayed at once.\n\n
<!---\nIncludes portions of [[TagglyTaggingViewTemplate|http://simonbaird.com/mptw/#TagglyTaggingViewTemplate]], v1.2 (16-Jan-2006).\nAlso adds a pair of tasksum macros around the tiddler, to summarize any contained tasks at the top. Removes the "-" in front of closeTiddler, which can easily bite you if you have a focusable element in a tiddler, such as a taskadder entry field.\nPortions written by Luke Blanshard are hereby released into the public domain.\n--->\n<!--{{{-->\n<div class="toolbar" macro="toolbar closeTiddler closeOthers +editTiddler permalink references jump newHere"></div>\n<div class="tagglyTagged" macro="tags"></div>\n<div><span class="title" macro="view title"></span><span class="miniTag" macro="miniTag"></span></div>\n<div macro="tasksum start here"></div>\n<div class="viewer" macro="view text wikified"></div>\n<div macro="tasksum end"></div>\n<div class="tagglyTagging" macro="tagglyListWithSort"></div>\n<!--}}}-->\n
You're looking at a [[TiddlyWiki|http://tiddlywiki.com]]&mdash;it's a single web page that presents its content in small pieces like this one.
<!---\nIncludes portions of [[TagglyTaggingViewTemplate|http://simonbaird.com/mptw/#TagglyTaggingViewTemplate]], v1.2 (16-Jan-2006).\nSee also TaskSummaryViewTemplate, which adds a pair of tasksum macros around the viewer. This view template leaves those out so that the TaskMacroTutorial can introduce the task macro before the tasksum macro.\nPortions written by Luke Blanshard are hereby released into the public domain.\n--->\n<!--{{{-->\n<div class="toolbar" macro="toolbar closeTiddler closeOthers +editTiddler permalink references jump newHere"></div>\n<div class="tagglyTagged" macro="tags"></div>\n<div><span class="title" macro="view title"></span><span class="miniTag" macro="miniTag"></span></div>\n<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date "DD MMM YYYY"'></span></div>\n<div class="viewer" macro="view text wikified"></div>\n<div class="tagglyTagging" macro="tagglyList"></div>\n<!--}}}-->\n
/***\nA little code to run at startup to appease Simon Baird's inquisitiveness.\n***/\n//{{{\nconfig.options.chkHttpReadOnly = false // Enable editing so that visitors can experiment with it\n//}}}\n