<!--{{{-->
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' />
<!--}}}-->
Background: #fff
Foreground: #000
PrimaryPale: #8cf
PrimaryLight: #18f
PrimaryMid: #04b
PrimaryDark: #014
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
/*{{{*/
body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}

a {color:[[ColorPalette::PrimaryMid]];}
a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
a img {border:0;}

h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}

.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}

.header {background:[[ColorPalette::PrimaryMid]];}
.headerShadow {color:[[ColorPalette::Foreground]];}
.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
.headerForeground {color:[[ColorPalette::Background]];}
.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}

.tabSelected{color:[[ColorPalette::PrimaryDark]];
	background:[[ColorPalette::TertiaryPale]];
	border-left:1px solid [[ColorPalette::TertiaryLight]];
	border-top:1px solid [[ColorPalette::TertiaryLight]];
	border-right:1px solid [[ColorPalette::TertiaryLight]];
}
.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
.tabContents .button {border:0;}

#sidebar {}
#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}

.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
	border:1px solid [[ColorPalette::PrimaryMid]];}
.wizardStep.wizardStepDone {background:[[ColorPalette::TertiaryLight]];}
.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}

.wizard .notChanged {background:transparent;}
.wizard .changedLocally {background:#80ff80;}
.wizard .changedServer {background:#8080ff;}
.wizard .changedBoth {background:#ff8080;}
.wizard .notFound {background:#ffff80;}
.wizard .putToServer {background:#ff80ff;}
.wizard .gotFromServer {background:#80ffff;}

#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}

.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}

.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}

.tiddler .defaultCommand {font-weight:bold;}

.shadow .title {color:[[ColorPalette::TertiaryDark]];}

.title {color:[[ColorPalette::SecondaryDark]];}
.subtitle {color:[[ColorPalette::TertiaryDark]];}

.toolbar {color:[[ColorPalette::PrimaryMid]];}
.toolbar a {color:[[ColorPalette::TertiaryLight]];}
.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}

.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
.tagging .button, .tagged .button {border:none;}

.footer {color:[[ColorPalette::TertiaryLight]];}
.selected .footer {color:[[ColorPalette::TertiaryMid]];}

.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
.sparktick {background:[[ColorPalette::PrimaryDark]];}

.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
.lowlight {background:[[ColorPalette::TertiaryLight]];}

.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}

.imageLink, #displayArea .imageLink {background:transparent;}

.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}

.viewer .listTitle {list-style-type:none; margin-left:-2em;}
.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}

.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}

.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
.viewer code {color:[[ColorPalette::SecondaryDark]];}
.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}

.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}

.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
.editorFooter {color:[[ColorPalette::TertiaryMid]];}

#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity:60)';}
/*}}}*/
/*{{{*/
* html .tiddler {height:1%;}

body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}

h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
h4,h5,h6 {margin-top:1em;}
h1 {font-size:1.35em;}
h2 {font-size:1.25em;}
h3 {font-size:1.1em;}
h4 {font-size:1em;}
h5 {font-size:.9em;}

hr {height:1px;}

a {text-decoration:none;}

dt {font-weight:bold;}

ol {list-style-type:decimal;}
ol ol {list-style-type:lower-alpha;}
ol ol ol {list-style-type:lower-roman;}
ol ol ol ol {list-style-type:decimal;}
ol ol ol ol ol {list-style-type:lower-alpha;}
ol ol ol ol ol ol {list-style-type:lower-roman;}
ol ol ol ol ol ol ol {list-style-type:decimal;}

.txtOptionInput {width:11em;}

#contentWrapper .chkOptionInput {border:0;}

.externalLink {text-decoration:underline;}

.indent {margin-left:3em;}
.outdent {margin-left:3em; text-indent:-3em;}
code.escaped {white-space:nowrap;}

.tiddlyLinkExisting {font-weight:bold;}
.tiddlyLinkNonExisting {font-style:italic;}

/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
a.tiddlyLinkNonExisting.shadow {font-weight:bold;}

#mainMenu .tiddlyLinkExisting,
	#mainMenu .tiddlyLinkNonExisting,
	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}

.header {position:relative;}
.header a:hover {background:transparent;}
.headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}

.siteTitle {font-size:3em;}
.siteSubtitle {font-size:1.2em;}

#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}

#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#sidebarOptions {padding-top:0.3em;}
#sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
#sidebarOptions input {margin:0.4em 0.5em;}
#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}

.wizard {padding:0.1em 1em 0em 2em;}
.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizardStep {padding:1em 1em 1em 1em;}
.wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
.wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
.wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
.wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}

#messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
.messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
#messageArea a {text-decoration:underline;}

.tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}

.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
.popup .popupMessage {padding:0.4em;}
.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
.popup li.disabled {padding:0.4em;}
.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
.listBreak {font-size:1px; line-height:1px;}
.listBreak div {margin:2px 0;}

.tabset {padding:1em 0em 0em 0.5em;}
.tab {margin:0em 0em 0em 0.25em; padding:2px;}
.tabContents {padding:0.5em;}
.tabContents ul, .tabContents ol {margin:0; padding:0;}
.txtMainTab .tabContents li {list-style:none;}
.tabContents li.listLink { margin-left:.75em;}

#contentWrapper {display:block;}
#splashScreen {display:none;}

#displayArea {margin:1em 17em 0em 14em;}

.toolbar {text-align:right; font-size:.9em;}

.tiddler {padding:1em 1em 0em 1em;}

.missing .viewer,.missing .title {font-style:italic;}

.title {font-size:1.6em; font-weight:bold;}

.missing .subtitle {display:none;}
.subtitle {font-size:1.1em;}

.tiddler .button {padding:0.2em 0.4em;}

.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
.isTag .tagging {display:block;}
.tagged {margin:0.5em; float:right;}
.tagging, .tagged {font-size:0.9em; padding:0.25em;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
.tagClear {clear:both;}

.footer {font-size:.9em;}
.footer li {display:inline;}

.annotation {padding:0.5em; margin:0.5em;}

* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer {line-height:1.4em; padding-top:0.5em;}
.viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}

.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
table.listView {font-size:0.85em; margin:0.8em 1.0em;}
table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}

.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
.viewer code {font-size:1.2em; line-height:1.4em;}

.editor {font-size:1.1em;}
.editor input, .editor textarea {display:block; width:100%; font:inherit;}
.editorFooter {padding:0.25em 0em; font-size:.9em;}
.editorFooter .button {padding-top:0px; padding-bottom:0px;}

.fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}

.sparkline {line-height:1em;}
.sparktick {outline:0;}

.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
.zoomer div {padding:1em;}

* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
#backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel {display:none; z-index:100; position:absolute; width:90%; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}

.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/
/***
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which need larger font sizes.
***/
/*{{{*/
body {font-size:0.8em;}
#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}
.subtitle {font-size:0.8em;}
.viewer table.listView {font-size:0.95em;}
/*}}}*/
/*{{{*/
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none ! important;}
#displayArea {margin: 1em 1em 0em 1em;}
/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
noscript {display:none;}
}
/*}}}*/
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='edit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser excludeLists'></span></div>
<!--}}}-->
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* MainMenu: The menu (usually on the left)
* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: <<option txtUserName>>
These InterfaceOptions for customising TiddlyWiki are saved in your browser

Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)

<<option txtUserName>>
<<option chkSaveBackups>> SaveBackups
<<option chkAutoSave>> AutoSave
<<option chkRegExpSearch>> RegExpSearch
<<option chkCaseSensitiveSearch>> CaseSensitiveSearch
<<option chkAnimate>> EnableAnimations

----
Also see [[AdvancedOptions]]
<<importTiddlers>>
* A tiddler's tags are show ''above'' the tiddlers title (instead of in a right-floated box).
* A tiddler's tagging list is shown ''below'' the tiddlers content (instead of in a left-floated box).
These layout changes are done with small changes to ViewTemplate (See TagglyTaggingViewTempplate) and EditTemplate (see TagglyTaggingEditTemplate) and with some CSS from TagglyTaggingStyles.
The default TW tagging macro is called "tagging". TagglyTagging provides an enhanced version of this called "tagglyTagging". So in the TagglyTaggingViewTemplate, macro="tagging" is replaced by "macro="tagglyTagging". The tagglyTagging macro lets you do things like:
* Sort the tagged list ascending or descending by title, modified date or created date
* Format the list in columns
* Hide the tagged list
It also remembers your preference on a per-tiddler basis. 
The "New Here" button is optional but very useful. It lets you quickly create a new tiddler that is automatically tagged with the tag you're looking at. Eg if you are looking at Addresses, clicking "new here" creates a new Address.
This is also optional. Because we often want to view a tag as a tiddler this makes the default action when you click on a tag. You can access to other tag functions by clicking the drop down icon to the right of the tag.
TagglyTagging (also known as "TagglyWiki Style Tagging") is set of plugins, templates and styles you can install in your TiddlyWiki that let you use tagging in powerful and useful ways.
Actually a lot less than there used to be since TiddlyWiki version 2.0! (TagglyTagging introduced the concept that a tag was a tiddler and vice-versa. Since version 2.0 this concept central to tagging in TiddlyWiki. See "Where did it come from?" below for more information).
TagglyTagging lets you better utilise the power of tagging your TiddlyWiki data. 
A brief list of what you can do with TagglyTagging:
* Dynamically define the structure of your data.
* Avoid hand maintaining "index" pages of your tiddlers.
* Make all your data easily locatable via your tag heirachy.

Other reasons you should give TagglyTagging a try:
* You find you aren't getting a lot of value from tagging your tiddlers in TiddlyWiki.
* You are sometimes annoyed by tag popup menus especially when they are longer than your page.

Sounds good but I'm not feelin' it...
* It is hard to explain without a demonstration. Try the TagglyTaggingTutorial.
The easiest way is to download an empty file from [[Download]]. You can of course install each component listed at TagglyTagging.
See [[TagglyTagging History]].
/***
|Name:|CloseOnCancelPlugin|
|Description:|Closes the tiddler if you click new tiddler then cancel. Default behaviour is to leave it open|
|Version:|3.0.1a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#CloseOnCancelPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
***/
//{{{
merge(config.commands.cancelTiddler,{

  handler_mptw_orig_closeUnsaved: config.commands.cancelTiddler.handler,

  handler: function(event,src,title) {
    this.handler_mptw_orig_closeUnsaved(event,src,title);
    if (!story.isDirty(title) && !store.tiddlerExists(title) && !store.isShadowTiddler(title))
      story.closeTiddler(title,true);
    return false;
  }

});

//}}}
Name: MptwBlue
Background: #fff
Foreground: #000
PrimaryPale: #cdf
PrimaryLight: #57c
PrimaryMid: #114
PrimaryDark: #012
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88

simon.baird/%%/@gmail.com
/***
|''Name:''|CryptoFunctionsPlugin|
|''Description:''|Support for cryptographic functions|
***/
//{{{
if(!version.extensions.CryptoFunctionsPlugin) {
version.extensions.CryptoFunctionsPlugin = {installed:true};

//--
//-- Crypto functions and associated conversion routines
//--

// Crypto "namespace"
function Crypto() {}

// Convert a string to an array of big-endian 32-bit words
Crypto.strToBe32s = function(str)
{
	var be = Array();
	var len = Math.floor(str.length/4);
	var i, j;
	for(i=0, j=0; i<len; i++, j+=4) {
		be[i] = ((str.charCodeAt(j)&0xff) << 24)|((str.charCodeAt(j+1)&0xff) << 16)|((str.charCodeAt(j+2)&0xff) << 8)|(str.charCodeAt(j+3)&0xff);
	}
	while (j<str.length) {
		be[j>>2] |= (str.charCodeAt(j)&0xff)<<(24-(j*8)%32);
		j++;
	}
	return be;
};

// Convert an array of big-endian 32-bit words to a string
Crypto.be32sToStr = function(be)
{
	var str = "";
	for(var i=0;i<be.length*32;i+=8)
		str += String.fromCharCode((be[i>>5]>>>(24-i%32)) & 0xff);
	return str;
};

// Convert an array of big-endian 32-bit words to a hex string
Crypto.be32sToHex = function(be)
{
	var hex = "0123456789ABCDEF";
	var str = "";
	for(var i=0;i<be.length*4;i++)
		str += hex.charAt((be[i>>2]>>((3-i%4)*8+4))&0xF) + hex.charAt((be[i>>2]>>((3-i%4)*8))&0xF);
	return str;
};

// Return, in hex, the SHA-1 hash of a string
Crypto.hexSha1Str = function(str)
{
	return Crypto.be32sToHex(Crypto.sha1Str(str));
};

// Return the SHA-1 hash of a string
Crypto.sha1Str = function(str)
{
	return Crypto.sha1(Crypto.strToBe32s(str),str.length);
};

// Calculate the SHA-1 hash of an array of blen bytes of big-endian 32-bit words
Crypto.sha1 = function(x,blen)
{
	// Add 32-bit integers, wrapping at 32 bits
	add32 = function(a,b)
	{
		var lsw = (a&0xFFFF)+(b&0xFFFF);
		var msw = (a>>16)+(b>>16)+(lsw>>16);
		return (msw<<16)|(lsw&0xFFFF);
	};
	// Add five 32-bit integers, wrapping at 32 bits
	add32x5 = function(a,b,c,d,e)
	{
		var lsw = (a&0xFFFF)+(b&0xFFFF)+(c&0xFFFF)+(d&0xFFFF)+(e&0xFFFF);
		var msw = (a>>16)+(b>>16)+(c>>16)+(d>>16)+(e>>16)+(lsw>>16);
		return (msw<<16)|(lsw&0xFFFF);
	};
	// Bitwise rotate left a 32-bit integer by 1 bit
	rol32 = function(n)
	{
		return (n>>>31)|(n<<1);
	};

	var len = blen*8;
	// Append padding so length in bits is 448 mod 512
	x[len>>5] |= 0x80 << (24-len%32);
	// Append length
	x[((len+64>>9)<<4)+15] = len;
	var w = Array(80);

	var k1 = 0x5A827999;
	var k2 = 0x6ED9EBA1;
	var k3 = 0x8F1BBCDC;
	var k4 = 0xCA62C1D6;

	var h0 = 0x67452301;
	var h1 = 0xEFCDAB89;
	var h2 = 0x98BADCFE;
	var h3 = 0x10325476;
	var h4 = 0xC3D2E1F0;

	for(var i=0;i<x.length;i+=16) {
		var j,t;
		var a = h0;
		var b = h1;
		var c = h2;
		var d = h3;
		var e = h4;
		for(j = 0;j<16;j++) {
			w[j] = x[i+j];
			t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
			e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
		}
		for(j=16;j<20;j++) {
			w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
			t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
			e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
		}
		for(j=20;j<40;j++) {
			w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
			t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k2);
			e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
		}
		for(j=40;j<60;j++) {
			w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
			t = add32x5(e,(a>>>27)|(a<<5),(b&c)|(d&(b|c)),w[j],k3);
			e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
		}
		for(j=60;j<80;j++) {
			w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
			t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k4);
			e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
		}

		h0 = add32(h0,a);
		h1 = add32(h1,b);
		h2 = add32(h2,c);
		h3 = add32(h3,d);
		h4 = add32(h4,e);
	}
	return Array(h0,h1,h2,h3,h4);
};


}
//}}}
[[Welcome]] [[Download]]
/***
|''Name:''|DeprecatedFunctionsPlugin|
|''Description:''|Support for deprecated functions removed from core|
***/
//{{{
if(!version.extensions.DeprecatedFunctionsPlugin) {
version.extensions.DeprecatedFunctionsPlugin = {installed:true};

//--
//-- Deprecated code
//--

// @Deprecated: Use createElementAndWikify and this.termRegExp instead
config.formatterHelpers.charFormatHelper = function(w)
{
	w.subWikify(createTiddlyElement(w.output,this.element),this.terminator);
};

// @Deprecated: Use enclosedTextHelper and this.lookaheadRegExp instead
config.formatterHelpers.monospacedByLineHelper = function(w)
{
	var lookaheadRegExp = new RegExp(this.lookahead,"mg");
	lookaheadRegExp.lastIndex = w.matchStart;
	var lookaheadMatch = lookaheadRegExp.exec(w.source);
	if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
		var text = lookaheadMatch[1];
		if(config.browser.isIE)
			text = text.replace(/\n/g,"\r");
		createTiddlyElement(w.output,"pre",null,null,text);
		w.nextMatch = lookaheadRegExp.lastIndex;
	}
};

// @Deprecated: Use <br> or <br /> instead of <<br>>
config.macros.br = {};
config.macros.br.handler = function(place)
{
	createTiddlyElement(place,"br");
};

// Find an entry in an array. Returns the array index or null
// @Deprecated: Use indexOf instead
Array.prototype.find = function(item)
{
	var i = this.indexOf(item);
	return i == -1 ? null : i;
};

// Load a tiddler from an HTML DIV. The caller should make sure to later call Tiddler.changed()
// @Deprecated: Use store.getLoader().internalizeTiddler instead
Tiddler.prototype.loadFromDiv = function(divRef,title)
{
	return store.getLoader().internalizeTiddler(store,this,title,divRef);
};

// Format the text for storage in an HTML DIV
// @Deprecated Use store.getSaver().externalizeTiddler instead.
Tiddler.prototype.saveToDiv = function()
{
	return store.getSaver().externalizeTiddler(store,this);
};

// @Deprecated: Use store.allTiddlersAsHtml() instead
function allTiddlersAsHtml()
{
	return store.allTiddlersAsHtml();
}

// @Deprecated: Use refreshPageTemplate instead
function applyPageTemplate(title)
{
	refreshPageTemplate(title);
}

// @Deprecated: Use story.displayTiddlers instead
function displayTiddlers(srcElement,titles,template,unused1,unused2,animate,unused3)
{
	story.displayTiddlers(srcElement,titles,template,animate);
}

// @Deprecated: Use story.displayTiddler instead
function displayTiddler(srcElement,title,template,unused1,unused2,animate,unused3)
{
	story.displayTiddler(srcElement,title,template,animate);
}

// @Deprecated: Use functions on right hand side directly instead
var createTiddlerPopup = Popup.create;
var scrollToTiddlerPopup = Popup.show;
var hideTiddlerPopup = Popup.remove;

// @Deprecated: Use right hand side directly instead
var regexpBackSlashEn = new RegExp("\\\\n","mg");
var regexpBackSlash = new RegExp("\\\\","mg");
var regexpBackSlashEss = new RegExp("\\\\s","mg");
var regexpNewLine = new RegExp("\n","mg");
var regexpCarriageReturn = new RegExp("\r","mg");

}
//}}}
This software is opensource and will always be free. However donations will encourage the continued enhancement and maintenance of MPTW (and also MonkeyGTD). Please use the link below if you would like to donate. Thank you!
<html><form target="_blank" action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_xclick">
<input type="hidden" name="business" value="simon.baird@gmail.com">
<input type="hidden" name="item_name" value="Support and appreciation for MonkeyPirateTiddlyWiki. Thanks!">
<input type="hidden" name="item_number" value="">
<input type="hidden" name="no_shipping" value="0">
<input type="hidden" name="no_note" value="1">
<input type="hidden" name="currency_code" value="USD">
<input type="hidden" name="tax" value="0">
<input type="hidden" name="bn" value="PP-DonationsBF">
<input type="image" src="https://www.paypal.com/en_US/i/btn/x-click-but04.gif" border="0" name="submit" alt="Make payments with PayPal - it's fast, free and secure!">
<img alt="" border="0" src="https://www.paypal.com/en_AU/i/scr/pixel.gif" width="1" height="1">
</form></html>
Right click on the following download link and select "Save link as..." or "Save target as...". Save the downloaded file on your desktop (or anywhere you like) then double click it to start using it.

{{downloadLink{
[[Download|http://mptw.tiddlyspot.com/empty.html]]
}}}

!!Updating from a 2.3 or higher MPTW
(Note, you can find your MPTW version number using {{{<<mptwVersion>>}}}. Current latest version is <<mptwVersion>>).
* Click backstage, import
* Select MptwUpgradeURL from predefined feeds and click open
* Tick all by clicking the checkbox in the column heading
* Untick "Keep these tiddlers linked..."
* Click import
* Save and reload

!!Updating from an older MPTW
* Since there's lots of changes you need to download a fresh MPTW using the above link then import your content. Try not to import any of the MPTW plugins from your old file. There is a thread [[here|http://groups.google.com/group/TiddlyWiki/browse_thread/thread/93b4c6edd3b6cd16]] that includes more details. (If you're not using Tiddlyspot then just skip the first part about downloading your site).
/***
|Name:|ExtentTagButtonPlugin|
|Description:|Adds a New tiddler button in the tag drop down|
|Version:|3.2a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#ExtendTagButtonPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License|http://mptw.tiddlyspot.com/#TheBSDLicense|
***/
//{{{

window.onClickTag_mptw_orig = window.onClickTag;
window.onClickTag = function(e) {
  window.onClickTag_mptw_orig.apply(this,arguments);
  var tag = this.getAttribute("tag");
  var title = this.getAttribute("tiddler");
  // Thanks Saq, you're a genius :)
  var popup = Popup.stack[Popup.stack.length-1].popup;
  createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
  wikify("<<newTiddler label:'New tiddler' tag:'"+tag+"'>>",createTiddlyElement(popup,"li"));
  return false;
}

//}}}
/***
|Name:|HideWhenPlugin|
|Description:|Allows conditional inclusion/exclusion in templates|
|Version:|3.2a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#HideWhenPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
For use in ViewTemplate and EditTemplate. Example usage:
{{{<div macro="showWhenTagged Task">[[TaskToolbar]]</div>}}}
{{{<div macro="showWhen tiddler.modifier == 'BartSimpson'"><img src="bart.gif"/></div>}}}

Warning: the showWhen and hideWhen macros will blindly eval paramString.
This could be used to execute harmful javascript from a tiddler.

(TODO: Make some effort to sanitize paramString. Perhaps disallow the equals sign?)
***/
//{{{

window.hideWhenLastTest = false;

window.removeElementWhen = function(test,place) {
  window.hideWhenLastTest = test;
  if (test) {
    jQuery(place).empty()
    place.parentNode.removeChild(place);
  }
};

merge(config.macros,{

  hideWhen: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
    removeElementWhen( eval(paramString), place );
  }},

  showWhen: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
    removeElementWhen( !eval(paramString), place );
  }},

  hideWhenTagged: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
    removeElementWhen( tiddler.tags.containsAll(params), place );
  }},

  showWhenTagged: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
    removeElementWhen( !tiddler.tags.containsAll(params), place );
  }},

  hideWhenTaggedAny: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
    removeElementWhen( tiddler.tags.containsAny(params), place );
  }},

  showWhenTaggedAny: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
    removeElementWhen( !tiddler.tags.containsAny(params), place );
  }},

  hideWhenTaggedAll: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
    removeElementWhen( tiddler.tags.containsAll(params), place );
  }},

  showWhenTaggedAll: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
    removeElementWhen( !tiddler.tags.containsAll(params), place );
  }},

  hideWhenExists: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
    removeElementWhen( store.tiddlerExists(params[0]) || store.isShadowTiddler(params[0]), place );
  }},

  showWhenExists: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
    removeElementWhen( !(store.tiddlerExists(params[0]) || store.isShadowTiddler(params[0])), place );
  }},

  hideWhenTitleIs: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
    removeElementWhen( tiddler.title == params[0], place );
  }},

  showWhenTitleIs: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
    removeElementWhen( tiddler.title != params[0], place );
  }},

  'else': { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
    removeElementWhen( !window.hideWhenLastTest, place );
  }}

});

//}}}

/***
|Name:|InstantTimestampPlugin|
|Description:|A handy way to insert timestamps in your tiddler content|
|Version:|1.0.10a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#InstantTimestampPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
!!Usage
If you enter {ts} in your tiddler content (without the spaces) it will be replaced with a timestamp when you save the tiddler. Full list of formats:
* {ts} or {t} -> timestamp
* {ds} or {d} -> datestamp
* !ts or !t at start of line -> !!timestamp
* !ds or !d at start of line -> !!datestamp
(I added the extra ! since that's how I like it. Remove it from translations below if required)
!!Notes
* Change the timeFormat and dateFormat below to suit your preference.
* See also http://mptw2.tiddlyspot.com/#AutoCorrectPlugin
* You could invent other translations and add them to the translations array below.
***/
//{{{

config.InstantTimestamp = {

  // adjust to suit
  timeFormat: 'DD/0MM/YY 0hh:0mm',
  dateFormat: 'DD/0MM/YY',

  translations: [
    [/^!ts?$/img,  "'!!{{ts{'+now.formatString(config.InstantTimestamp.timeFormat)+'}}}'"],
    [/^!ds?$/img,  "'!!{{ds{'+now.formatString(config.InstantTimestamp.dateFormat)+'}}}'"],

    // thanks Adapted Cat
    [/\{ts?\}(?!\}\})/ig,"'{{ts{'+now.formatString(config.InstantTimestamp.timeFormat)+'}}}'"],
    [/\{ds?\}(?!\}\})/ig,"'{{ds{'+now.formatString(config.InstantTimestamp.dateFormat)+'}}}'"]

  ],

  excludeTags: [
    "noAutoCorrect",
    "noTimestamp",
    "html",
    "CSS",
    "css",
    "systemConfig",
    "systemConfigDisabled",
    "zsystemConfig",
    "Plugins",
    "Plugin",
    "plugins",
    "plugin",
    "javascript",
    "code",
    "systemTheme",
    "systemPalette"
  ],

  excludeTiddlers: [
    "StyleSheet",
    "StyleSheetLayout",
    "StyleSheetColors",
    "StyleSheetPrint"
    // more?
  ]

};

TiddlyWiki.prototype.saveTiddler_mptw_instanttimestamp = TiddlyWiki.prototype.saveTiddler;
TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created) {

  tags = tags ? tags : []; // just in case tags is null
  tags = (typeof(tags) == "string") ? tags.readBracketedList() : tags;
  var conf = config.InstantTimestamp;

  if ( !tags.containsAny(conf.excludeTags) && !conf.excludeTiddlers.contains(newTitle) ) {

    var now = new Date();
    var trans = conf.translations;
    for (var i=0;i<trans.length;i++) {
      newBody = newBody.replace(trans[i][0], eval(trans[i][1]));
    }
  }

  // TODO: use apply() instead of naming all args?
  return this.saveTiddler_mptw_instanttimestamp(title,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created);
}

// you can override these in StyleSheet
setStylesheet(".ts,.ds { font-style:italic; }","instantTimestampStyles");

//}}}
/***
|Name:|LessBackupsPlugin|
|Description:|Intelligently limit the number of backup files you create|
|Version:|3.0.1a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#LessBackupsPlugin|
|Author:|Simon Baird|
|Email:|simon.baird@gmail.com|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
!!Description
You end up with just backup one per year, per month, per weekday, per hour, minute, and second.  So total number won't exceed about 200 or so. Can be reduced by commenting out the seconds/minutes/hours line from modes array
!!Notes
Works in IE and Firefox only.  Algorithm by Daniel Baird. IE specific code by by Saq Imtiaz.
***/
//{{{

var MINS  = 60 * 1000;
var HOURS = 60 * MINS;
var DAYS  = 24 * HOURS;

if (!config.lessBackups) {
  config.lessBackups = {
    // comment out the ones you don't want or set config.lessBackups.modes in your 'tweaks' plugin
    modes: [
      ["YYYY",  365*DAYS], // one per year for ever
      ["MMM",   31*DAYS],  // one per month
      ["ddd",   7*DAYS],   // one per weekday
      //["d0DD",  1*DAYS],   // one per day of month
      ["h0hh",  24*HOURS], // one per hour
      ["m0mm",  1*HOURS],  // one per minute
      ["s0ss",  1*MINS],   // one per second
      ["latest",0]         // always keep last version. (leave this).
    ]
  };
}

window.getSpecialBackupPath = function(backupPath) {

  var now = new Date();

  var modes = config.lessBackups.modes;

  for (var i=0;i<modes.length;i++) {

    // the filename we will try
    var specialBackupPath = backupPath.replace(/(\.)([0-9]+\.[0-9]+)(\.html)$/,
        '$1'+now.formatString(modes[i][0]).toLowerCase()+'$3')

    // open the file
    try {
      if (config.browser.isIE) {
        var fsobject = new ActiveXObject("Scripting.FileSystemObject")
        var fileExists  = fsobject.FileExists(specialBackupPath);
        if (fileExists) {
          var fileObject = fsobject.GetFile(specialBackupPath);
          var modDate = new Date(fileObject.DateLastModified).valueOf();
        }
      }
      else {
        netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
        var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
        file.initWithPath(specialBackupPath);
        var fileExists = file.exists();
        if (fileExists) {
          var modDate = file.lastModifiedTime;
        }
      }
    }
    catch(e) {
      // give up
      return backupPath;
    }

    // expiry is used to tell if it's an 'old' one. Eg, if the month is June and there is a
    // June file on disk that's more than an month old then it must be stale so overwrite
    // note that "latest" should be always written because the expiration period is zero (see above)
    var expiry = new Date(modDate + modes[i][1]);
    if (!fileExists || now > expiry)
      return specialBackupPath;
  }
}

// hijack the core function
window.getBackupPath_mptw_orig = window.getBackupPath;
window.getBackupPath = function(localPath) {
  return getSpecialBackupPath(getBackupPath_mptw_orig(localPath));
}

//}}}
Plugins on this site will always be free of charge and open source. They are licensed under TheBSDLicense.
MPTW is a distribution or edition of TiddlyWiki that includes a standard TiddlyWiki core packaged with some plugins designed to improve usability and provide a better way to organise your information. For more information see http://mptw.tiddlyspot.com/.
[[Welcome]] [[Download]] <<tag Plugins|systemConfig>> [[More Plugins]] [[What's New]] [[Blog|New Blog]] MonkeyGTD [[Donate]] [[Contact]] [[License]]
MonkeyGTD is a Getting Things Done&trade;* system powered by MonkeyPirateTiddlyWiki. 

The currently stable version is based on TW 2.0.x and can be found at http://monkeygtd.tiddlyspot.com/

There is also a pretty usable (if you have Firefox) alpha over at http://mgtd-alpha.tiddlyspot.com/ (right click [[here|http://mgtd-alpha.tiddlyspot.com/empty.html]] to download) and a MonkeyGTD blog at http://monkeygtd.blogspot.com/
That's what MPTW stands for.
* [[WordCountMacro|http://wordcountmacro.tiddlyspot.com/]] - a one-liner that displays a tiddler's word count
* [[LinkToMeMacro|http://linktomemacro.tiddlyspot.com/]] - another one-liner that automatically makes a link to the current TiddlyWiki file

Older and experimental stuff:
* http://mptw-beta.tiddlyspot.com/
* http://mptw2.tiddlyspot.com/
* http://mptw1.tiddlyspot.com/
Name: MptwBlack
Background: #000
Foreground: #fff
PrimaryPale: #333
PrimaryLight: #555
PrimaryMid: #888
PrimaryDark: #aaa
SecondaryPale: #111
SecondaryLight: #222
SecondaryMid: #555
SecondaryDark: #888
TertiaryPale: #222
TertiaryLight: #666
TertiaryMid: #888
TertiaryDark: #aaa
Error: #300

Name: MptwBlue
Background: #fff
Foreground: #000
PrimaryPale: #cdf
PrimaryLight: #57c
PrimaryMid: #114
PrimaryDark: #012
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88

//{{{
// Pretty sure this is incomplete and experimental
// TODO: Fix it or remove it.

(function($){

merge(config.macros,{
  mptwCollapse: {
    handler: function(place,macroName,params) {
      createTiddlyButton(place, params[0] == '+' ? '\u25AD' : '\u25AC', 'collapse/uncollapse', function(){
        $(story.findContainingTiddler(place)).toggleClass('collapsed');
      });
    }
  }
});

/* this doesn't work unless you have a modified ViewTempate */
config.shadowTiddlers["MptwCollapsePluginStyles"] = ""
  +".collapsed .uncollapsedView { display:none;       }"
  +".collapsedView              { display:none;       }"
  +".collapsed .collapsedView   { display:block;      }"
  +".tiddler.collapsed          { padding-bottom:1em; }"
  +".tiddler.collapsed .title   { font-size:100%;     }"
;

store.addNotification("MptwCollapsePluginStyles",refreshStyles);

})(jQuery);

//}}}
/***
|Name:|MptwConfigPlugin|
|Description:|Miscellaneous tweaks used by MPTW|
|Version:|1.0a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#MptwConfigPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#MptwConfigPlugin|
!!Note: instead of editing this you should put overrides in MptwUserConfigPlugin
***/
//{{{
var originalReadOnly = readOnly;
var originalShowBackstage = showBackstage;

config.options.chkHttpReadOnly = false;  // means web visitors can experiment with your site by clicking edit
readOnly = false;                        // needed because the above doesn't work any more post 2.1 (??)
showBackstage = true;                    // show backstage for same reason

config.options.chkInsertTabs = true;     // tab inserts a tab when editing a tiddler
config.views.wikified.defaultText = "";  // don't need message when a tiddler doesn't exist
config.views.editor.defaultText = "";    // don't need message when creating a new tiddler

config.options.chkSaveBackups = true;         // do save backups
config.options.txtBackupFolder = 'twbackup';  // put backups in a backups folder

config.options.chkAutoSave = (window.location.protocol == "file:"); // do autosave if we're in local file

config.mptwVersion = "2.7.4";

config.macros.mptwVersion={handler:function(place){wikify(config.mptwVersion,place);}};

if (config.options.txtTheme == '')
  config.options.txtTheme = 'MptwTheme';

// add to default GettingStarted
config.shadowTiddlers.GettingStarted += "\n\nSee also [[MPTW]].";

// add select theme and palette controls in default OptionsPanel
config.shadowTiddlers.OptionsPanel = config.shadowTiddlers.OptionsPanel.replace(/(\n\-\-\-\-\nAlso see \[\[AdvancedOptions\]\])/, "{{select{<<selectTheme>>\n<<selectPalette>>}}}$1");

// these are used by ViewTemplate
config.mptwDateFormat = 'DD/MM/YY';
config.mptwJournalFormat = 'Journal DD/MM/YY';

//}}}
Name: MptwGreen
Background: #fff
Foreground: #000
PrimaryPale: #9b9
PrimaryLight: #385
PrimaryMid: #031
PrimaryDark: #020
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88

Name: MptwRed
Background: #fff
Foreground: #000
PrimaryPale: #eaa
PrimaryLight: #c55
PrimaryMid: #711
PrimaryDark: #500
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88

|Name|MptwRounded|
|Description|Mptw Theme with some rounded corners (Firefox only)|
|ViewTemplate|MptwTheme##ViewTemplate|
|EditTemplate|MptwTheme##EditTemplate|
|PageTemplate|MptwTheme##PageTemplate|
|StyleSheet|##StyleSheet|

!StyleSheet
/*{{{*/

[[MptwTheme##StyleSheet]]

.tiddler,
.sliderPanel,
.button,
.tiddlyLink,
.tabContents
{ -moz-border-radius: 1em; }

.tab {
	-moz-border-radius-topleft: 0.5em;
	-moz-border-radius-topright: 0.5em;
}
#topMenu {
	-moz-border-radius-bottomleft: 2em;
	-moz-border-radius-bottomright: 2em;
}

/*}}}*/

Name: MptwSmoke
Background: #fff
Foreground: #000
PrimaryPale: #aaa
PrimaryLight: #777
PrimaryMid: #111
PrimaryDark: #000
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88

|Name|MptwStandard|
|Description|Mptw Theme with the default TiddlyWiki PageLayout and Styles|
|ViewTemplate|MptwTheme##ViewTemplate|
|EditTemplate|MptwTheme##EditTemplate|
Name: MptwTeal
Background: #fff
Foreground: #000
PrimaryPale: #B5D1DF
PrimaryLight: #618FA9
PrimaryMid: #1a3844
PrimaryDark: #000
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #f8f8f8
TertiaryLight: #bbb
TertiaryMid: #999
TertiaryDark: #888
Error: #f88
|Name|MptwTheme|
|Description|Mptw Theme including custom PageLayout|
|PageTemplate|##PageTemplate|
|ViewTemplate|##ViewTemplate|
|EditTemplate|##EditTemplate|
|StyleSheet|##StyleSheet|

http://mptw.tiddlyspot.com/#MptwTheme ($Rev: 1829 $)

!PageTemplate
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
	<div class='headerShadow'>
		<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
		<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
	</div>
	<div class='headerForeground'>
		<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
		<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
	</div>
</div>
<!-- horizontal MainMenu -->
<div id='topMenu' refresh='content' tiddler='MainMenu'></div>
<!-- original MainMenu menu -->
<!-- <div id='mainMenu' refresh='content' tiddler='MainMenu'></div> -->
<div id='sidebar'>
	<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
	<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea'>
	<div id='messageArea'></div>
	<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->

!ViewTemplate
<!--{{{-->
<div class="uncollapsedView">
[[MptwTheme##ViewTemplateToolbar]]

<div class="tagglyTagged" macro="tags"></div>

<div class='titleContainer'>
	<span class='title' macro='view title'></span>
	<span macro="miniTag"></span>
</div>

<div class='subtitle'>
	(updated <span macro='view modified date {{config.mptwDateFormat?config.mptwDateFormat:"MM/0DD/YY"}}'></span>
	by <span macro='view modifier link'></span>)
	<!--
	(<span macro='message views.wikified.createdPrompt'></span>
	<span macro='view created date {{config.mptwDateFormat?config.mptwDateFormat:"MM/0DD/YY"}}'></span>)
	-->
</div>

<div macro="showWhen tiddler.tags.containsAny(['css','html','pre','systemConfig']) && !tiddler.text.match('{{'+'{')">
	<div class='viewer'><pre macro='view text'></pre></div>
</div>
<div macro="else">
	<div class='viewer' macro='view text wikified'></div>
</div>

<div class="tagglyTagging" macro="tagglyTagging"></div>
</div>
<div class="collapsedView">
	<span class='toolbar'>
		<span macro='toolbar closeTiddler'></span>
		<span macro='mptwCollapse +'></span>
	</span>
	<span class='title' macro='view title'></span>
</div>

<!--}}}-->

!ViewTemplateToolbar
<!--{{{-->
<div class='toolbar'>
	<span macro="showWhenTagged systemConfig">
		<span macro="toggleTag systemConfigDisable . '[[disable|systemConfigDisable]]'"></span>
	</span>
	<span macro="showWhenTagged systemTheme"><span macro="applyTheme"></span></span>
	<span macro="showWhenTagged systemPalette"><span macro="applyPalette"></span></span>
	<span macro="showWhen tiddler.tags.contains('css') || tiddler.title == 'StyleSheet'"><span macro="refreshAll"></span></span>
	<span style="padding:1em;"></span>
	<span macro='toolbar closeTiddler closeOthers +editTiddler deleteTiddler > fields syncing permalink references jump'></span> <span macro='newHere label:"new here"'></span>
	<span macro='newJournalHere {{config.mptwJournalFormat?config.mptwJournalFormat:"MM/0DD/YY"}}'></span>
	<!--span macro='mptwCollapse -'></span-->
</div>
<!--}}}-->

!EditTemplate
<!--{{{-->
<div class="toolbar" macro="toolbar +saveTiddler saveCloseTiddler closeOthers -cancelTiddler cancelCloseTiddler deleteTiddler"></div>
<div class="title" macro="view title"></div>
<div class="editLabel">Title</div><div class="editor" macro="edit title"></div>
<div macro='annotations'></div>
<div class="editLabel">Content</div><div class="editor" macro="edit text"></div>
<div class="editLabel">Tags</div><div class="editor" macro="edit tags"></div>
<div class="editorFooter"><span macro="message views.editor.tagPrompt"></span><span macro="tagChooser"></span></div>
<!--}}}-->

!StyleSheet
/*{{{*/

/* a contrasting background so I can see where one tiddler ends and the other begins */
body {
	background: [[ColorPalette::TertiaryLight]];
}

/* sexy colours and font for the header */
.headerForeground {
	color: [[ColorPalette::PrimaryPale]];
}
.headerShadow, .headerShadow a {
	color: [[ColorPalette::PrimaryMid]];
}

/* separate the top menu parts */
.headerForeground, .headerShadow {
	padding: 1em 1em 0;
}

.headerForeground, .headerShadow {
	font-family: 'Trebuchet MS', sans-serif;
	font-weight:bold;
}
.headerForeground .siteSubtitle {
	color: [[ColorPalette::PrimaryLight]];
}
.headerShadow .siteSubtitle {
	color: [[ColorPalette::PrimaryMid]];
}

/* make shadow go and down right instead of up and left */
.headerShadow {
	left: 1px;
	top: 1px;
}

/* prefer monospace for editing */
.editor textarea, .editor input {
	font-family: 'Consolas', monospace;
	background-color:[[ColorPalette::TertiaryPale]];
}


/* sexy tiddler titles */
.title {
	font-size: 250%;
	color: [[ColorPalette::PrimaryLight]];
	font-family: 'Trebuchet MS', sans-serif;
}

/* more subtle tiddler subtitle */
.subtitle {
	padding:0px;
	margin:0px;
	padding-left:1em;
	font-size: 90%;
	color: [[ColorPalette::TertiaryMid]];
}
.subtitle .tiddlyLink {
	color: [[ColorPalette::TertiaryMid]];
}

/* a little bit of extra whitespace */
.viewer {
	padding-bottom:3px;
}

/* don't want any background color for headings */
h1,h2,h3,h4,h5,h6 {
	background-color: transparent;
	color: [[ColorPalette::Foreground]];
}

/* give tiddlers 3d style border and explicit background */
.tiddler {
	background: [[ColorPalette::Background]];
	border-right: 2px [[ColorPalette::TertiaryMid]] solid;
	border-bottom: 2px [[ColorPalette::TertiaryMid]] solid;
	margin-bottom: 1em;
	padding:1em 2em 2em 1.5em;
}

/* make options slider look nicer */
#sidebarOptions .sliderPanel {
	border:solid 1px [[ColorPalette::PrimaryLight]];
}

/* the borders look wrong with the body background */
#sidebar .button {
	border-style: none;
}

/* this means you can put line breaks in SidebarOptions for readability */
#sidebarOptions br {
	display:none;
}
/* undo the above in OptionsPanel */
#sidebarOptions .sliderPanel br {
	display:inline;
}

/* horizontal main menu stuff */
#displayArea {
	margin: 1em 15.7em 0em 1em; /* use the freed up space */
}
#topMenu br {
	display: none;
}
#topMenu {
	background: [[ColorPalette::PrimaryMid]];
	color:[[ColorPalette::PrimaryPale]];
}
#topMenu {
	padding:2px;
}
#topMenu .button, #topMenu .tiddlyLink, #topMenu a {
	margin-left: 0.5em;
	margin-right: 0.5em;
	padding-left: 3px;
	padding-right: 3px;
	color: [[ColorPalette::PrimaryPale]];
	font-size: 115%;
}
#topMenu .button:hover, #topMenu .tiddlyLink:hover {
	background: [[ColorPalette::PrimaryDark]];
}

/* make 2.2 act like 2.1 with the invisible buttons */
.toolbar {
	visibility:hidden;
}
.selected .toolbar {
	visibility:visible;
}

/* experimental. this is a little borked in IE7 with the button 
 * borders but worth it I think for the extra screen realestate */
.toolbar { float:right; }

/* fix for TaggerPlugin. from sb56637. improved by FND */
.popup li .tagger a {
   display:inline;
}

/* makes theme selector look a little better */
#sidebarOptions .sliderPanel .select .button {
  padding:0.5em;
  display:block;
}
#sidebarOptions .sliderPanel .select br {
	display:none;
}

/* make it print a little cleaner */
@media print {
	#topMenu {
		display: none ! important;
	}
	/* not sure if we need all the importants */
	.tiddler {
		border-style: none ! important;
		margin:0px ! important;
		padding:0px ! important;
		padding-bottom:2em ! important;
	}
	.tagglyTagging .button, .tagglyTagging .hidebutton {
		display: none ! important;
	}
	.headerShadow {
		visibility: hidden ! important;
	}
	.tagglyTagged .quickopentag, .tagged .quickopentag {
		border-style: none ! important;
	}
	.quickopentag a.button, .miniTag {
		display: none ! important;
	}
}

/* get user styles specified in StyleSheet */
[[StyleSheet]]

/*}}}*/

|Name|MptwTrim|
|Description|Mptw Theme with a reduced header to increase useful space|
|ViewTemplate|MptwTheme##ViewTemplate|
|EditTemplate|MptwTheme##EditTemplate|
|StyleSheet|MptwTheme##StyleSheet|
|PageTemplate|##PageTemplate|

!PageTemplate
<!--{{{-->

<!-- horizontal MainMenu -->
<div id='topMenu' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
<span refresh='content' tiddler='SiteTitle' style="padding-left:1em;font-weight:bold;"></span>:
<span refresh='content' tiddler='MainMenu'></span>
</div>
<div id='sidebar'>
	<div id='sidebarOptions'>
		<div refresh='content' tiddler='SideBarOptions'></div>
		<div style="margin-left:0.1em;"
			macro='slider chkTabSliderPanel SideBarTabs {{"tabs \u00bb"}} "Show Timeline, All, Tags, etc"'></div>
	</div>
</div>
<div id='displayArea'>
	<div id='messageArea'></div>
	<div id='tiddlerDisplay'></div>
</div>

For upgrading. See [[ImportTiddlers]].
URL: http://mptw.tiddlyspot.com/upgrade.html
/***
|Description:|A place to put your config tweaks so they aren't overwritten when you upgrade MPTW|
***/
//{{{

config.mptwDateFormat = 'DD/MM/YY';
config.mptwJournalFormat = 'Journal DD/MM/YY';
//config.mptwDateFormat = 'MM/0DD/YY';
//config.mptwJournalFormat = 'Journal MM/0DD/YY';

config.options.txtTheme = 'MptwTheme';

//}}}
There is now a blog for MPTW. Check it out [[here|http://monkeypiratetiddlywiki.blogspot.com/]].
!!Theme overhaul plus theme and palette switcher
Click options and try "select theme" and "select palette". Also there is no longer the empty_nolayout. You can just download the empty.html and change theme to MptwStandard. If you like to customise your templates see [[Note about customising templates]].
!!Slice based tagging list modes
You can now show tagging lists with all slices or description slices. See [[systemConfig]] for an example of the description slice. To use these click the excerpts button to cycle through the modes.
!!New plugins
I've included InstantTimestampPlugin and PrettyDatesPlugin as part of the standard MPTW distribution.
!!Latest TW core
Finally. <<version>>. Sorry about the long wait.
/***
|Name:|NewHerePlugin|
|Description:|Creates the new here and new journal macros|
|Version:|3.0a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#NewHerePlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License|http://mptw.tiddlyspot.com/#TheBSDLicense|
***/
//{{{
merge(config.macros, {
  newHere: {
    handler: function(place,macroName,params,wikifier,paramString,tiddler) {
      wikify("<<newTiddler "+paramString+" tag:[["+tiddler.title+"]]>>",place,null,tiddler);
    }
  },
  newJournalHere: {
    handler: function(place,macroName,params,wikifier,paramString,tiddler) {
      wikify("<<newJournal "+paramString+" tag:[["+tiddler.title+"]]>>",place,null,tiddler);
    }
  }
});

//}}}
/***
|Name:|NewMeansNewPlugin|
|Description:|If 'New Tiddler' already exists then create 'New Tiddler (1)' and so on|
|Version:|1.1.1a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/empty.html#NewMeansNewPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License|http://mptw.tiddlyspot.com/#TheBSDLicense|
!!Note: I think this should be in the core
***/
//{{{

// change this or set config.newMeansNewForJournalsToo it in MptwUuserConfigPlugin
if (config.newMeansNewForJournalsToo == undefined) config.newMeansNewForJournalsToo = true;

String.prototype.getNextFreeName = function() {
  numberRegExp = / \(([0-9]+)\)$/;
  var match = numberRegExp.exec(this);
  if (match) {
  var num = parseInt(match[1]) + 1;
    return this.replace(numberRegExp," ("+num+")");
  }
  else {
    return this + " (1)";
  }
}

config.macros.newTiddler.checkForUnsaved = function(newName) {
  var r = false;
  story.forEachTiddler(function(title,element) {
    if (title == newName)
      r = true;
  });
  return r;
}

config.macros.newTiddler.getName = function(newName) {
  while (store.getTiddler(newName) || config.macros.newTiddler.checkForUnsaved(newName))
    newName = newName.getNextFreeName();
  return newName;
}


config.macros.newTiddler.onClickNewTiddler = function()
{
  var title = this.getAttribute("newTitle");
  if(this.getAttribute("isJournal") == "true") {
    title = new Date().formatString(title.trim());
  }

  // ---- these three lines should be the only difference between this and the core onClickNewTiddler
  if (config.newMeansNewForJournalsToo || this.getAttribute("isJournal") != "true")
    title = config.macros.newTiddler.getName(title);

  var params = this.getAttribute("params");
  var tags = params ? params.split("|") : [];
  var focus = this.getAttribute("newFocus");
  var template = this.getAttribute("newTemplate");
  var customFields = this.getAttribute("customFields");
  if(!customFields && !store.isShadowTiddler(title))
    customFields = String.encodeHashMap(config.defaultCustomFields);
  story.displayTiddler(null,title,template,false,null,null);
  var tiddlerElem = story.getTiddler(title);
  if(customFields)
    story.addCustomFields(tiddlerElem,customFields);
  var text = this.getAttribute("newText");
  if(typeof text == "string")
    story.getTiddlerField(title,"text").value = text.format([title]);
  for(var t=0;t<tags.length;t++)
    story.setTiddlerTag(title,tags[t],+1);
  story.focusTiddler(title,focus);
  return false;
};

//}}}
MPTW is now using the new theme framework from TW 2.3. It's pretty new and not well documented so I will make some notes here for those who want to customise their MPTW. 

Take a look at the tiddlers tagged with <<tag systemTheme>>. The slices there specify the templates and stylesheets used by that theme. Notice also that you can use the new Tiddler##Section notation to use a section of a tiddler.

If you want to customise your templates make a new systemTheme copied from MptwTheme then modify that as required.
/***
|Name:|PrettyDatesPlugin|
|Description:|Provides a new date format ('pppp') that displays times such as '2 days ago'|
|Version:|1.0a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#PrettyDatesPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
!!Notes
* If you want to you can rename this plugin. :) Some suggestions: LastUpdatedPlugin, RelativeDatesPlugin, SmartDatesPlugin, SexyDatesPlugin.
* Inspired by http://ejohn.org/files/pretty.js
***/
//{{{
Date.prototype.prettyDate = function() {
  var diff = (((new Date()).getTime() - this.getTime()) / 1000);
  var day_diff = Math.floor(diff / 86400);

  if (isNaN(day_diff))      return "";
  else if (diff < 0)        return "in the future";
  else if (diff < 60)       return "just now";
  else if (diff < 120)      return "1 minute ago";
  else if (diff < 3600)     return Math.floor(diff/60) + " minutes ago";
  else if (diff < 7200)     return "1 hour ago";
  else if (diff < 86400)    return Math.floor(diff/3600) + " hours ago";
  else if (day_diff == 1)   return "Yesterday";
  else if (day_diff < 7)    return day_diff + " days ago";
  else if (day_diff < 14)   return  "a week ago";
  else if (day_diff < 31)   return Math.ceil(day_diff/7) + " weeks ago";
  else if (day_diff < 62)   return "a month ago";
  else if (day_diff < 365)  return "about " + Math.ceil(day_diff/31) + " months ago";
  else if (day_diff < 730)  return "a year ago";
  else                      return Math.ceil(day_diff/365) + " years ago";
}

Date.prototype.formatString_orig_mptw = Date.prototype.formatString;

Date.prototype.formatString = function(template) {
  return this.formatString_orig_mptw(template).replace(/pppp/,this.prettyDate());
}

// for MPTW. otherwise edit your ViewTemplate as required.
// config.mptwDateFormat = 'pppp (DD/MM/YY)';
config.mptwDateFormat = 'pppp';

//}}}
/***
|Name:|QuickOpenTagPlugin|
|Description:|Changes tag links to make it easier to open tags as tiddlers|
|Version:|3.0.1a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#QuickOpenTagPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
***/
//{{{
config.quickOpenTag = {

  dropdownChar: (document.all ? "\u25bc" : "\u25be"), // the little one doesn't work in IE?

  createTagButton: function(place,tag,excludeTiddler) {
    // little hack so we can do this: <<tag PrettyTagName|RealTagName>>
    var splitTag = tag.split("|");
    var pretty = tag;
    if (splitTag.length == 2) {
      tag = splitTag[1];
      pretty = splitTag[0];
    }

    var sp = createTiddlyElement(place,"span",null,"quickopentag");
    createTiddlyText(createTiddlyLink(sp,tag,false),pretty);

    var theTag = createTiddlyButton(sp,config.quickOpenTag.dropdownChar,
                        config.views.wikified.tag.tooltip.format([tag]),onClickTag);
    theTag.setAttribute("tag",tag);
    if (excludeTiddler)
      theTag.setAttribute("tiddler",excludeTiddler);
        return(theTag);
  },

  miniTagHandler: function(place,macroName,params,wikifier,paramString,tiddler) {
    var tagged = store.getTaggedTiddlers(tiddler.title);
    if (tagged.length > 0) {
      var theTag = createTiddlyButton(place,config.quickOpenTag.dropdownChar,
                          config.views.wikified.tag.tooltip.format([tiddler.title]),onClickTag);
      theTag.setAttribute("tag",tiddler.title);
      theTag.className = "miniTag";
    }
  },

  allTagsHandler: function(place,macroName,params) {
    var tags = store.getTags(params[0]);
    var filter = params[1]; // new feature
    var ul = createTiddlyElement(place,"ul");
    if(tags.length == 0)
      createTiddlyElement(ul,"li",null,"listTitle",this.noTags);
    for(var t=0; t<tags.length; t++) {
      var title = tags[t][0];
      if (!filter || (title.match(new RegExp('^'+filter)))) {
        var info = getTiddlyLinkInfo(title);
        var theListItem =createTiddlyElement(ul,"li");
        var theLink = createTiddlyLink(theListItem,tags[t][0],true);
        var theCount = " (" + tags[t][1] + ")";
        theLink.appendChild(document.createTextNode(theCount));
        var theDropDownBtn = createTiddlyButton(theListItem," " +
          config.quickOpenTag.dropdownChar,this.tooltip.format([tags[t][0]]),onClickTag);
        theDropDownBtn.setAttribute("tag",tags[t][0]);
      }
    }
  },

  // todo fix these up a bit
  styles: [
"/*{{{*/",
"/* created by QuickOpenTagPlugin */",
".tagglyTagged .quickopentag, .tagged .quickopentag ",
" { margin-right:1.2em; border:1px solid #eee; padding:2px; padding-right:0px; padding-left:1px; }",
".quickopentag .tiddlyLink { padding:2px; padding-left:3px; }",
".quickopentag a.button { padding:1px; padding-left:2px; padding-right:2px;}",
"/* extra specificity to make it work right */",
"#displayArea .viewer .quickopentag a.button, ",
"#displayArea .viewer .quickopentag a.tiddyLink, ",
"#mainMenu .quickopentag a.tiddyLink, ",
"#mainMenu .quickopentag a.tiddyLink ",
" { border:0px solid black; }",
"#displayArea .viewer .quickopentag a.button, ",
"#mainMenu .quickopentag a.button ",
" { margin-left:0px; padding-left:2px; }",
"#displayArea .viewer .quickopentag a.tiddlyLink, ",
"#mainMenu .quickopentag a.tiddlyLink ",
" { margin-right:0px; padding-right:0px; padding-left:0px; margin-left:0px; }",
"a.miniTag {font-size:150%;} ",
"#mainMenu .quickopentag a.button ",
" /* looks better in right justified main menus */",
" { margin-left:0px; padding-left:2px; margin-right:0px; padding-right:0px; }",
"#topMenu .quickopentag { padding:0px; margin:0px; border:0px; }",
"#topMenu .quickopentag .tiddlyLink { padding-right:1px; margin-right:0px; }",
"#topMenu .quickopentag .button { padding-left:1px; margin-left:0px; border:0px; }",
"/*}}}*/",
    ""].join("\n"),

  init: function() {
    // we fully replace these builtins. can't hijack them easily
    window.createTagButton = this.createTagButton;
    config.macros.allTags.handler = this.allTagsHandler;
    config.macros.miniTag = { handler: this.miniTagHandler };
    config.shadowTiddlers["QuickOpenTagStyles"] = this.styles;
    store.addNotification("QuickOpenTagStyles",refreshStyles);
  }
}

config.quickOpenTag.init();

//}}}
/***
|Name:|RenameTagsPlugin|
|Description:|Allows you to easily rename or delete tags across multiple tiddlers|
|Version:|3.1|
|Date:|17-Jun-2013|
|Source:|http://mptw.tiddlyspot.com/#RenameTagsPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License|http://mptw.tiddlyspot.com/#TheBSDLicense|
Rename a tag and you will be prompted to rename it in all its tagged tiddlers.
***/
//{{{
config.renameTags = {

  prompts: {
    rename: "Rename the tag '%0' to '%1' in %2 tiddler%3?",
    remove: "Remove the tag '%0' from %1 tiddler%2?"
  },

  removeTag: function(tag,tiddlers) {
    store.suspendNotifications();
    for (var i=0;i<tiddlers.length;i++) {
      store.setTiddlerTag(tiddlers[i].title,false,tag);
    }
    store.resumeNotifications();
    store.notifyAll();
  },

  renameTag: function(oldTag,newTag,tiddlers) {
    store.suspendNotifications();
    for (var i=0;i<tiddlers.length;i++) {
      var title = tiddlers[i].title;
      store.setTiddlerTag(title, false, oldTag); // remove old
      store.setTiddlerTag(title, true, newTag);  // add new
      store.getTiddler(title).modified = new Date(); // touch modified date
    }
    store.resumeNotifications();
    store.notifyAll();
  },

  storeMethods: {

    saveTiddler_orig_renameTags: TiddlyWiki.prototype.saveTiddler,

    saveTiddler: function(title,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created,creator) {
      if (title != newTitle) {
        var tagged = this.getTaggedTiddlers(title);
        if (tagged.length > 0) {
          // then we are renaming a tag
          if (confirm(config.renameTags.prompts.rename.format([title,newTitle,tagged.length,tagged.length>1?"s":""])))
            config.renameTags.renameTag(title,newTitle,tagged);

          if (!this.tiddlerExists(title) && newBody == "")
            // dont create unwanted tiddler
            return null;
        }
      }
      return this.saveTiddler_orig_renameTags(title,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created,creator);
    },

    removeTiddler_orig_renameTags: TiddlyWiki.prototype.removeTiddler,

    removeTiddler: function(title) {
      var tagged = this.getTaggedTiddlers(title);
      if (tagged.length > 0)
        if (confirm(config.renameTags.prompts.remove.format([title,tagged.length,tagged.length>1?"s":""])))
          config.renameTags.removeTag(title,tagged);
      return this.removeTiddler_orig_renameTags(title);
    }

  },

  init: function() {
    merge(TiddlyWiki.prototype,this.storeMethods);
  }
}

config.renameTags.init();

//}}}
/***
|Name:|SaveCloseTiddlerPlugin|
|Description:|Provides two extra toolbar commands, saveCloseTiddler and cancelCloseTiddler|
|Version:|3.0a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#SaveCloseTiddlerPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
To use these add them to the commands in ToolbarCommands under EditToolbar,
or in the MptwTheme tiddler under EditTemplate.
***/
//{{{
merge(config.commands,{

  saveCloseTiddler: {
    text: 'done/close',
    tooltip: 'Save changes to this tiddler and close it',
    handler: function(ev,src,title) {
      var closeTitle = title;
      var newTitle = story.saveTiddler(title,ev.shiftKey);
      if (newTitle)
        closeTitle = newTitle;
      return config.commands.closeTiddler.handler(ev,src,closeTitle);
    }
  },

  cancelCloseTiddler: {
    text: 'cancel/close',
    tooltip: 'Undo changes to this tiddler and close it',
    handler: function(ev,src,title) {
      // the same as closeTiddler now actually
      return config.commands.closeTiddler.handler(ev,src,title);
    }
  }

});

//}}}
/***
|Name:|SelectThemePlugin|
|Description:|Lets you easily switch theme and palette|
|Version:|1.0.1a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#SelectThemePlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
!Notes
* Borrows largely from ThemeSwitcherPlugin by Martin Budden http://www.martinswiki.com/#ThemeSwitcherPlugin
* Theme is cookie based. But set a default by setting config.options.txtTheme in MptwConfigPlugin (for example)
* Palette is not cookie based. It actually overwrites your ColorPalette tiddler when you select a palette, so beware.
!Usage
* {{{<<selectTheme>>}}} makes a dropdown selector
* {{{<<selectPalette>>}}} makes a dropdown selector
* {{{<<applyTheme>>}}} applies the current tiddler as a theme
* {{{<<applyPalette>>}}} applies the current tiddler as a palette
* {{{<<applyTheme TiddlerName>>}}} applies TiddlerName as a theme
* {{{<<applyPalette TiddlerName>>}}} applies TiddlerName as a palette
***/
//{{{

config.macros.selectTheme = {
  label: {
    selectTheme:"select theme",
    selectPalette:"select palette"
  },
  prompt: {
    selectTheme:"Select the current theme",
    selectPalette:"Select the current palette"
  },
  tags: {
    selectTheme:'systemTheme',
    selectPalette:'systemPalette'
  }
};

config.macros.selectTheme.handler = function(place,macroName)
{
  var btn = createTiddlyButton(place,this.label[macroName],this.prompt[macroName],this.onClick);
  // want to handle palettes and themes with same code. use mode attribute to distinguish
  btn.setAttribute('mode',macroName);
};

config.macros.selectTheme.onClick = function(ev)
{
  var e = ev ? ev : window.event;
  var popup = Popup.create(this);
  var mode = this.getAttribute('mode');
  var tiddlers = store.getTaggedTiddlers(config.macros.selectTheme.tags[mode]);
  // for default
  if (mode == "selectPalette") {
    var btn = createTiddlyButton(createTiddlyElement(popup,'li'),"(default)","default color palette",config.macros.selectTheme.onClickTheme);
    btn.setAttribute('theme',"(default)");
    btn.setAttribute('mode',mode);
  }
  for(var i=0; i<tiddlers.length; i++) {
    var t = tiddlers[i].title;
    var name = store.getTiddlerSlice(t,'Name');
    var desc = store.getTiddlerSlice(t,'Description');
    var btn = createTiddlyButton(createTiddlyElement(popup,'li'), name?name:t, desc?desc:config.macros.selectTheme.label['mode'], config.macros.selectTheme.onClickTheme);
    btn.setAttribute('theme',t);
    btn.setAttribute('mode',mode);
  }
  Popup.show();
  return stopEvent(e);
};

config.macros.selectTheme.onClickTheme = function(ev)
{
  var mode = this.getAttribute('mode');
  var theme = this.getAttribute('theme');
  if (mode == 'selectTheme')
    story.switchTheme(theme);
  else // selectPalette
    config.macros.selectTheme.updatePalette(theme);
  return false;
};

config.macros.selectTheme.updatePalette = function(title)
{
  if (title != "") {
    store.deleteTiddler("ColorPalette");
    if (title != "(default)")
      store.saveTiddler("ColorPalette","ColorPalette",store.getTiddlerText(title),
          config.options.txtUserName,undefined,"");
    refreshAll();
    if(config.options.chkAutoSave)
      saveChanges(true);
  }
};

config.macros.applyTheme = {
  label: "apply",
  prompt: "apply this theme or palette" // i'm lazy
};

config.macros.applyTheme.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
  var useTiddler = params[0] ? params[0] : tiddler.title;
  var btn = createTiddlyButton(place,this.label,this.prompt,config.macros.selectTheme.onClickTheme);
  btn.setAttribute('theme',useTiddler);
  btn.setAttribute('mode',macroName=="applyTheme"?"selectTheme":"selectPalette"); // a bit untidy here
}

config.macros.selectPalette = config.macros.selectTheme;
config.macros.applyPalette = config.macros.applyTheme;

config.macros.refreshAll = { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
  createTiddlyButton(place,"refresh","refresh layout and styles",function() { refreshAll(); });
}};

//}}}
tiddlywiki powered up @@font-size:70%;padding-left:1.5em;(<<mptwVersion>>)@@
[[MPTW|Welcome]]
/***
|''Name:''|SparklinePlugin|
|''Description:''|Sparklines macro|
***/
//{{{
if(!version.extensions.SparklinePlugin) {
version.extensions.SparklinePlugin = {installed:true};

//--
//-- Sparklines
//--

config.macros.sparkline = {};
config.macros.sparkline.handler = function(place,macroName,params)
{
	var data = [];
	var min = 0;
	var max = 0;
	var v;
	for(var t=0; t<params.length; t++) {
		v = parseInt(params[t]);
		if(v < min)
			min = v;
		if(v > max)
			max = v;
		data.push(v);
	}
	if(data.length < 1)
		return;
	var box = createTiddlyElement(place,"span",null,"sparkline",String.fromCharCode(160));
	box.title = data.join(",");
	var w = box.offsetWidth;
	var h = box.offsetHeight;
	box.style.paddingRight = (data.length * 2 - w) + "px";
	box.style.position = "relative";
	for(var d=0; d<data.length; d++) {
		var tick = document.createElement("img");
		tick.border = 0;
		tick.className = "sparktick";
		tick.style.position = "absolute";
		tick.src = "data:image/gif,GIF89a%01%00%01%00%91%FF%00%FF%FF%FF%00%00%00%C0%C0%C0%00%00%00!%F9%04%01%00%00%02%00%2C%00%00%00%00%01%00%01%00%40%02%02T%01%00%3B";
		tick.style.left = d*2 + "px";
		tick.style.width = "2px";
		v = Math.floor(((data[d] - min)/(max-min)) * h);
		tick.style.top = (h-v) + "px";
		tick.style.height = v + "px";
		box.appendChild(tick);
	}
};


}
//}}}
/*{{{*/
.downloadLink {
	font-size:110%;
	padding-left:2em;
}
.downloadLink a {
	border:2px solid #ddd;
	padding:0.5em;
}
/*}}}*/
<<allTags excludeLists>>
<<tabs txtMoreTab "Tags" "All Tags" TabAllTags "Miss" "Missing tiddlers" TabMoreMissing "Orph" "Orphaned tiddlers" TabMoreOrphans "Shad" "Shadowed tiddlers" TabMoreShadowed>>
<<allTags excludeLists [a-z]>>
Note, this stuff is a bit old and in need of redrafting in some places. Apologies.

!Chapter 1
Early versions of TiddlyWiki didn't have tagging. A couple of smart TiddlyWiki hackers realised that being able to tag your data in TiddlyWiki would be really good and (as is often the case with TiddlyWiki) they were inspired to have a go at adding it themselves. The result was two TiddlyWiki adaptations that implemented tagging, one called TagglyWiki created by Jody Foo, and one called TiddlyTagWiki by Johnny ~LeRoy. 

TiddlyTagWiki used a 'tags as separate things' philosophy, space separated tags and popup menus when you click on a tag. TagglyWiki (and there was also a GTDTagglyWiki) had a 'tiddler is a tag is a tiddler' philosophy, had comma separated tags and introduced the concept of tiddlers displaying a list of their tagged tiddlers.

When Jeremy added tagging to the official TiddlyWiki version he considered both approaches and decided on the TiddlyTagWiki implementation. Jody Foo stopped maintaining TagglyWiki at around that time so TagglyWiki fans like me were forced to stick with their aging [[TagglyWiki]]s, missing out on numerous upgrades and cool new features as each revision of TiddlyWiki came out, until eventually they couldn't take it any more and started wondering what they could do to get hold of an up-to-date TiddlyWiki with TagglyWiki style tagging. (Okay, maybe that last part was just me!)

So when TiddlyWiki began to support enhancements via systemConfig plugins I decided to try to write a version of TagglyWiki style tagging as a plugin. It turned out to be easier than I thought. I managed to get it working and created MonkeyPirateTiddlyWiki (see [[Ancient Stuff]]) to share it (and my other plugins). Since then there have been a few enhancements like dynamic updating of tagged lists and the new sort controls.

TiddlyTagWiki has continued to be maintained by Johnny and now contains some interesting new plugins.

!Chapter 2
In TiddlyWiki version 2.0 Jeremy incorporated the "tiddler is a tag is a tiddler" concept into TiddlyWiki and introduced the "tagging" list, the other central component of TagglyTagging. Some of the main reasons for TagglyTagging's existence had vanished! But instead of disappearing TagglyTagging became the set of tools it is now, an enhanced tagging list with sort controls, some layout changes and a few other simple tools (NewHerePlugin, QuickOpenTagPlugin and RenameTagsPlugin) that facilitate using tags in the powerful TagglyTagging way.

!Chapter 3
After the TiddlyWiki 2.1 release TagglyTaggingPlugin was completely rewritten. Some new features were added like site map mode and better support for columns. Also support for the new refresh mechanism was added which greatly improved the efficiency of dynamically updating the tagging lists.
You can do this right here and now. Maybe you should print this out so it's easy to follow while working on screen. I presume you have some familiarity with TiddlyWiki basics. If you are having trouble following this then try this [[excellent tutorial|http://www.blogjones.com/TiddlyWikiTutorial.html]] first.

!Getting started: Create some content
* First hit "close others" to clear up your screen
* Click "new tiddler" from the main menu.
* Name the tiddler "~MyStuff" by typing into the title box.
* Add some contents to the contents box, for example "This is my stuff"
* Click the Done button to save the new tiddler. (We will return to this one later).
* Let's say you just returned from a meeting and want to enter a couple of notes about it
** Click "new tiddler"
** Enter tiddler title as "Meeting with Leo - 12 Oct"
** Add some meeting notes in the content box
** Don't click Done just yet...

!Tagging your content
* Before you save the "Meeting with Leo..." tiddler let's add some tags
** It's a meeting so let's give it a tag called "Meetings". Type Meetings in the tags box.
** The meeting was with Leo so let's give it a tag called Leo. Type Leo in the tags box.
** Let's say the meeting was about budget planning for example. Type Budget in the tags box.
** So your tags box should look like this: {{{Meetings Leo Budget}}}
*Now save the tiddler by clicking the Done button.

!Using the tags
*No big deal so far, right? Now we start to get into TagglyTagging territory.
*Notice the tags appear above the title. They are italicised to indicate they don't exist yet (just like any other tiddler that doesn't exist yet).
*Click on the Leo tag. You will open an empty tiddler called Leo.
**Give it some content like "Leo Runcible, ext 1234. Likes cats". Or whatever.
**Leo's a person so give him a tag of "People" by typing People in the tags box.
**Save it by clicking Done.

!Lets pick up the pace
*Go back to your "Meeting with Leo..." tiddler.
** Click on Meetings. Give Meetings a tag of ~MyStuff and save it.
** Click on People. Give People a tag of ~MyStuff and save it.
** Click on Budget. Give Budget a tag of Projects. Type something about the Budget project in the tiddler contents if you want. Save it.
** Now above the Budget title click on the Projects tag. Give Projects a tag of ~MyStuff. Save that.
* Now find your ~MyStuff tiddler. Look at the tagged tiddler list which appears at the bottom of the tiddler. It should be a list containing Meetings, People and Projects.
* Close a few tiddlers and try navigating to your meeting tiddler from ~MyStuff using the tagged tiddler lists.

That's a quick look at the basics of TagglyTagging. Hopefully by now you have some idea about what TagglyTagging can do. Play around with it a little more if you like. Then continue to...

!The New Here Button
* Click on People.
* Do you know any other people?
* If so click, "new here" in the People tiddler
** Notice that the tags box already contains the tag People. (This is what the new here button does. It creates a new tiddler with the tag already filled in).
** Type someone's name and some notes on that person. Click Done to Save.
* Add a couple more people for fun
** Notice what happens to the "tagged tiddler" list at the bottom of the People tiddler.

!Changing the structure of your data
Let's look at how easy it is to change your structure. Suppose you decide that you want to have different types of Projects: Ongoing, Current and Future
* Go to your Budget tiddler. Edit it and change the Projects tag to OngoingProjects. Save.
* Click the OngoingProjects tag. Give that a tag of "Projects".
* That's it. You're done. Well actually you haven't added the Ongoing and Future Projects but you can see that's not hard. Maybe click "new here" at the Projects tiddler. Or maybe just tag a project as Future Projects and do it from the bottom up.

!Another example
* Suppose your original meeting had an action item for you. Type it into the meeting notes as a wiki word or {{{[[}}}Do Something{{{]]}}}.
* Now click on it and give it a tag of Todo. Type any extra information about how you're going to do it when it's due by etc into the contents. Put a reminder in there if you have ReminderPlugin installed.
* Now make sure you can get to your Todos by tagging Todo tiddler as MyStuff.
* For convenience let's put a link to MyStuff in your MainMenu. This will put all your new organised information at your fingertips at all times.

!Sort Controls and Columns
* If you mouse over a tagged tiddler list you should see some buttons. Try them out. Note that if you save it remembers your choice.

!Wrapup
Hopefully you can see that TagglyTagging gives you a powerful way to organise and structure your information. Don't forget that you can still use conventional wiki links to navigate around your tiddlers. TagglyTagging just gives you another way to do cool stuff with your TiddlyWiki.


~~This tutorial is a draft. Feedback is welcome. Please [[contact|Contact]] me with comments and suggestions.~~
See [[TagglyTagging FAQ]].
/***
|Name:|TagglyTaggingPlugin|
|Description:|tagglyTagging macro is a replacement for the builtin tagging macro in your ViewTemplate|
|Version:|3.3.2a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#TagglyTaggingPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
!Notes
See http://mptw.tiddlyspot.com/#TagglyTagging
***/
//{{{

merge(String.prototype,{

  parseTagExpr: function(debug) {

    if (this.trim() == "")
      return "(true)";

    var anyLogicOp = /(!|&&|\|\||\(|\))/g;
    var singleLogicOp = /^(!|&&|\|\||\(|\))$/;

    var spaced = this.
      // because square brackets in templates are no good
      // this means you can use [(With Spaces)] instead of [[With Spaces]]
      replace(/\[\(/g," [[").
      replace(/\)\]/g,"]] ").
      // space things out so we can use readBracketedList. tricky eh?
      replace(anyLogicOp," $1 ");

    var expr = "";

    var tokens = spaced.readBracketedList(false); // false means don't uniq the list. nice one JR!

    for (var i=0;i<tokens.length;i++)
      if (tokens[i].match(singleLogicOp))
        expr += tokens[i];
      else
        expr += "tiddler.tags.contains('%0')".format([tokens[i].replace(/'/,"\\'")]); // fix single quote bug. still have round bracket bug i think

    if (debug)
      alert(expr);

    return '('+expr+')';
  }

});

merge(TiddlyWiki.prototype,{
  getTiddlersByTagExpr: function(tagExpr,sortField) {

    var result = [];

    var expr = tagExpr.parseTagExpr();

    store.forEachTiddler(function(title,tiddler) {
      if (eval(expr))
        result.push(tiddler);
    });

    if(!sortField)
      sortField = "title";

    result.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});

    return result;
  }
});

config.taggly = {

  // for translations
  lingo: {
    labels: {
      asc:        "\u2191", // down arrow
      desc:       "\u2193", // up arrow
      title:      "title",
      modified:   "modified",
      created:    "created",
      show:       "+",
      hide:       "-",
      normal:     "normal",
      group:      "group",
      commas:     "commas",
      sitemap:    "sitemap",
      numCols:    "cols\u00b1", // plus minus sign
      label:      "Tagged as '%0':",
      exprLabel:  "Matching tag expression '%0':",
      excerpts:   "excerpts",
      descr:      "descr",
      slices:     "slices",
      contents:   "contents",
      sliders:    "sliders",
      noexcerpts: "title only",
      noneFound:  "(none)"
    },

    tooltips: {
      title:      "Click to sort by title",
      modified:   "Click to sort by modified date",
      created:    "Click to sort by created date",
      show:       "Click to show tagging list",
      hide:       "Click to hide tagging list",
      normal:     "Click to show a normal ungrouped list",
      group:      "Click to show list grouped by tag",
      sitemap:    "Click to show a sitemap style list",
      commas:     "Click to show a comma separated list",
      numCols:    "Click to change number of columns",
      excerpts:   "Click to show excerpts",
      descr:      "Click to show the description slice",
      slices:     "Click to show all slices",
      contents:   "Click to show entire tiddler contents",
      sliders:    "Click to show tiddler contents in sliders",
      noexcerpts: "Click to show entire title only"
    },

    tooDeepMessage: "* //sitemap too deep...//"
  },

  config: {
    showTaggingCounts: true,
    listOpts: {
      // the first one will be the default
      sortBy:     ["title","modified","created"],
      sortOrder:  ["asc","desc"],
      hideState:  ["show","hide"],
      listMode:   ["normal","group","sitemap","commas"],
      numCols:    ["1","2","3","4","5","6"],
      excerpts:   ["noexcerpts","excerpts","descr","slices","contents","sliders"]
    },
    valuePrefix: "taggly.",
    excludeTags: ["excludeLists","excludeTagging"],
    excerptSize: 50,
    excerptMarker: "/%"+"%/",
    siteMapDepthLimit: 25
  },

  getTagglyOpt: function(title,opt) {
    var val = store.getValue(title,this.config.valuePrefix+opt);
    return val ? val : this.config.listOpts[opt][0];
  },

  setTagglyOpt: function(title,opt,value) {
    // create it silently if it doesn't exist
    if (!store.tiddlerExists(title)) {
      store.saveTiddler(title,title,config.views.editor.defaultText.format([title]),config.options.txtUserName,new Date(),"");

      // <<tagglyTagging expr:"...">> creates a tiddler to store its display settings
      // Make those tiddlers less noticeable by tagging as excludeSearch and excludeLists
      // Because we don't want to hide real tags, check that they aren't actually tags before doing so
      // Also tag them as tagglyExpression for manageability
      // (contributed by RA)
      if (!store.getTaggedTiddlers(title).length) {
        store.setTiddlerTag(title,true,"excludeSearch");
        store.setTiddlerTag(title,true,"excludeLists");
        store.setTiddlerTag(title,true,"tagglyExpression");
      }
    }

    // if value is default then remove it to save space
    return store.setValue(title, this.config.valuePrefix+opt, value == this.config.listOpts[opt][0] ? null : value);
  },

  getNextValue: function(title,opt) {
    var current = this.getTagglyOpt(title,opt);
    var pos = this.config.listOpts[opt].indexOf(current);
    // supposed to automagically don't let cols cycle up past the number of items
    // currently broken in some situations, eg when using an expression
    // lets fix it later when we rewrite for jquery
    // the columns thing should be jquery table manipulation probably
    var limit = (opt == "numCols" ? store.getTaggedTiddlers(title).length : this.config.listOpts[opt].length);
    var newPos = (pos + 1) % limit;
    return this.config.listOpts[opt][newPos];
  },

  toggleTagglyOpt: function(title,opt) {
    var newVal = this.getNextValue(title,opt);
    this.setTagglyOpt(title,opt,newVal);
  },

  createListControl: function(place,title,type) {
    var lingo = config.taggly.lingo;
    var label;
    var tooltip;
    var onclick;

    if ((type == "title" || type == "modified" || type == "created")) {
      // "special" controls. a little tricky. derived from sortOrder and sortBy
      label = lingo.labels[type];
      tooltip = lingo.tooltips[type];

      if (this.getTagglyOpt(title,"sortBy") == type) {
        label += lingo.labels[this.getTagglyOpt(title,"sortOrder")];
        onclick = function() {
          config.taggly.toggleTagglyOpt(title,"sortOrder");
          return false;
        }
      }
      else {
        onclick = function() {
          config.taggly.setTagglyOpt(title,"sortBy",type);
          config.taggly.setTagglyOpt(title,"sortOrder",config.taggly.config.listOpts.sortOrder[0]);
          return false;
        }
      }
    }
    else {
      // "regular" controls, nice and simple
      label = lingo.labels[type == "numCols" ? type : this.getNextValue(title,type)];
      tooltip = lingo.tooltips[type == "numCols" ? type : this.getNextValue(title,type)];
      onclick = function() {
        config.taggly.toggleTagglyOpt(title,type);
        return false;
      }
    }

    // hide button because commas don't have columns
    if (!(this.getTagglyOpt(title,"listMode") == "commas" && type == "numCols"))
      createTiddlyButton(place,label,tooltip,onclick,type == "hideState" ? "hidebutton" : "button");
  },

  makeColumns: function(orig,numCols) {
    var listSize = orig.length;
    var colSize = listSize/numCols;
    var remainder = listSize % numCols;

    var upperColsize = colSize;
    var lowerColsize = colSize;

    if (colSize != Math.floor(colSize)) {
      // it's not an exact fit so..
      upperColsize = Math.floor(colSize) + 1;
      lowerColsize = Math.floor(colSize);
    }

    var output = [];
    var c = 0;
    for (var j=0;j<numCols;j++) {
      var singleCol = [];
      var thisSize = j < remainder ? upperColsize : lowerColsize;
      for (var i=0;i<thisSize;i++)
        singleCol.push(orig[c++]);
      output.push(singleCol);
    }

    return output;
  },

  drawTable: function(place,columns,theClass) {
    var newTable = createTiddlyElement(place,"table",null,theClass);
    var newTbody = createTiddlyElement(newTable,"tbody");
    var newTr = createTiddlyElement(newTbody,"tr");
    for (var j=0;j<columns.length;j++) {
      var colOutput = "";
      for (var i=0;i<columns[j].length;i++)
        colOutput += columns[j][i];
      var newTd = createTiddlyElement(newTr,"td",null,"tagglyTagging"); // todo should not need this class
      wikify(colOutput,newTd);
    }
    return newTable;
  },

  createTagglyList: function(place,title,isTagExpr) {
    switch(this.getTagglyOpt(title,"listMode")) {
      case "group":  return this.createTagglyListGrouped(place,title,isTagExpr); break;
      case "normal": return this.createTagglyListNormal(place,title,false,isTagExpr); break;
      case "commas": return this.createTagglyListNormal(place,title,true,isTagExpr); break;
      case "sitemap":return this.createTagglyListSiteMap(place,title,isTagExpr); break;
    }
  },

  getTaggingCount: function(title,isTagExpr) {
    // thanks to Doug Edmunds
    if (this.config.showTaggingCounts) {
      var tagCount = config.taggly.getTiddlers(title,'title',isTagExpr).length;
      if (tagCount > 0)
        return " ("+tagCount+")";
    }
    return "";
  },

  getTiddlers: function(titleOrExpr,sortBy,isTagExpr) {
    return isTagExpr ? store.getTiddlersByTagExpr(titleOrExpr,sortBy) : store.getTaggedTiddlers(titleOrExpr,sortBy);
  },

  getExcerpt: function(inTiddlerTitle,title,indent) {
    if (!indent)
      indent = 1;

    var displayMode = this.getTagglyOpt(inTiddlerTitle,"excerpts");
    var t = store.getTiddler(title);

    if (t && displayMode == "excerpts") {
      var text = t.text.replace(/\n/," ");
      var marker = text.indexOf(this.config.excerptMarker);
      if (marker != -1) {
        return " {{excerpt{<nowiki>" + text.substr(0,marker) + "</nowiki>}}}";
      }
      else if (text.length < this.config.excerptSize) {
        return " {{excerpt{<nowiki>" + t.text + "</nowiki>}}}";
      }
      else {
        return " {{excerpt{<nowiki>" + t.text.substr(0,this.config.excerptSize) + "..." + "</nowiki>}}}";
      }
    }
    else if (t && displayMode == "contents") {
      return "\n{{contents indent"+indent+"{\n" + t.text + "\n}}}";
    }
    else if (t && displayMode == "sliders") {
      return "<slider slide>\n{{contents{\n" + t.text + "\n}}}\n</slider>";
    }
    else if (t && displayMode == "descr") {
      var descr = store.getTiddlerSlice(title,'Description');
      return descr ? " {{excerpt{" + descr  + "}}}" : "";
    }
    else if (t && displayMode == "slices") {
      var result = "";
      var slices = store.calcAllSlices(title);
      for (var s in slices)
        result += "|%0|<nowiki>%1</nowiki>|\n".format([s,slices[s]]);
      return result ? "\n{{excerpt excerptIndent{\n" + result  + "}}}" : "";
    }
    return "";
  },

  notHidden: function(t,inTiddler) {
    if (typeof t == "string")
      t = store.getTiddler(t);
    return (!t || !t.tags.containsAny(this.config.excludeTags) ||
        (inTiddler && this.config.excludeTags.contains(inTiddler)));
  },

  // this is for normal and commas mode
  createTagglyListNormal: function(place,title,useCommas,isTagExpr) {

    var list = config.taggly.getTiddlers(title,this.getTagglyOpt(title,"sortBy"),isTagExpr);

    if (this.getTagglyOpt(title,"sortOrder") == "desc")
      list = list.reverse();

    var output = [];
    var first = true;
    for (var i=0;i<list.length;i++) {
      if (this.notHidden(list[i],title)) {
        var countString = this.getTaggingCount(list[i].title);
        var excerpt = this.getExcerpt(title,list[i].title);
        if (useCommas)
          output.push((first ? "" : ", ") + "[[" + list[i].title + "]]" + countString + excerpt);
        else
          output.push("*[[" + list[i].title + "]]" + countString + excerpt + "\n");

        first = false;
      }
    }

    return this.drawTable(place,
      this.makeColumns(output,useCommas ? 1 : parseInt(this.getTagglyOpt(title,"numCols"))),
      useCommas ? "commas" : "normal");
  },

  // this is for the "grouped" mode
  createTagglyListGrouped: function(place,title,isTagExpr) {
    var sortBy = this.getTagglyOpt(title,"sortBy");
    var sortOrder = this.getTagglyOpt(title,"sortOrder");

    var list = config.taggly.getTiddlers(title,sortBy,isTagExpr);

    if (sortOrder == "desc")
      list = list.reverse();

    var leftOvers = []
    for (var i=0;i<list.length;i++)
      leftOvers.push(list[i].title);

    var allTagsHolder = {};
    for (var i=0;i<list.length;i++) {
      for (var j=0;j<list[i].tags.length;j++) {

        if (list[i].tags[j] != title) { // not this tiddler

          if (this.notHidden(list[i].tags[j],title)) {

            if (!allTagsHolder[list[i].tags[j]])
              allTagsHolder[list[i].tags[j]] = "";

            if (this.notHidden(list[i],title)) {
              allTagsHolder[list[i].tags[j]] += "**[["+list[i].title+"]]"
                    + this.getTaggingCount(list[i].title) + this.getExcerpt(title,list[i].title) + "\n";

              leftOvers.setItem(list[i].title,-1); // remove from leftovers. at the end it will contain the leftovers

            }
          }
        }
      }
    }

    var allTags = [];
    for (var t in allTagsHolder)
      allTags.push(t);

    var sortHelper = function(a,b) {
      if (a == b) return 0;
      if (a < b) return -1;
      return 1;
    };

    allTags.sort(function(a,b) {
      var tidA = store.getTiddler(a);
      var tidB = store.getTiddler(b);
      if (sortBy == "title") return sortHelper(a,b);
      else if (!tidA && !tidB) return 0;
      else if (!tidA) return -1;
      else if (!tidB) return +1;
      else return sortHelper(tidA[sortBy],tidB[sortBy]);
    });

    var leftOverOutput = "";
    for (var i=0;i<leftOvers.length;i++)
      if (this.notHidden(leftOvers[i],title))
        leftOverOutput += "*[["+leftOvers[i]+"]]" + this.getTaggingCount(leftOvers[i]) + this.getExcerpt(title,leftOvers[i]) + "\n";

    var output = [];

    if (sortOrder == "desc")
      allTags.reverse();
    else if (leftOverOutput != "")
      // leftovers first...
      output.push(leftOverOutput);

    for (var i=0;i<allTags.length;i++)
      if (allTagsHolder[allTags[i]] != "")
        output.push("*[["+allTags[i]+"]]" + this.getTaggingCount(allTags[i]) + this.getExcerpt(title,allTags[i]) + "\n" + allTagsHolder[allTags[i]]);

    if (sortOrder == "desc" && leftOverOutput != "")
      // leftovers last...
      output.push(leftOverOutput);

    return this.drawTable(place,
        this.makeColumns(output,parseInt(this.getTagglyOpt(title,"numCols"))),
        "grouped");

  },

  // used to build site map
  treeTraverse: function(title,depth,sortBy,sortOrder,isTagExpr) {

    var list = config.taggly.getTiddlers(title,sortBy,isTagExpr);

    if (sortOrder == "desc")
      list.reverse();

    var indent = "";
    for (var j=0;j<depth;j++)
      indent += "*"

    var childOutput = "";

    if (depth > this.config.siteMapDepthLimit)
      childOutput += indent + this.lingo.tooDeepMessage + "\n";
    else
      for (var i=0;i<list.length;i++)
        if (list[i].title != title)
          if (this.notHidden(list[i].title,this.config.inTiddler))
            childOutput += this.treeTraverse(list[i].title,depth+1,sortBy,sortOrder,false);

    if (depth == 0)
      return childOutput;
    else
      return indent + "[["+title+"]]" + this.getTaggingCount(title) + this.getExcerpt(this.config.inTiddler,title,depth) + "\n" + childOutput;
  },

  // this if for the site map mode
  createTagglyListSiteMap: function(place,title,isTagExpr) {
    this.config.inTiddler = title; // nasty. should pass it in to traverse probably
    var output = this.treeTraverse(title,0,this.getTagglyOpt(title,"sortBy"),this.getTagglyOpt(title,"sortOrder"),isTagExpr);
    return this.drawTable(place,
        this.makeColumns(output.split(/(?=^\*\[)/m),parseInt(this.getTagglyOpt(title,"numCols"))), // regexp magic
        "sitemap"
        );
  },

  macros: {
    tagglyTagging: {
      handler: function (place,macroName,params,wikifier,paramString,tiddler) {
        var parsedParams = paramString.parseParams("tag",null,true);
        var refreshContainer = createTiddlyElement(place,"div");

        // do some refresh magic to make it keep the list fresh - thanks Saq
        refreshContainer.setAttribute("refresh","macro");
        refreshContainer.setAttribute("macroName",macroName);

        var tag = getParam(parsedParams,"tag");
        var expr = getParam(parsedParams,"expr");

        if (expr) {
          refreshContainer.setAttribute("isTagExpr","true");
          refreshContainer.setAttribute("title",expr);
          refreshContainer.setAttribute("showEmpty","true");
        }
        else {
          refreshContainer.setAttribute("isTagExpr","false");
          if (tag) {
                refreshContainer.setAttribute("title",tag);
            refreshContainer.setAttribute("showEmpty","true");
          }
          else {
                refreshContainer.setAttribute("title",tiddler.title);
            refreshContainer.setAttribute("showEmpty","false");
          }
        }
        this.refresh(refreshContainer);
      },

      refresh: function(place) {
        var title = place.getAttribute("title");
        var isTagExpr = place.getAttribute("isTagExpr") == "true";
        var showEmpty = place.getAttribute("showEmpty") == "true";
        jQuery(place).empty()
        jQuery(place).addClass("tagglyTagging");
        var countFound = config.taggly.getTiddlers(title,'title',isTagExpr).length
        if (countFound > 0 || showEmpty) {
          var lingo = config.taggly.lingo;
          config.taggly.createListControl(place,title,"hideState");
          if (config.taggly.getTagglyOpt(title,"hideState") == "show") {
            createTiddlyElement(place,"span",null,"tagglyLabel",
                isTagExpr ? lingo.labels.exprLabel.format([title]) : lingo.labels.label.format([title]));
            config.taggly.createListControl(place,title,"title");
            config.taggly.createListControl(place,title,"modified");
            config.taggly.createListControl(place,title,"created");
            config.taggly.createListControl(place,title,"listMode");
            config.taggly.createListControl(place,title,"excerpts");
            config.taggly.createListControl(place,title,"numCols");
            config.taggly.createTagglyList(place,title,isTagExpr);
            if (countFound == 0 && showEmpty)
              createTiddlyElement(place,"div",null,"tagglyNoneFound",lingo.labels.noneFound);
          }
        }
      }
    }
  },

  // todo fix these up a bit
  styles: [
"/*{{{*/",
"/* created by TagglyTaggingPlugin */",
".tagglyTagging { padding-top:0.5em; }",
".tagglyTagging li.listTitle { display:none; }",
".tagglyTagging ul {",
" margin-top:0px; padding-top:0.5em; padding-left:2em;",
" margin-bottom:0px; padding-bottom:0px;",
"}",
".tagglyTagging { vertical-align: top; margin:0px; padding:0px; }",
".tagglyTagging table { margin:0px; padding:0px; }",
".tagglyTagging .button { visibility:hidden; margin-left:3px; margin-right:3px; }",
".tagglyTagging .button, .tagglyTagging .hidebutton {",
" color:[[ColorPalette::TertiaryLight]]; font-size:90%;",
" border:0px; padding-left:0.3em;padding-right:0.3em;",
"}",
".tagglyTagging .button:hover, .hidebutton:hover, ",
".tagglyTagging .button:active, .hidebutton:active  {",
" border:0px; background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]];",
"}",
".selected .tagglyTagging .button { visibility:visible; }",
".tagglyTagging .hidebutton { color:[[ColorPalette::Background]]; }",
".selected .tagglyTagging .hidebutton { color:[[ColorPalette::TertiaryLight]] }",
".tagglyLabel { color:[[ColorPalette::TertiaryMid]]; font-size:90%; }",
".tagglyTagging ul {padding-top:0px; padding-bottom:0.5em; margin-left:1em; }",
".tagglyTagging ul ul {list-style-type:disc; margin-left:-1em;}",
".tagglyTagging ul ul li {margin-left:0.5em; }",
".editLabel { font-size:90%; padding-top:0.5em; }",
".tagglyTagging .commas { padding-left:1.8em; }",
"/* not technically tagglytagging but will put them here anyway */",
".tagglyTagged li.listTitle { display:none; }",
".tagglyTagged li { display: inline; font-size:90%; }",
".tagglyTagged ul { margin:0px; padding:0px; }",
".excerpt { color:[[ColorPalette::TertiaryDark]]; }",
".excerptIndent { margin-left:4em; }",
"div.tagglyTagging table,",
"div.tagglyTagging table tr,",
"td.tagglyTagging",
" {border-style:none!important; }",
".tagglyTagging .contents { border-bottom:2px solid [[ColorPalette::TertiaryPale]]; padding:0 1em 1em 0.5em;",
"  margin-bottom:0.5em; }",
".tagglyTagging .indent1  { margin-left:3em;  }",
".tagglyTagging .indent2  { margin-left:4em;  }",
".tagglyTagging .indent3  { margin-left:5em;  }",
".tagglyTagging .indent4  { margin-left:6em;  }",
".tagglyTagging .indent5  { margin-left:7em;  }",
".tagglyTagging .indent6  { margin-left:8em;  }",
".tagglyTagging .indent7  { margin-left:9em;  }",
".tagglyTagging .indent8  { margin-left:10em; }",
".tagglyTagging .indent9  { margin-left:11em; }",
".tagglyTagging .indent10 { margin-left:12em; }",
".tagglyNoneFound { margin-left:2em; color:[[ColorPalette::TertiaryMid]]; font-size:90%; font-style:italic; }",
"/*}}}*/",
    ""].join("\n"),

  init: function() {
    merge(config.macros,this.macros);
    config.shadowTiddlers["TagglyTaggingStyles"] = this.styles;
    store.addNotification("TagglyTaggingStyles",refreshStyles);
  }
};

config.taggly.init();

//}}}

/***
InlineSlidersPlugin
By Saq Imtiaz
http://tw.lewcid.org/sandbox/#InlineSlidersPlugin

// syntax adjusted to not clash with NestedSlidersPlugin
// added + syntax to start open instead of closed

***/
//{{{
config.formatters.unshift( {
  name: "inlinesliders",
  // match: "\\+\\+\\+\\+|\\<slider",
  match: "\\<slider",
  // lookaheadRegExp: /(?:\+\+\+\+|<slider) (.*?)(?:>?)\n((?:.|\n)*?)\n(?:====|<\/slider>)/mg,
  lookaheadRegExp: /(?:<slider)(\+?) (.*?)(?:>)\n((?:.|\n)*?)\n(?:<\/slider>)/mg,
  handler: function(w) {
    this.lookaheadRegExp.lastIndex = w.matchStart;
    var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
    if(lookaheadMatch && lookaheadMatch.index == w.matchStart ) {
      var btn = createTiddlyButton(w.output,lookaheadMatch[2] + " "+"\u00BB",lookaheadMatch[2],this.onClickSlider,"button sliderButton");
      var panel = createTiddlyElement(w.output,"div",null,"sliderPanel");
      panel.style.display = (lookaheadMatch[1] == '+' ? "block" : "none");
      wikify(lookaheadMatch[3],panel);
      w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
    }
   },
   onClickSlider : function(e) {
    if(!e) var e = window.event;
    var n = this.nextSibling;
    n.style.display = (n.style.display=="none") ? "block" : "none";
    return false;
  }
});

//}}}
See [[TagglyTagging Tutorial]].
@@border:solid 1px black;display:block;padding:1em 2em 2em;background:#ffe;margin:1em;
Copyright (c) 2007-2008, Simon Baird
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name Simon Baird nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@
TiddlyWiki is a personal [[wiki|Wiki]] that runs in your web browswer. It is created and maintained by JeremyRuston. Try these links for more information:
*http://www.tiddlywiki.com/
*http://www.tiddlywiki.org/
*http://en.wikipedia.org/wiki/Tiddlywiki]]
/***
|Name:|ToggleTagPlugin|
|Description:|Makes a checkbox which toggles a tag in a tiddler|
|Version:|3.1.0a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#ToggleTagPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
!!Usage
{{{<<toggleTag }}}//{{{TagName TiddlerName LabelText}}}//{{{>>}}}
* TagName - the tag to be toggled, default value "checked"
* TiddlerName - the tiddler to toggle the tag in, default value the current tiddler
* LabelText - the text (gets wikified) to put next to the check box, default value is '{{{[[TagName]]}}}' or '{{{[[TagName]] [[TiddlerName]]}}}'
(If a parameter is '.' then the default will be used)
* TouchMod flag - if non empty then touch the tiddlers mod date. Note, can set config.toggleTagAlwaysTouchModDate to always touch mod date
!!Examples
|Code|Description|Example|h
|{{{<<toggleTag>>}}}|Toggles the default tag (checked) in this tiddler|<<toggleTag>>|
|{{{<<toggleTag TagName>>}}}|Toggles the TagName tag in this tiddler|<<toggleTag TagName>>|
|{{{<<toggleTag TagName TiddlerName>>}}}|Toggles the TagName tag in the TiddlerName tiddler|<<toggleTag TagName TiddlerName>>|
|{{{<<toggleTag TagName TiddlerName 'click me'>>}}}|Same but with custom label|<<toggleTag TagName TiddlerName 'click me'>>|
|{{{<<toggleTag . . 'click me'>>}}}|dot means use default value|<<toggleTag . . 'click me'>>|
!!Notes
* If TiddlerName doesn't exist it will be silently created
* Set label to '-' to specify no label
* See also http://mgtd-alpha.tiddlyspot.com/#ToggleTag2
!!Known issues
* Doesn't smoothly handle the case where you toggle a tag in a tiddler that is current open for editing
* Should convert to use named params
***/
//{{{

if (config.toggleTagAlwaysTouchModDate == undefined) config.toggleTagAlwaysTouchModDate = false;

merge(config.macros,{

  toggleTag: {

    createIfRequired: true,
    shortLabel: "[[%0]]",
    longLabel: "[[%0]] [[%1]]",

    handler: function(place,macroName,params,wikifier,paramString,tiddler) {
      var tiddlerTitle = tiddler ? tiddler.title : '';
      var tag   = (params[0] && params[0] != '.') ? params[0] : "checked";
      var title = (params[1] && params[1] != '.') ? params[1] : tiddlerTitle;
      var defaultLabel = (title == tiddlerTitle ? this.shortLabel : this.longLabel);
      var label = (params[2] && params[2] != '.') ? params[2] : defaultLabel;
      var touchMod = (params[3] && params[3] != '.') ? params[3] : "";
      label = (label == '-' ? '' : label); // dash means no label
      var theTiddler = (title == tiddlerTitle ? tiddler : store.getTiddler(title));
      var cb = createTiddlyCheckbox(place, label.format([tag,title]), theTiddler && theTiddler.isTagged(tag), function(e) {
        if (!store.tiddlerExists(title)) {
          if (config.macros.toggleTag.createIfRequired) {
            var content = store.getTiddlerText(title); // just in case it's a shadow
            store.saveTiddler(title,title,content?content:"",config.options.txtUserName,new Date(),null);
          }
          else
            return false;
        }
        if ((touchMod != "" || config.toggleTagAlwaysTouchModDate) && theTiddler)
            theTiddler.modified = new Date();
        store.setTiddlerTag(title,this.checked,tag);
        return true;
      });
    }
  }
});

//}}}

/***
Description:Contains the stuff you need to use Tiddlyspot
Note you must also have UploadPlugin installed
***/
//{{{

// edit this if you are migrating sites or retrofitting an existing TW
config.tiddlyspotSiteId = 'mptw';

// make it so you can by default see edit controls via http
config.options.chkHttpReadOnly = false;
window.readOnly = false; // make sure of it (for tw 2.2)
window.showBackstage = true;

// disable autosave in d3
if (window.location.protocol != "file:")
	config.options.chkGTDLazyAutoSave = false;

// tweak shadow tiddlers to add upload button, password entry box etc
with (config.shadowTiddlers) {
	SiteUrl = 'http://'+config.tiddlyspotSiteId+'.tiddlyspot.com';
	SideBarOptions = SideBarOptions.replace(/(<<saveChanges>>)/,"$1<<tiddler TspotSidebar>>");
	OptionsPanel = OptionsPanel.replace(/^/,"<<tiddler TspotOptions>>");
	DefaultTiddlers = DefaultTiddlers.replace(/^/,"[[Welcome to Tiddlyspot]] ");
	MainMenu = MainMenu.replace(/^/,"[[Welcome to Tiddlyspot]] ");
}

// create some shadow tiddler content
merge(config.shadowTiddlers,{

'Welcome to Tiddlyspot':[
 "This document is a ~TiddlyWiki from tiddlyspot.com.  A ~TiddlyWiki is an electronic notebook that is great for managing todo lists, personal information, and all sorts of things.",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //What now?// &nbsp;&nbsp;@@ Before you can save any changes, you need to enter your password in the form below.  Then configure privacy and other site settings at your [[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]] (your control panel username is //" + config.tiddlyspotSiteId + "//).",
 "<<tiddler TspotControls>>",
 "See also GettingStarted.",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Working online// &nbsp;&nbsp;@@ You can edit this ~TiddlyWiki right now, and save your changes using the \"save to web\" button in the column on the right.",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Working offline// &nbsp;&nbsp;@@ A fully functioning copy of this ~TiddlyWiki can be saved onto your hard drive or USB stick.  You can make changes and save them locally without being connected to the Internet.  When you're ready to sync up again, just click \"upload\" and your ~TiddlyWiki will be saved back to tiddlyspot.com.",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Help!// &nbsp;&nbsp;@@ Find out more about ~TiddlyWiki at [[TiddlyWiki.com|http://tiddlywiki.com]].  Also visit [[TiddlyWiki Guides|http://tiddlywikiguides.org]] for documentation on learning and using ~TiddlyWiki. New users are especially welcome on the [[TiddlyWiki mailing list|http://groups.google.com/group/TiddlyWiki]], which is an excellent place to ask questions and get help.  If you have a tiddlyspot related problem email [[tiddlyspot support|mailto:support@tiddlyspot.com]].",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Enjoy :)// &nbsp;&nbsp;@@ We hope you like using your tiddlyspot.com site.  Please email [[feedback@tiddlyspot.com|mailto:feedback@tiddlyspot.com]] with any comments or suggestions."
].join("\n"),

'TspotControls':[
 "| tiddlyspot password:|<<option pasUploadPassword>>|",
 "| site management:|<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . .  " + config.tiddlyspotSiteId + ">>//(requires tiddlyspot password)//<<br>>[[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]], [[download (go offline)|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download]]|",
 "| links:|[[tiddlyspot.com|http://tiddlyspot.com/]], [[FAQs|http://faq.tiddlyspot.com/]], [[announcements|http://announce.tiddlyspot.com/]], [[blog|http://tiddlyspot.com/blog/]], email [[support|mailto:support@tiddlyspot.com]] & [[feedback|mailto:feedback@tiddlyspot.com]], [[donate|http://tiddlyspot.com/?page=donate]]|"
].join("\n"),

'TspotSidebar':[
 "<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . .  " + config.tiddlyspotSiteId + ">><html><a href='http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download' class='button'>download</a></html>"
].join("\n"),

'TspotOptions':[
 "tiddlyspot password:",
 "<<option pasUploadPassword>>",
 ""
].join("\n")

});
//}}}
| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |
| 17/07/2021 12:55:56 | YourName | [[/|http://mptw.tiddlyspot.com/]] | [[store.cgi|http://mptw.tiddlyspot.com/store.cgi]] | . | [[index.html | http://mptw.tiddlyspot.com/index.html]] | . |
| 17/07/2021 12:56:24 | YourName | [[/|http://mptw.tiddlyspot.com/]] | [[store.cgi|http://mptw.tiddlyspot.com/store.cgi]] | . | [[index.html | http://mptw.tiddlyspot.com/index.html]] | . |
| 17/07/2021 12:56:46 | YourName | [[/|http://mptw.tiddlyspot.com/]] | [[store.cgi|http://mptw.tiddlyspot.com/store.cgi]] | . | [[index.html | http://mptw.tiddlyspot.com/index.html]] | . |
| 17/07/2021 18:11:25 | YourName | [[/|http://mptw.tiddlyspot.com/]] | [[store.cgi|http://mptw.tiddlyspot.com/store.cgi]] | . | [[index.html | http://mptw.tiddlyspot.com/index.html]] | . |
| 12/12/2022 18:19:19 | YourName | [[/|http://mptw.tiddlyspot.com/]] | [[store.cgi|http://mptw.tiddlyspot.com/store.cgi]] | . | [[index.html | http://mptw.tiddlyspot.com/index.html]] | . |
| 12/12/2022 18:20:09 | YourName | [[/|http://mptw.tiddlyspot.com/]] | [[store.cgi|http://mptw.tiddlyspot.com/store.cgi]] | . | [[index.html | http://mptw.tiddlyspot.com/index.html]] | . |
| 12/12/2022 18:20:27 | YourName | [[/|http://mptw.tiddlyspot.com/]] | [[store.cgi|http://mptw.tiddlyspot.com/store.cgi]] | . | [[index.html | http://mptw.tiddlyspot.com/index.html]] | . |
| 12/12/2022 18:21:17 | YourName | [[/|http://mptw.tiddlyspot.com/]] | [[store.cgi|http://mptw.tiddlyspot.com/store.cgi]] | . | [[index.html | http://mptw.tiddlyspot.com/index.html]] | . |
| 12/12/2022 18:22:16 | YourName | [[/|http://mptw.tiddlyspot.com/]] | [[store.cgi|http://mptw.tiddlyspot.com/store.cgi]] | . | [[index.html | http://mptw.tiddlyspot.com/index.html]] | . |
| 16/12/2022 15:38:23 | YourName | [[/|http://mptw.tiddlyspot.com/]] | [[store.cgi|http://mptw.tiddlyspot.com/store.cgi]] | . | [[index.html | http://mptw.tiddlyspot.com/index.html]] | . |
/***
|''Name:''|PasswordOptionPlugin|
|''Description:''|Extends TiddlyWiki options with non encrypted password option.|
|''Version:''|1.0.2|
|''Date:''|Apr 19, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#PasswordOptionPlugin|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0 (Beta 5)|
***/
//{{{
version.extensions.PasswordOptionPlugin = {
	major: 1, minor: 0, revision: 2, 
	date: new Date("Apr 19, 2007"),
	source: 'http://tiddlywiki.bidix.info/#PasswordOptionPlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	license: '[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D]]',
	coreVersion: '2.2.0 (Beta 5)'
};

config.macros.option.passwordCheckboxLabel = "Save this password on this computer";
config.macros.option.passwordInputType = "password"; // password | text
setStylesheet(".pasOptionInput {width: 11em;}\n","passwordInputTypeStyle");

merge(config.macros.option.types, {
	'pas': {
		elementType: "input",
		valueField: "value",
		eventName: "onkeyup",
		className: "pasOptionInput",
		typeValue: config.macros.option.passwordInputType,
		create: function(place,type,opt,className,desc) {
			// password field
			config.macros.option.genericCreate(place,'pas',opt,className,desc);
			// checkbox linked with this password "save this password on this computer"
			config.macros.option.genericCreate(place,'chk','chk'+opt,className,desc);			
			// text savePasswordCheckboxLabel
			place.appendChild(document.createTextNode(config.macros.option.passwordCheckboxLabel));
		},
		onChange: config.macros.option.genericOnChange
	}
});

merge(config.optionHandlers['chk'], {
	get: function(name) {
		// is there an option linked with this chk ?
		var opt = name.substr(3);
		if (config.options[opt]) 
			saveOptionCookie(opt);
		return config.options[name] ? "true" : "false";
	}
});

merge(config.optionHandlers, {
	'pas': {
 		get: function(name) {
			if (config.options["chk"+name]) {
				return encodeCookie(config.options[name].toString());
			} else {
				return "";
			}
		},
		set: function(name,value) {config.options[name] = decodeCookie(value);}
	}
});

// need to reload options to load passwordOptions
loadOptionsCookie();

/*
if (!config.options['pasPassword'])
	config.options['pasPassword'] = '';

merge(config.optionsDesc,{
		pasPassword: "Test password"
	});
*/
//}}}

/***
|''Name:''|UploadPlugin|
|''Description:''|Save to web a TiddlyWiki|
|''Version:''|4.1.0|
|''Date:''|May 5, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#UploadPlugin|
|''Documentation:''|http://tiddlywiki.bidix.info/#UploadPluginDoc|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0 (#3125)|
|''Requires:''|PasswordOptionPlugin|
***/
//{{{
version.extensions.UploadPlugin = {
	major: 4, minor: 1, revision: 0,
	date: new Date("May 5, 2007"),
	source: 'http://tiddlywiki.bidix.info/#UploadPlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	coreVersion: '2.2.0 (#3125)'
};

//
// Environment
//

if (!window.bidix) window.bidix = {}; // bidix namespace
bidix.debugMode = false;	// true to activate both in Plugin and UploadService
	
//
// Upload Macro
//

config.macros.upload = {
// default values
	defaultBackupDir: '',	//no backup
	defaultStoreScript: "store.php",
	defaultToFilename: "index.html",
	defaultUploadDir: ".",
	authenticateUser: true	// UploadService Authenticate User
};
	
config.macros.upload.label = {
	promptOption: "Save and Upload this TiddlyWiki with UploadOptions",
	promptParamMacro: "Save and Upload this TiddlyWiki in %0",
	saveLabel: "save to web", 
	saveToDisk: "save to disk",
	uploadLabel: "upload"	
};

config.macros.upload.messages = {
	noStoreUrl: "No store URL in parmeters or options",
	usernameOrPasswordMissing: "Username or password missing"
};

config.macros.upload.handler = function(place,macroName,params) {


	if (readOnly)
		return;
	var label;
	if (document.location.toString().substr(0,4) == "http") 
		label = this.label.saveLabel;
	else
		label = this.label.uploadLabel;
	var prompt;
	if (params[0]) {
		prompt = this.label.promptParamMacro.toString().format([this.destFile(params[0], 
			(params[1] ? params[1]:bidix.basename(window.location.toString())), params[3])]);
	} else {
		prompt = this.label.promptOption;
	}
	createTiddlyButton(place, label, prompt, function() {config.macros.upload.action(params);}, null, null, this.accessKey);
};

config.macros.upload.action = function(params)
{
		// for missing macro parameter set value from options
		var storeUrl = params[0] ? params[0] : config.options.txtUploadStoreUrl;
		var toFilename = params[1] ? params[1] : config.options.txtUploadFilename;
		var backupDir = params[2] ? params[2] : config.options.txtUploadBackupDir;
		var uploadDir = params[3] ? params[3] : config.options.txtUploadDir;
		var username = params[4] ? params[4] : config.options.txtUploadUserName;
		var password = config.options.pasUploadPassword; // for security reason no password as macro parameter	
		// for still missing parameter set default value
		if ((!storeUrl) && (document.location.toString().substr(0,4) == "http")) 
			storeUrl = bidix.dirname(document.location.toString())+'/'+config.macros.upload.defaultStoreScript;
		if (storeUrl.substr(0,4) != "http")
			storeUrl = bidix.dirname(document.location.toString()) +'/'+ storeUrl;
		if (!toFilename)
			toFilename = bidix.basename(window.location.toString());
		if (!toFilename)
			toFilename = config.macros.upload.defaultToFilename;
		if (!uploadDir)
			uploadDir = config.macros.upload.defaultUploadDir;
		if (!backupDir)
			backupDir = config.macros.upload.defaultBackupDir;
		// report error if still missing
		if (!storeUrl) {
			alert(config.macros.upload.messages.noStoreUrl);
			clearMessage();
			return false;
		}
		if (config.macros.upload.authenticateUser && (!username || !password)) {
			alert(config.macros.upload.messages.usernameOrPasswordMissing);
			clearMessage();
			return false;
		}
		bidix.upload.uploadChanges(false,null,storeUrl, toFilename, uploadDir, backupDir, username, password); 
		return false; 
};

config.macros.upload.destFile = function(storeUrl, toFilename, uploadDir) 
{
	if (!storeUrl)
		return null;
		var dest = bidix.dirname(storeUrl);
		if (uploadDir && uploadDir != '.')
			dest = dest + '/' + uploadDir;
		dest = dest + '/' + toFilename;
	return dest;
};

//
// uploadOptions Macro
//

config.macros.uploadOptions = {
	handler: function(place,macroName,params) {
		var wizard = new Wizard();
		wizard.createWizard(place,this.wizardTitle);
		wizard.addStep(this.step1Title,this.step1Html);
		var markList = wizard.getElement("markList");
		var listWrapper = document.createElement("div");
		markList.parentNode.insertBefore(listWrapper,markList);
		wizard.setValue("listWrapper",listWrapper);
		this.refreshOptions(listWrapper,false);
		var uploadCaption;
		if (document.location.toString().substr(0,4) == "http") 
			uploadCaption = config.macros.upload.label.saveLabel;
		else
			uploadCaption = config.macros.upload.label.uploadLabel;
		
		wizard.setButtons([
				{caption: uploadCaption, tooltip: config.macros.upload.label.promptOption, 
					onClick: config.macros.upload.action},
				{caption: this.cancelButton, tooltip: this.cancelButtonPrompt, onClick: this.onCancel}
				
			]);
	},
	refreshOptions: function(listWrapper) {
		var uploadOpts = [
			"txtUploadUserName",
			"pasUploadPassword",
			"txtUploadStoreUrl",
			"txtUploadDir",
			"txtUploadFilename",
			"txtUploadBackupDir",
			"chkUploadLog",
			"txtUploadLogMaxLine",
			]
		var opts = [];
		for(i=0; i<uploadOpts.length; i++) {
			var opt = {};
			opts.push()
			opt.option = "";
			n = uploadOpts[i];
			opt.name = n;
			opt.lowlight = !config.optionsDesc[n];
			opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
			opts.push(opt);
		}
		var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
		for(n=0; n<opts.length; n++) {
			var type = opts[n].name.substr(0,3);
			var h = config.macros.option.types[type];
			if (h && h.create) {
				h.create(opts[n].colElements['option'],type,opts[n].name,opts[n].name,"no");
			}
		}
		
	},
	onCancel: function(e)
	{
		backstage.switchTab(null);
		return false;
	},
	
	wizardTitle: "Upload with options",
	step1Title: "These options are saved in cookies in your browser",
	step1Html: "<input type='hidden' name='markList'></input><br>",
	cancelButton: "Cancel",
	cancelButtonPrompt: "Cancel prompt",
	listViewTemplate: {
		columns: [
			{name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
			{name: 'Option', field: 'option', title: "Option", type: 'String'},
			{name: 'Name', field: 'name', title: "Name", type: 'String'}
			],
		rowClasses: [
			{className: 'lowlight', field: 'lowlight'} 
			]}
}

//
// upload functions
//

if (!bidix.upload) bidix.upload = {};

if (!bidix.upload.messages) bidix.upload.messages = {
	//from saving
	invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
	backupSaved: "Backup saved",
	backupFailed: "Failed to upload backup file",
	rssSaved: "RSS feed uploaded",
	rssFailed: "Failed to upload RSS feed file",
	emptySaved: "Empty template uploaded",
	emptyFailed: "Failed to upload empty template file",
	mainSaved: "Main TiddlyWiki file uploaded",
	mainFailed: "Failed to upload main TiddlyWiki file. Your changes have not been saved",
	//specific upload
	loadOriginalHttpPostError: "Can't get original file",
	aboutToSaveOnHttpPost: 'About to upload on %0 ...',
	storePhpNotFound: "The store script '%0' was not found."
};

bidix.upload.uploadChanges = function(onlyIfDirty,tiddlers,storeUrl,toFilename,uploadDir,backupDir,username,password)
{
	var callback = function(status,uploadParams,original,url,xhr) {
		if (!status) {
			displayMessage(bidix.upload.messages.loadOriginalHttpPostError);
			return;
		}
		if (bidix.debugMode) 
			alert(original.substr(0,500)+"\n...");
		// Locate the storeArea div's 
		var posDiv = locateStoreArea(original);
		if((posDiv[0] == -1) || (posDiv[1] == -1)) {
			alert(config.messages.invalidFileError.format([localPath]));
			return;
		}
		bidix.upload.uploadRss(uploadParams,original,posDiv);
	};
	
	if(onlyIfDirty && !store.isDirty())
		return;
	clearMessage();
	// save on localdisk ?
	if (document.location.toString().substr(0,4) == "file") {
		var path = document.location.toString();
		var localPath = getLocalPath(path);
		saveChanges();
	}
	// get original
	var uploadParams = Array(storeUrl,toFilename,uploadDir,backupDir,username,password);
	var originalPath = document.location.toString();
	// If url is a directory : add index.html
	if (originalPath.charAt(originalPath.length-1) == "/")
		originalPath = originalPath + "index.html";
	var dest = config.macros.upload.destFile(storeUrl,toFilename,uploadDir);
	var log = new bidix.UploadLog();
	log.startUpload(storeUrl, dest, uploadDir,  backupDir);
	displayMessage(bidix.upload.messages.aboutToSaveOnHttpPost.format([dest]));
	if (bidix.debugMode) 
		alert("about to execute Http - GET on "+originalPath);
	var r = doHttp("GET",originalPath,null,null,null,null,callback,uploadParams,null);
	if (typeof r == "string")
		displayMessage(r);
	return r;
};

bidix.upload.uploadRss = function(uploadParams,original,posDiv) 
{
	var callback = function(status,params,responseText,url,xhr) {
		if(status) {
			var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
			displayMessage(bidix.upload.messages.rssSaved,bidix.dirname(url)+'/'+destfile);
			bidix.upload.uploadMain(params[0],params[1],params[2]);
		} else {
			displayMessage(bidix.upload.messages.rssFailed);			
		}
	};
	// do uploadRss
	if(config.options.chkGenerateAnRssFeed) {
		var rssPath = uploadParams[1].substr(0,uploadParams[1].lastIndexOf(".")) + ".xml";
		var rssUploadParams = Array(uploadParams[0],rssPath,uploadParams[2],'',uploadParams[4],uploadParams[5]);
		bidix.upload.httpUpload(rssUploadParams,convertUnicodeToUTF8(generateRss()),callback,Array(uploadParams,original,posDiv));
	} else {
		bidix.upload.uploadMain(uploadParams,original,posDiv);
	}
};

bidix.upload.uploadMain = function(uploadParams,original,posDiv) 
{
	var callback = function(status,params,responseText,url,xhr) {
		var log = new bidix.UploadLog();
		if(status) {
			// if backupDir specified
			if ((params[3]) && (responseText.indexOf("backupfile:") > -1))  {
				var backupfile = responseText.substring(responseText.indexOf("backupfile:")+11,responseText.indexOf("\n", responseText.indexOf("backupfile:")));
				displayMessage(bidix.upload.messages.backupSaved,bidix.dirname(url)+'/'+backupfile);
			}
			var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
			displayMessage(bidix.upload.messages.mainSaved,bidix.dirname(url)+'/'+destfile);
			store.setDirty(false);
			log.endUpload("ok");
		} else {
			alert(bidix.upload.messages.mainFailed);
			displayMessage(bidix.upload.messages.mainFailed);
			log.endUpload("failed");			
		}
	};
	// do uploadMain
	var revised = bidix.upload.updateOriginal(original,posDiv);
	bidix.upload.httpUpload(uploadParams,revised,callback,uploadParams);
};

bidix.upload.httpUpload = function(uploadParams,data,callback,params)
{
	var localCallback = function(status,params,responseText,url,xhr) {
		url = (url.indexOf("nocache=") < 0 ? url : url.substring(0,url.indexOf("nocache=")-1));
		if (xhr.status == httpStatus.NotFound)
			alert(bidix.upload.messages.storePhpNotFound.format([url]));
		if ((bidix.debugMode) || (responseText.indexOf("Debug mode") >= 0 )) {
			alert(responseText);
			if (responseText.indexOf("Debug mode") >= 0 )
				responseText = responseText.substring(responseText.indexOf("\n\n")+2);
		} else if (responseText.charAt(0) != '0') 
			alert(responseText);
		if (responseText.charAt(0) != '0')
			status = null;
		callback(status,params,responseText,url,xhr);
	};
	// do httpUpload
	var boundary = "---------------------------"+"AaB03x";	
	var uploadFormName = "UploadPlugin";
	// compose headers data
	var sheader = "";
	sheader += "--" + boundary + "\r\nContent-disposition: form-data; name=\"";
	sheader += uploadFormName +"\"\r\n\r\n";
	sheader += "backupDir="+uploadParams[3] +
				";user=" + uploadParams[4] +
				";password=" + uploadParams[5] +
				";uploaddir=" + uploadParams[2];
	if (bidix.debugMode)
		sheader += ";debug=1";
	sheader += ";;\r\n"; 
	sheader += "\r\n" + "--" + boundary + "\r\n";
	sheader += "Content-disposition: form-data; name=\"userfile\"; filename=\""+uploadParams[1]+"\"\r\n";
	sheader += "Content-Type: text/html;charset=UTF-8" + "\r\n";
	sheader += "Content-Length: " + data.length + "\r\n\r\n";
	// compose trailer data
	var strailer = new String();
	strailer = "\r\n--" + boundary + "--\r\n";
	data = sheader + data + strailer;
	if (bidix.debugMode) alert("about to execute Http - POST on "+uploadParams[0]+"\n with \n"+data.substr(0,500)+ " ... ");
	var r = doHttp("POST",uploadParams[0],data,"multipart/form-data; boundary="+boundary,uploadParams[4],uploadParams[5],localCallback,params,null);
	if (typeof r == "string")
		displayMessage(r);
	return r;
};

// same as Saving's updateOriginal but without convertUnicodeToUTF8 calls
bidix.upload.updateOriginal = function(original, posDiv)
{
	if (!posDiv)
		posDiv = locateStoreArea(original);
	if((posDiv[0] == -1) || (posDiv[1] == -1)) {
		alert(config.messages.invalidFileError.format([localPath]));
		return;
	}
	var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n" +
				store.allTiddlersAsHtml() + "\n" +
				original.substr(posDiv[1]);
	var newSiteTitle = getPageTitle().htmlEncode();
	revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
	revised = updateMarkupBlock(revised,"PRE-HEAD","MarkupPreHead");
	revised = updateMarkupBlock(revised,"POST-HEAD","MarkupPostHead");
	revised = updateMarkupBlock(revised,"PRE-BODY","MarkupPreBody");
	revised = updateMarkupBlock(revised,"POST-SCRIPT","MarkupPostBody");
	return revised;
};

//
// UploadLog
// 
// config.options.chkUploadLog :
//		false : no logging
//		true : logging
// config.options.txtUploadLogMaxLine :
//		-1 : no limit
//      0 :  no Log lines but UploadLog is still in place
//		n :  the last n lines are only kept
//		NaN : no limit (-1)

bidix.UploadLog = function() {
	if (!config.options.chkUploadLog) 
		return; // this.tiddler = null
	this.tiddler = store.getTiddler("UploadLog");
	if (!this.tiddler) {
		this.tiddler = new Tiddler();
		this.tiddler.title = "UploadLog";
		this.tiddler.text = "| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |";
		this.tiddler.created = new Date();
		this.tiddler.modifier = config.options.txtUserName;
		this.tiddler.modified = new Date();
		store.addTiddler(this.tiddler);
	}
	return this;
};

bidix.UploadLog.prototype.addText = function(text) {
	if (!this.tiddler)
		return;
	// retrieve maxLine when we need it
	var maxLine = parseInt(config.options.txtUploadLogMaxLine,10);
	if (isNaN(maxLine))
		maxLine = -1;
	// add text
	if (maxLine != 0) 
		this.tiddler.text = this.tiddler.text + text;
	// Trunck to maxLine
	if (maxLine >= 0) {
		var textArray = this.tiddler.text.split('\n');
		if (textArray.length > maxLine + 1)
			textArray.splice(1,textArray.length-1-maxLine);
			this.tiddler.text = textArray.join('\n');		
	}
	// update tiddler fields
	this.tiddler.modifier = config.options.txtUserName;
	this.tiddler.modified = new Date();
	store.addTiddler(this.tiddler);
	// refresh and notifiy for immediate update
	story.refreshTiddler(this.tiddler.title);
	store.notify(this.tiddler.title, true);
};

bidix.UploadLog.prototype.startUpload = function(storeUrl, toFilename, uploadDir,  backupDir) {
	if (!this.tiddler)
		return;
	var now = new Date();
	var text = "\n| ";
	var filename = bidix.basename(document.location.toString());
	if (!filename) filename = '/';
	text += now.formatString("0DD/0MM/YYYY 0hh:0mm:0ss") +" | ";
	text += config.options.txtUserName + " | ";
	text += "[["+filename+"|"+location + "]] |";
	text += " [[" + bidix.basename(storeUrl) + "|" + storeUrl + "]] | ";
	text += uploadDir + " | ";
	text += "[[" + bidix.basename(toFilename) + " | " +toFilename + "]] | ";
	text += backupDir + " |";
	this.addText(text);
};

bidix.UploadLog.prototype.endUpload = function(status) {
	if (!this.tiddler)
		return;
	this.addText(" "+status+" |");
};

//
// Utilities
// 

bidix.checkPlugin = function(plugin, major, minor, revision) {
	var ext = version.extensions[plugin];
	if (!
		(ext  && 
			((ext.major > major) || 
			((ext.major == major) && (ext.minor > minor))  ||
			((ext.major == major) && (ext.minor == minor) && (ext.revision >= revision))))) {
			// write error in PluginManager
			if (pluginInfo)
				pluginInfo.log.push("Requires " + plugin + " " + major + "." + minor + "." + revision);
			eval(plugin); // generate an error : "Error: ReferenceError: xxxx is not defined"
	}
};

bidix.dirname = function(filePath) {
	if (!filePath) 
		return;
	var lastpos;
	if ((lastpos = filePath.lastIndexOf("/")) != -1) {
		return filePath.substring(0, lastpos);
	} else {
		return filePath.substring(0, filePath.lastIndexOf("\\"));
	}
};

bidix.basename = function(filePath) {
	if (!filePath) 
		return;
	var lastpos;
	if ((lastpos = filePath.lastIndexOf("#")) != -1) 
		filePath = filePath.substring(0, lastpos);
	if ((lastpos = filePath.lastIndexOf("/")) != -1) {
		return filePath.substring(lastpos + 1);
	} else
		return filePath.substring(filePath.lastIndexOf("\\")+1);
};

bidix.initOption = function(name,value) {
	if (!config.options[name])
		config.options[name] = value;
};

//
// Initializations
//

// require PasswordOptionPlugin 1.0.1 or better
bidix.checkPlugin("PasswordOptionPlugin", 1, 0, 1);

// styleSheet
setStylesheet('.txtUploadStoreUrl, .txtUploadBackupDir, .txtUploadDir {width: 22em;}',"uploadPluginStyles");

//optionsDesc
merge(config.optionsDesc,{
	txtUploadStoreUrl: "Url of the UploadService script (default: store.php)",
	txtUploadFilename: "Filename of the uploaded file (default: in index.html)",
	txtUploadDir: "Relative Directory where to store the file (default: . (downloadService directory))",
	txtUploadBackupDir: "Relative Directory where to backup the file. If empty no backup. (default: ''(empty))",
	txtUploadUserName: "Upload Username",
	pasUploadPassword: "Upload Password",
	chkUploadLog: "do Logging in UploadLog (default: true)",
	txtUploadLogMaxLine: "Maximum of lines in UploadLog (default: 10)"
});

// Options Initializations
bidix.initOption('txtUploadStoreUrl','');
bidix.initOption('txtUploadFilename','');
bidix.initOption('txtUploadDir','');
bidix.initOption('txtUploadBackupDir','');
bidix.initOption('txtUploadUserName','');
bidix.initOption('pasUploadPassword','');
bidix.initOption('chkUploadLog',true);
bidix.initOption('txtUploadLogMaxLine','10');


/* don't want this for tiddlyspot sites

// Backstage
merge(config.tasks,{
	uploadOptions: {text: "upload", tooltip: "Change UploadOptions and Upload", content: '<<uploadOptions>>'}
});
config.backstageTasks.push("uploadOptions");

*/


//}}}


<html><img src="http://simonbaird.com/images/mptwlogosml.gif" alt="logo" align="right"></html>Welcome to MPTW, the TiddlyWiki formerly known as MonkeyPirateTiddlyWiki. MPTW is a distribution or edition of TiddlyWiki that includes a standard TiddlyWiki core packaged with some [[plugins|systemConfig]] designed to improve usability and provide a better way to [[organise your information|TagglyTagging]].
* [[Why use MPTW?]]
* [[Using "TagglyTagging" to organise your data|TagglyTagging]]
* [[MPTW Plugins|systemConfig]]

!!! 2022 Update: See [[MPTW5|https://mptw5-ext.tiddlyhost.com/]], a new take on MPTW for TiddlyWiki 5. [[Try it on Tiddlyhost now|https://tiddlyhost.com/sites/new?clone=mptw5-ext]]

MPTW has all the power and convenience of standard TiddlyWiki but it includes a new way of managing your data called TagglyTagging. TagglyTagging makes it easier to keep your information organised and structured. It does this using an enhanced version of the built in tagging macro and a few other complimentary tools like the 'new here' button. To learn more see the [[FAQ|TagglyTagging FAQ]] and the [[Tutorial|TagglyTagging Tutorial]].
If you've no idea what a wiki is then perhaps you're jumping in the deep end a little bit. But you can read [[this page in WikiPedia|http://en.wikipedia.org/wiki/Wiki]], the most famous wiki of all. Note that TiddlyWiki is different to other wikis in that:
* it doesn't require a server and a database to run
* it's focus is on personal use rather than multi-user collaboration