<!--{{{-->
<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>>
/***
!!!608/609/610 toolbars - toggles, separators and transclusion
***/
// // {{groupbox small{
/***
http://trac.tiddlywiki.org/ticket/608 - OPEN (more/less toggle)
http://trac.tiddlywiki.org/ticket/609 - OPEN (separators)
http://trac.tiddlywiki.org/ticket/610 - OPEN (wikify tiddler/slice/section content)

This combination tweak extends the """<<toolbar>>""" macro to add use of '<' to insert a 'less' menu command (the opposite of '>' == 'more'), as well as use of '*' to insert linebreaks and "!" to insert a vertical line separator between toolbar items.  In addition, this tweak add the ability to use references to tiddlernames, slices, or sections and render their content inline within the toolbar, allowing easy creation of new toolbar commands using TW content (such as macros, links, inline scripts, etc.)

To produce a one-line style, with "less" at the end, use
| ViewToolbar| foo bar baz > yabba dabba doo < |
or to use a two-line style with more/less toggle:
| ViewToolbar| foo bar baz > < * yabba dabba doo |
***/
//{{{
merge(config.macros.toolbar,{
	moreLabel: 'more\u25BC',
	morePrompt: 'Show additional commands',
	lessLabel: '\u25C4less',
	lessPrompt: 'Hide additional commands',
	separator: '|'
});
config.macros.toolbar.onClickMore = function(ev) {
	var e = this.nextSibling;
	e.style.display = 'inline'; // show menu
	this.style.display = 'none'; // hide button
	return false;
};
config.macros.toolbar.onClickLess = function(ev) {
	var e = this.parentNode;
	var m = e.previousSibling;
	e.style.display = 'none'; // hide menu
	m.style.display = 'inline'; // show button
	return false;
};
config.macros.toolbar.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
	for(var t=0; t<params.length; t++) {
		var c = params[t];
		switch(c) {
			case '!':  // ELS - SEPARATOR (added)
				createTiddlyText(place,this.separator);
				break;
			case '*':  // ELS - LINEBREAK (added)
				createTiddlyElement(place,'BR');
				break;
			case '<': // ELS - LESS COMMAND (added)
				var btn = createTiddlyButton(place,
					this.lessLabel,this.lessPrompt,config.macros.toolbar.onClickLess,'moreCommand');
				break;
			case '>':
				var btn = createTiddlyButton(place,
					this.moreLabel,this.morePrompt,config.macros.toolbar.onClickMore,'moreCommand');
				var e = createTiddlyElement(place,'span',null,'moreCommand');
				e.style.display = 'none';
				place = e;
				break;
			default:
				var theClass = '';
				switch(c.substr(0,1)) {
					case '+':
						theClass = 'defaultCommand';
						c = c.substr(1);
						break;
					case '-':
						theClass = 'cancelCommand';
						c = c.substr(1);
						break;
				}
				if(c in config.commands)

					this.createCommand(place,c,tiddler,theClass);
				else { // ELS - WIKIFY TIDDLER/SLICE/SECTION (added)
					if (c.substr(0,1)=='~') c=c.substr(1); // ignore leading ~
					var txt=store.getTiddlerText(c);
					if (txt) {
						// trim any leading/trailing newlines
						txt=txt.replace(/^\n*/,'').replace(/\n*$/,'');
						// trim PRE format wrapper if any
						txt=txt.replace(/^\{\{\{\n/,'').replace(/\n\}\}\}$/,'');
						// render content into toolbar
						wikify(txt,createTiddlyElement(place,'span'),null,tiddler);
					}
				} // ELS - end WIKIFY CONTENT
				break;
		}
	}
};
//}}}
[[GettingStarted]]
/***
|Name|[[EditSectionPlugin]]|
|Source|http://www.TiddlyTools.com/#EditSectionPlugin|
|Documentation|http://www.TiddlyTools.com/#EditSectionPlugin|
|Version|1.8.1|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|invoke popup 'section editor' for specified section of a tiddler|
!!!!!Usage
<<<
{{{
<<editSection TiddlerName##sectionname label tooltip>>
}}}
This macro adds a command link that invokes a popup editor for a specific section, where:
*''~TiddlerName##sectionname'' specifies the tiddler and section you want to edit.
**If you omit the "##sectionname" portion (i.e., only enter "~TiddlerName"), the entire content of the indicated tiddler is edited.
**If you omit the "~TiddlerName" portion (i.e., only enter "##sectionname"), the current containing tiddler (if any) is assumed.
**Changing the section name in the popup editor //renames// that section within the tiddler.
**Changing the tiddler title in the popup editor //copies// that section to another tiddler.
**If the indicated tiddler and/or section does not yet exist, it will be created when you press 'save'.
*''label'' and ''tooltip'' (both //optional//) specify the link label and mouseover help text for the 'edit section' command link.
You can also add the following macro, //at the end of a tiddler//, to automatically add an 'edit section' command link for each section shown in the tiddler.
{{{
<<editSections label tooltip>>
}}}
*''label'' and ''tooltip'' (both //optional//) specify the link label and mouseover help text for the 'edit section' command link.
>//Note: when a document is viewed in 'readOnly' mode, both of these macros are bypassed and no output is produced.//
<<<
!!!!!Sample
<<<
This is an example section for you to try
<<<
!!!!!Example
<<<
{{{
<<editSection ##Sample>>
}}}
<<editSection ##Sample>>
<<<
!!!!!Configuration
<<<
To customize and/or translate the HTML form layout used to render the section editor, edit the [[EditSectionTemplate]] shadow tiddler.
<<<
!!!!!Revisions
<<<
2012.01.29 1.8.1 invoke autoSaveChanges() when tiddlers are modified.
2011.12.22 1.8.0 added {{{<<editSections>>}}} macro for automatic adding of 'edit section...' links to headings
2011.12.20 1.7.0 added drag handling for editor panels
2011.10.28 1.6.8 fixed getMX()/getMY() for Chrome scroll offset handling
2011.09.02 1.6.7 more refactoring and cleanup of default form init/save handlers
2011.08.02 1.6.6 major code refactor to allow customization of form handling for type-specific [[PasteUpHelperPlugin]] extensions
2011.07.30 1.6.5 in removePanel(), call Popup.remove() so 'child' popups are closed when panel is closed
2011.07.24 1.6.4 refactored save() to provide updateTiddler() entry point for use with PasteUpHelperPlugin 'quickmenu' commands. Added getMX() and getMY() for cross-browser mouse coordinates
2011.06.05 1.6.3 added TiddlySpace cloneFields() to delete and save handlers so editing sections automatically copies/owns an included tiddler
2011.05.05 1.6.2 renamed delete() to deleteSection() to avoid javascript keyword errors
2011.05.04 1.6.1 in delete(), use removeTiddler() for proper notification handling
2011.05.01 1.6.0 added delete() functionality
2011.01.09 1.5.1 in handler(), don't render command link if document is readOnly
2010.12.24 1.5.0 replace use of core Popups with custom panel handling (bypass core interactions and errors)
2010.11.07 1.4.0 in popup(), render HTML form from EditSectionTemplate, and then applyHtmlMacros() to render wiki-syntax macro-generated form elements (e.g., {{{<<option>>, <<select>>}}}, etc.).  Also, refactored form init/save handling to add customization 'hooks'
2010.10.25 1.3.0 added support for editing complete tiddlers using "~TiddlerName" syntax (omit "##sectionname")
2010.07.15 1.2.0 added confirmation when section rename will overwrite other section
2010.07.13 1.1.1 added 'dirty flag' confirmation to popup handling to avoid discarding unsaved changes
2010.07.11 1.1.0 fixed handling for creating new sections in existing tiddlers. Copied StickyPopupPlugin to eliminate dependency. Added Popup.showHere()
2010.05.24 1.0.2 in save(), escape regexp chars in section titles (e.g. "!SectionNameWith?InIt")
2009.09.07 1.0.1 documentation/code cleanup
2009.09.01 1.0.0 initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.EditSectionPlugin= {major: 1, minor: 8, revision: 1, date: new Date(2012,1,29)};

config.macros.editSection = {
	label: 'edit section...',
	tip: 'edit %0',
	sectionfmt: '{{hidden{\n!%0\n%1\n!end\n}}}',
	sectionerr: 'Invalid tiddler/section name: %0',
	newtiddlertxt: '',
	discardmsg: 'Discard unsaved changes for %0?',
	deletemsg: 'Are you sure you want to delete %0?',
	overwritemsg: '%0##%2 already exists. Choose OK to overwrite it',
	template: 'EditSectionTemplate', // DEFAULT FORM TEMPLATE
//}}}
// // PLUGIN INITIALIZATION
//{{{
	init: function() {
		// SHADOW PAYLOAD FOR DEFAULT EditSectionTemplate FORM DEFINITION
		config.shadowTiddlers[this.template]
			=store.getTiddlerText('EditSectionPlugin##HTML','');

		// CLOSE PANELS IF CLICK on other than POPUP OR EDITOR PANEL
		addEvent(document,'click',function(ev) {
			var p=resolveTarget(ev||window.event);
			while (p) {
				if (hasClass(p,'editSectionPanel')) break;
				if (hasClass(p,'popup')) break;
				p=p.parentNode;
			}
			if (!p) config.macros.editSection.removeAllPanels();
			return true;
		});

		// HIJACK QuickEditPlugin's getField() to support use with editSectionPanel
		if (config.quickEdit) {
			config.quickEdit.getTiddlerField=config.quickEdit.getField;
			config.quickEdit.getField=function(where) {
				var e=where; while(e) {	if (hasClass(e,'editSectionPanel')) break; e=e.parentNode; }
				return e?e.getElementsByTagName('textarea')[0]:this.getTiddlerField(where);
			}

		}
	},
//}}}
// // GENERAL UTILITIES
//{{{
	ok: function(ev) { var ev=ev||window.event;
		ev.cancelBubble=true;
		if(ev.stopPropagation)ev.stopPropagation();
		return false;
	},
	getMX: function(ev) { var ev=ev||window.event; // GET MOUSE X
		if (config.browser.isIE)	return ev.clientX+findScrollX();// IE
		if (config.userAgent.indexOf('chrome')!=-1) return ev.pageX;	// Chrome
		if (config.browser.isSafari) 	return ev.pageX+findScrollX(); 	// Webkit
		else				return ev.pageX;		// Firefox/other
	},
	getMY: function(ev) { var ev=ev||window.event; // GET MOUSE Y
		if (config.browser.isIE)	return ev.clientY+findScrollY();// IE
		if (config.userAgent.indexOf('chrome')!=-1) return ev.pageY;	// Chrome
		if (config.browser.isSafari) 	return ev.pageY+findScrollY();	// Webkit
		else				return ev.pageY;		// Firefox/other
	},
	cloneFields: function(fields) { // for TIDDLYSPACE compatibility
		var f=merge({},fields); // copy object
		if (f["server.workspace"]!=config.defaultCustomFields["server.workspace"]) {
			f=merge(f,config.defaultCustomFields); // overwrite with defaults
			f["server.permissions"] = "read, write, create, delete";
			delete f["server.page.revision"];
			delete f["server.title"];
			delete f["server.etag"];
		}
		return f;
	},
//}}}
// // MACRO/CLICK HANDLER
//{{{
	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		if (readOnly) return;
		var here=story.findContainingTiddler(place);
		var tid=params[0];
		var label=params[1]||this.label.format([tid]);
		var tip=params[2]||this.tip.format([tid]);
		var btn=createTiddlyButton(place,label,tip,this.click);
		btn.setAttribute('tid',tid);
	},
	click: function(ev,type) { // note: optional 'type' is passed in from PasteUpPluginHelper
		var parts=this.getAttribute('tid').split('##');
		var title=parts[0]; var section=parts[1];
		var here=story.findContainingTiddler(this);
		if (!title&&here) title=here.getAttribute('tiddler');
		if (!title) return false;
		var tid=title; if (section&&section.length) tid=[title,section].join('##');
		return config.macros.editSection.createPanel(this,ev,tid,title,section,type);
	},
//}}}
// // EDITOR PANEL HANDLER
//{{{
	createPanel: function(here,ev,tid,title,section,type) {
		if (!this.removeAllPanels()) return this.ok(ev);
		var p=createTiddlyElement(document.body,"ol",
			"editSectionPanel","popup smallform editSectionPanel");
		p.root=here; 
		p.setAttribute('dirty',null);
		p.setAttribute('message',this.discardmsg.format([tid]));
		p.onmousedown=this.mousedown; p.style.cursor='move'; // for panel dragging
		p.innerHTML=store.getRecursiveTiddlerText(this.getForm(tid,type),'',10);
		applyHtmlMacros(p,store.getTiddler(title));
		var f=p.getElementsByTagName('form')[0];
		f.panel=p;
		f.title.value=title;
		f.section.value=section||'';
		f.init=this.getInitForm(tid,type);
		f.save=this.getSaveForm(tid,type);
		f.init(here,f,title,section,type);
		this.showPanel(p,here,ev);
		return this.ok(ev);
	},
	showPanel: function(p,here,ev) {
		var x=this.getMX(ev); var y=this.getMY(ev);
		var winw=findWindowWidth();
		var scrollw=winw-document.body.offsetWidth;
		if(p.offsetWidth>winw*0.75) p.style.width=winw*0.75 + "px";
		if(x+p.offsetWidth>winw-scrollw-1) x=winw-p.offsetWidth-scrollw-1;
		var s=p.style; s.left=x+'px'; s.top=y+'px'; s.display='block';
		if(config.options.chkAnimate && anim)	anim.startAnimating(new Scroller(p));
		else					window.scrollTo(0,ensureVisible(p));
	},
	removePanel: function(p) {
		Popup.remove(); // child popup (if any) is closed when panel is closed
		if (!p || p.getAttribute('dirty')!='true' || confirm(p.getAttribute('message')))
			{ if (p) removeNode(p); return true; }
		return false;
	},
	removeAllPanels: function() {
		var ok=true;
		jQuery('.editSectionPanel').each(function(index){
			var f=this.getElementsByTagName('form')[0];
			if (f.content) f.content.blur(); // force onchange (sets 'dirty' flag as needed)
			ok=config.macros.editSection.removePanel(this);
			return true;
		});
		return ok; // FALSE if panels remain
	},
//}}}
// // PANEL DRAG HANDLER
//{{{
	mousedown: function(ev) { ev=ev||window.event; // MOVE PANEL
		var cme=config.macros.editSection; // abbrev

		// ignore clickthrough from form fields, links, and images
		var target=resolveTarget(ev);
		if (['TEXTAREA','SELECT','INPUT','A','IMG'].contains(target.nodeName.toUpperCase()))
			return true;

		// GET TRACKING ELEMENT
		var track=this; // if 'capture' not supported, track in element only
		if (document.body.setCapture) var track=document.body; // IE
		if (window.captureEvents) var track=window; // moz
		if (!track.save_onmousemove) track.save_onmousemove=track.onmousemove;
		if (!track.save_onmouseup)   track.save_onmouseup  =track.onmouseup;
		if (!track.save_onkeyup)     track.save_onkeyup    =track.onkeyup;
		track.onmousemove=cme.dragmove;
		track.onmouseup	 =cme.dragup;
		track.onkeyup	 =cme.dragkeyup;
		// SAVE INITIAL POSITION
		track.elem=this;		// panel element
		track.start={
			X: cme.getMX(ev),  Y: cme.getMY(ev),	// mouse position
			T: this.offsetTop, L: this.offsetLeft,	// panel position
		}
		return cme.ok(ev);
	},
	dragmove: function(ev) { ev=ev||window.event; // MOVE PANEL
		var cme=config.macros.editSection; // abbrev
		// CAPTURE MOUSE EVENTS DURING DRAG
		if (document.body.setCapture) document.body.setCapture(); // IE
		if (window.captureEvents) window.captureEvents(Event.MouseMove|Event.MouseUp,true); // moz
		var e=this.elem; var s=e.style;
		var dX=cme.getMX(ev)-this.start.X;
		var dY=cme.getMY(ev)-this.start.Y;
		e.changed=e.changed||(Math.abs(dX)>1)||(Math.abs(dY)>1); // MINIMUM 2px MOVEMENT
		if (!e.changed) return cme.ok(ev);
		s.top =this.start.T+dY+'px';
		s.left=this.start.L+dX+'px';
		return cme.ok(ev);
	},
	dragkeyup: function(ev) { ev=ev||window.event; // STOP DRAG (ESC key)
		if (ev.keyCode==27) {
			var s=this.elem.style;
			s.top=this.start.T+'px';
			s.left=this.start.L+'px';
			return this.onmouseup(ev);
		}
	},
	dragup: function(ev) { ev=ev||window.event; // RELEASE MOUSE
		var cme=config.macros.editSection; // abbrev
		if (document.body.releaseCapture) document.body.releaseCapture(); // IE
		if (window.releaseEvents) window.releaseEvents(Event.MouseMove|Event.MouseUp); // moz
		this.onmousemove=this.save_onmousemove;
		this.onmouseup  =this.save_onmouseup;
		this.onkeyup    =this.save_onkeyup;
		return cme.ok(ev);
	},
//}}}
// // EDITOR FORM HANDLER
//{{{
	getForm:	function(tid,type) { return this.template; }, // see PasteUpHelperPlugin
	getInitForm:	function(tid,type) { return this.initForm; }, // see PasteUpHelperPlugin
	getSaveForm:	function(tid,type) { return this.saveForm; }, // see PasteUpHelperPlugin
	initForm: function(here,form,title,section,type) { // SET FORM CONTENT FROM TIDDLER SECTION
		var tid=title; if (section) tid=[title,section].join('##');
		form.newsection.value=tid; // target for output
		form.content.value=store.getTiddlerText(tid,'');
		if (version.extensions.TextAreaPlugin) new window.TextAreaResizer(form.content);
	},
	saveForm: function(here,ev) { // GET SECTION CONTENT FROM FORM (DEFAULT=TEXT CONTENT ONLY)
		var f=here.form;

		// GET TARGET TITLE/SECTION
		var tid=f.newsection.value;
		var parts=tid.split('##');
		var title=parts[0];
		var section=parts[1];
		var oldsection=f.section.value;
		var where=f.panel.root;
		if (!title) title=story.findContainingTiddler(where).getAttribute('tiddler');
		if (!title) {
			displayMessage(this.sectionerr.format([f.newsection.value]));
			f.newsection.focus(); f.newsection.select(); return false;
		}
		// CHECK FOR TIDDLER OVERWRITE
		if (!section && title!=f.title.value && store.tiddlerExists(title)) {
			if (!confirm(config.messages.overwriteWarning.format([title])))
				{ f.newsection.focus(); f.newsection.select(); return this.ok(ev); }

		}
		// WRITE TIDDLER CONTENT and CLOSE PANEL
		this.updateTiddler(f.content.value,title,section,oldsection);
		f.panel.setAttribute('dirty',null); this.removePanel(f.panel);
		return this.ok(ev);
	},
	changed: function(here,ev) {
		here.form.panel.setAttribute('dirty','true');
		return this.ok(ev);
	},
	cancel: function(here,ev) {
		this.removePanel(here.form.panel);
		return this.ok(ev);
	},
	remove: function(here,ev) {
		var f=here.form;
		var title=f.title.value;
		var section=f.section.value;
		var tid=title; if (section.length) tid=[title,section].join('##');
		var msg=this.deletemsg.format([tid]);
		if (!confirm(msg)) return this.ok(ev);
		this.deleteSection(title,section);
		f.panel.setAttribute('dirty',null);
		this.removePanel(f.panel);
		return this.ok(ev);
	},
//}}}
// // TIDDLER I/O
//{{{
	updateTiddler: function(txt,title,section,oldsection) {
		// GET (or CREATE) TIDDLER
		var t=store.getTiddler(title);
		var who =t&&config.options.chkForceMinorUpdate?t.modifier:config.options.txtUserName;
		var when=t&&config.options.chkForceMinorUpdate?t.modified:new Date();
		if (!t) {
			t=new Tiddler(); t.text=store.getTiddlerText(title,'');
			if (section&&!t.text.length) t.text=this.newtiddlertxt.format([title,section]);
		}
		// ADD/REVISE/RENAME SECTION CONTENT (if any)
		if (section) {
			// GET SECTION VALUES
			if (!oldsection) var oldsection=section;
			var oldval=store.getTiddlerText(title+'##'+oldsection); // previous section value
			var newval=txt; // revised section value
			var existingval=store.getTiddlerText(title+'##'+section); // existing section value
			// REVISE TIDDLER TEXT
			var txt=t.text; // default tiddler text = unchanged
			var pattern=new RegExp('(.*!{1,6})'+oldsection.escapeRegExp()+'\\n'
				+(oldval||'').escapeRegExp()+'((?:\\n!{1,6}|$).*)');
			var altpattern=this.sectionfmt.format([oldsection,oldval||'']);
			if (section!=oldsection && existingval) { // rename overwrites another section...
				if (!confirm(this.overwritemsg.format([title,section])))
					return this.ok(ev);
				txt=txt.replace(altpattern,''); // REMOVE old section (auto-generated)
				txt=txt.replace(pattern,'$2');  // REMOVE old section (generic format)
				// TARGET new section name and value
				pattern=new RegExp('(.*!{1,6})'+section.escapeRegExp()+'\\n'
					+existingval.escapeRegExp()+'((?:\\n!{1,6}|$).*)');
				oldval=existingval;
			}
			if (typeof oldval=="string") // section exists... update/rename it
				txt=txt.replace(pattern,'$1'+section+'\n'+newval+'$2');
			else // otherwise, append a new section to end of tiddler
				txt=txt+this.sectionfmt.format([section,newval]);
		}
		// SAVE TIDDLER
		var fields=this.cloneFields(t.fields);
		store.saveTiddler(title,title,txt,who,when,t.tags,fields);
		story.refreshTiddler(title,null,true);
		autoSaveChanges();
	},
	deleteSection: function(title,section) {
		// GET TIDDLER
		var t=store.getTiddler(title); if (!t) return;
		var who =t&&config.options.chkForceMinorUpdate?t.modifier:config.options.txtUserName;
		var when=t&&config.options.chkForceMinorUpdate?t.modified:new Date();
		if (!section) { // REMOVE TIDDLER
			store.removeTiddler(title);
		} else { // REMOVE SECTION FROM TIDDLER
			var val=store.getTiddlerText(title+'##'+section); // CURRENT SECTION VALUE
			if (typeof val=="string") { // section exists
				var txt=t.text; // default tiddler text = unchanged
				var pattern=new RegExp('(.*!{1,6})'+section.escapeRegExp()+'\\n'
					+(val||'').escapeRegExp()+'((?:\\n!{1,6}|$).*)');
				var altpattern=this.sectionfmt.format([section,val||'']);
				txt=txt.replace(altpattern,''); // REMOVE old section (auto-generated)
				txt=txt.replace(pattern,'$2');  // REMOVE old section (generic format)
				var fields=this.cloneFields(t.fields);
				store.saveTiddler(title,title,txt,who,when,t.tags,fields);
				story.refreshTiddler(title,null,true);
				autoSaveChanges();
			}
		}
	}
}
//}}}
// // EDIT SECTIONS MACRO
//{{{
config.macros.editSections = {
	label: 'edit...',
	tip: 'edit this section',
	command: '~~<<editSection [[##%0]] "%1" "%2">>~~',
	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		if (readOnly) return;
		var elems=place.parentNode.getElementsByTagName("*");
		for (var i=0; i<elems.length; i++) { var e=elems[i]; // for each heading element
			if (!['H1','H2','H3','H4','H5'].contains(e.nodeName)) continue;
			var section=e.textContent;
			var label=(params[0]||this.label).format([section]);
			var tip  =(params[1]||this.tip  ).format([section]);
			wikify(this.command.format([section,label,tip]),e);
		}		
	}
}
//}}}
/***
//{{{
!HTML
<!--{{{-->
<!--
|Name|EditSectionTemplate|
|Source||
|Version||
|Author||
|License|http://www.TiddlyTools.com/#LegalStatements|
|Type|template|
|Requires|EditSectionPlugin|
|Description|popup editor form template used by EditSectionPlugin|
-->
<form action='javascript:;' style="white-space:nowrap">
<input type="hidden" name="title" value="">
<input type="hidden" name="section" value="">
<input type="text" name="newsection" value="" autocomplete="off" style="width:61%"
	onchange="return config.macros.editSection.changed(this,event);">
<input type=button value="save" style="width:12%"
	onclick="return config.macros.editSection.saveForm(this,event)">
<input type=button value="cancel" style="width:12%"
	onclick="return config.macros.editSection.cancel(this,event)">
<input type=button value="delete" style="width:12%"
	onclick="return config.macros.editSection.remove(this,event)">
<div macro="tiddler QuickEditToolbar"></div>
<textarea name="content" rows="15" cols="80" autocomplete="off"
	onchange="return config.macros.editSection.changed(this,event)"></textarea>
</form>
<!--}}}-->
!end
//}}}
***/
// //<<editSections "edit">> 
<!--{{{-->
<!--
|Name|EditSectionTemplate|
|Source||
|Version||
|Author||
|License|http://www.TiddlyTools.com/#LegalStatements|
|Type|template|
|Requires|EditSectionPlugin|
|Description|popup editor form template used by EditSectionPlugin|
-->
<center> Click & drag</center>
<form action='javascript:;' style="white-space:nowrap">
<input type="hidden" name="title" value="">
<input type="hidden" name="section" value="">
<input type="text" name="newsection" value="" autocomplete="off" style="width:61%"
	onchange="return config.macros.editSection.changed(this,event);">
<input type=button value="save" style="width:12%"
	onclick="return config.macros.editSection.saveForm(this,event)">
<input type=button value="cancel" style="width:12%"
	onclick="return config.macros.editSection.cancel(this,event)">
<input type=button value="delete" style="width:12%"
	onclick="return config.macros.editSection.remove(this,event)">
<div macro="tiddler QuickEditToolbar"></div>
<textarea name="content" rows="15" cols="80" autocomplete="off"
	onchange="return config.macros.editSection.changed(this,event)"></textarea>
</form>
<!--}}}-->
/***
|Name|FoldHeadingsPlugin|
|Source|http://www.TiddlyTools.com/#FoldHeadingsPlugin|
|Version|1.1.2|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|automatically turn headings into slider-like panels that can be folded/unfolded with a single click|
This plugin defines a macro that automatically converts heading-formatted content into sliders that let you expand/collapse their content by clicking on individual headings.
!!!!!Usage
<<<
{{{
<<foldHeadings opened|closed tag tag tag...>>
}}}
where: ''opened'' or ''closed'' is a keyword indicating the initial state of the sections (default: opened), and ''tag tag tag...'' is an optional list of tags to match, so that the foldable effect is only applied to tiddlers that contain one (or more) of the indicated tags.  

When you place the macro in a tiddler, any heading-formatted content (i.e, "!" through "!!!!!") in that tiddler will automatically become //'fold-able'//, allowing you to expand/collapse the content that follows each heading simply by clicking on that heading.  Each content section begins with the first element following a heading, and continues until either another heading is found or the end of the tiddler is reached.  For example:
{{{
<<foldHeadings closed>>
}}}
is embedded in ''this'' tiddler in order to make all the headings it contains 'fold-able'.  Note that the macro has been placed at the //end// of the tiddler because it only operates on *rendered* content.  Thus, only headings that //precede// it in the same tiddler will become fold-able, as any headings that //follow// it are not actually rendered until //after// the macro has been processed.

You can further limit the effect of the macro within the tiddler by surrounding several headings in a "CSS class wrapper" ("""{{classname{...}}}""") or other containing DOM element (e.g., """@@display:inline;...@@""") and then embedding the {{{<<foldHeadings>>}}} macro inside that container (at the end)... only those headings that are also within that container will be made fold-able, instead of converting ''all'' the headings in that tiddler.

Conversely, if you want the fold-able ability to apply to the headings in //all// tiddlers, ''without having to alter //any// of those individual tiddlers'', you can add the macro to the end of your [[ViewTemplate]], so that it will be invoked after the content in each tiddler has been rendered, causing all headings they contain to automatically become fold-able.  For example:
{{{
<span macro="foldHeadings closed"></span>
}}}
You can also limit this effect to selected tiddlers by specifying one or more tags as additional macro parameters.  For example:
{{{
<span macro="foldHeadings closed systemConfig"></span>
}}}
is only applied to headings contained in //plugin tiddlers// (i.e., tiddlers tagged with <<tag systemConfig>>), while headings in other tiddlers remain unaffected by the macro, even though it is embedded in the common [[ViewTemplate]] definition.
<<<
!!!!!Revisions
<<<
2009.11.30 [1.1.2] corrected CSS 'text-weight' to 'font-weight'
2009.01.06 [1.1.1] removed hijack of scrollToSection() (see [[SectionLinksPlugin]] for equivalent code)
2008.11.17 [1.1.0] added hijack of 'scrollToSection()' function (see [[CoreTweaks]] and http://trac.tiddlywiki.org/ticket/784)
2007.12.06 [1.0.2] fix handling for empty sections when checking for sliderPanel/floatingPanel
2007.12.02 [1.0.1] fix handling when content following a heading is already a sliderPanel/floatingPanel
2007.12.01 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.FoldHeadingsPlugin= {major: 1, minor: 1, revision: 2, date: new Date(2009,11,30)};

config.macros.foldHeadings = {
	guideText: "opened|closed className",
	showtip: "click to show '%0'",
	hidetip: "click to hide '%0'",
	showlabel: "more...",
	hidelabel: "[x]",
	html: "<span style='float:right;font-weight:normal;font-size:80%;' class='TiddlyLinkExisting'>%0&nbsp;</span>",
	handler: function(place,macroName,params) {
		var show=params[0] && params.shift().toLowerCase()!="closed";
		if (params.length) { // if filtering by tag(s)
			var here=story.findContainingTiddler(place);
			if (here) var tid=store.getTiddler(here.getAttribute("tiddler"));
			if (!tid || !tid.tags.containsAny(params)) return; // in a tiddler and not tagged... do nothing...
		}
		var elems=place.parentNode.getElementsByTagName("*");
		var heads=[]; for (var i=0; i<elems.length; i++) { // get non-foldable heading elements
			var n=elems[i].nodeName; var foldable=hasClass(elems[i],"foldable");
			if ((n=="H1"||n=="H2"||n=="H3"||n=="H4"||n=="H5")&&!foldable)
				heads.push(elems[i]);
			}
		for (var i=0; i<heads.length; i++) { var h=heads[i]; // for each heading element...
			// find start/end of section content (up to next heading or end of content)
			var start=end=h.nextSibling; while (end && end.nextSibling) {
				var n=end.nextSibling.nodeName.toUpperCase();
				if (n=="H1"||n=="H2"||n=="H3"||n=="H4"||n=="H5") break;
				end=end.nextSibling;
			}
			if (start && hasClass(start,"sliderPanel")||hasClass(start,"floatingPanel")) continue; // heading is already a slider!
			var span=createTiddlyElement(null,"span",null,"sliderPanel"); // create container
			span.style.display=show?"inline":"none"; // set initial display state
			h.parentNode.insertBefore(span,start); // and insert it following the heading element
			// move section elements into container...
			var e=start; while (e) { var next=e.nextSibling; span.insertBefore(e,null); if (e==end) break; e=next; }
			// set heading label/tip/cursor...
			h.title=(show?this.hidetip:this.showtip).format([h.textContent])
			h.innerHTML=this.html.format([show?this.hidelabel:this.showlabel])+h.innerHTML;
			h.style.cursor='pointer';
			addClass(h,"foldable"); // so we know it been done (and to add extra styles)
			h.onclick=function() {
				var panel=this.nextSibling; var show=panel.style.display=="none";
				// update panel display state
				if (config.options.chkAnimate) anim.startAnimating(new Slider(panel,show));
				else panel.style.display = show?"inline":"none";
				// update heading label/tip
				this.removeChild(this.firstChild); // remove existing label
				var fh=config.macros.foldHeadings; // abbreviation for readability...
				this.title=(show?fh.hidetip:fh.showtip).format([this.textContent])
				this.innerHTML=fh.html.format([show?fh.hidelabel:fh.showlabel])+this.innerHTML;
			}
		}		
	}
}

if (story.scrollToSection) {
Story.prototype.foldheadings_scrollToSection=Story.prototype.scrollToSection;
Story.prototype.scrollToSection=function(title,section) {
	var e=this.foldheadings_scrollToSection.apply(this,arguments);
	// if scrolling to a folded section heading, click to expand it
	if (e && hasClass(e,'foldable') && e.nextSibling.style.display=='none') e.onclick();
}
}
//}}}
// //<<foldHeadings closed>>
Hi Craig

I've added:
!EditSectionPlugin
Allow editing of sections
!FoldHeadingsPlugin
Fold sectionheadings - added in the ViewTemplate for all tiddlers tagged with <<tag systemConfig>> and <<tag closed>>..
!SectionLinksPlugin
TOC and scroll section into view
!NestedSlidersPlugin
Create a button and show toc in a slider
!zzCombineEdit&Toc
Hijack Eric's SectionLinksPlugin for autogenerated TOC + editsection buttons
!MoveablePanelPlugin
Allow for detaching and hovering of the editable toc slider content
!!SliderToc
Content of the slider - style it any way you want...
- added an "scroll to top" arrow for when the toc is detached and hovered/follows page...
<<<
Tip: First detach - then click hover...
<<<
!608/609/610 toolbars - toggles, separators and transclusion 
(Coretweak from tiddlytools - to add the SliderToc tiddler content to ToolbarCommands)
!Enjoy

Måns Mårtensson
/***
|''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);
}
//}}}
/***
|Name|[[MoveablePanelPlugin]]|
|Source|http://www.TiddlyTools.com/#MoveablePanelPlugin|
|Documentation|http://www.TiddlyTools.com/#MoveablePanelPluginInfo|
|Version|3.0.4|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|move/size any tiddler or page element|
Use the mouse to move/resize any specific tiddler content, page element, or [[floating slider panel|NestedSlidersPlugin]].
!!!!!Documentation
>see [[MoveablePanelPluginInfo]]
!!!!!Configuration
<<<
<<option chkMoveablePanelShowStatus>> show position/size while moving/resizing a panel
<<option chkMoveablePanelShowManager>> automatically add Panel Manager button to undocked panels (see [[PanelManagerPlugin]])
<<<
!!!!!Revisions
<<<
2010.12.24 3.0.4 fixed findMouseX/findMouseY for webkit browsers
|please see [[MoveablePanelPluginInfo]] for additional revision details|
2006.03.04 1.0.0 Initial public release
<<<
!!!!!Code
***/
//{{{
version.extensions.MoveablePanelPlugin= {major: 3, minor: 0, revision: 4, date: new Date(2010,12,24)};
if (config.macros.moveablePanel===undefined) config.macros.moveablePanel={};
//}}}
// // translate
//{{{
// TRANSLATORS: copy this section to MoveablePanelPluginLingoXX (where 'XX' is a language/country code)
if (config.macros.moveablePanel===undefined) config.macros.moveablePanel={};
merge(config.macros.moveablePanel,{

	foldLabel:	'\u2212', // minus
	foldTip:	'FOLD=reduce panel size',
	unfoldLabel:	'+',
	unfoldTip:	'UNFOLD=restore panel size',
	hoverLabel:	'^',
	hoverTip:	'HOVER=keep panel in view when scrolling',
	scrollLabel:	'\u2248', // asymp
	scrollTip:	'SCROLL=allow panel to move with page',
	closeLabel:	'X',
	closeTip:	'CLOSE=hide this panel',
	dockLabel:	'\u221A', // radic
	dockTip:	'DOCK=reset size/position',

	noPid:		'unnamed panel',

	statusMsg:	'%0: pos=(%1,%2)%3 size=(%4,%5) z=%6',
	hoveredMsg:	'[hovering]',
	dockedTip:	'%0: docked',
	scrollMsg:	'%0: pos=(%1,%2)',
	msgDuration:	3000,

	moveTip:	'%0DRAG EDGE=move',
	sizeTip:	'(SHIFT=resize)',
	sizeWidthTip:	'(SHIFT=resize width)',
	sizeHeightTip:	'(SHIFT=resize height)',
	clickTip:	  'CLICK=bring to front, SHIFT-CLICK=send to back',
	dblclickdockTip:  'DOUBLE-CLICK=dock',
	dblclickunfoldTip:'DOUBLE-CLICK=unfold',

	foldParam:	'fold',
	hoverParam:	'hover',
	nocloseParam:	'noclose',
	nodockParam:	'nodock',
	undockedParam:	'undocked',

	jumpParam:	'jump',
	dockParam:	'dock',
	moveParam:	'move',
	labelParam:	'label',
	promptParam:	'prompt',

	allParam:	'all',
	nameParam:	'name',
	topParam:	'top',
	leftParam:	'left',
	widthParam:	'width',
	heightParam:	'height',

	managerParam:	'manager'
});
//}}}
// // global functions (general utilities)
//{{{
// if removeCookie() function is not defined by TW core, define it here (for <TW2.5)
if (window.removeCookie===undefined) {
	window.removeCookie=function(name) {
		document.cookie = name+'=; expires=Thu, 01-Jan-1970 00:00:01 UTC; path=/;'; 
	}
}
if (window.copyObject===undefined) {
	window.copyObject=function(src)	{
		for (var i in src) this[i]=typeof src[i]!='object'?src[i]:new copyObject(src[i]);
	}
}
if (window.compareObjects===undefined) {
	window.compareObjects=function(a,b) {
		if (a===b) return true;
		if (a==undefined||b==undefined) return false;
		for (var i in a) if (typeof a[i]!='object'?a[i]!==b[i]:!compareObjects(a[i],b[i])) return false;
		return true;
	}
}
if (window.isEmptyObject===undefined) {
	window.isEmptyObject=function(src) { for (var i in src) return false; return true; }
}

// cross-browser metrics
window.findMouseX=function(ev) { if (!ev) return 0; var x=0;
	if (config.browser.isIE)	return ev.clientX+findScrollX();// IE
	if (config.browser.isSafari) 	return ev.pageX+findScrollX(); 	// Webkit
	else				return ev.pageX;		// Firefox/other
}
window.findMouseY=function(ev){ if (!ev) return 0; var y=0;
	if (config.browser.isIE)	return ev.clientY+findScrollY();// IE
	if (config.browser.isSafari) 	return ev.pageY+findScrollY();	// Webkit
	else				return ev.pageY;		// Firefox/other
}
//}}}
// // macro
//{{{
merge(config.macros.moveablePanel,{
	handler: function(place,macroName,params,wikifier,paramString,tiddler) {

		// ALTERNATIVE OUTPUT: Panel Manager macro extensions...
		if (this.manager && this.manager.handler(place,macroName,params,wikifier,paramString,tiddler))
			return; // processed by PanelManager

		// UNPACK KEYWORD PARAMS
		var showfold	 =params.contains(this.foldParam);
		var showhover	 =params.contains(this.hoverParam);
		var showclose	 =!params.contains(this.nocloseParam);
		var showdock	 =!params.contains(this.nodockParam);
		var showmanager  =params.contains(this.managerParam);
		var startundocked=params.contains(this.undockedParam);
		var jump	 =params.contains(this.jumpParam);
		var dock	 =params.contains(this.dockParam);
		var move	 =params.contains(this.moveParam);
		var all		 =params.contains(this.allParam);

		// UNPACK VALUE PARAMS
		params=paramString.parseParams('anon',null,true,false,false);
		var label =getParam(params,this.labelParam,null);
		var prompt=getParam(params,this.promptParam,null);
		var name  =getParam(params,this.nameParam,null);
		var top   =getParam(params,this.topParam,null);
		var left  =getParam(params,this.leftParam,null);
		var width =getParam(params,this.widthParam,null);
		var height=getParam(params,this.heightParam,null);

		// COMMANDS: JUMP, MOVE, DOCK
		if (jump||move||dock) {
			if (!label||!label.length) {
				var p=this.findPanel(name);
				if (jump) { if (p) this.scrollToPanel(p,true); else window.scrollTo(left,top); }
				if (move) { this.movePanel(p,left,top,true,true); }
				if (dock) { if (all) this.forAllPanels(this.dockPanel); else if (p) this.dockPanel(p); }
				return;
			}
			var tip=(jump?this.jumpParam:move?this.moveParam:dock?this.dockParam:'')+': '+name;
			var b=createTiddlyButton(place,label,prompt||tip, function(ev) {
				var cmm=config.macros.moveablePanel; var p=cmm.findPanel(this.name);
				if (this.jump) { if (p) cmm.scrollToPanel(p,true); else window.scrollTo(this.left,this.top); }
				if (this.move) { if (p) cmm.movePanel(p,this.left,this.top,true,true); }
				if (this.dock) { if (p) cmm.dockPanel(p); else if (this.all) cmm.forAllPanels(cmm.dockPanel); }
				return cmm.processed(ev)
			},'button');
			b.jump=jump; b.move=move; b.dock=dock;
			b.name=name; b.all=all;   b.left=left; b.top=top;
			return;
		}

		// PANEL SETUP
		var p=this.getPanel(place);
		this.cachePanel(p);
		addClass(p,'moveablePanel');
		p.pid=name;
		p.showmanager=showmanager;
		p.fixedheight=height||undefined;
		p.fixedwidth=width||undefined;
		this.addPanelButtons(p,showfold,showhover,showclose,showdock,showmanager);
		this.addMouseHandlers(p);
		if (startundocked) {
			this.undockPanel(p);
			if (!startingUp) { this.bringPanelToFront(p); this.scrollToPanel(p); }
		}
		if (this.manager) { this.manager.applyMap(p); this.manager.trackMap(p); }
		this.notify(p);
	},
//}}}
// // notifications
//{{{
	quiet: 0, // flag to suspend/resume notifications
	notify: function(p) { // notify others of panel changes
		if (this.quiet) return;
		if (this.manager) this.manager.notify(p); // pass notices to manager (updates viewers)
	},
//}}}
// // general panel utilities
//{{{
	getPanel: function(place) { // find containing panel or floating slider (use current element as fallback)
		var p=place;
		while (p && !(hasClass(p,'moveablePanel')||hasClass(p,'floatingPanel'))) p=p.parentNode;
		return p||place;
	},
	getAllPanels: function(zSort) { // find 'moveablePanel' elements (optionally sort by zIndex)
		var panels=[];
		var sortByZindex=function(a,b){
			var v1=parseInt(a.style.zIndex); if (isNaN(v1)) v1=0;
			var v2=parseInt(b.style.zIndex); if (isNaN(v2)) v2=0;
			return(v1==v2)?0:(v1>v2?1:-1);
		}
		// if native browser fn is defined, use it (*much* more efficient!)
		if (document.getElementsByClassName) { 
			var elems=document.getElementsByClassName('moveablePanel');
			for (var i=0; i<elems.length; i++) panels.push(elems[i]);
			return zSort?panels.sort(sortByZindex):panels;
		}
		// otherwise, find all DIVs and SPANs with the right class
		// NOTE: IE requires use of Enumerator() to iterate over elements, or it FREEZES UP COMPLETELY!!
		var isIE=config.browser.isIE;
		var elems=document.getElementsByTagName('DIV');
		for (var i=isIE?new Enumerator(elems):0; isIE?!i.atEnd():(i<elems.length); isIE?i.moveNext():i++) {
			var panel=isIE?i.item():elems[i];
			if (hasClass(panel,'moveablePanel')) panels.push(panel);
		}
		var elems=document.getElementsByTagName('SPAN');
		for (var i=isIE?new Enumerator(elems):0; isIE?!i.atEnd():(i<elems.length); isIE?i.moveNext():i++) {
			var panel=isIE?i.item():elems[i];
			if (hasClass(panel,'moveablePanel')) panels.push(panel);
		}
		return zSort?panels.sort(sortByZindex):panels;
	},
	findPanel: function(pid) { // find a named panel
		var p=this.getAllPanels();
		for (var i=0; i<p.length; i++) { if (pid && p[i].pid==pid) return p[i]; }
		return undefined;
	},
	forAllPanels: function(callback) { // invoke a function on each panel
		var panels=this.getAllPanels();
		this.quiet++;
		for (var i=0; i<panels.length; i++) callback.apply(this,[panels[i]]);
		this.quiet--;
		this.notify('all');
	},
	cachePanel: function(p) { // save original styles and handlers
		if (!p.saved) p.saved={ 
			x:p.style.left||'', y:p.style.top||'', w:p.style.width||'', h:p.style.height||'',
			z:p.style.zIndex||'', pos:p.style.position||'', title: p.title,
			mouseover:p.onmouseover, mouseout:p.onmouseout,
			mousedown:p.onmousedown, mousemove:p.onmousemove, dblclick:p.ondblclick
		};
	},
	restorePanel: function(p) { // restore original styles
		if (!p.saved) return;
		p.style.left=p.saved.x; p.style.top=p.saved.y; p.style.width=p.saved.w; p.style.height=p.saved.h;
		p.style.zIndex=p.saved.z; p.style.position=p.saved.pos; p.title=p.saved.title;
		removeClass(p,'folded'); removeClass(p,'hover'); removeClass(p,'undocked');
	},
//}}}
// // panel metrics
//{{{
	getPanelOffset: function(p) { // adjustment for child elements inside relative/floatingPanel containers
		var r=new Object(); r.x=0; r.y=0; if (!p) return r;
		var pp=p.parentNode; while (pp && !(pp.style&&pp.style.position=='relative')) pp=pp.parentNode;
		if (pp) { r.x+=findPosX(pp); r.y+=findPosY(pp); }
		var pp=p.parentNode; while (pp && !hasClass(pp,'floatingPanel')) pp=pp.parentNode;
		if (pp) { r.x+=findPosX(pp); r.y+=findPosY(pp); }
		return r;
	},
	// PROBLEM: the offsetWidth/offsetHeight do not seem to account for padding or borders
	// WORKAROUND: subtract padding and border (in px) from width and height
	// ISSUE: I still don't understand why this is needed...
	// TBD: get padding/border values from p.style and convert to px
	// NOTE: 10.6667 seems to be about 1em...
	getPanelEdgeWidth:
	  	function(p) { return 10.6667; },
	getPanelEdgeHeight:
		function(p) { return 10.6667; },
	getPanelHeight:
		function(p) { var pad=10.6667; var border=1; return p.offsetHeight-(pad*2+border*2); },
	getPanelWidth:
		function(p) { var pad=10.6667; var border=1; return p.offsetWidth -(pad*2+border*2); },
//}}}
// // panel stacking (zIndex)
//{{{
	isStackable: function(p) { // zIndex is only effective with absolute or fixed elements
		return (['absolute','fixed'].contains(p.style.position)&&!hasClass(p,'popup'));
	},
	normalizeStack: function(panels) { // set zIndex to correspond to stack order
		for (var i=0; i<panels.length; i++) {var z=panels[i].style.zIndex;
			if (z==0||z=='auto') continue; // if not stacking (e.g., 'auto', '', or null)
			if (z<10000 || z>10000) continue; // use large values for "always in front/back"
			if (z!=i+2) panels[i].style.zIndex=i+2;
			if (this.manager) this.manager.trackMap(panels[i]);
		}
		return panels;
	},
	bringPanelToFront: function(p) { if (!p) return;
		if (!this.isStackable(p)) return; // can't be stacked
		var panels=this.getAllPanels(true);
// WFFL - normalizing every time works, but takes too long
//		if (p.style.zIndex>panels.length+2) return; // stay in front
//		this.normalizeStack(panels);
//		p.style.zIndex=panels.length+2;
// WFFL - for now, just bump up the max (ignore z>10000) and normalize much less often
		if (p.style.zIndex>1000) this.normalizeStack(panels);
		var zMax=0; if (panels.length) {
			var i=panels.length-1; zMax=parseInt(panels[i].style.zIndex);
			while (zMax>10000 && i>0) zMax=parseInt(panels[--i].style.zIndex);
			if (p==panels[i]) return; // already in front
			if (isNaN(zMax)) zMax=0;
		}
		p.style.zIndex=zMax+1;
		this.notify(p);
	},
	sendPanelToBack: function(p) { if (!p) return;
		if (!this.isStackable(p)) return; // can't be stacked
		var panels=this.getAllPanels(true);
// WFFL - normalizing every time works, but takes too long
//		if (p.style.zIndex<2) return; // stay in back
//		this.normalizeStack(panels);
//		p.style.zIndex=1;
// WFFL - for now, just bump down the min (ignore z<10000) and normalize much less often
		if (p.style.zIndex<1000) this.normalizeStack(panels);
		var zMin=0; if (panels.length) {
			var i=0; zMin=parseInt(panels[i].style.zIndex);
			while (zMin<10000 && i<panels.length-1) zMin=parseInt(panels[++i].style.zIndex);
			if (p==panels[i]) return; // already in back
			if (isNaN(zMin)) zMin=0;
		}
		p.style.zIndex=zMin-1;
		this.notify(p);
	},
	returnPanelToStack: function(p) { if (!p) return;
		p.style.zIndex=p.saved?p.saved.zIndex:'';
		this.notify(p);
	},
//}}}
// // panel scrolling 
//{{{
	noScrollX: 0, // flags to disable TW built-in scrolling behavior
	noScrollY: 0, // set by hijacks, cleared by ensurePanelVisible(), below
	// scroll view to show panel along nearest edge of window or centered (optional)
	scrollToPanel: function(p,center) { if (!p) return;
		if (hasClass(p,'popup')) return; // popup=let core scrolling handle it
		if (hasClass(p,'hover')) return; // hover=always in view=don't scroll
		var scrollSize=findWindowWidth()-document.body.offsetWidth; // width of scrollbar
		var sx=findScrollX();	var ww=findWindowWidth()-scrollSize;
		var sy=findScrollY();	var wh=findWindowHeight()-scrollSize;
		var px=findPosX(p);	var pw=p.offsetWidth;
		var py=findPosY(p);	var ph=p.offsetHeight;
		var nx=sx; var ny=sy; // assume no scrolling is needed
		// if BR is not in view, scroll to show BR
		if (px+pw>sx+ww) nx=px+pw-ww;
		if (py+ph>sy+wh) ny=py+ph-wh;
		// if TL not in view or too big... scroll to show TL
		if (px<nx || px>nx+ww || px+pw>nx+ww) nx=px;
		if (py<ny || py>ny+wh || py+ph>ny+wh) ny=py;
		// optionally, center in view (if panel fits)
		if (center && pw<ww) nx-=(ww-pw)/2;
		if (center && ph<wh) ny-=(wh-ph)/2;
		if (nx!=sx||ny!=sy) { // if we need to scroll...
			window.scrollTo(nx,ny);
			if (config.options.chkMoveablePanelShowStatus && !startingUp) {
				var id=hasClass(p,'tiddler')?p.getAttribute('tiddler'):p.pid;
				this.timedMessage(this.scrollMsg.format([id||this.noPid,px,py]),this.msgDuration);
			}
			this.notify(p);
		}
	},
	// bring to front and scroll into view (with optional ASYNC)
	ensurePanelVisible: function(p,delay) { if (!p) return;
		if (delay && !startingUp) { // wait for core animation to complete...
			if (hasClass(p,'tiddler'))
				p=config.macros.moveablePanel.findPanel(p.getAttribute('tiddler'))||p;
			if (!p.id) p.id=new Date().getTime()+Math.random(); // unique ID
			var code='config.macros.moveablePanel.ensurePanelVisible(document.getElementById("%0"));';
			setTimeout(code.format([p.id]),delay);
			return;
		}
		// unblock scrolling and bring the panel into view
		if (this.noScrollX>0) this.noScrollX--; if (this.noScrollY>0) this.noScrollY--;
		if (hasClass(p,'popup')) return; // leave popups alone!
		this.bringPanelToFront(p);
		if (this.noScrollX+this.noScrollY==0 && !startingUp) // no scroll during document startup
			this.scrollToPanel(p);
	},
//}}}
// // panel status
//{{{
	formatPanelStatus: function(p) {
		var s=p.style; var msg=this.statusMsg.format([p.pid||this.noPid,
			s.left,s.top,hasClass(p,'hover')?this.hoveredMsg:'',s.width,s.height,s.zIndex]);
		return msg.replace(/(\.[0-9]+)|px/g,''); // remove decimals and 'px'
	},
	showPanelStatus: function(p,show) { // display panel info in titlebar while moving/sizing
		if (!config.options.chkMoveablePanelShowStatus) return;
		if (show) document.title=this.formatPanelStatus(p)
		else refreshPageTitle();
	},
	timedMessage: function(msg,duration) {
		document.title=msg; setTimeout('refreshPageTitle()',duration);
	},
	getPanelTooltip: function(p) {
		return hasClass(p,'undocked')?this.formatPanelStatus(p):this.dockedTip.format([p.pid||this.noPid]);
	},
//}}}
// // panel actions
//{{{
	undockPanel: function(p,front) { // undocked with default pos/size
		if (hasClass(p,'undocked')) return; // already undocked
		// get size BEFORE undocking
		p.style.width=p.fixedwidth  ||(this.getPanelWidth(p)+'px');
		p.style.height=p.fixedheight||(this.getPanelHeight(p)+'px');
		addClass(p,'undocked');	if (!this.isStackable(p)) p.style.position='absolute'; // UNDOCK it
		// set position AFTER undocking
		var offset=this.getPanelOffset(p);
		p.style.left=findPosX(p)-offset.x+'px'; p.style.top=findPosY(p)-offset.y+'px';
		if (front) this.bringPanelToFront(p);
		this.notify(p);
	},
	dockPanel: function(p) { // reset to docked pos/size
		if (!hasClass(p,'undocked')) return; // already docked
		this.restorePanel(p); // reset panel
		// FOR FLOATING SLIDERS: trigger slider adjustment handler (if any)
		if (hasClass(p,'floatingPanel') && window.adjustSliderPos)
			window.adjustSliderPos(p.parentNode,p.button,p);
		this.quiet++; if (this.manager) this.manager.trackMap(p); this.quiet--;
		this.notify(p)
	},
	closePanel: function(p) { // dock panel, then close (for tiddlers and floating sliders)
		var t=story.findContainingTiddler(p);
		var isTiddler=t&&this.findPanel(t.getAttribute('tiddler'));
		var isFloating=hasClass(p,'floatingPanel');
		if (!isTiddler) // when closing TIDDLERS, leave them undocked (keeps size/pos)
			this.dockPanel(p);
		// FOR FLOATING SLIDERS: set focus and do a fake click on slider button
		if (isFloating) { p.button.focus(); onClickNestedSlider({target:p.button}); }
		// FOR TIDDLERS: call story.closeTiddler()
		if (isTiddler) { story.closeTiddler(t.getAttribute('tiddler')); }
	},
	movePanel: function(p,x,y,show,centered) { if (!p) return;
		this.quiet++;
		this.undockPanel(p);
		// adjust for child elements inside relative/floatingPanel containers
		var offset=this.getPanelOffset(p);
		p.style.left=x-offset.x+'px'; p.style.top=y-offset.y+'px';
		if (show) { this.bringPanelToFront(p); this.scrollToPanel(p,centered); }
		this.quiet--;
		this.showPanelStatus(p,true);
		if (this.manager) this.manager.trackMap(p);
	},
	foldPanel: function(p) { // toggle panel height
		if (hasClass(p,'folded')) removeClass(p,'folded'); else addClass(p,'folded');
		if (this.manager) this.manager.trackMap(p);
		this.notify(p);
	},
	hoverPanel: function(p) { // toggle fixed position
		if (hasClass(p,'hover')) {
			removeClass(p,'hover');
			var offset=this.getPanelOffset(p);
			p.style.left=p.offsetLeft+findScrollX()-offset.x+'px';
			p.style.top=p.offsetTop+findScrollY()-offset.y+'px';
		} else {
			var offset=this.getPanelOffset(p);
			var ww=findWindowWidth(); var wh=findWindowHeight();
			p.style.left=(p.offsetLeft-findScrollX()+offset.x)%ww+'px';
			p.style.top =(p.offsetTop -findScrollY()+offset.y)%wh+'px';
			addClass(p,'hover'); 
		}
		if (this.manager) this.manager.trackMap(p);
		this.notify(p);
	},
	resetPanel: function(p) { // reset to session starting pos/size
		if (this.manager) this.manager.resetPanel(p); else this.dockPanel(p);
	},
//}}}
// // menu buttons
//{{{
	processed: function(ev) { var ev=ev||window.event; // use to end event handling for menus and mouse actions
		if (ev) { ev.cancelBubble=true; if (ev.stopPropagation) ev.stopPropagation(); } return false;
	},
	addPanelButtons: function(p,showfold,showhover,showclose,showdock,showmanager) {
		if (p.menu) return; // only once per panel
		function cmd(menu,label,tip,callback,show,arg) {
			var fn=function(ev){return this.callback.apply(config.macros.moveablePanel,[this,ev,this.arg]);}
			var b=createTiddlyButton(menu,label,tip,fn,'moveablePanelButton');
			b.style.display=show?'inline':'none'; b.callback=callback; b.arg=arg;
			return b;
		}
		var m=createTiddlyElement(p,'div',null,'moveablePanelMenu');
		p.showfold=showfold;
		p.foldbutton= cmd(m,this.foldLabel,this.foldTip,this.foldHandler,showfold);
		p.unfoldbutton= cmd(m,this.unfoldLabel,this.unfoldTip,this.foldHandler,false);
		p.showhover=showhover;
		p.hoverbutton=cmd(m,this.hoverLabel,this.hoverTip,this.hoverHandler,showhover);
		p.scrollbutton=cmd(m,this.scrollLabel,this.scrollTip,this.hoverHandler,false);
		p.showdock=showdock;
		p.dockbutton= cmd(m,this.dockLabel,this.dockTip,this.dockHandler,showdock);
		p.showclose=showclose;
		p.closebutton=cmd(m,this.closeLabel,this.closeTip,this.closeHandler,showclose);
		p.showmanager=showmanager;
		if (this.manager) p.managerbutton=cmd(m,this.manager.buttonLabel,this.manager.buttonTip,
			this.manager.popup,showmanager,p.pid);
		p.menu=m;
	},
	togglePanelButtons: function(p,show) { if (!p||!p.menu) return;
		var undocked=hasClass(p,'undocked');
		var floating=hasClass(p,'floatingPanel');
		var hover=hasClass(p,'hover');
		var folded=hasClass(p,'folded');
		var t=story.findContainingTiddler(p);
		var tiddler=t&&this.findPanel(t.getAttribute('tiddler'));
		var show=show&&(undocked||floating);
		p.menu.style.display=show?'inline':'none';
		if (p.showfold)  p.foldbutton.style.display  =!folded?'inline':'none';
		if (p.showfold)  p.unfoldbutton.style.display= folded?'inline':'none';
		if (p.showhover) p.hoverbutton.style.display =!hover?'inline':'none';
		if (p.showhover) p.scrollbutton.style.display= hover?'inline':'none';
		if (p.showdock)  p.dockbutton.style.display =undocked?'inline':'none';
		if (p.showclose) p.closebutton.style.display=floating||(tiddler&&undocked)?'inline':'none';
		if (p.managerbutton) { // see [[PanelManagerPlugin]]
			var show=p.showmanager||config.options.chkMoveablePanelShowManager;
			p.managerbutton.style.display=show?'inline':'none';
		}
	},
	foldHandler: function(place,ev){ var p=this.getPanel(place);
		this.foldPanel(p); this.togglePanelButtons(p,true); return this.processed(ev); },
	hoverHandler: function(place,ev){ var p=this.getPanel(place);
		this.hoverPanel(p); this.togglePanelButtons(p,true); return this.processed(ev); },
	dockHandler: function(place,ev){ var p=this.getPanel(place);
		this.dockPanel(p); this.togglePanelButtons(p,true); return this.processed(ev); },
	closeHandler: function(place,ev){ var p=this.getPanel(place);
		this.closePanel(p); this.togglePanelButtons(p,true); return this.processed(ev); },
//}}}
// // mouse handlers
//{{{
	addMouseHandlers: function(p) {
		if (p.handlers) return true; // only add handlers ONCE
		p.onmouseover=function(ev) { var ev=ev||window.event;
			var r=config.macros.moveablePanel.mouseover(this,ev);
			return r&&this.saved.mouseover?this.saved.mouseover.apply(this,arguments):true;
		};
		p.onmouseout=function(ev) { var ev=ev||window.event;
			var r=config.macros.moveablePanel.mouseout(this,ev);
			return r&&this.saved.mouseout?this.saved.mouseout.apply(this,arguments):true;
		};
		p.onmousemove=function(ev) { var ev=ev||window.event;
			var r=config.macros.moveablePanel.mousemove(this,ev);
			return r&&this.saved.mousemove?this.saved.mousemove.apply(this,arguments):true;
		};
		p.ondblclick=function(ev) { var ev=ev||window.event;
			var r=config.macros.moveablePanel.dblclick(this,ev);
			return r&&this.saved.dblclick?this.saved.dblclick.apply(this,arguments):r;
		};
		p.onmousedown=function(ev) { var ev=ev||window.event;
			var r=config.macros.moveablePanel.mousedown(this,ev);
			return r&&this.saved.mousedown?this.saved.mousedown.apply(this,arguments):r;
		};
		p.handlers=true;
	},
	isEdge: function(p,ev) { // near 'edge' of panel (or child element)?
		var ev=ev||window.event; var target=resolveTarget(ev);
		if (!p) return false;
		// ignore form input fields
		if (['input','select','option','textarea'].contains(target.nodeName.toLowerCase())) return false;
		var left=findPosX(p); var top=findPosY(p);
		var width=p.offsetWidth; var height=p.offsetHeight;
		var x=findMouseX(ev); var y=findMouseY(ev);
		if (hasClass(p,'hover')) { x-=findScrollX(); y-=findScrollY(); } // window-relative panel
		if (x<left||y<top||x>=left+width||y>=top+height) { // outside of panel
			if (p==target || p!=this.getPanel(target)) return false;
			return this.isEdge(target,ev); // check target child element
		}
		var edgeW=this.getPanelEdgeWidth(p); var edgeH=this.getPanelEdgeHeight(p);
		var isT=(y-top<edgeH); var isL=(x-left<edgeW);
		var isB=(top+height-y<edgeH); var isR=(left+width-x<edgeW);
		return isT||isL||isB||isR;
	},
	// temporary element during move/size keeps document from shrinking 
	addGhost: function(p) {
		var g=document.getElementById('moveablePanelGhost');
		if (!g) g=createTiddlyElement(document.body,'div','moveablePanelGhost','moveablePanelGhost');
		var border=1; // note: must match CSS for 'moveablePanelGhost' WFFL-HACK
		g.style.left=findPosX(p)+'px';
		g.style.top=findPosY(p)+'px';
		g.style.width=((p.offsetWidth-border*2)||0)+'px';
		g.style.height=((p.offsetHeight-border*2)||0)+'px';
	},
	clearGhost: function() {
		var e=document.getElementById('moveablePanelGhost');
		if (e) e.parentNode.removeChild(e);
	},
	// MOUSEOVER=SHOW MENU
	mouseover: function(place,ev) { var ev=ev||window.event;
		var p=this.getPanel(place);
		addClass(p,'selected'); // shows toolbar-classed items
		this.togglePanelButtons(p,true);
		return true;
	},
	// MOUSEOUT=HIDE MENU
	mouseout: function(place,ev) { var ev=ev||window.event;
		var p=this.getPanel(place);
		removeClass(p,'selected'); // hides toolbar-classed items
		this.togglePanelButtons(p,false);
		return true;
	},
	// MOUSEMOVE=SHOW MENU AND SET CURSOR/TIP
	mousemove: function(place,ev) { var ev=ev||window.event;
		var p=this.getPanel(place);
		p.style.cursor='auto'; p.title=p.saved?p.saved.title:'';
		if (!this.isEdge(p,ev)) return true;
		var fw=p.fixedwidth;  if (fw==null) fw=undefined;
		var fh=p.fixedheight; if (fh==null) fh=undefined;

		p.title=this.moveTip.format([p.pid?p.pid+': ':'']);
		if (fw===undefined&&fh===undefined) p.title+=' '+this.sizeTip;
		else if  (fw===undefined) p.title+=' '+this.sizeWidthTip;
		else if  (fh===undefined) p.title+=' '+this.sizeHeightTip;
		if (hasClass(p,'undocked')) {
			p.title+=', '+this.clickTip+', ';
			p.title+=hasClass(p,'folded')?this.dblclickunfoldTip:this.dblclickdockTip;
		}
		p.style.cursor='move';
		if (ev.shiftKey&&!(fw&&fh)) { // set resizing cursor (if not fixed width/height)
			var left=findPosX(p); var top=findPosY(p);
			var width=p.offsetWidth; var height=p.offsetHeight;
			var x=findMouseX(ev); var y=findMouseY(ev);
			if (hasClass(p,'hover')) { x-=findScrollX(); y-=findScrollY(); } // window-relative panel
			var edgeW=this.getPanelEdgeWidth(p); var edgeH=this.getPanelEdgeHeight(p);
			var isT=(y-top<edgeH); var isL=(x-left<edgeW);
			var isB=(top+height-y<edgeH); var isR=(left+width-x<edgeW);
			p.style.cursor=(fh===undefined?(isT?'n':(isB?'s':'')):'')
				+(fw===undefined?(isL?'w':(isR?'e':'')):'')+'-resize';
		}
		return true;
	},
	// DOUBLE-CLICK=DOCK OR UNFOLD
	dblclick: function(place,ev) { var ev=ev||window.event;
		var p=this.getPanel(place);
		if (!this.isEdge(p,ev)) return true;
		// if folded... unfold, otherwise... undock
		if (hasClass(p,'folded')) this.foldPanel(p); else this.dockPanel(p);
		this.togglePanelButtons(p,false);
		return this.processed(ev);
	},
	// MOUSEDOWN=START MOVE/SIZE, CLICK=BRING TO FRONT, SHIFT-CLICK=SEND TO BACK
	mousedown: function(place,ev) { var ev=ev||window.event;
		var p=this.getPanel(place);

		// CLICK ALWAYS BRINGS TO FRONT
		this.quiet++;
		this.bringPanelToFront(p);
		if (this.manager) this.manager.trackMap(p);
		this.quiet--;
		if (!this.isEdge(p,ev)) return true;

		// start capturing mouse events and set mouse/key handlers
		var target=p; // if 'capture' not supported, track in panel only
		if (document.body.setCapture) // IE
			{ document.body.setCapture(); var target=document.body; }
		if (window.captureEvents) // moz
			{ window.captureEvents(Event.MouseMove|Event.MouseUp,true); var target=window; }
 		if (target.onmousemove!=undefined) target.saved_mousemove=target.onmousemove;
		target.onmousemove=this.dragmove;
		if (target.onmouseup!=undefined) target.saved_mouseup=target.onmouseup;
		target.onmouseup=this.dragstop;
 		if (target.onkeydown!=undefined) target.saved_keydown=target.onkeydown;
		target.onkeydown=this.dragkey;

		// calculate and save drag data in target element
		var x=findMouseX(ev); var left=findPosX(p); var width =p.offsetWidth;
		var y=findMouseY(ev); var top =findPosY(p); var height=p.offsetHeight;
		var sizing=ev.shiftKey;
		var edgeW=this.getPanelEdgeWidth(p); var edgeH=this.getPanelEdgeHeight(p);
		var isT=(y-top<edgeH); var isL=(x-left<edgeW);
		var isB=(top+height-y<edgeH); var isR=(left+width-x<edgeW);
		var d=new Object();
		d.panel=p; d.left=left; d.top=top;
		d.width=this.getPanelWidth(p); d.height=this.getPanelHeight(p);
		d.sizing=sizing; d.edgeW=edgeW; d.edgeH=edgeH;
		d.isT=isT; d.isL=isL; d.isB=isB; d.isR=isR; d.offset=this.getPanelOffset(p);
		d.saved={ x:p.style.left, y:p.style.top, w:p.style.width, h:p.style.height,
			z:p.style.zIndex, pos:p.style.position, classname:p.className };
		target.data=d;
		this.addGhost(p); // keep document from shrinking during move/size
		return this.processed(ev);
	},
	// MOUSEMOVE (during drag)=move/size panel
	dragmove: function(ev){ var ev=ev||window.event; var cmm=config.macros.moveablePanel;
		var d=this.data; var p=d.panel;
		if (!p) { this.onmousemove=this.saved_mousemove?this.saved_mousemove:null; return; }

		cmm.quiet++; // save all notifications until the end...

		// ensure panel is undocked and scrolled into view, THEN get starting mouse and scroll positions
		if (!hasClass(p,'undocked'))
			{ cmm.undockPanel(p,true); if (this.manager) this.manager.trackMap(p); }
		if (d.x===undefined) // first move event only
			{ cmm.scrollToPanel(p); d.x=findMouseX(ev); d.y=findMouseY(ev); }

		// get current mouse pos
		var newX=findMouseX(ev); var newY=findMouseY(ev);

		// calculate new TLWH (start with current panel pos/size)
		var startX=d.x; var startY=d.y; var offsetX=d.offset.x; var offsetY=d.offset.y;
		var L=d.left; var T=d.top; var W=d.width; var H=d.height;
		var newL=L; var newT=T; var newW=p.fixedwidth||W; var newH=p.fixedheight||H;
		if (d.sizing) { // resize panel
			var minW=d.edgeW*2; var minH=d.edgeH*2; // stay bigger than edge areas
			if (hasClass(p,'folded')) this.fold(p.foldButton,ev); // un-fold first!
			if (d.isT) newH=H-newY+startY+1;
			if (d.isB) newH=H+newY-startY+1;
			if (d.isL) newW=W-newX+startX+1;
			if (d.isR) newW=W+newX-startX+1;
			if (d.isT) newT=T-offsetY+newY-startY+1; else newT=T-offsetY; 
			if (d.isL) newL=L-offsetX+newX-startX+1; else newL=L-offsetX; 
			if ((d.isL||d.isR)&&!p.fixedwidth)  newW=(newW>minW?newW:minW);
			if ((d.isT||d.isB)&&!p.fixedheight) newH=(newH>minH?newH:minH);
		} else { // move panel
			newL=L-offsetX+newX-startX+1;
			newT=T-offsetY+newY-startY+1;
		}
		if (hasClass(p,'hover')) { // hover=stay on first screen
			var ww=findWindowWidth(); var wh=findWindowHeight();
			newL+=offsetX; newT+=offsetY; // hover=no relative offset (window-relative)
			// WFFL lower right is off... a bit too far (perhaps scrollwidth?)
			if (newL+newW>ww) newL=ww-newW; if (newT+newH>wh) newT=wh-newH; // limit lower right
			if (newL<0) newL=0; if (newT<0) newT=0; // limit upper left
		} else { // normal floating panel=limit upper left (stay on page)
			if (newL+offsetX<0) newL=0-offsetX; if (newT+offsetY<0) newT=0-offsetY;
		}

		// move the panel and scroll into view as needed
		p.style.left=newL.toString()+'px';
		p.style.top=newT.toString()+'px';
		if (d.sizing) p.style.width=newW.toString()+'px';
		if (d.sizing) p.style.height=newH.toString()+'px';
		cmm.scrollToPanel(p);

		// report new position and notify panel manager... done!
		cmm.quiet--; cmm.showPanelStatus(p,true); cmm.notify(p);
		return cmm.processed(ev);
	},
	dragkey: function(ev){ var ev=ev||window.event;
		var d=this.data; var p=d.panel;
		if (ev.keyCode==27) { // ESC=CANCEL... restore panel to previous pos/size
			p.style.left =d.saved.x; p.style.top   =d.saved.y;
			p.style.width=d.saved.w; p.style.height=d.saved.h;
			p.style.zIndex=d.saved.z;
			p.style.position=d.saved.pos;
			p.className=d.saved.classname;
			return this.onmouseup(ev);
		}
		if (this.saved_keydown) return this.saved_keydown(ev);
	},
	// MOUSEUP: END MOVE/SIZE, SHIFT-CLICK=SEND TO BACK
	dragstop: function(ev){ var ev=ev||window.event; var cmm=config.macros.moveablePanel;
		var newX=findMouseX(ev); var newY=findMouseY(ev);
		if (this.releaseCapture) this.releaseCapture(); // IE
		if (this.releaseEvents) this.releaseEvents(Event.MouseMove|Event.MouseUp); // moz
		this.onmousemove=this.saved_mousemove?this.saved_mousemove:null;
		this.onmouseup=this.saved_mouseup?this.saved_mouseup:null;
		this.onkeydown=this.saved_keydown?this.saved_keydown:null;
		var d=this.data; var p=d.panel;
		if (ev.shiftKey && d.x==newX && d.y==newY && cmm.isEdge(p,ev))
			cmm.sendPanelToBack(p); // SHIFT-CLICK *EDGE*
		cmm.togglePanelButtons(p,true);
		cmm.quiet++; if (cmm.manager) cmm.manager.trackMap(p); cmm.quiet--;
		cmm.clearGhost(); // allow document to adjust extents (if needed)
		cmm.showPanelStatus(p,false); cmm.timedMessage(cmm.formatPanelStatus(p),cmm.msgDuration);
		return cmm.processed(ev);
	},
//}}}
// // CSS definitions
//{{{
	css: '/*{{{*/\n'
		+'.moveablePanelMenu\n'
			+'\t{ display:none; position:absolute; right:.5em; top:-1em; }\n'
		+'.undocked .selected.moveablePanelMenu\n'
			+'\t{ display:inline; }\n'
		+'.floatingPanel .selected .moveablePanelMenu\n'
			+'\t{ display:inline; }\n'
		+'.hover\n'
			+'\t{ position:fixed !important; }\n'
		+'.folded\n'
			+'\t{ height:1.5em !important; overflow:hidden !important; }\n'
		+'.tiddler .folded\n'
			+'\t{ height:2em !important; }\n'
		+'.folded  .moveablePanelMenu\n'
			+'\t{ top:.5em; }	/* buttons fit in folded panel */\n'
		+'.tiddler .moveablePanelMenu\n'
			+'\t{ top:.2em; }	/* buttons fit in tiddler title */\n'
		+'.undocked .toolbar\n'
			+'\t{ padding-right:7.5em; }	/* make room for buttons next to toolbar */\n'
		+'.floatingPanel .moveablePanelMenu\n'
			+'\t{ right:1em;top:1em; } /* buttons fit in floating panel */\n'
		+'.moveablePanelButton\n {'
			+'\tbackground:#ccc !important; color:#000 !important;\n'
			+'\tborder:1px solid #666; padding:0 .25em; margin:0px 1px;\n'
			+'}\n'
		+'.moveablePanelButton:hover\n'
			+'\t{ background:#fff !important; color:#000 !important; }\n'
		+'.popup\n'
			+'\t{ z-index:9999999 !important; } /* popups MUST always be on top!  */\n'
		+'.moveablePanelGhost\n'
			+'\t{ position:absolute; border:1px dotted #999; }\n'
		+'/*}}}*/'
});
//}}}
// // load time initialization
//{{{
// defaults for options
if (config.options.txtMoveablePanelMapName===undefined)
	config.options.txtMoveablePanelMapName='DefaultMap';
if (config.options.chkMoveablePanelShowStatus===undefined)
	config.options.chkMoveablePanelShowStatus=true;
if (config.options.chkMoveablePanelShowManager===undefined)
	config.options.chkMoveablePanelShowManager=true;

// set up shadow stylesheet, then load styles (might be customized)
config.shadowTiddlers.MoveablePanelStyles=config.macros.moveablePanel.css;
var css=store.getRecursiveTiddlerText('MoveablePanelStyles',config.macros.moveablePanel.css,10);
setStylesheet(css,'moveablePanelStyles');
//}}}
// // hijacks
//{{{
// adjust popup placement to account for the current horizontal scrollbar
// offset (if any), so that popups will appear in the correct location, even
// when their 'root' element is scrolled far from the page origin.
var fn=Popup.place; fn=fn.toString(); if (fn.indexOf('findScrollX')==-1) { // only once
	fn=fn.replace(/winWidth\s*-\s*scrollWidth\s*-\s*1/,
		'findScrollX() + winWidth - scrollWidth - 1');
	fn=fn.replace(/winWidth\s*-\s*popupWidth\s*-\s*scrollWidth\s*-\s*1/,
		'findScrollX() + winWidth - popupWidth - scrollWidth - 1');
	eval('Popup.place='+fn);
}
//}}}
//{{{
// window.scrollTo() is used throughout the core (and plugins) to scroll to a *vertical*
// position as computed by ensureVisible(), in order to bring a tiddler into view.
// Unfortunately, the *horizontal* scroll position is almost always hard-coded to 0
// (i.e. a return to the left edge of the page).  Normally, this is not a problem,
// since a page is rarely scrolled horizontally.  However, when there are moveable
// panels, they can appear far from the left edge, so always scrolling to the left
// edge is very disruptive.  In addition, unwanted scrolling can occur as a side
// effect when displaying or refreshing a tiddler (e.g., switching between
// view/edit templates).  These hijacks adds control flags ('noScrollX' and 'noscrollY')
// that can be used to temporarily disable scrolling within the document.
if (window.scrollTo_moveablePanel==undefined) { // only once
window.scrollTo_moveablePanel=window.scrollTo;
	window.scrollTo=function(x,y) {
		var cmm=config.macros.moveablePanel;
		if (cmm.noScrollX&&cmm.noScrollY) return;
		x=cmm.noScrollX?findScrollX():x;
		y=cmm.noScrollY?findScrollY():y;
		window.scrollTo_moveablePanel(x,y);
	}
}
// ensureVisible() is used to calculate the y-offset of a tiddler, just before scrolling
// This tweak sets up an ASYNC timer to invoke the 'bring to front/scroll into view'
// function for 'tiddler' and 'popup' classes.  This allows the function
// to be triggered *after* core scrolling occurs, so the fixups are not
// stomped on by the core's normal scroll handling
if (window.ensureVisible_moveablePanel==undefined) { // only once
	window.ensureVisible_moveablePanel=window.ensureVisible;
	window.ensureVisible=function(e) {
		var ny=ensureVisible_moveablePanel.apply(this,arguments); // get core value

		// fixup height to account for horizontal scrollbar (if present)
		var atBottom=findPosY(e)+e.offsetHeight>=findScrollY()+findWindowHeight();
		var hasHScroll=document.documentElement.scrollWidth>findWindowWidth();
		var hScrollSize=findWindowWidth()-document.body.offsetWidth;
		if (atBottom && hasHScroll) ny+=hScrollSize;

		// defer scrolling for tiddlers and popups (except during startup)
		if (!startingUp && (hasClass(e,'tiddler')||hasClass(e,'popup'))) {
			var cmm=config.macros.moveablePanel;
			cmm.noScrollX++; if (hasClass(e,'tiddler')) cmm.noScrollY++;
			var delay=config.options.chkAnimate?config.animDuration+50:50;
			cmm.ensurePanelVisible(e,delay); // ASYNC SCROLL
		}
		return ny;
	}
}

// story.refreshTiddler()
if (Story.prototype.refreshTiddler_moveablePanel==undefined) { // only once
	Story.prototype.refreshTiddler_moveablePanel=Story.prototype.refreshTiddler;
	Story.prototype.refreshTiddler=function() {
		var cmm=config.macros.moveablePanel;
		cmm.noScrollX++; cmm.noScrollY++; // DON'T SCROLL AT ALL
		var r=this.refreshTiddler_moveablePanel.apply(this,arguments);
		cmm.noScrollX--; cmm.noScrollY--;
		return r;

	}
}
// story.displayTiddler()
if (Story.prototype.displayTiddler_moveablePanel==undefined) { // only once
	Story.prototype.displayTiddler_moveablePanel=Story.prototype.displayTiddler;
	Story.prototype.displayTiddler=function(srcElement,tiddler) {
		var cmm=config.macros.moveablePanel;
//WFFL		cmm.noScrollX++; cmm.noScrollY++;
		var r=this.displayTiddler_moveablePanel.apply(this,arguments);
		var title=(tiddler instanceof Tiddler)?tiddler.title:tiddler;
		var panel=cmm.findPanel(title); // if moveable... unfold panel (but not during startup)
		if (panel&&hasClass(panel,'folded')&&!startingUp) cmm.foldPanel(panel);
		var delay=config.options.chkAnimate?config.animDuration+50:50;
		cmm.ensurePanelVisible(this.getTiddler(title),delay); // ASYNC SCROLL
		return r;

	}
}
//}}}
//{{{
// Zoomer() displays an animated bounding box when showing a tiddler.  But this box 'zooms' to the tiddler's 'anchor point', not the current panel position, which can cause a 'scroll blink'.  This hijack redirects the zoomer's target directly to the undocked panel (if any)
if (window.Zoomer_moveablePanel==undefined) { // only once
	window.Zoomer_moveablePanel=window.Zoomer;
	window.Zoomer=function(text,startElement,targetElement,unused) {
		if (hasClass(targetElement,'tiddler')) {
			var tid=targetElement.getAttribute('tiddler');
			arguments[2]=config.macros.moveablePanel.findPanel(tid)||targetElement;			
		}
		return window.Zoomer_moveablePanel.apply(this,arguments);
	}
}
//}}}
/***
|Name|NestedSlidersPlugin|
|Source|http://www.TiddlyTools.com/#NestedSlidersPlugin|
|Documentation|http://www.TiddlyTools.com/#NestedSlidersPluginInfo|
|Version|2.4.9|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|show content in nest-able sliding/floating panels, without creating separate tiddlers for each panel's content|
!!!!!Documentation
>see [[NestedSlidersPluginInfo]]
!!!!!Configuration
<<<
<<option chkFloatingSlidersAnimate>> allow floating sliders to animate when opening/closing
>Note: This setting can cause 'clipping' problems in some versions of InternetExplorer.
>In addition, for floating slider animation to occur you must also allow animation in general (see [[AdvancedOptions]]).
<<<
!!!!!Revisions
<<<
2008.11.15 - 2.4.9 in adjustNestedSlider(), don't make adjustments if panel is marked as 'undocked' (CSS class).  In onClickNestedSlider(), SHIFT-CLICK docks panel (see [[MoveablePanelPlugin]])
|please see [[NestedSlidersPluginInfo]] for additional revision details|
2005.11.03 - 1.0.0 initial public release.  Thanks to RodneyGomes, GeoffSlocock, and PaulPetterson for suggestions and experiments.
<<<
!!!!!Code
***/
//{{{
version.extensions.NestedSlidersPlugin= {major: 2, minor: 4, revision: 9, date: new Date(2008,11,15)};

// options for deferred rendering of sliders that are not initially displayed
if (config.options.chkFloatingSlidersAnimate===undefined)
	config.options.chkFloatingSlidersAnimate=false; // avoid clipping problems in IE

// default styles for 'floating' class
setStylesheet(".floatingPanel { position:absolute; z-index:10; padding:0.5em; margin:0em; \
	background-color:#eee; color:#000; border:1px solid #000; text-align:left; }","floatingPanelStylesheet");

// if removeCookie() function is not defined by TW core, define it here.
if (window.removeCookie===undefined) {
	window.removeCookie=function(name) {
		document.cookie = name+'=; expires=Thu, 01-Jan-1970 00:00:01 UTC; path=/;'; 
	}
}

config.formatters.push( {
	name: "nestedSliders",
	match: "\\n?\\+{3}",
	terminator: "\\s*\\={3}\\n?",
	lookahead: "\\n?\\+{3}(\\+)?(\\([^\\)]*\\))?(\\!*)?(\\^(?:[^\\^\\*\\@\\[\\>]*\\^)?)?(\\*)?(\\@)?(?:\\{\\{([\\w]+[\\s\\w]*)\\{)?(\\[[^\\]]*\\])?(\\[[^\\]]*\\])?(?:\\}{3})?(\\#[^:]*\\:)?(\\>)?(\\.\\.\\.)?\\s*",
	handler: function(w)
		{
			lookaheadRegExp = new RegExp(this.lookahead,"mg");
			lookaheadRegExp.lastIndex = w.matchStart;
			var lookaheadMatch = lookaheadRegExp.exec(w.source)
			if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
			{
				var defopen=lookaheadMatch[1];
				var cookiename=lookaheadMatch[2];
				var header=lookaheadMatch[3];
				var panelwidth=lookaheadMatch[4];
				var transient=lookaheadMatch[5];
				var hover=lookaheadMatch[6];
				var buttonClass=lookaheadMatch[7];
				var label=lookaheadMatch[8];
				var openlabel=lookaheadMatch[9];
				var panelID=lookaheadMatch[10];
				var blockquote=lookaheadMatch[11];
				var deferred=lookaheadMatch[12];

				// location for rendering button and panel
				var place=w.output;

				// default to closed, no cookie, no accesskey, no alternate text/tip
				var show="none"; var cookie=""; var key="";
				var closedtext=">"; var closedtip="";
				var openedtext="<"; var openedtip="";

				// extra "+", default to open
				if (defopen) show="block";

				// cookie, use saved open/closed state
				if (cookiename) {
					cookie=cookiename.trim().slice(1,-1);
					cookie="chkSlider"+cookie;
					if (config.options[cookie]==undefined)
						{ config.options[cookie] = (show=="block") }
					show=config.options[cookie]?"block":"none";
				}

				// parse label/tooltip/accesskey: [label=X|tooltip]
				if (label) {
					var parts=label.trim().slice(1,-1).split("|");
					closedtext=parts.shift();
					if (closedtext.substr(closedtext.length-2,1)=="=")	
						{ key=closedtext.substr(closedtext.length-1,1); closedtext=closedtext.slice(0,-2); }
					openedtext=closedtext;
					if (parts.length) closedtip=openedtip=parts.join("|");
					else { closedtip="show "+closedtext; openedtip="hide "+closedtext; }
				}

				// parse alternate label/tooltip: [label|tooltip]
				if (openlabel) {
					var parts=openlabel.trim().slice(1,-1).split("|");
					openedtext=parts.shift();
					if (parts.length) openedtip=parts.join("|");
					else openedtip="hide "+openedtext;
				}

				var title=show=='block'?openedtext:closedtext;
				var tooltip=show=='block'?openedtip:closedtip;

				// create the button
				if (header) { // use "Hn" header format instead of button/link
					var lvl=(header.length>5)?5:header.length;
					var btn = createTiddlyElement(createTiddlyElement(place,"h"+lvl,null,null,null),"a",null,buttonClass,title);
					btn.onclick=onClickNestedSlider;
					btn.setAttribute("href","javascript:;");
					btn.setAttribute("title",tooltip);
				}
				else
					var btn = createTiddlyButton(place,title,tooltip,onClickNestedSlider,buttonClass);
				btn.innerHTML=title; // enables use of HTML entities in label

				// set extra button attributes
				btn.setAttribute("closedtext",closedtext);
				btn.setAttribute("closedtip",closedtip);
				btn.setAttribute("openedtext",openedtext);
				btn.setAttribute("openedtip",openedtip);
				btn.sliderCookie = cookie; // save the cookiename (if any) in the button object
				btn.defOpen=defopen!=null; // save default open/closed state (boolean)
				btn.keyparam=key; // save the access key letter ("" if none)
				if (key.length) {
					btn.setAttribute("accessKey",key); // init access key
					btn.onfocus=function(){this.setAttribute("accessKey",this.keyparam);}; // **reclaim** access key on focus
				}
				btn.setAttribute("hover",hover?"true":"false");
				btn.onmouseover=function(ev) {
					// optional 'open on hover' handling
					if (this.getAttribute("hover")=="true" && this.sliderPanel.style.display=='none') {
						document.onclick.call(document,ev); // close transients
						onClickNestedSlider(ev); // open this slider
					}
					// mouseover on button aligns floater position with button
					if (window.adjustSliderPos) window.adjustSliderPos(this.parentNode,this,this.sliderPanel);
				}

				// create slider panel
				var panelClass=panelwidth?"floatingPanel":"sliderPanel";
				if (panelID) panelID=panelID.slice(1,-1); // trim off delimiters
				var panel=createTiddlyElement(place,"div",panelID,panelClass,null);
				panel.button = btn; // so the slider panel know which button it belongs to
				btn.sliderPanel=panel; // so the button knows which slider panel it belongs to
				panel.defaultPanelWidth=(panelwidth && panelwidth.length>2)?panelwidth.slice(1,-1):"";
				panel.setAttribute("transient",transient=="*"?"true":"false");
				panel.style.display = show;
				panel.style.width=panel.defaultPanelWidth;
				panel.onmouseover=function(event) // mouseover on panel aligns floater position with button
					{ if (window.adjustSliderPos) window.adjustSliderPos(this.parentNode,this.button,this); }

				// render slider (or defer until shown) 
				w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
				if ((show=="block")||!deferred) {
					// render now if panel is supposed to be shown or NOT deferred rendering
					w.subWikify(blockquote?createTiddlyElement(panel,"blockquote"):panel,this.terminator);
					// align floater position with button
					if (window.adjustSliderPos) window.adjustSliderPos(place,btn,panel);
				}
				else {
					var src = w.source.substr(w.nextMatch);
					var endpos=findMatchingDelimiter(src,"+++","===");
					panel.setAttribute("raw",src.substr(0,endpos));
					panel.setAttribute("blockquote",blockquote?"true":"false");
					panel.setAttribute("rendered","false");
					w.nextMatch += endpos+3;
					if (w.source.substr(w.nextMatch,1)=="\n") w.nextMatch++;
				}
			}
		}
	}
)

function findMatchingDelimiter(src,starttext,endtext) {
	var startpos = 0;
	var endpos = src.indexOf(endtext);
	// check for nested delimiters
	while (src.substring(startpos,endpos-1).indexOf(starttext)!=-1) {
		// count number of nested 'starts'
		var startcount=0;
		var temp = src.substring(startpos,endpos-1);
		var pos=temp.indexOf(starttext);
		while (pos!=-1)  { startcount++; pos=temp.indexOf(starttext,pos+starttext.length); }
		// set up to check for additional 'starts' after adjusting endpos
		startpos=endpos+endtext.length;
		// find endpos for corresponding number of matching 'ends'
		while (startcount && endpos!=-1) {
			endpos = src.indexOf(endtext,endpos+endtext.length);
			startcount--;
		}
	}
	return (endpos==-1)?src.length:endpos;
}
//}}}
//{{{
window.onClickNestedSlider=function(e)
{
	if (!e) var e = window.event;
	var theTarget = resolveTarget(e);
	while (theTarget && theTarget.sliderPanel==undefined) theTarget=theTarget.parentNode;
	if (!theTarget) return false;
	var theSlider = theTarget.sliderPanel;
	var isOpen = theSlider.style.display!="none";

	// if SHIFT-CLICK, dock panel first (see [[MoveablePanelPlugin]])
	if (e.shiftKey && config.macros.moveablePanel) config.macros.moveablePanel.dock(theSlider,e);

	// toggle label
	theTarget.innerHTML=isOpen?theTarget.getAttribute("closedText"):theTarget.getAttribute("openedText");
	// toggle tooltip
	theTarget.setAttribute("title",isOpen?theTarget.getAttribute("closedTip"):theTarget.getAttribute("openedTip"));

	// deferred rendering (if needed)
	if (theSlider.getAttribute("rendered")=="false") {
		var place=theSlider;
		if (theSlider.getAttribute("blockquote")=="true")
			place=createTiddlyElement(place,"blockquote");
		wikify(theSlider.getAttribute("raw"),place);
		theSlider.setAttribute("rendered","true");
	}

	// show/hide the slider
	if(config.options.chkAnimate && (!hasClass(theSlider,'floatingPanel') || config.options.chkFloatingSlidersAnimate))
		anim.startAnimating(new Slider(theSlider,!isOpen,e.shiftKey || e.altKey,"none"));
	else
		theSlider.style.display = isOpen ? "none" : "block";

	// reset to default width (might have been changed via plugin code)
	theSlider.style.width=theSlider.defaultPanelWidth;

	// align floater panel position with target button
	if (!isOpen && window.adjustSliderPos) window.adjustSliderPos(theSlider.parentNode,theTarget,theSlider);

	// if showing panel, set focus to first 'focus-able' element in panel
	if (theSlider.style.display!="none") {
		var ctrls=theSlider.getElementsByTagName("*");
		for (var c=0; c<ctrls.length; c++) {
			var t=ctrls[c].tagName.toLowerCase();
			if ((t=="input" && ctrls[c].type!="hidden") || t=="textarea" || t=="select")
				{ try{ ctrls[c].focus(); } catch(err){;} break; }
		}
	}
	var cookie=theTarget.sliderCookie;
	if (cookie && cookie.length) {
		config.options[cookie]=!isOpen;
		if (config.options[cookie]!=theTarget.defOpen) window.saveOptionCookie(cookie);
		else window.removeCookie(cookie); // remove cookie if slider is in default display state
	}

	// prevent SHIFT-CLICK from being processed by browser (opens blank window... yuck!)
	// prevent clicks *within* a slider button from being processed by browser
	// but allow plain click to bubble up to page background (to close transients, if any)
	if (e.shiftKey || theTarget!=resolveTarget(e))
		{ e.cancelBubble=true; if (e.stopPropagation) e.stopPropagation(); }
	Popup.remove(); // close open popup (if any)
	return false;
}
//}}}
//{{{
// click in document background closes transient panels 
document.nestedSliders_savedOnClick=document.onclick;
document.onclick=function(ev) { if (!ev) var ev=window.event; var target=resolveTarget(ev);

	if (document.nestedSliders_savedOnClick)
		var retval=document.nestedSliders_savedOnClick.apply(this,arguments);
	// if click was inside a popup... leave transient panels alone
	var p=target; while (p) if (hasClass(p,"popup")) break; else p=p.parentNode;
	if (p) return retval;
	// if click was inside transient panel (or something contained by a transient panel), leave it alone
	var p=target; while (p) {
		if ((hasClass(p,"floatingPanel")||hasClass(p,"sliderPanel"))&&p.getAttribute("transient")=="true") break;
		p=p.parentNode;
	}
	if (p) return retval;
	// otherwise, find and close all transient panels...
	var all=document.all?document.all:document.getElementsByTagName("DIV");
	for (var i=0; i<all.length; i++) {
		 // if it is not a transient panel, or the click was on the button that opened this panel, don't close it.
		if (all[i].getAttribute("transient")!="true" || all[i].button==target) continue;
		// otherwise, if the panel is currently visible, close it by clicking it's button
		if (all[i].style.display!="none") window.onClickNestedSlider({target:all[i].button})
		if (!hasClass(all[i],"floatingPanel")&&!hasClass(all[i],"sliderPanel")) all[i].style.display="none";
	}
	return retval;
};
//}}}
//{{{
// adjust floating panel position based on button position
if (window.adjustSliderPos==undefined) window.adjustSliderPos=function(place,btn,panel) {
	if (hasClass(panel,"floatingPanel") && !hasClass(panel,"undocked")) {
		// see [[MoveablePanelPlugin]] for use of 'undocked'
		var rightEdge=document.body.offsetWidth-1;
		var panelWidth=panel.offsetWidth;
		var left=0;
		var top=btn.offsetHeight; 
		if (place.style.position=="relative" && findPosX(btn)+panelWidth>rightEdge) {
			left-=findPosX(btn)+panelWidth-rightEdge; // shift panel relative to button
			if (findPosX(btn)+left<0) left=-findPosX(btn); // stay within left edge
		}
		if (place.style.position!="relative") {
			var left=findPosX(btn);
			var top=findPosY(btn)+btn.offsetHeight;
			var p=place; while (p && !hasClass(p,'floatingPanel')) p=p.parentNode;
			if (p) { left-=findPosX(p); top-=findPosY(p); }
			if (left+panelWidth>rightEdge) left=rightEdge-panelWidth;
			if (left<0) left=0;
		}
		panel.style.left=left+"px"; panel.style.top=top+"px";
	}
}
//}}}
//{{{
// TW2.1 and earlier:
// hijack Slider stop handler so overflow is visible after animation has completed
Slider.prototype.coreStop = Slider.prototype.stop;
Slider.prototype.stop = function()
	{ this.coreStop.apply(this,arguments); this.element.style.overflow = "visible"; }

// TW2.2+
// hijack Morpher stop handler so sliderPanel/floatingPanel overflow is visible after animation has completed
if (version.major+.1*version.minor+.01*version.revision>=2.2) {
	Morpher.prototype.coreStop = Morpher.prototype.stop;
	Morpher.prototype.stop = function() {
		this.coreStop.apply(this,arguments);
		var e=this.element;
		if (hasClass(e,"sliderPanel")||hasClass(e,"floatingPanel")) {
			// adjust panel overflow and position after animation
			e.style.overflow = "visible";
			if (window.adjustSliderPos) window.adjustSliderPos(e.parentNode,e.button,e);
		}
	};
}
//}}}
/***
!NestedSlidersPlugin 
***/
/*{{{*/
.floatingPanel
	{ z-index:700; padding:1em; margin:0em; border:1px solid; -moz-border-radius:1em;-webkit-border-radius:1em; font-size:8pt; text-align:left; }
.floatingPanel hr
	{ margin:2px 0 1px 0; padding:0; }
#sidebarOptions .sliderPanel
	{ margin:0; padding:0; font-size:1em; background:transparent; }
#sidebarOptions .sliderPanel a
	{ font-weight:normal; }
#sidebarOptions .sliderPanel blockquote
	{ margin:0;padding:0;margin-left:1em; border-left:1px dotted; padding-left:1em }

.selected .floatingPanel .button,
.selected .floatingPanel a:link,
.selected .floatingPanel a:hover,
.selected .floatingPanel a:visited,
.floatingPanel .button,
.floatingPanel a:link,
.floatingPanel a:hover,
.floatingPanel a:visited
	{ color:[[ColorPalette::PrimaryDark]] !important; }

/*}}}*/
/***
|''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"
	});
*/
//}}}
/%
!info
|Name|RefreshTiddler|
|Source|http://www.TiddlyTools.com/#RefreshTiddler|
|Version|2.0.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|transclusion|
|Description|create a link to force an immediate refresh of the current tiddler|
Usage
<<<
{{{
<<tiddler RefreshTiddler>>
<<tiddler RefreshTiddler with: label tip>>
}}}
<<<
Example
<<<
{{{<<tiddler RefreshTiddler with: "click me">>}}}
<<tiddler RefreshTiddler##show with: "click me">>
content displayed at <<today 0hh:0mm:0ss>>
<<<
!end
!show
<html><nowiki><a href="javascript:;" title="$2"
onclick="
	var here=story.findContainingTiddler(this);
	if (here) story.refreshTiddler(here.getAttribute('tiddler'),null,true);
	return false;
">$1</a></html>
!end
%/<<tiddler {{var src='RefreshTiddler'; src+(tiddler&&tiddler.title==src?'##info':'##show')}}
	with:	{{'$1'!='$'+'1'?'$1':'refresh'}}
		{{'$2'!='$'+'2'?'$2':'redisplay current tiddler content'}}
>>
&nbsp;<<tiddler RefreshTiddler>>
/***
|Name|SectionLinksPlugin|
|Source|http://www.TiddlyTools.com/#SectionLinksPlugin|
|Documentation|http://www.TiddlyTools.com/#SectionLinksPlugin|
|Version|1.4.2|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|allow tiddler sections in TiddlyLinks to be used as anchor points|
This plugin enhances tiddler links so that they can include section references that ''auto-scroll to the indicated section heading'' within a tiddler (i.e., similar to the 'anchor' behavior provided in HTML by {{{<a name="foo">}}} and {{{<a href="#foo">...</a>}}}).  The {{{<<tiddler>>}}} macro syntax has also be extended to allow section references without a tiddler name, so that transclusion of //hidden sections from the same tiddler// can be easily accomplished.  The plugin also adds a new macro, <<sectionTOC>> which can auto-generate and embed a 'Table of Contents' outline view into a tiddler to enable quick navigation to sections within that tiddler.
!!!Usage
<<<
!!!!~TiddlyLink syntax
You can link to a section of a tiddler by adding the "##sectionname" syntax to the tiddlername:
{{{
[[SomeTiddler##SomeSection]]
}}}
When clicked, the tiddler is displayed and the specified section heading is automatically scrolled into view. If the tiddler title is omitted or the 'here' keyword is used, e.g.,
{{{
[[##SomeSection]] or [[here##SomeSection]]>>
}}}
then the current containing tiddler is implied by default.
!!!!HTML anchor syntax
You can use HTML syntax to create a scrollable 'anchor' location within a tiddler without use of the standard TW section heading syntax:
{{{
<html><a name="sectionname" /></html>
}}}
You can then link to that section using the enhanced TiddlyLink syntax as above.
!!!!{{{<<tiddler>>}}} macro 
The {{{<<tiddler>>}}} syntax has been extended so that when the tiddler title is omitted or the 'here' keyword is used, e.g.,
{{{
<<tiddler ##SomeSection>> or <<tiddler here##SomeSection>>
}}}
then the current containing tiddler is implied by default.
!!!!"""<<sectionTOC>>""" macro
This macro generates a 'Table of Contents' outline-style bullet list with links to all sections within the current tiddler.  Simply place the following macro at the //end of the tiddler content// (i.e., following all section headings).  Important note: //''The {{{<<sectionTOC>>}}} macro must occur at the end of the tiddler in order to locate the rendered section headings that precede it.''//
{{{
<<sectionTOC>> or <<sectionTOC className>>
}}}
To position the macro's //output// within the tiddler, you must create a special 'target element' that uses a specific classname (default='sectionTOC'), like this:
{{{
{{sectionTOC{}}}
}}}
When the {{{<<sectionTOC>>}}} macro is rendered, it will find the matching 'sectionTOC'-classed element and writes it's output there.  You can also add the macro and/or target elements directly to the [[ViewTemplate]] definition, so that every tiddler can automatically display the table of contents:
{{{
<span class='sectionTOC'></span> <!-- target element -->
...
<span macro='sectionTOC'></span> <!-- must be at end of tiddler -->
}}}
<<<
!!!Configuration
<<<
You can change the {{{<<SectionTOC>>}}} output link format by adding the following statement to a tiddler tagged with <<tag systemConfig>>
{{{
config.macros.sectionTOC.linkFormat='[[%0|##%0]]';
}}}
The default value (shown above) produces a link to each section within the tiddler, using "%0" to insert the section name into the link.  You can add extra formatting to generate additional output to suit your purposes.  For example, if you have EditSectionPlugin installed, you could include a link that invokes that plugin's popup editor directly from each item in the TOC display, like this:
{{{
config.macros.sectionTOC.linkFormat='[[%0|##%0]] <<editSection [[##%0]] [[(edit)]]>>';
}}}
<<<
!!!Examples
<<<
links to sections defined by ''TW heading syntax'' (e.g, {{{!!!sectionname}}}):{{indent{
[[SectionLinksPlugin##onClickTiddlerLink]]
[[##onClickTiddlerLink]] //(current tiddler implied)//}}}
links to anchors defined by ''HTML syntax'' (e.g., {{{<html><a href="anchorname"></html>}}}):{{indent{
[[SectionLinksPlugin##sampleanchorlink]]
[[##sampleanchorlink]] //(current tiddler implied)//}}}
<<<
!!!Revisions
<<<
2011.12.21 1.4.2 refactor sectionTOCformat to permit customization
2011.02.08 1.4.1 in isExternalLink() hijack, strip section references before testing for external link
2010.08.09 1.4.0 in scrollToSection(), added support for using HTML <a name="..."> anchor elements
2009.08.21 1.3.4 added handling to ignore leading/trailing whitespace in section references
2009.08.21 1.3.3 in createTiddlyLink(), add tiddlyLinkNonExistingSection class if matching section is not found
2009.08.14 1.3.2 in createTiddlyLink(), don't override core value for ~TiddlyLink attribute
2009.08.02 1.3.1 in sectionTOC.handler(), trim leading/trailing whitespace from generated section links
2009.08.01 1.3.0 in scrollToSection(), apply 3-tier section matching (exact, startsWith, contains)
2009.07.06 1.2.2 fixed displayTiddler() hijack
2009.07.03 1.2.1 in {{{<<sectionTOC>>}}}, suppress output if target is not found
2009.06.02 1.2.0 added support for 'here' keyword in {{{[[here##section]]}}} links and {{{<<tiddler here##section>>}}} macro
2009.04.09 1.1.1 in sectionTOC macro, make target visible when TOC is rendered.
2009.01.18 1.1.0 added {{{<<sectionTOC>>}}} macro to generate numbered-bullet links to sections of current tiddler
2009.01.06 1.0.0 converted to stand-alone plugin
2008.10.14 0.0.0 initial release (as [[CoreTweaks]] #784 - http://trac.tiddlywiki.org/ticket/784)
<<<
!!!Code
***/
//{{{
version.extensions.SectionLinksPlugin= {major: 1, minor: 4, revision: 2, date: new Date(2011,12,21)};

Story.prototype.scrollToSection = function(title,section) {
	if (!title||!section) return; var t=this.getTiddler(title); if (!t) return null;
	var elems=t.getElementsByTagName('*');
	var heads=[]; var anchors=[];
	for (var i=0; i<elems.length; i++)
		if (['H1','H2','H3','H4','H5'].contains(elems[i].nodeName)) heads.push(elems[i]);
	for (var i=0; i<elems.length; i++)
		if (elems[i].nodeName=='A' && (elems[i].getAttribute('name')||'').length) anchors.push(elems[i]);
	var found=null;
	for (var i=0; i<heads.length; i++)
		if (getPlainText(heads[i]).trim()==section) { found=heads[i]; break; }
	if (!found) for (var i=0; i<heads.length; i++)
		if (getPlainText(heads[i]).trim().startsWith(section)) { found=heads[i]; break; }
	if (!found) for (var i=0; i<heads.length; i++)
		if (getPlainText(heads[i]).trim().indexOf(section)!=-1) { found=heads[i]; break; }
	if (!found) for (var i=0; i<anchors.length; i++)
		if (anchors[i].getAttribute('name')==section) { found=anchors[i]; break; }
	if (!found) for (var i=0; i<anchors.length; i++)
		if (anchors[i].getAttribute('name').startsWith(section)) { found=anchors[i]; break; }
	if (!found) for (var i=0; i<anchors.length; i++)
		if (anchors[i].getAttribute('name').indexOf(section)!=-1) { found=anchors[i]; break; }
	if (found) {
		// if section heading is collapsed, click to expand it - see [[FoldHeadingsPlugin]]
		if (hasClass(found,'foldable') && found.nextSibling.style.display=='none') found.onclick();
		// scroll *after* tiddler animation
		var delay=config.options.chkAnimate?config.animDuration+100:0;
		setTimeout('window.scrollTo('+findPosX(found)+','+findPosY(found)+')',delay);
		return found;
	}
}
//}}}
/***
!!!!core hijacks
***/
/***
!!!!!createTiddlyLink
***/
//{{{
// [[tiddlername##section]] and [[##section]]
if (!window.createTiddlyLink_section)
	window.createTiddlyLink_section=window.createTiddlyLink;
window.createTiddlyLink=function(place,title) {
	var t=story.findContainingTiddler(place); var tid=t?t.getAttribute('tiddler'):'';
	var parts=title.split(config.textPrimitives.sectionSeparator);
	var title=parts[0]; var section=parts[1]; if (section) section=section.trim();
	if (!title.length || title.toLowerCase()=='here') title=tid;  // default=current tiddler
	arguments[1]=title;
	var btn=createTiddlyLink_section.apply(this,arguments);
	if (section) {
		btn.setAttribute('section',section);
		if (store.getTiddlerText(title+config.textPrimitives.sectionSeparator+section)===null)
			addClass(btn,'tiddlyLinkNonExistingSection');
	}
	return btn;
}
//}}}
/***
!!!!!onClickTiddlerLink
***/
//{{{
if (!window.onClickTiddlerLink_section)
	window.onClickTiddlerLink_section=window.onClickTiddlerLink;
window.onClickTiddlerLink=function(ev) {
	var e=ev||window.event;	var target=resolveTarget(e); var title=null;
	while (target!=null && title==null) {
		title=target.getAttribute('tiddlyLink');
		section=target.getAttribute('section');
		target=target.parentNode;
	} 
	var t=story.findContainingTiddler(target); var tid=t?t.getAttribute('tiddler'):'';
	if (title!=tid||!section) // avoid excess scrolling for intra-tiddler links
		onClickTiddlerLink_section.apply(this,arguments);
	story.scrollToSection(title,section);
	return false;
}
//}}}
/***
!!!!! displayTiddler
***/
//{{{
if (!Story.prototype.displayTiddler_section)
	Story.prototype.displayTiddler_section=Story.prototype.displayTiddler;
Story.prototype.displayTiddler = function(srcElement,tiddler)
{
	var title=(tiddler instanceof Tiddler)?tiddler.title:tiddler;
	var parts=title.split(config.textPrimitives.sectionSeparator);
	var title=parts[0]; var section=parts[1]; if (section) section=section.trim();
	if (!title.length || title.toLowerCase()=='here') {
		var t=story.findContainingTiddler(place);
		title=t?t.getAttribute('tiddler'):'';
	}
	arguments[1]=title;  // default=current tiddler
	this.displayTiddler_section.apply(this,arguments);
	story.scrollToSection(title,section);
}
//}}}
/***
<html><a name="sampleanchorlink" /></html>This is a sample ''anchor link'': {{{<html><a name="sampleanchorlink" /></html>}}}
!!!!!isExternalLink
***/
//{{{
if (!config.formatterHelpers.isExternalLink_section)
	config.formatterHelpers.isExternalLink_section=config.formatterHelpers.isExternalLink;
config.formatterHelpers.isExternalLink=function(link) {  // remove section references before testing
	var l=link.split(config.textPrimitives.sectionSeparator)[0];
	return config.formatterHelpers.isExternalLink_section(l);
}
//}}}
/***
!!!!!tiddler.handler
***/
//{{{
if (!config.macros.tiddler.handler_section)
	config.macros.tiddler.handler_section=config.macros.tiddler.handler;
config.macros.tiddler.handler=function(place,macroName,params,wikifier,paramString,tiddler)
{
	if (!params[0]) return;
	var sep=config.textPrimitives.sectionSeparator;
	var parts=params[0].split(sep); var tid=parts[0]; var sec=parts[1]; if (sec) sec=sec.trim();
	if ((tid.toLowerCase()=='here'||!tid.length) && sec) { // fixup for 'here##section' and '##section'
		var here=story.findContainingTiddler(place)
		var tid=here?here.getAttribute('tiddler'):tiddler?tiddler.title:'';
		arguments[2][0]=tid+sep+sec;
		arguments[4]=paramString.replace(new RegExp('(here)?'+sep+sec),tid+sep+sec);
	}
	config.macros.tiddler.handler_section.apply(this,arguments);
}
//}}}
/***
!!!!sectionTOC macro
***/
//{{{
config.macros.sectionTOC = {
	targetClass: 'sectionTOC',
	linkFormat: '[[%0|##%0]]',
	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		var out=[];
		var targetClass=params[0]||this.targetClass;
		var t=story.findContainingTiddler(place); if (!t) return;
		var elems=t.getElementsByTagName('*');
		var level=5; // topmost heading level
		for (var i=0; i<elems.length; i++) {
			var txt=getPlainText(elems[i]).trim();
			var link=this.linkFormat.format([txt]);
			switch(elems[i].nodeName) {
				case 'H1': out.push('#'+link);		level=1; break;
				case 'H2': out.push('##'+link);		level=level<2?level:2; break;
				case 'H3': out.push('###'+link);	level=level<3?level:3; break;
				case 'H4': out.push('####'+link);	level=level<4?level:4; break;
				case 'H5': out.push('#####'+link);	level=level<5?level:5; break;
				default: if (hasClass(elems[i],targetClass)) var target=elems[i];
			}
		}
		// trim excess bullet levels
		if (level>1) for (var i=0; i<out.length; i++) out[i]=out[i].substr(level-1);
		// show numbered list
		if (out.length && target) {
			if (target.style.display=='none') target.style.display='block';
			wikify(out.join('\n'),target);
		}
	}
}
//}}}
/***
!!!Invoke macro
{{{
<<sectionTOC>>
}}}
***/
// //<<sectionTOC>>
@@position:relative;+++^18em^*[toc]{{sectionTOC{<<moveablePanel name:{{tiddler?tiddler.title:""}} docked width:18em height:auto fold hover noclose>>
<<tiddler [[SliderToc##EditIndex]] with:{{tiddler.title}}>>}}}
<html><div style="float:right;cursor:pointer;font-size:11pt;"><a href="javascript:window.scrollTo(0,0)" title="jump to the top of the page"> &#8657;</a></div><br></html>===@@/%
!EditIndex
<<editSection [[$1##]] [[Index for $1]]>>
! %/
[[NestedSlidersPluginStyleSheet]]
|~ViewToolbar|closeTiddler closeOthers +editTiddler SliderToc > fields syncing permalink references jump < RefreshTiddlerToolbar|
|~EditToolbar|+saveTiddler -cancelTiddler deleteTiddler|
/***
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 = 'edittoc';

// 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/04/2012 12:02:28 | YourName | [[/|http://edittoc.tiddlyspot.com/]] | [[store.cgi|http://edittoc.tiddlyspot.com/store.cgi]] | . | [[index.html | http://edittoc.tiddlyspot.com/index.html]] | . |
| 14/04/2012 12:17:22 | YourName | [[/|http://edittoc.tiddlyspot.com/]] | [[store.cgi|http://edittoc.tiddlyspot.com/store.cgi]] | . | [[index.html | http://edittoc.tiddlyspot.com/index.html]] | . |
| 14/04/2012 13:09:53 | YourName | [[/|http://edittoc.tiddlyspot.com/]] | [[store.cgi|http://edittoc.tiddlyspot.com/store.cgi]] | . | [[index.html | http://edittoc.tiddlyspot.com/index.html]] | . |
| 14/04/2012 13:12:09 | YourName | [[/|http://edittoc.tiddlyspot.com/]] | [[store.cgi|http://edittoc.tiddlyspot.com/store.cgi]] | . | [[index.html | http://edittoc.tiddlyspot.com/index.html]] | . | ok |
| 14/04/2012 13:12:47 | YourName | [[/|http://edittoc.tiddlyspot.com/]] | [[store.cgi|http://edittoc.tiddlyspot.com/store.cgi]] | . | [[index.html | http://edittoc.tiddlyspot.com/index.html]] | . | ok |
| 14/04/2012 13:17:08 | YourName | [[/|http://edittoc.tiddlyspot.com/]] | [[store.cgi|http://edittoc.tiddlyspot.com/store.cgi]] | . | [[index.html | http://edittoc.tiddlyspot.com/index.html]] | . |
| 21/04/2012 09:15:08 | YourName | [[/|http://edittoc.tiddlyspot.com/]] | [[store.cgi|http://edittoc.tiddlyspot.com/store.cgi]] | . | [[index.html | http://edittoc.tiddlyspot.com/index.html]] | . |
| 21/04/2012 09:19:20 | YourName | [[/|http://edittoc.tiddlyspot.com/]] | [[store.cgi|http://edittoc.tiddlyspot.com/store.cgi]] | . | [[index.html | http://edittoc.tiddlyspot.com/index.html]] | . |
| 21/04/2012 09:48:26 | YourName | [[/|http://edittoc.tiddlyspot.com/]] | [[store.cgi|http://edittoc.tiddlyspot.com/store.cgi]] | . | [[index.html | http://edittoc.tiddlyspot.com/index.html]] | . |
| 21/04/2012 09:50:08 | YourName | [[/|http://edittoc.tiddlyspot.com/]] | [[store.cgi|http://edittoc.tiddlyspot.com/store.cgi]] | . | [[index.html | http://edittoc.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");


//}}}

<!--{{{-->
<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 macro="sectionTOC"></div>
<span macro="foldHeadings closed systemConfig fold"></span>
<div class='tagClear'></div>
<!--}}}-->
//{{{
config.macros.sectionTOC.linkFormat='[[%0|##%0]] <<editSection [[##%0]] [[✏]]>>';
//}}}