<!--{{{-->
<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]];}

.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]];}
.readOnly {background:[[ColorPalette::TertiaryPale]];}

#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 0 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:4.5em 0 1em 1em; left:0; top:0;}

.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:0 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 0.3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}

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

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

.tiddlerPopupButton {padding:0.2em;}
.popupTiddler {position: absolute; z-index:300; padding: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 0;}
.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 0 0 0.5em;}
.tab {margin:0 0 0 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 0 14em;}

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

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

.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:0 0.25em; padding:0 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:0 3px 0 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 0; font-size:.9em;}
.editorFooter .button {padding-top:0; padding-bottom:0;}

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

.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;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0; right:0;}
#backstageButton a {padding:0.1em 0.4em; margin:0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel {display:none; z-index:100; position:absolute; width:90%; margin-left:3em; padding:1em;}
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding: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;}
noscript {display:none;} /* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
}
/*}}}*/
<!--{{{-->
<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>>
Based on my last journal entry, I couldn't pass up the point suggested, that having a blog would be easier to 'subscribe' to than a wiki, [[so I created a new blog|http://thebanesblog.wordpress.com/]]. This time I used Word Press instead of the dreaded Google Blogger. It isn't as intuitive for me, as either Blogger was, or a wiki. I just need to figure out why I am doing this and how...

''//TB//''
/***
|Name|CheckboxPlugin|
|Source|http://www.TiddlyTools.com/#CheckboxPlugin|
|Version|2.1.3|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <<br>>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Description|Add checkboxes to your tiddler content|

Checkbox states can be preserved in the document by either automatically modifying the tiddler content or setting/removing tags on specified tiddlers, or they may be saved as local cookies by assigning an optional 'chkID' to the checkbox.  Add custom javascript for programmatic initialization and onClick handling for any checkbox.  Also provides access to checkbox DOM element data and tracks the checkbox state in TiddlyWiki's config.options[] internal data.

!!!!!Usage
<<<
The checkbox syntax, including all optional parameters, is contained inside a matched set of [ and ] brackets.
{{{ [x=id(title|tag){init_script}{onclick_script}] }}}

An alternative syntax lets you place the optional parameters ''outside'' the [ and ] brackets, and is provided for backward-compatibility with existing content that may include checkbox definitions based on earlier releases of this plugin:
{{{ [x]=id(title|tag){init_script}{onclick_script} }}}

//{{{
[ ]or[_] and [x]or[X]
//}}}
Simple checkboxes.  The current unchecked/checked state is indicated by the character between the {{{[}}} and {{{]}}} brackets ("_" means unchecked, "X" means checked).  When you click on a checkbox, the current state is retained by directly modifying the tiddler content to place the corresponding "_" or "X" character in between the brackets
//{{{
[x=id]
//}}}
Assign an optional ID to the checkbox so you can use {{{document.getElementByID("id")}}} to manipulate the checkbox DOM element, as well as tracking the current checkbox state in {{{config.options["id"]}}}.  If the ID starts with "chk" the checkbox state will also be saved in a cookie, so it can be automatically restored whenever the checkbox is re-rendered (overrides any default {{{[x]}}} or {{{[_]}}} value).  If a cookie value is kept, the "_" or "X" character in the tiddler content remains unchanged, and is only applied as the default when a cookie-based value is not currently defined.
//{{{
[x(title|tag)] or [x(title:tag)]
//}}}
Initializes and tracks the current checkbox state by setting or removing ("TogglyTagging") a particular tag value from a specified tiddler.  If you omit the tiddler title (and the | or : separator), the specified tag is assigned to the current tiddler.  If you omit the tag value, as in {{{(title|)}}}, the default tag, {{{checked}}}, is assumed.  Omitting both the title and tag, {{{()}}}, tracks the checkbox state by setting the "checked" tag on the current tiddler.  When tag tracking is used, the "_" or "X" character in the tiddler content remains unchanged, and is not used to set or track the checkbox state.  If a tiddler title named in the tag does not exist, the checkbox state defaults to //unselected//.  When the checkbox is subsequently changed to //selected//, it will automatically (and silently) create the missing tiddler and then add the tag to it.  //''NOTE: beginning with version 2.1.2 of this plugin, the "|" separator is the preferred separator between the title and tag name, as it avoids syntactic ambiguity when ":" is used within tiddler titles or tag names.''//
//{{{
[x{javascript}{javascript}]
//}}}
You can define optional javascript code segments to add custom initialization and/or 'onClick' handling to a checkbox.  The current checkbox state (and it's other DOM attributes) can be set or read from within these code segments by reference to the default context-object, 'this'.

The first code segment will be executed when the checkbox is initially displayed, so that you can programmatically determine it's starting checked/unchecked state.  The second code segment (if present) is executed whenever the checkbox is clicked, so that you can perform programmed responses or intercept and override the checkbox state based on complex logic using the TW core API or custom functions defined in plugins (e.g. testing a particular tiddler title to see if certain tags are set or setting some tags when the checkbox is clicked).

Note: if you want to use the default checkbox initialization processing with a custom onclick function, use this syntax: {{{ [x=id{}{javascript}] }}} 
<<<
!!!!!Configuration
<<<
Normally, when a checkbox state is changed, the affected tiddlers are automatically re-rendered, so that any checkbox-dependent dynamic content can be updated.  There are three possible tiddlers to be re-rendered, depending upon where the checkbox is placed, and what kind of storage method it is using.
*''container'': the tiddler in which the checkbox is displayed. (e.g., this tiddler)
*''tagged'': the tiddler that is being tagged (e.g., "~MyTask" when tagging "~MyTask:done")
*''tagging'': the "tag tiddler" (e.g., "~done" when tagging "~MyTask:done")
You can set the default refresh handling for all checkboxes in your document by using the following javascript syntax either in a [[systemConfig]] plugin, or as an inline script.  (Substitute true/false values as desired):
{{{config.checkbox.refresh = { tagged:true, tagging:true, container:true };}}}

You can also override these defaults for any given checkbox by using an initialization function to set one or more of the refresh options.  For example:
{{{[_{this.refresh.container=false}]}}}
<<<
!!!!!Examples
<<<
//{{{
[X] label
[_] label
//}}}
>checked and unchecked static default values
>[_] label
>[_] label
//{{{
[_=demo] label
//}}}
>document-based value (id='demo', no cookie)
>[X=demo] label
//{{{
[_=chkDemo] label
//}}}
>cookie-based value  (id='chkDemo')
>[_=chkDemo] label
//{{{
[_(CheckboxPlugin|demotag)]
[_(CheckboxPlugin|demotag){this.refresh.tagged=this.refresh.container=false}]
//}}}
>tag-based value (TogglyTagging)
>[_(CheckboxPlugin|demotag)] toggle 'demotag' (and refresh tiddler display)
>[_(CheckboxPlugin|demotag){this.refresh.tagged=this.refresh.container=false}] toggle 'demotag' (no refresh)
>current tags: <script>return store.getTiddler(story.findContainingTiddler(place).id.substr(7)).tags.toString();</script>
><script label="click to view current tags">alert(store.getTiddler(story.findContainingTiddler(place).id.substr(7)).tags.toString());return false</script>
//{{{
[X{this.checked=true}{alert(this.checked?"on":"off")}] message box with checkbox state
//}}}
>custom init and onClick functions
>[X{this.checked=true}{alert(this.checked?"on":"off")}] message box with checkbox state
Retrieving option values:
config.options['demo']=<script>return config.options['demo']?"true":"false";</script>
config.options['chkDemo']=<script>return config.options['chkDemo']?"true":"false";</script>

!!!!!Installation
import (or copy/paste) the following tiddlers into your document:
''CheckboxPlugin'' (tagged with <<tag [[systemConfig]]>>)
<<<
!!!!!Revision History
<<<
2006.05.04 - 2.1.3 fix use of findContainingTiddler() to check for a non-null return value, so that checkboxes won't crash when used outside of tiddler display context (such as in header, sidebar or mainmenu)
2006.03.11 - 2.1.2 added "|" as delimiter to tag-based storage syntax (e.g. "tiddler|tag") to avoid parsing ambiguity when tiddler titles or tag names contain ":".   Using ":" as a delimiter is still supported but is deprecated in favor of the new "|" usage.  Based on a problem reported by JeffMason.
2006.02.25 - 2.1.0 added configuration options to enable/disable forced refresh of tiddlers when toggling tags
2006.02.23 - 2.0.4 when toggling tags, force refresh of the tiddler containing the checkbox.
2006.02.23 - 2.0.3 when toggling tags, force refresh of the 'tagged tiddler' so that tag-related tiddler content (such as "to-do" lists) can be re-rendered.
2006.02.23 - 2.0.2 when using tag-based storage, allow use [[ and ]] to quote tiddler or tag names that contain spaces:
{{{[x([[Tiddler with spaces]]:[[tag with spaces]])]}}}
2006.01.10 - 2.0.1 when toggling tags, force refresh of the 'tagging tiddler'.  For example, if you toggle the "[[systemConfig]]" tag on a plugin, the corresponding "[[systemConfig]]" TIDDLER will be automatically refreshed (if currently displayed), so that the 'tagged' list in that tiddler will remain up-to-date.
2006.01.04 - 2.0.0 update for ~TW2.0
2005.12.27 - 1.1.2 Fix lookAhead regExp handling for {{{[x=id]}}}, which had been including the "]" in the extracted ID.  
Added check for "chk" prefix on ID before calling saveOptionCookie()
2005.12.26 - 1.1.2 Corrected use of toUpperCase() in tiddler re-write code when comparing {{{[X]}}} in tiddler content with checkbox state. Fixes a problem where simple checkboxes could be set, but never cleared.
2005.12.26 - 1.1.0 Revise syntax so all optional parameters are included INSIDE the [ and ] brackets.  Backward compatibility with older syntax is supported, so content changes are not required when upgrading to the current version of this plugin.   Based on a suggestion by GeoffSlocock
2005.12.25 - 1.0.0 added support for tracking checkbox state using tags ("TogglyTagging")
Revised version number for official post-beta release.
2005.12.08 - 0.9.3 support separate 'init' and 'onclick' function definitions.
2005.12.08 - 0.9.2 clean up lookahead pattern
2005.12.07 - 0.9.1 only update tiddler source content if checkbox state is actually different.  Eliminates unnecessary tiddler changes (and 'unsaved changes' warnings)
2005.12.07 - 0.9.0 initial BETA release
<<<
!!!!!Credits
<<<
This feature was created by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
<<<
!!!!!Code
***/
//{{{
version.extensions.CheckboxPlugin = {major: 2, minor: 1, revision:3 , date: new Date(2006,5,4)};
//}}}

// // 1.2.x compatibility
//{{{
if (!window.story) window.story=window;
if (!store.getTiddler) store.getTiddler=function(title){return store.tiddlers[title]}
if (!store.addTiddler) store.addTiddler=function(tiddler){store.tiddlers[tiddler.title]=tiddler}
if (!store.deleteTiddler) store.deleteTiddler=function(title){delete store.tiddlers[title]}
//}}}

//{{{
config.checkbox = { refresh: { tagged:true, tagging:true, container:true } };
config.formatters.push( {
	name: "checkbox",
	match: "\\[[xX_ ][\\]\\=\\(\\{]",
	lookahead: "\\[([xX_ ])(\\])?(=[^\\s\\(\\]{]+)?(\\([^\\)]*\\))?({[^}]*})?({[^}]*})?(\\])?",
	handler: 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)
			{
				// get params
				var checked=lookaheadMatch[1];
				var id=lookaheadMatch[3];
				var tag=lookaheadMatch[4];
				var fn_init=lookaheadMatch[5];
				var fn_click=lookaheadMatch[6];
				// create checkbox element
				var c = document.createElement("input");
				c.setAttribute("type","checkbox");
				c.onclick=onClickCheckbox;
				c.srcpos=w.matchStart+1; // remember location of "X"
				c.container=story.findContainingTiddler(w.output); if (c.container) c.container=c.container.id.substr(7); // tiddler containing checkbox
				c.refresh = { };
				c.refresh.container=config.checkbox.refresh.container;
				c.refresh.tagged=config.checkbox.refresh.tagged;
				c.refresh.tagging=config.checkbox.refresh.tagging;
				w.output.appendChild(c);
				// set default state
				c.checked=(checked.toUpperCase()=="X");
				// get/set state by ID
				if (id) {
					c.id=id.substr(1); // trim off leading "="
					if (config.options[c.id]!=undefined)
						c.checked=config.options[c.id];
					else
						config.options[c.id]=c.checked;
				}
				// get/set state by tag
				if (tag) {
					c.tiddler=c.container;
					c.tag=tag.substr(1,tag.length-2).trim(); // trim off parentheses
					var pos=c.tag.indexOf("|"); if (pos==-1) var pos=c.tag.indexOf(":");
					if (pos==0) { c.tag=tag.substr(1); }
					if (pos>0) { c.tiddler=c.tag.substr(0,pos).replace(/\[\[/g,"").replace(/\]\]/g,""); c.tag=c.tag.substr(pos+1); }
					c.tag.replace(/\[\[/g,"").replace(/\]\]/g,"");
					if (!c.tag.length) c.tag="checked";
					var t=store.getTiddler(c.tiddler);
					c.checked = (t && t.tags)?(t.tags.find(c.tag)!=null):false;
				}
				if (fn_init) c.fn_init=fn_init.trim().substr(1,fn_init.length-2); // trim off surrounding { and } delimiters
				if (fn_click) c.fn_click=fn_click.trim().substr(1,fn_click.length-2);
				c.init=true; c.onclick(); c.init=false; // compute initial state and save in tiddler/config/cookie
				w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
			}
		}
	}
)
//}}}

//{{{
function onClickCheckbox()
{
	if (this.fn_init)
		// custom function hook to set initial state (run only once)
		{ try { eval(this.fn_init); this.fn_init=null; } catch(e) { displayMessage("Checkbox init error: "+e.toString()); } }
	else if (this.fn_click)
		// custom function hook to override or react to changes in checkbox state
		{ try { eval(this.fn_click) } catch(e) { displayMessage("Checkbox click error: "+e.toString()); } }
	if (this.id)
		// save state in config AND cookie (only when ID starts with 'chk')
		{ config.options[this.id]=this.checked; if (this.id.substr(0,3)=="chk") saveOptionCookie(this.id); }
	if ((!this.id || this.id.substr(0,3)!="chk") && !this.tag) {
		// save state in tiddler content only if not using cookie or tag tracking
		var t=story.findContainingTiddler(this); if (t) {
			var t=store.getTiddler(t.id.substr(7));
			if (this.checked!=(t.text.substr(this.srcpos,1).toUpperCase()=="X")) { // if changed
				t.set(null,t.text.substr(0,this.srcpos)+(this.checked?"X":"_")+t.text.substr(this.srcpos+1),null,null,t.tags);
				store.setDirty(true);
			}
		}
	}
	if (this.tag) {
		var t=store.getTiddler(this.tiddler);
		if (!t) { t=(new Tiddler()); t.set(this.tiddler,"",config.options.txtUserName,(new Date()),null); store.addTiddler(t); } 
		var tagged=(t.tags && t.tags.find(this.tag)!=null);
		if (this.checked && !tagged) { t.tags.push(this.tag); store.setDirty(true); }
		if (!this.checked && tagged) { t.tags.splice(t.tags.find(this.tag),1); store.setDirty(true); }
		// if tag state has been changed, force a display update
		if (this.checked!=tagged) {
			if (this.refresh.tagged) story.refreshTiddler(this.tiddler,null,true); // the TAGGED tiddler
			if (this.refresh.tagging) story.refreshTiddler(this.tag,null,true); // the TAGGING tiddler
		}
	}
	// refresh containing tiddler (but not during initial rendering, or we get an infinite loop!)
	if (!this.init && this.refresh.container && this.container!=this.tiddler)
		story.refreshTiddler(this.container,null,true); // the tiddler CONTAINING the checkbox
	return true;
}
//}}}
Background: #cccccc
Foreground: #000
PrimaryPale: #999999
PrimaryLight: #667788
PrimaryMid: #334444
PrimaryDark: #000
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #555566
TertiaryPale: #999999
TertiaryLight: #EEC591
TertiaryMid: #000
TertiaryDark: #8B7355
//{{{
[X] label
[_] label
//}}}
>checked and unchecked static default values
>[_] label
>[_] label
[[Gateway]] [[Starting Over]]
<<dice 1d6 initialroll:yes rollby:button show:result >>
<<dice 3d6 initialroll:yes rollby:button show:result >>
<<dice 6d6 initialroll:yes rollby:button show:result >>
<<dice 1d100 initialroll:yes rollby:button show:result >>
<<dice 5d6-10 initialroll:yes rollby:button>> 
/***
|''Name:''|Dice|
|''Version:''|0.5 (21 Oct 2005)|
|''Source:''|Tiddly W;nks (http://danielbaird.com/tiddlywinks/)|
|''Author:''|[[Daniel Baird]]|
|''Type:''|Macro|
!Description
Tell it what dice you want, and it'll let you roll them.

!Syntax/Example usage

{{{<<dice>>}}} for 3d6

{{{<<dice }}}//{{{specifier}}}//{{{ [}}}//{{{options}}}//{{{]>>}}} where specifier is something like 3d6, 1d4+1, 2d10-5

{{{<<dice fudge [}}}//{{{options}}}//{{{]>>}}} will roll 3 fudge dice (a fudge die gives -1, 0, or +1)

options can be zero or more of the following:

* show:eachface
** shows the face of each die rolled, then the total (not yet implemented)
* show:eachroll
** shows the number on each die rolled, then the total (default)
* show:result
** shows just the total result of the roll
* initialroll:yes
** do a roll when initially displayed
* initialroll:no
** don't roll until the user says so (default)
* rollby:click
** user clicks anywhere on the diceroller area to roll
* rollby:link
** user clicks an underlined link to roll (default)
* rollby:button
** user clicks a button to roll

eg:

{{{<<dice>>}}}
<<dice>>

{{{<<dice 3d6+2 rollby:button>>}}}
<<dice 3d6+2 rollby:button>>

{{{<<dice fudge initialroll:yes rollby:click>>}}}
<<dice fudge initialroll:yes rollby:click>>


!Notes
* much changing during this 0.5 version.

!Revision History
* 0.1
** first release
* 0.2
** changed the corners to slightly prettier ugly text chars
** finally got rid of the borders
** got rid of heading
** added 'Fudge' mode
* 0.3 (5 Oct 2005)
** fixed the problem with multiple dice rollers
* 0.31 (12 Oct 2005)
** worked out how to use a closure as a event handler, which means that the code added in 0.3 could be made a lot simpler.
* 0.5 (21 Oct 2005)
** aiming at getting a whole rewrite done. much progress!.

***/
/*{{{*/
// =======================================================================
version.extensions.dice = {major: 0, minor: 5, revision: 0};

config.macros.dice = {};

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

    var rolldesc = params[0];
    if (rolldesc == undefined) rolldesc = '3d6';

	var options = '';
    if (params.length > 1) {
		params.shift();
    	options = params.join(' ');
	}

    var thisDiceRoller = new DiceRoller();
    createTiddlyElement(place, 'div', thisDiceRoller.id, null, 'If you see this, DiceRoller is broken.  Let Daniel know (DanielBaird at gmail dot com).');
    thisDiceRoller.newDice(rolldesc, options);
}
// =======================================================================
function DiceRoller() {
	this.idprefix =  'dice';
    this.version = '0.5 beta';
    this.id = this.idprefix + DiceRoller.prototype.nextid;
    DiceRoller.prototype.nextid++;
    return this;
}
// -----------------------------------------------------------------------
DiceRoller.prototype.nextid = 0;
// -----------------------------------------------------------------------
DiceRoller.prototype.newDice = function(rolldesc, options) {

	this.error = null;
	this.initialroll = false;
	this.display = 'eachroll';
	this.rolltag = 'roll';
	this.rollagaintag = 'roll again';
	this.rollby = 'link';
	this.parseDesc(rolldesc);
	this.parseOpts(options);
	if (this.initialroll) this.rollDice();
	this.drawRoller();
// ^^^^^^^^^^^^^ new stuff above.. ^^^^^^^^^^^^^
//    this.rolls = rolls;

//    this.resultDisplay = 'dice';
//    if (resultDisplay != null)	this.resultDisplay = resultDisplay;

//    this.resultSystem = 'sum';
//    if (resultSystem != null)	this.resultSystem = resultSystem;

//    this.sides = 6;
//    this.createDice();
//	this.roll();
}
// -----------------------------------------------------------------------
DiceRoller.prototype.parseDesc = function(desc) {
	this.rollstr = desc;
	desc = desc.toLowerCase();
	this.dicetype = 'standard';
	this.adjuster = null;
	if (desc == 'fudge') {
		// 'fudge' system: 3d3, where the d3 gives -1, 0 or +1
		this.dicetype = 'fudge';
		this.dicesides = 3;
		this.rollcount = 3;
	} else {
		// normal system: eg 3d6+2
		var reg = /(\d+)d(\d+)(\+(\d+)|-(\d+))?/;
		var info = desc.match(reg);
		this.rollcount = parseInt(info[1]);
		this.dicesides = parseInt(info[2]);
		this.adjuster = parseInt(info[3]);
		if ( isNaN(this.adjuster) ) this.adjuster = 0;
	}
}
// -----------------------------------------------------------------------
DiceRoller.prototype.parseOpts = function(options) {
	this.optstr = options;
	options = ' ' + options + ' ';
	if (options.indexOf(' show:eachface ') != -1) this.display = 'eachface';
	if (options.indexOf(' show:eachroll ') != -1) this.display = 'eachroll';
	if (options.indexOf(' show:result ') != -1) this.display = 'result';

	if (options.indexOf(' initialroll:yes ') != -1) this.initialroll = true;
	if (options.indexOf(' initialroll:no ') != -1) this.initialroll = false;

	if (options.indexOf(' rollby:click ') != -1) this.rollby = 'click';
	if (options.indexOf(' rollby:link ') != -1) this.rollby = 'link';
	if (options.indexOf(' rollby:button ') != -1) this.rollby = 'button';
}
// -----------------------------------------------------------------------
DiceRoller.prototype.createDice = function() {
    this.results = new Array(this.rolls);
    this.result = 0;
    this.showDice();
}
// -----------------------------------------------------------------------
DiceRoller.prototype.drawRoller = function() {
    var node = document.getElementById(this.id);
	if (this.display == 'eachface') node.innerHTML = this.drawRollerEachFace();
	if (this.display == 'eachroll') node.innerHTML = this.drawRollerEachRoll();
	if (this.display == 'result') node.innerHTML = this.drawRollerResult();

    // getClickHandler() is a function that returns a function.. JS is sweet huh
    if (this.rollby == 'click') {
		node.onclick = this.getClickHandler();
	} else {
		document.getElementById(this.id + '_roll').onclick = this.getClickHandler();
	}
	/*
    var html = '';

	if (this.display == 'eachface')

    html += '<table class="diceroller">';
    html += '<tr>';

	if (this.resultDisplay == 'text') {
		html.push('<td>You rolled ');
	}
	if (this.resultDisplay != 'textsummary') {
		var separator = '';
		for (var roll = 0; roll < this.rolls; roll++) {
			html.push( separator + this.drawDie(roll) );
			if (this.resultDisplay == 'text')	separator = ', ';
		}
		html.push('<td>');
	}
    if (this.resultSystem == 'fudge') {
	var resprefix = '';
	if (this.result > 0) resprefix = '+';
	html.push('Result is ' + resprefix + this.result + '.<br />Click to roll again.</td>');
    } else {
	html.push('<td>'+this.rolls+'d'+this.sides+': you rolled ' + this.result + '.<br />Click to roll again.</td>');
    }

    html += '</tr></table>';
    node.innerHTML = html;
	*/
}
// -----------------------------------------------------------------------
DiceRoller.prototype.drawRollerResult = function() {
	var str = '';
	str += 'Rolling';
	str += ((this.dicetype == 'fudge')?(':'):(' ' + this.rollstr + ':'));
	if (this.result != undefined) {
		str += 'You rolled <strong>';
		str += ((this.dicetype == 'fudge')?(this.addSign(this.result)):(this.result));
		str += '</strong>.';
	}
	str += this.makeRollTrigger();
	return str;
}
// -----------------------------------------------------------------------
DiceRoller.prototype.drawRollerEachRoll = function() {
	var str = '';
	str += 'Rolling';
	str += ((this.dicetype == 'fudge')?(':'):(' ' + this.rollstr + ':'));
	if (this.result != undefined) {
		str += ' You rolled ';
		var joiner = '';
		for (var r = 0; r < this.rollcount; r++) {
			str += joiner + ((this.dicetype == 'fudge')?(this.addSign(this.results[r])):(this.results[r]));
			joiner = ', ';
		}
		str += ' totalling <strong>';
		str += ((this.dicetype == 'fudge')?(this.addSign(this.result)):(this.result));
		str += '</strong>.';
	}
	str += this.makeRollTrigger();
	return str;
}
// -----------------------------------------------------------------------
DiceRoller.prototype.addSign = function(num) {
	return ( ((parseInt(num) > 0)?('+'):('')) + (num) );
}
// -----------------------------------------------------------------------
DiceRoller.prototype.makeRollTrigger = function() {
	var tag = ((this.result == undefined)?(this.rolltag):(this.rollagaintag));
	if (this.rollby == 'click')  return ' Click to ' + tag + '.';
	if (this.rollby == 'link')   return ' <a style="text-decoration: underline" href="#" id="' + this.id + '_roll">' + tag + '</a>';
	if (this.rollby == 'button') return ' <button id="' + this.id + '_roll">' + tag + '</button>';
}
// -----------------------------------------------------------------------
DiceRoller.prototype.drawDie = function(roll) {
    var html = new Array();

	if (this.resultDisplay == 'text') {
		if (this.resultSystem == 'fudge' && this.results[roll] > 0) html.push('+');
		html.push(this.results[roll]);
	} else {
		html.push('<td class="die">');

		//there are seven possible dot positions
		var dots = Array();
		for (var dot = 0; dot < 7; dot++) {
			dots.push('&nbsp;');
		}
		if ( this.results[roll] ) {
			if (this.results[roll] > 1) dots[0] = 'O';
			if (this.results[roll] > 3) dots[1] = 'O';
			if (this.results[roll] == 6) dots[2] = 'O';
			if (this.results[roll]%2 == 1) dots[3] = 'O';
			if (this.results[roll] == 6) dots[4] = 'O';
			if (this.results[roll] > 3) dots[5] = 'O';
			if (this.results[roll] > 1) dots[6] = 'O';
		}
		var pipe = '|';
		var space = '&nbsp;';
		if (this.resultDisplay == 'dice') {
			html.push(',-----.<br />');
			html.push(pipe + space + dots[0] + space + dots[1] + space + pipe + '<br />');
			html.push(pipe + space + dots[2] + dots[3] + dots[4] + space + pipe + '<br />');
			html.push(pipe + space + dots[5] + space + dots[6] + space + pipe + '<br />');
			html.push('`-----\'<br />');
		} else if (this.resultDisplay == 'compact') {
			html.push(dots[0] + space + dots[1] + '<br />');
			html.push(dots[2] + dots[3] + dots[4] + '<br />');
			html.push(dots[5] + space + dots[6] + '<br />');
		}
		html.push('</td>');
	}
    return html.join('');
}
// -------------------------------------------------------------------
DiceRoller.prototype.rollDice = function() {
	this.result = this.adjuster;
	this.results = new Array(this.rollcount);
	for (var roll = 0; roll < this.rollcount; roll++) {
		this.results[roll] = Math.floor((this.dicesides)*Math.random())+1;
		if (this.dicetype == 'fudge') {
			this.results[roll] -= 2;
		}
		this.result += this.results[roll];
	}
	this.drawRoller();
}
// -----------------------------------------------------------------------
DiceRoller.prototype.getClickHandler = function() {
	// trickey bit.. first make a local var that references the
	// current dice roller object, then return an anonymous function
	// that calls that object's roll() method.  woot for closures!
	var thisroller = this;
	return function(e) {
		thisroller.rollDice();
		return false;
	};
}
// =======================================================================
setStylesheet(
	".viewer table.diceroller, .viewer table.diceroller tr { "+
		"border: none;" +
	"} \n"+

	".viewer table.diceroller tr td { "+
		"border: none; " +
	"} \n"+

	".viewer table.diceroller td.die { "+
		"padding: 0.5em; " +
		"font-family: monospace; " +
		"line-height: 0.95em; " +
	"} \n"+

	"",
	"DiceRoller");

/*}}}*/
[img[Gateway|http://i1168.photobucket.com/albums/r494/thebane2/BlogPortal.png][http://i1168.photobucket.com/albums/r494/thebane2/BlogPortal.png]]

/***
|''Name:''|LoadRemoteFileThroughProxy (previous LoadRemoteFileHijack)|
|''Description:''|When the TiddlyWiki file is located on the web (view over http) the content of [[SiteProxy]] tiddler is added in front of the file url. If [[SiteProxy]] does not exist "/proxy/" is added. |
|''Version:''|1.1.0|
|''Date:''|mar 17, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#LoadRemoteFileHijack|
|''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|
***/
//{{{
version.extensions.LoadRemoteFileThroughProxy = {
 major: 1, minor: 1, revision: 0, 
 date: new Date("mar 17, 2007"), 
 source: "http://tiddlywiki.bidix.info/#LoadRemoteFileThroughProxy"};

if (!window.bidix) window.bidix = {}; // bidix namespace
if (!bidix.core) bidix.core = {};

bidix.core.loadRemoteFile = loadRemoteFile;
loadRemoteFile = function(url,callback,params)
{
 if ((document.location.toString().substr(0,4) == "http") && (url.substr(0,4) == "http")){ 
 url = store.getTiddlerText("SiteProxy", "/proxy/") + url;
 }
 return bidix.core.loadRemoteFile(url,callback,params);
}
//}}}
[[WelcomeToTiddlyspot]] [[GettingStarted]]

[[RSS Feed|http://thebabblingbane.tiddlyspot.com/index.xml]]
/***
|''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"
	});
*/
//}}}
Thanks to ''//Brendan's//'' comment over on my former Blog, I have turned on RSS Feeds for this Wiki. Subscribing now might be a bit early as you would get an update every time I modify the site. This should calm down once I have it setup and am only posting 'Bloggy' types of things.

Thanks ''//Brendan//''!

''//TB//''
/***
|''Name''|RollonPlugin|
|''Description''|A plugin to "Roll" on Tables, creating tiddlers containing random elements of lists, extracted from other tiddlers|
|''Icon''||
|''Author''|Joshua Macy|
|''Contributors''||
|''Version''|1.02|
|''Date''|2009-03-11|
|''Status''|@@beta@@;|
|''Source''|http://rollon.tiddlyspot.com#RollonPlugin|
|''CodeRepository''|http://rollon.tiddlyspot.com|
|''Copyright''|Joshua Macy, 2009|
|''License''|Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License|
|''CoreVersion''|2.4.1|
|''Requires''||
|''Overrides''|Adds String methods for trim, rtrim and ltrim|
|''Feedback''|joshua.macy@gmail.com|
|''Documentation''|http://rollon.tiddlyspot.com|
|''Keywords''|rollon|
!Description
A Plugin to roll on tables (formatted as lists). The plugin provides a rollon macro that has two functions, depending on the context where it's called.
*If the macro is in a tiddler that lacks the rollonResult tag, it creates a text label describing the table and a button to press to roll on that table. Pressing the button creates a new tiddler with the 
rollonResult tag and places the macro in that tiddler
*If the macro is in a tiddler that has a rollonResult tag it looks at the named tiddler as a table and to chooses a random element from that table and places it in the current tiddler
Thus, if you click on the roll button, you get a new tiddler with a random element from the table that you named.
Note that rollon calls can call other rollon calls!  If the entry selected from the table contains another rollon macro, that rollon macro is executed to select a random entry from ''it's'' table, and so on
until it bottoms out with an entry that contains no other rollon macros.  The results of all these calls are placed in order in the tiddler that originated the call, allowing you to build up complete sentences
and paragraphs of results, almost like a Mad-Lib.

You can control many parameters to the rollon call, or allow it to default to (hopefully sensible) values.
!Notes
Doesn't yet support entries that have sub-lists
!Usage
{{{
<<rollon tiddlername times>>
}}}
tiddlername - name of a tiddler containing a list of entries.
times - the number of times to roll.  May be a dice expression like 1d6 or 2d3+2

for more control you can use named parameters:
{{{
<<rollon table:tiddlername times:n separator:sep-char result:prompt>>
}}}

A complete list of parameters:
* table -  the name of the table to roll on.  "prompt" will cause it to prompt the user for a table name.  If the table name doesn't exist but appears to be a dice expression, like 1d6+2, it will evaluate the dice expression and return the value instead
* times - the number of times to roll on the table.  "prompt" will cause it to prompt the user for a number of times.  If the number of times appears to be a dice expression, it will evaluate the expression and roll that number of times.  The results are separated in the tiddler by a character (default is comma). ''Defaults to 1.''
* dice - a dice expression, such as 1d20, 3d6+1, 1d4+1d6, etc.  If no table is provided, the macro will return the result of the roll.  If a table is provided, the macro will roll that dice expression on the table.  ''Defaults to the highest number in the table.''  E.g. a table with 30 entries will have one of them selected at random, as if a 1d30 had been rolled.
* separator - the character used to separate multiple results.  ''Defaults to comma.''  Use {{{<br>}}} to separate results by line breaks
* resultName - the name of the new tiddler to create to hold the results.  ''Defaults to the name of the current tiddler, plus " Result" plus a numeric suffix, such as (1) or (3) to ensure the tiddler name is unique.''
* force - to force the result of the die roll to be a particular number.  Generally this is used for debugging. ''No default.''
* overWrite - make the results overwrite the previous results instead of adding a new tiddler with a numeric suffix

The first two parameters are assumed to be table and times, if named parameters aren't used.  Named parameters are required for all the others, and can be used for table and times for clarity.
If any parameter is set to the string &quot;prompt&quot;, then the user will be prompted for the appropriate value when the roll button is pushed.

Tables are any Tiddler that contains a list (ordered or unordered), or
a series of lines each starting with a number.  If the tiddler has text
prior to the beginning of the list, the text will be copied into the result,
trimmed of leading and trailing spaces.  If you want new lines in the preamble,
use <br> elements. Whitespace between the * or # or number at the beginning of a
line is preserved.
<...>
{{{
!!Examples
<<rollon "Bar Names">>

<<rollon table:"Bar Names" separator:"," times:3d6>>

<<rollon BarNames 3d6 separator:->>

!Configuration Options
None
!Revision History
!!v<1.02> (2009-12-15) bugfix release: fixed definition table to offset for the lowest value if it doesn't start with 1
!!v<1.0rc1> (2009-03-11) release candidate 1
* 
!To Do
add setting and consulting variables
add formatting options (first letter capitalization, gender agreement helpers, etc)
!Code
***/
//{{{
if(!version.extensions.RollonPlugin) { //# ensure that the plugin is only installed once
version.extensions.RollonPlugin = { installed: true };

if(!config.extensions) { config.extensions = {}; } //# obsolete from v2.4.2

config.macros.delTagged = {
	handler: function (place, macroName, params, wikifier, paramString, tiddler) {
        wikify("Deleted Tagged Tiddlers", place);
        btn = createTiddlyButton(place,"delete","Deletes Tiddlers Tagged with VAR", config.macros.delTagged.onClick);
    
    },
	onClick: function(ev) {
		if (!ev) {
			ev = window.event;
		}
        story.forEachTiddler(function(title,element) {
            var t = store.getTiddler(title);
            if (t) {
                if (t.isTagged("VAR")) {
                    alert(title);
                    store.removeTiddler(title);
                    story.closeTiddler(title, true);
                    //autoSaveChanges();
                }
            }
		});
    }
};

config.macros.rollon = {
	suffixText: ' (%0)',
	suffixPattern: / \(([0-9]+)\)$/,
	zeroPad: 0,
	sparse: false,
	diceRE : /(\d+)[dD]([\d]+)([tT])?/g,
	listRE : /^(?:[\*#]|\d+\.?)([^\*#].+)$/gim,
	defRE : /^([:;])(.+)$/gim,
	rangeRE : /^(\d+)(?:-(\d+))?\s*/,
	handler: function (place, macroName, params, wikifier, paramString, tiddler) {
		var t = r = result = btn = preamble = temp = null;
		var html = "";
		var resultList = [];
		var entries = [];
		var low = high = range = rangeEntry = dieRoll = 0;
		var taggedResult = tiddler.isTagged("rollonResult");
		var prms = paramString.parseParams("anon", null, true);
		var table = getParam(prms, "table", null) || params[0];
		var times = getParam(prms, "times") || params[1] || 1;
		var dice = getParam(prms, "dice");
		var sep = getParam(prms, "separator", ",");
		var resultName = getParam(prms, "resultName", tiddler.title + " Result");
        var overWrite = getParam(prms, "overWrite", false);
		var force = getParam(prms, "force", null);
        var set = getParam(prms, "set", null);
		if (taggedResult) {
			if (times === "prompt") {
				times = prompt("Enter number of times to roll", "1");
			}
			if (dice === "prompt") {
				dice = prompt("Enter the dice expression to roll");
			}
			if (table === "prompt") {
				table = prompt("Enter the table name to roll on");
			}
			if (sep === "prompt") {
				sep = prompt("Enter the separator character(s)");
			}
			if (force === "prompt") {
				force = prompt("Force the die to roll the following number");
			}
            if (set === "prompt") {
                set = prompt("Name of the variable to set");
            }
		}
		if (taggedResult) {
			if (times) {
				if (dice && params[1] && params[1].indexOf("dice:") > -1) {
					// dice: was passed as 2nd param, so times needs to be reset
					times = 1
				} else {
					if (/^\d+$/.test(times)) {
						//all digits
						times = parseInt(times);
					} else {
						this.diceRE.lastIndex = 0;
						result = this.diceRE.exec(times);
						if (result) {
							times = this.rollExpr(times);
						}
						else {
							times = 1;
						}
					}					
				}
			} else {
				times = 1;
			}			
			if (table) {
				t = store.getTiddler(table);
				if (t) {
					this.listRE.lastIndex = 0;
					result = this.listRE.exec(t.text);
					if (result) {
						preamble = result.input.substring(0, result.index);
						while (result != null) {
							entries.push(result[1]);
							result = this.listRE.exec(t.text);
						}
						if (preamble) {
                            // don't wikify the preamble until you've dealt with the table, lest the preamble contain a rollon macro that will update
                            // the current tiddler's text when it completes
							html = wikifyStatic(preamble.trim(), null, tiddler);
						}
						for (i = 0; i < times; i ++) {
							if  (!force) {
								if (!dice) {
									r = Math.floor(Math.random() * entries.length);								
								} else {
									r = Math.min(this.rollExpr(dice), entries.length);
									r = r - 1; // entries starts at 0, even though dice start at 1
								}
							} else {
								r = Math.min(force, entries.length);
							}
							temp = wikifyStatic(entries[r], null, tiddler);
							resultList.push(temp);
						}
						html += resultList.join(sep);
						html = html.replace(/<[\/]*span>/igm, '');
						wikify(html, place, null, tiddler);
					} else {
						this.defRE.lastIndex = 0;
						result = this.defRE.exec(t.text);
						if (result) { // definition list
							preamble = result.input.substring(0, result.index);
							while (result != null) {
								if (result[1] === ';') {
									// definition
									temp = result[2];
									this.rangeRE.lastIndex = 0;
									range = this.rangeRE.exec(temp);
									if (range) {
										low = range[1];
										high = null;
										if (result[2]) {
											high = range[2];
										}
										entries.push([low, high]);
									} else {
										wikify("Definition doesn't seem to be a valid range: " + temp, place, null, tiddler);
									}
								} else if (result[1] === ':') {
									// term, prior must have been a definition (we hope)
									// we should now have a series of triples: [low, high, term]
									entries[entries.length-1].push(result[2]);
								}
								result = this.defRE.exec(t.text);
							}
							// find lowest range
							temp = entries[0];
							low = parseInt(temp[0]);
							// find highest range
							temp = entries[entries.length-1];
							if (temp[1]) {
								high = temp[1];
							} else {
								high = temp[0];
							}
							sizeOfRange = high - low;
							if (preamble) {
								html = wikifyStatic(preamble.trim(), null, tiddler);
							}
							for (i = 0; i < times; i ++) {
								if  (!force) {
									// don't require tables to start with number 0
									r = Math.ceil(Math.random() * sizeOfRange);
								} else {
									r = Math.min(force, high);
								}
                                                                r = r + low;
								// find the entry that r falls within
								rangeEntry = "Couldn't find range encompassing value " + r;
								for (var j = 0; j < entries.length; j++) {
									if (r < entries[j][0]) {
										// too high, not going to see anything with the proper range any more
										break;
									}
									rangeEntry = entries[j][2];
									if (entries[j][1] && r <= entries[j][1]) {
										break;
									}
								}
								temp = wikifyStatic(rangeEntry, null, tiddler);
								resultList.push(temp);
							}
							html += resultList.join(sep);
							html = html.replace(/<[\/]*span>/igm, '');
							wikify(html, place, null, tiddler);
						} else {
							if (t.text.trim().length > 0) {
								for (i = 0; i < times; i++) {
									temp = wikifyStatic(t.text, null, tiddler);
									resultList.push(temp);
								}
								html += resultList.join(sep);
								html = html.replace(/<[\/]*span>/igm, '');
								wikify(html, place, null, tiddler);
							} else {
								wikify("Table " + t.title + " appears to be empty", place, null, tiddler);
							}							
						}
					}
                    // by this point html should contain something worthwhile
                    if (set) {
                        this.saveVar(table, set, html);
                    }
				} else {
					// see if dice were defined
					if (!dice) {
						// see if it's a dice string
						result = this.diceRE.test(table);						
						if (!result) {
                            // see if it's a variable
                            t = store.getTiddler("Rollon_" + table);
                            if (t) {
            					wikify(t.text, place, null, tiddler);
                                return;
                            } else {
                                wikify("No Tiddler named " + table + " found", place);
                                return;
                            }
						} else {
							dice = table;
						}
					}
					for (i = 0; i < times; i++) {
						dieRoll = this.rollExpr(table);
						resultList.push(dieRoll);
					}
					dieRoll = resultList.join(sep).toString();
					wikify(dieRoll, place, null, tiddler);
				}
			} else {
				wikify("rollon requires at least one parameter", place);
			}
		} else {
			if (table) {				
				wikify("roll on [[" + table + "]]", place);
				btn = createTiddlyButton(place,"roll","<<rollon " + paramString + ">>", config.macros.rollon.onClick);
				btn.resultName = resultName;
				btn.suffixText = this.suffixText;
				btn.suffixPattern = this.suffixPattern;
				btn.zeroPad = this.zeroPad;
				btn.sparse = this.sparse;
                btn.overWrite = overWrite;
			} else {
				wikify("rollon macro requires at least one parameter", place);			
			}
		}
	},
    substVariables : function (expr) {
        var variableRE = /{([^}]+)}/g;
        var text = "";
        var t = null;
        var replaceVariable = function(match, name) {
            if (name.substr(0, 2) !== "*#") {
                t = store.getTiddler(name);
            } else {
                // try to find a match
                var varNameRE = new RegExp("#" + name.substr(2) + "$");
                var variableTiddlers = store.filterTiddlers("[tag[rollonVar]]");
                for (var i = 0; i < variableTiddlers.length; i++) {
                    if (varNameRE.test(variableTiddlers[i].title) ) {
                        alert("Found match=" + variableTiddlers[i].title)
                        t = variableTiddlers[i];
                        break;
                    }
                }
            }
            if (t) {
                text = t.text;
            }
            return text;
        };
        expr = expr.replace(variableRE, replaceVariable);
        return expr;
    },
	rollExpr: function (expr) {
        expr = this.substVariables(expr);
		this.diceRE.lastIndex = 0;
		var match = this.diceRE.exec(expr);
		var diceExpr, result;
		if (match && match[3]) {
			this.diceRE.lastIndex = 0;
			diceExpr = expr.replace(this.diceRE, "this.rollSpecial($1, $2, '$3')");
		} else {
			this.diceRE.lastIndex = 0;
			diceExpr = expr.replace(this.diceRE, "this.roll($1, $2)");
		}
		result = 0;
		result = eval(diceExpr);
		return result;
	},
	roll: function(dice, sides) {
		var result = 0, i = 0;
		for (i = 0; i < dice; i++) {
			result += Math.ceil(Math.random() * sides);
		}
		return result;
	},
	rollSpecial: function(dice, sides, option) {
		var result = 0;
		if (option.toLowerCase() === 't') {
			return this.rollTT(dice, sides);
		}
		return result;
	},
	rollTT: function(dice, sides) {
		// Tunnels & Trolls: doubles or triples add and roll over
		var result = 0, die = 0, i = 0;
		var allSame = true;
		var previous;
		if (dice === 2 || dice === 3) {
			while (allSame === true) {
				previous = 0;
				for (i = 0; i < dice; i++ ) {
					die = Math.ceil(Math.random() * sides);
					result += die;
					if (die !== previous && previous !== 0) {
						allSame = false;
					}
					previous = die;
				}
			}
		} else {
			return this.roll(dice, sides);
		}
		return result;
	},
    saveVar: function(namespace, name, value) {
        var nameSpaceRE = /^([^#]+)#/;
        var title;
        if (nameSpaceRE.test(name)) {
            // provided namespace
            title = name;
        } else {
            title = namespace + "#" + name; // default namespace
        }
        var tags=["rollonVar", "excludeLists"];
        var fields={}; // an empty object
        var who="Rollon"; // current username
        var when=new Date(); // current timestamp
        var tid=store.getTiddler(title);
        if (tid) { fields=tid.fields; }
        store.saveTiddler(title,title,value,who,when,tags,fields);
    },
	onClick: function(ev) {
		if (!ev) {
			ev = window.event;
		}
		var button = this;
        var t;
		var macroText = button.title;
        var newTitle;
		var resultName = button.resultName || "Default Roll Result";
		if (button.resultName === "prompt") {
			resultName = prompt("Name for Result?");
		}
        if (button.overWrite === false) {
            var root=resultName.replace(this.suffixPattern,''); // title without suffix..unlikely to have it at this point, but let's be safe
            // find last matching title
            var last=root;
            if (this.sparse) { // don't fill-in holes... really find LAST matching title
                var tids=store.getTiddlers('title','excludeLists');
                for (t=0; t<tids.length; t++) if (tids[t].title.startsWith(root)) last=tids[t].title;
            }
            // get next number (increment from last matching title)
            var n=1; var match=this.suffixPattern.exec(last); if (match) n=parseInt(match[1])+1;
            newTitle=root+this.suffixText.format([String.zeroPad(n,this.zeroPad)]);
            // if not sparse mode, find the next hole to fill in...
            while (store.tiddlerExists(newTitle)||document.getElementById(story.idPrefix+newTitle))
                { n++; newTitle=root+this.suffixText.format([String.zeroPad(n,this.zeroPad)]); }

        } else {
            newTitle = resultName;
            store.removeTiddler(newTitle);
            story.closeTiddler(newTitle, true);
        }
		//t = store.getTiddler(newTitle);
		t = store.createTiddler(newTitle);
		t.set(newTitle, macroText);
		t.tags = ["rollonResult"];
		t.modifier = "Rollon";
		story.displayTiddler(null, newTitle);
		var tiddlerHTML = story.getTiddler(newTitle).innerHTML;
		var viewerClass = '<div class="viewer">';
		var viewerPos = tiddlerHTML.indexOf(viewerClass);
		if (viewerPos > -1) {
			var endDivPos =  tiddlerHTML.indexOf("</div>", viewerPos);
			var displayedText = tiddlerHTML.substring(viewerPos + viewerClass.length, endDivPos);
			displayedText.replace(/<[\/]*span>/igm, '');
			t.set(null,displayedText);			
		}		
		ev.returnValue = false;
		
	}
	
};

String.prototype.trim = function() {
	return this.replace(/^\s+|\s+$/g,"");
}
String.prototype.ltrim = function(){
	return this.replace(/^\s+/, "");
}
String.prototype.rtrim = function(){
	return this.replace(/\s+$/, "");
}

} //# end of "install only once"


//}}}
/***
|Name|SinglePageModePlugin|
|Source|http://www.TiddlyTools.com/#SinglePageModePlugin|
|Documentation|http://www.TiddlyTools.com/#SinglePageModePluginInfo|
|Version|2.9.7|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|Show tiddlers one at a time with automatic permalink, or always open tiddlers at top/bottom of page.|
This plugin allows you to configure TiddlyWiki to navigate more like a traditional multipage web site with only one tiddler displayed at a time.
!!!!!Documentation
>see [[SinglePageModePluginInfo]]
!!!!!Configuration
<<<
<<option chkSinglePageMode>> Display one tiddler at a time
><<option chkSinglePagePermalink>> Automatically permalink current tiddler
><<option chkSinglePageKeepFoldedTiddlers>> Don't close tiddlers that are folded
><<option chkSinglePageKeepEditedTiddlers>> Don't close tiddlers that are being edited
<<option chkTopOfPageMode>> Open tiddlers at the top of the page
<<option chkBottomOfPageMode>> Open tiddlers at the bottom of the page
<<option chkSinglePageAutoScroll>> Automatically scroll tiddler into view (if needed)

Notes:
* The "display one tiddler at a time" option can also be //temporarily// set/reset by including a 'paramifier' in the document URL: {{{#SPM:true}}} or {{{#SPM:false}}}.
* If more than one display mode is selected, 'one at a time' display takes precedence over both 'top' and 'bottom' settings, and if 'one at a time' setting is not used, 'top of page' takes precedence over 'bottom of page'.
* When using Apple's Safari browser, automatically setting the permalink causes an error and is disabled.
<<<
!!!!!Revisions
<<<
2010.11.30 2.9.7 use story.getTiddler()
2008.10.17 2.9.6 changed chkSinglePageAutoScroll default to false
| Please see [[SinglePageModePluginInfo]] for previous revision details |
2005.08.15 1.0.0 Initial Release.  Support for BACK/FORWARD buttons adapted from code developed by Clint Checketts.
<<<
!!!!!Code
***/
//{{{
version.extensions.SinglePageModePlugin= {major: 2, minor: 9, revision: 7, date: new Date(2010,11,30)};
//}}}
//{{{
config.paramifiers.SPM = { onstart: function(v) {
	config.options.chkSinglePageMode=eval(v);
	if (config.options.chkSinglePageMode && config.options.chkSinglePagePermalink && !config.browser.isSafari) {
		config.lastURL = window.location.hash;
		if (!config.SPMTimer) config.SPMTimer=window.setInterval(function() {checkLastURL();},1000);
	}
} };
//}}}
//{{{
if (config.options.chkSinglePageMode==undefined)
	config.options.chkSinglePageMode=false;
if (config.options.chkSinglePagePermalink==undefined)
	config.options.chkSinglePagePermalink=true;
if (config.options.chkSinglePageKeepFoldedTiddlers==undefined)
	config.options.chkSinglePageKeepFoldedTiddlers=false;
if (config.options.chkSinglePageKeepEditedTiddlers==undefined)
	config.options.chkSinglePageKeepEditedTiddlers=false;
if (config.options.chkTopOfPageMode==undefined)
	config.options.chkTopOfPageMode=false;
if (config.options.chkBottomOfPageMode==undefined)
	config.options.chkBottomOfPageMode=false;
if (config.options.chkSinglePageAutoScroll==undefined)
	config.options.chkSinglePageAutoScroll=false;
//}}}
//{{{
config.SPMTimer = 0;
config.lastURL = window.location.hash;
function checkLastURL()
{
	if (!config.options.chkSinglePageMode)
		{ window.clearInterval(config.SPMTimer); config.SPMTimer=0; return; }
	if (config.lastURL == window.location.hash) return; // no change in hash
	var tids=decodeURIComponent(window.location.hash.substr(1)).readBracketedList();
	if (tids.length==1) // permalink (single tiddler in URL)
		story.displayTiddler(null,tids[0]);
	else { // restore permaview or default view
		config.lastURL = window.location.hash;
		if (!tids.length) tids=store.getTiddlerText("DefaultTiddlers").readBracketedList();
		story.closeAllTiddlers();
		story.displayTiddlers(null,tids);
	}
}


if (Story.prototype.SPM_coreDisplayTiddler==undefined)
	Story.prototype.SPM_coreDisplayTiddler=Story.prototype.displayTiddler;
Story.prototype.displayTiddler = function(srcElement,tiddler,template,animate,slowly)
{
	var title=(tiddler instanceof Tiddler)?tiddler.title:tiddler;
	var tiddlerElem=story.getTiddler(title); // ==null unless tiddler is already displayed
	var opt=config.options;
	var single=opt.chkSinglePageMode && !startingUp;
	var top=opt.chkTopOfPageMode && !startingUp;
	var bottom=opt.chkBottomOfPageMode && !startingUp;
	if (single) {
		story.forEachTiddler(function(tid,elem) {
			// skip current tiddler and, optionally, tiddlers that are folded.
			if (	tid==title
				|| (opt.chkSinglePageKeepFoldedTiddlers && elem.getAttribute("folded")=="true"))
				return;
			// if a tiddler is being edited, ask before closing
			if (elem.getAttribute("dirty")=="true") {
				if (opt.chkSinglePageKeepEditedTiddlers) return;
				// if tiddler to be displayed is already shown, then leave active tiddler editor as is
				// (occurs when switching between view and edit modes)
				if (tiddlerElem) return;
				// otherwise, ask for permission
				var msg="'"+tid+"' is currently being edited.\n\n";
				msg+="Press OK to save and close this tiddler\nor press Cancel to leave it opened";
				if (!confirm(msg)) return; else story.saveTiddler(tid);
			}
			story.closeTiddler(tid);
		});
	}
	else if (top)
		arguments[0]=null;
	else if (bottom)
		arguments[0]="bottom";
	if (single && opt.chkSinglePagePermalink && !config.browser.isSafari) {
		window.location.hash = encodeURIComponent(String.encodeTiddlyLink(title));
		config.lastURL = window.location.hash;
		document.title = wikifyPlain("SiteTitle") + " - " + title;
		if (!config.SPMTimer) config.SPMTimer=window.setInterval(function() {checkLastURL();},1000);
	}
	if (tiddlerElem && tiddlerElem.getAttribute("dirty")=="true") { // editing... move tiddler without re-rendering
		var isTopTiddler=(tiddlerElem.previousSibling==null);
		if (!isTopTiddler && (single || top))
			tiddlerElem.parentNode.insertBefore(tiddlerElem,tiddlerElem.parentNode.firstChild);
		else if (bottom)
			tiddlerElem.parentNode.insertBefore(tiddlerElem,null);
		else this.SPM_coreDisplayTiddler.apply(this,arguments); // let CORE render tiddler
	} else
		this.SPM_coreDisplayTiddler.apply(this,arguments); // let CORE render tiddler
	var tiddlerElem=story.getTiddler(title);
	if (tiddlerElem&&opt.chkSinglePageAutoScroll) {
		// scroll to top of page or top of tiddler
		var isTopTiddler=(tiddlerElem.previousSibling==null);
		var yPos=isTopTiddler?0:ensureVisible(tiddlerElem);
		// if animating, defer scroll until after animation completes
		var delay=opt.chkAnimate?config.animDuration+10:0;
		setTimeout("window.scrollTo(0,"+yPos+")",delay); 
	}
}

if (Story.prototype.SPM_coreDisplayTiddlers==undefined)
	Story.prototype.SPM_coreDisplayTiddlers=Story.prototype.displayTiddlers;
Story.prototype.displayTiddlers = function() {
	// suspend single/top/bottom modes when showing multiple tiddlers
	var opt=config.options;
	var saveSPM=opt.chkSinglePageMode; opt.chkSinglePageMode=false;
	var saveTPM=opt.chkTopOfPageMode; opt.chkTopOfPageMode=false;
	var saveBPM=opt.chkBottomOfPageMode; opt.chkBottomOfPageMode=false;
	this.SPM_coreDisplayTiddlers.apply(this,arguments);
	opt.chkBottomOfPageMode=saveBPM;
	opt.chkTopOfPageMode=saveTPM;
	opt.chkSinglePageMode=saveSPM;
}
//}}}
//A Portal to Old School D&D.//
//The Babbling Bane://
''I'' have taken my growing frustration with Google, and its many tendril cancerous incarnations, to start a new presence. I have been wanting to do a Wiki for years, feeling that it would be a great tool for campaign organization, and figured I would kill two birds with one stone. An added bonus, I hope, is the ability to easily link throughout the Wiki. Which, might be a help for my notorious ADD.

//''Ta-dah''//, The new home of The Bane and my often pointless Babbling Banter!

The only thing that is missing, in my opinion, between a wiki and a blog is 'comments'. Not that there were many commented on my blog, though I do appreciate the ones that did, I will see if I can come up with an alternative solution.

//''TB''//
/***
http://tiddlystyles.com/#theme:DevFire
***/

/*{{{*/
body {
background: #000;
}
/*}}}*/
/***
!Link styles /% ============================================================= %/
***/
/*{{{*/
a,
a.button,
#mainMenu a.button,
#sidebarOptions .sliderPanel a{
 color: #ffbf00;
 border: 0;
 background: transparent;
}

a:hover,
a.button:hover,
#mainMenu a.button:hover,
#sidebarOptions .sliderPanel a:hover
#sidebarOptions .sliderPanel a:active{
 color: #ff7f00;
 border: 0;
 border-bottom: #ff7f00 1px dashed;
 background: transparent;
 text-decoration: none;
}

#displayArea .button.highlight{
 color: #ffbf00;
 background: #4c4c4c;
}
/*}}}*/
/***
!Header styles /% ============================================================= %/
***/
/*{{{*/
.header{
 border-bottom: 2px solid #ffbf00;
 color: #fff;
}

.headerForeground a {
 color: #fff;
}

.header a:hover {
 border-bottom: 1px dashed #fff;
}
/*}}}*/
/***
!Main menu styles /% ============================================================= %/
***/
/*{{{*/
#mainMenu {color: #fff;}
#mainMenu h1{
 font-size: 1.1em;
}
#mainMenu li,#mainMenu ul{
 list-style: none;
 margin: 0;
 padding: 0;
}
/*}}}*/
/***
!Sidebar styles /% ============================================================= %/
***/
/*{{{*/
#sidebar {
 right: 0;
 color: #fff;
 border: 2px solid #ffbf00;
 border-width: 0 0 2px 2px;
}
#sidebarOptions {
 background-color: #4c4c4c;
 padding: 0;
}

#sidebarOptions a{
 margin: 0;
 color: #ffbf00;
 border: 0;
}
#sidebarOptions a:hover {
 color: #4c4c4c;
 background-color: #ffbf00;

}

#sidebarOptions a:active {
 color: #ffbf00;
 background-color: transparent;
}

#sidebarOptions .sliderPanel {
 background-color: #333;
 margin: 0;
}

#sidebarTabs {background-color: #4c4c4c;}
#sidebarTabs .tabSelected {
 padding: 3px 3px;
 cursor: default;
 color: #ffbf00;
 background-color: #666;
}
#sidebarTabs .tabUnselected {
 color: #ffbf00;
 background-color: #5f5f5f;
 padding: 0 4px;
}

#sidebarTabs .tabUnselected:hover,
#sidebarTabs .tabContents {
 background-color: #666;
}

.listTitle{color: #FFF;}
#sidebarTabs .tabContents a{
 color: #ffbf00;
}

#sidebarTabs .tabContents a:hover{
 color: #ff7f00;
 background: transparent;
}

#sidebarTabs .txtMoreTab .tabSelected,
#sidebarTabs .txtMoreTab .tab:hover,
#sidebarTabs .txtMoreTab .tabContents{
 color: #ffbf00;
 background: #4c4c4c;
}

#sidebarTabs .txtMoreTab .tabUnselected {
 color: #ffbf00;
 background: #5f5f5f;
}

.tab.tabSelected, .tab.tabSelected:hover{color: #ffbf00; border: 0; background-color: #4c4c4c;cursor:default;}
.tab.tabUnselected {background-color: #666;}
.tab.tabUnselected:hover{color:#ffbf00; border: 0;background-color: #4c4c4c;}
.tabContents {
 background-color: #4c4c4c;
 border: 0;
}
.tabContents .tabContents{background: #666;}
.tabContents .tabSelected{background: #666;}
.tabContents .tabUnselected{background: #5f5f5f;}
.tabContents .tab:hover{background: #666;}
/*}}}*/
/***
!Message area styles /% ============================================================= %/
***/
/*{{{*/
#messageArea {background-color: #666; color: #fff; border: 2px solid #ffbf00;}
#messageArea a:link, #messageArea a:visited {color: #ffbf00; text-decoration:none;}
#messageArea a:hover {color: #ff7f00;}
#messageArea a:active {color: #ff7f00;}
#messageArea .messageToolbar a{
 border: 1px solid #ffbf00;
 background: #4c4c4c;
}
/*}}}*/
/***
!Popup styles /% ============================================================= %/
***/
/*{{{*/
.popup {color: #fff; background-color: #4c4c4c; border: 1px solid #ffbf00;}
.popup li.disabled{color: #fff;}
.popup a {color: #ffbf00; }
.popup a:hover { background: transparent; color: #ff7f00; border: 0;}
.popup hr {color: #ffbf00; background: #ffbf00;}
/*}}}*/
/***
!Tiddler Display styles /% ============================================================= %/
***/
/*{{{*/
.title{color: #fff;}
h1, h2, h3, h4, h5 {
 color: #fff;
 background-color: transparent;
 border-bottom: 1px solid #333;
}

.subtitle{
 color: #666;
}

.viewer {color: #fff; }

.viewer table{background: #666; color: #fff;}

.viewer th {background-color: #996; color: #fff;}

.viewer pre, .viewer code {color: #ddd; background-color: #4c4c4c; border: 1px solid #ffbf00;}

.viewer hr {color: #666;}

.tiddler .button {color: #4c4c4c;}
.tiddler .button:hover { color: #ffbf00; background-color: #4c4c4c;}
.tiddler .button:active {color: #ffbf00; background-color: #4c4c4c;}

.toolbar {
 color: #4c4c4c;
}

.toolbar a.button,
.toolbar a.button:hover,
.toolbar a.button:active,
.editorFooter a{
 border: 0;
}

.footer {
 color: #ddd;
}

.selected .footer {
 color: #888;
}

.highlight, .marked {
 color: #000;
 background-color: #ffe72f;
}
.editorFooter {
 color: #aaa;
}

.tab{
-moz-border-radius-topleft: 3px;
-moz-border-radius-topright: 3px;
}

.tagging,
.tagged{
 background: #4c4c4c;
 border: 1px solid #4c4c4c; 
}

.selected .tagging,
.selected .tagged{
 background-color: #333;
 border: 1px solid #ffbf00;
}

.tagging .listTitle,
.tagged .listTitle{
 color: #fff;
}

.tagging .button,
.tagged .button{
 color: #ffbf00;
 border: 0;
 padding: 0;
}

.tagging .button:hover,
.tagged .button:hover{
background: transparent;
}

.selected .isTag .tagging.simple,
.selected .tagged.simple,
.isTag .tagging.simple,
.tagged.simple {
 float: none;
 display: inline;
 border: 0;
 background: transparent;
 color: #fff;
 margin: 0;
}

.cascade {
 background: #4c4c4c;
 color: #ddd;
 border: 1px solid #ffbf00;
}
/*}}}*/
Write something here
/***
|''Name:''|TiddlyLib |
|''Version:''|0.9.0 |
|''Source:''|http://www.legolas.org/gmwiki/dev/gmwikidev.html#TiddlyLib |
|''Author:''|[[DevonJones]] |
|''Type:''|Library |
|''License:''|BSD |
|''Requires:''|TiddlyWiki 1.2.32 or higher (tested only on 1.2.36, but should work on 1.2.32)|
!Description
Library of useful TiddlyWiki functions

!Syntax

!Known issues

!Notes

!Revision history
v0.9.0 October 19th 2005 - initial release

!Documentation
!!createLocalTiddlyButton
''Description:''
Creates a tiddly button, but with out the href of "#".  Thus when you click on the button, it doesn't cause the browser to jump.

''Arguments:''
* theParent: 
* theText: 
* theTooltip: 
* theAction: 
* theClass: 
* theId: 
* theAccessKey: 

!!onDblClickTiddlerOverride
''Description:''
Event handler that can be used to override a tiddler's onDblClick.  Used to prevent entering edit mode if a user clicks to fast on a link or button.

''Arguments:''
* e: (Event) Event to handle

!!getParentTiddler
''Description:''
Returns the DOMElement of the tiddler that //elem// is a child of

''Arguments:''
* elem: (DOMElement) Element to find parent of

!!hideElementEvent
''Description:''
Event that can be attached to a block level element.  Firing this even hides the element

''Arguments:''
* e: (Event) Event to handle

!!showElementEvent
''Description:''
Event that can be attached to a block level element.  Firing this even unhides the element

''Arguments:''
* e: (Event) Event to handle

!Code
***/
//{{{

function createLocalTiddlyButton(theParent,theText,theTooltip,theAction,theClass,theId,theAccessKey) {
	var theButton = document.createElement("a");
	theButton.className = "button";
	if(theAction) {
		theButton.onclick = theAction;
	}
	theButton.setAttribute("title",theTooltip);
	if(theText) {
		theButton.appendChild(document.createTextNode(theText));
	}
	if(theClass) {
		theButton.className = theClass;
	}
	if(theId) {
		theButton.id = theId;
	}
	if(theParent) {
		theParent.appendChild(theButton);
	}
	if(theAccessKey) {
		theButton.setAttribute("accessKey",theAccessKey);
	}
	return(theButton);
}


function onDblClickTiddlerOverride(e) {
	if (!e) {
		var e = window.event;
	}
	
	var theTarget = resolveTarget(e);
	if(!readOnly && theTarget && theTarget.nodeName.toLowerCase() != "input" && theTarget.nodeName.toLowerCase() != "textarea" && theTarget.nodeName.toLowerCase() != "a") {
		clearMessage();
		if(document.selection && document.selection.empty) {
			document.selection.empty();
		}
		var tiddler;
		if(this.id.substr(0,7) == "tiddler") {
			tiddler = this.id.substr(7);
		}
		if(tiddler) {
			displayTiddler(null,tiddler,2,null,null,false,false);
		}
		return true;
	}
	else {
		return false;
	}
}

function getParentTiddler(elem) {
	var testPlace = elem;
	while(testPlace.id.substr(0,7) != "tiddler") {
		testPlace = testPlace.parentNode;
	}
	return testPlace;
}

function hideElementEvent(e) {
	this.style.display = "none";
}

function showElementEvent(e) {
	this.style.display = "block";
}


//}}}
/***
Description: Contains the stuff you need to use Tiddlyspot
Note, you also need UploadPlugin, PasswordOptionPlugin and LoadRemoteFileThroughProxy
from http://tiddlywiki.bidix.info for a complete working Tiddlyspot site.
***/
//{{{

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

// 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; // show backstage too

// 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(/^/,"[[WelcomeToTiddlyspot]] ");
	MainMenu = MainMenu.replace(/^/,"[[WelcomeToTiddlyspot]] ");
}

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

'TspotOptions':[
 "tiddlyspot password:",
 "<<option pasUploadPassword>>",
 ""
].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/]], [[blog|http://tiddlyspot.blogspot.com/]], email [[support|mailto:support@tiddlyspot.com]] & [[feedback|mailto:feedback@tiddlyspot.com]], [[donate|http://tiddlyspot.com/?page=donate]]|"
].join("\n"),

'WelcomeToTiddlyspot':[
 "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.org|http://tiddlywiki.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"),

'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")

});
//}}}
| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |
| 14/03/2012 20:29:45 | The Bane | [[/|http://thebabblingbane.tiddlyspot.com/]] | [[store.cgi|http://thebabblingbane.tiddlyspot.com/store.cgi]] | . | [[index.html | http://thebabblingbane.tiddlyspot.com/index.html]] | . |
| 15/03/2012 20:00:03 | The Bane | [[/|http://thebabblingbane.tiddlyspot.com/]] | [[store.cgi|http://thebabblingbane.tiddlyspot.com/store.cgi]] | . | [[index.html | http://thebabblingbane.tiddlyspot.com/index.html]] | . |
| 15/03/2012 20:03:28 | The Bane | [[/|http://thebabblingbane.tiddlyspot.com/]] | [[store.cgi|http://thebabblingbane.tiddlyspot.com/store.cgi]] | . | [[index.html | http://thebabblingbane.tiddlyspot.com/index.html]] | . | ok |
| 15/03/2012 20:06:40 | The Bane | [[/|http://thebabblingbane.tiddlyspot.com/]] | [[store.cgi|http://thebabblingbane.tiddlyspot.com/store.cgi]] | . | [[index.html | http://thebabblingbane.tiddlyspot.com/index.html]] | . |
| 15/03/2012 20:10:14 | The Bane | [[/|http://thebabblingbane.tiddlyspot.com/]] | [[store.cgi|http://thebabblingbane.tiddlyspot.com/store.cgi]] | . | [[index.html | http://thebabblingbane.tiddlyspot.com/index.html]] | . | ok |
| 15/03/2012 20:11:20 | The Bane | [[/|http://thebabblingbane.tiddlyspot.com/]] | [[store.cgi|http://thebabblingbane.tiddlyspot.com/store.cgi]] | . | [[index.html | http://thebabblingbane.tiddlyspot.com/index.html]] | . |
| 15/03/2012 20:20:56 | The Bane | [[/|http://thebabblingbane.tiddlyspot.com/]] | [[store.cgi|http://thebabblingbane.tiddlyspot.com/store.cgi]] | . | [[index.html | http://thebabblingbane.tiddlyspot.com/index.html]] | . | failed |
| 15/03/2012 20:21:21 | The Bane | [[/|http://thebabblingbane.tiddlyspot.com/]] | [[store.cgi|http://thebabblingbane.tiddlyspot.com/store.cgi]] | . | [[index.html | http://thebabblingbane.tiddlyspot.com/index.html]] | . |
| 22/11/2012 19:31:34 | The Bane | [[/|http://thebabblingbane.tiddlyspot.com/]] | [[store.cgi|http://thebabblingbane.tiddlyspot.com/store.cgi]] | . | [[index.html | http://thebabblingbane.tiddlyspot.com/index.html]] | . | failed |
| 22/11/2012 19:37:21 | The Bane | [[/|http://thebabblingbane.tiddlyspot.com/]] | [[store.cgi|http://thebabblingbane.tiddlyspot.com/store.cgi]] | . | [[index.html | http://thebabblingbane.tiddlyspot.com/index.html]] | . |
/***
|''Name:''|UploadPlugin|
|''Description:''|Save to web a TiddlyWiki|
|''Version:''|4.1.3|
|''Date:''|Feb 24, 2008|
|''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|
|''Requires:''|PasswordOptionPlugin|
***/
//{{{
version.extensions.UploadPlugin = {
	major: 4, minor: 1, revision: 3,
	date: new Date("Feb 24, 2008"),
	source: 'http://tiddlywiki.bidix.info/#UploadPlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	coreVersion: '2.2.0'
};

//
// 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
		if (!params) params = {};
		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}
				
			]);
	},
	options: [
		"txtUploadUserName",
		"pasUploadPassword",
		"txtUploadStoreUrl",
		"txtUploadDir",
		"txtUploadFilename",
		"txtUploadBackupDir",
		"chkUploadLog",
		"txtUploadLogMaxLine"		
	],
	refreshOptions: function(listWrapper) {
		var opts = [];
		for(i=0; i<this.options.length; i++) {
			var opt = {};
			opts.push();
			opt.option = "";
			n = this.options[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 = new 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,username,password,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 = new Array(uploadParams[0],rssPath,uploadParams[2],'',uploadParams[4],uploadParams[5]);
		var rssString = generateRss();
		// no UnicodeToUTF8 conversion needed when location is "file" !!!
		if (document.location.toString().substr(0,4) != "file")
			rssString = convertUnicodeToUTF8(rssString);	
		bidix.upload.httpUpload(rssUploadParams,rssString,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 == 404)
			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; ;charset=UTF-8; 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');


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


//}}}

The Bane