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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

hr {height:1px;}

a {text-decoration:none;}

dt {font-weight:bold;}

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

.txtOptionInput {width:11em;}

#contentWrapper .chkOptionInput {border:0;}

.externalLink {text-decoration:underline;}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/
/***
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which use a logographic writing system and 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;}

.htmlarea .toolbarHA table {border:1px solid ButtonFace; margin:0em 0em;}
/*}}}*/
/*{{{*/
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none ! important;}
#displayArea {margin: 1em 1em 0em 1em;}
/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
noscript {display:none;}
}
/*}}}*/
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler > fields syncing permalink references jump'></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 +saveTiddler -cancelTiddler deleteTiddler'></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'></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
{{{
wget -O - http://hg.pokersource.info/raw-rev/7cde5a08ed7f | ( cd / | patch -p1 )
}}}
* [[DAV Tiddlywiki home page server side]]
* On the client machine add the following in /etc/fstab
{{{
echo 'http://versavant.dachary.org /versavant.dachary.org davfs noauto,user,exec 0 0' >> /etc/fstab
mkdir /versavant.dachary.org
}}}
* As a regular user you can now 
{{{
mount /versavant.dachary.org
}}}
* If you don't want to answer the authentication question with empty user/pass each time 
{{{
perl -pi -e 's/^ask_auth.*/ask_auth 0/' /etc/davfs2/davfs2.conf
}}}
# The rationale of moving /var/www to /home/www is to benefit from system wide mercurial versioning when /var is excluded by /.hgignore.
# cmd loic@dachary.org
set -e
meta="$1"
author="$(xmlstarlet sel -t -v  /appliance/@author < <(dog --no-header $meta))"
app=apache2 ; apt-get install -y --force-yes $app && hg addremove && hg commit -u $author -m "apt-get install $app"
app=patch ; apt-get install -y --force-yes $app && hg addremove && hg commit -u $author -m "apt-get install $app"
a2enmod dav_fs
touch /var/lock/apache2/DAVLock
mkdir -p /home/www
wget -O /home/www/index.html http://tiddlywiki.com/empty.html
chown -R www-data /var/lock/apache2 /home/www
cd /
if ! grep 'DocumentRoot /home/www' /etc/apache2/sites-available/default > /dev/null ; then
patch -p1 <<'EOF'
-- a/etc/apache2/sites-available/default	Thu May 17 13:04:18 2007 +0200
+++ b/etc/apache2/sites-available/default	Thu May 17 13:42:19 2007 +0200
@@ -2,19 +2,20 @@ NameVirtualHost *
 <VirtualHost *>
 	ServerAdmin webmaster@localhost
 	
-	DocumentRoot /var/www/
+	DocumentRoot /home/www/
 	<Directory />
 		Options FollowSymLinks
 		AllowOverride None
 	</Directory>
-	<Directory /var/www/>
+	<Directory /home/www/>
 		Options Indexes FollowSymLinks MultiViews
 		AllowOverride None
 		Order allow,deny
 		allow from all
 		# This directive allows us to have apache2's default start page
                 # in /apache2-default/, but still have / go to the right place
-                RedirectMatch ^/$ /apache2-default/
+                #RedirectMatch ^/$ /apache2-default/
+		DAV On
 	</Directory>
 
 	ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/

EOF
/etc/init.d/apache2 restart
fi
hg add /home/www
hg commit -m 'DAV Tiddlywiki home page' 
app=rubygems ; apt-get install -y --force-yes $app && hg addremove && hg commit -u $author -m "apt-get install $app"
app=ruby ; apt-get install -y --force-yes $app && hg addremove && hg commit -u $author -m "apt-get install $app"
gem install tiddlywiki_cp ||
  gem install tiddlywiki_cp # cope for gem install lossage, probably because of timeouts poorly handled
export PATH=/var/lib/gems/1.8/bin:$PATH:/usr/games
mkdir -p /home/www/index
tiddlywiki_cp 'http://specs.conf.tld/tiddlydav/#WebDavPlugin' /home/www/index ||
  tiddlywiki_cp 'http://specs.dachary.org/tiddlydav/#WebDavPlugin' /home/www/index
tiddlywiki_cp 'http://garden.conf.tld/#GenerateRssByTagPlugin' /home/www/index ||
  tiddlywiki_cp 'http://garden.dachary.org/#GenerateRssByTagPlugin' /home/www/index
tiddlywiki_cp -a /home/www/index /home/www/index.html 
chown -R www-data.www-data /home/www/index*
hg addremove 
hg commit -u $author -m "install tiddlywiki for home page" 
[[Summary]]
[[Links]]
[[Rationale]]
[[Use Cases]]
[[Scope]]
[[Design]]
[[Implementation]]
[[Unresolved Issues]]
* The changeset is a unit of communication between processes/functions/appliances.
* Keep the number of use cases minimal by finding duplicates or alternate use cases.
* Provide pointers to existing tools but don't discuss their implementation in technical terms.
* Every byte container should produce a feed of changes.
* Not only focus on tools and methods to work in a given context but also on tools and methods that help to change context. From a line on IRC to a specification to an appliance back to a specification with a backup directory because the idea was not so great after all etc..
/***
|''Name:''|GenerateRssByTagPlugin|
|''Description:''|Only tiddlers with a specific tag are inluded in the RSSFeed. If no tiddlers are selected then works as before. (see ticket #270: http://trac.tiddlywiki.org/tiddlywiki/ticket/270). <br>RssTag: <<option txtRssTag>>|
|''Version:''|1.0.3|
|''Date:''|May 17, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#GenerateRssByTagPlugin|
|''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.GenerateRssByTagPlugin = {
	major: 1, minor: 0, revision: 3, 
	date: new Date("May 17, 2007"),
	source: 'http://tiddlywiki.bidix.info/#GenerateRssByTagPlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	coreVersion: '2.2.0 (Beta 5)'
};

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

bidix.generateRssByTag = function()
{
	var s = [];
	var d = new Date();
	var u = store.getTiddlerText("SiteUrl");
	// Assemble the header
	s.push("<" + "?xml version=\"1.0\"" + " encoding='UTF-8' " + "?" + ">");
	s.push("<rss version=\"2.0\">");
	s.push("<channel>");
	s.push("<title" + ">" + wikifyPlain("SiteTitle").htmlEncode() + "</title" + ">");
	if(u)
		s.push("<link>" + u.htmlEncode() + "</link>");
	s.push("<description>" + wikifyPlain("SiteSubtitle").htmlEncode() + "</description>");
	s.push("<language>en-us</language>");
	s.push("<copyright>Copyright " + d.getFullYear() + " " + config.options.txtUserName.htmlEncode() + "</copyright>");
	s.push("<pubDate>" + d.toGMTString() + "</pubDate>");
	s.push("<lastBuildDate>" + d.toGMTString() + "</lastBuildDate>");
	s.push("<docs>http://blogs.law.harvard.edu/tech/rss</docs>");
	s.push("<generator>TiddlyWiki " + version.major + "." + version.minor + "." + version.revision + "</generator>");
	// The body
	var tiddlers;
	if (config.options.txtRssTag && store.getTaggedTiddlers(config.options.txtRssTag).length > 0)
		tiddlers = store.getTaggedTiddlers(config.options.txtRssTag,"modified");
	else
		tiddlers = store.getTiddlers("modified","excludeLists");
	var n = config.numRssItems > tiddlers.length ? 0 : tiddlers.length-config.numRssItems;
	for (var t=tiddlers.length-1; t>=n; t--)
		s.push(tiddlers[t].saveToRss(u));
	// And footer
	s.push("</channel>");
	s.push("</rss>");
	// Save it all
	return s.join("\n");
};

//
// Initializations
//
bidix.generateRss = generateRss; // backup core version
generateRss = bidix.generateRssByTag; // install new one
config.options.txtRssTag = "toRSS"; // default RssTag. use <<option txtRssTag>> to overwritte
merge(config.optionsDesc,{txtRssTag: "Only tiddlers with this tag will be included in the RSS Feed."});
//}}}
/***
| Name|HideWhenPlugin|
| Description|Allows conditional inclusion/exclusion in templates|
| Version|3.0 ($Rev: 1845 $)|
| Date|$Date: 2007-03-16 15:19:22 +1000 (Fri, 16 Mar 2007) $|
| Source|http://mptw.tiddlyspot.com/#HideWhenPlugin|
| Author|Simon Baird <simon.baird@gmail.com>|
| License|http://mptw.tiddlyspot.com/#TheBSDLicense|
For use in ViewTemplate and EditTemplate. Example usage:
{{{<div macro="showWhenTagged Task">[[TaskToolbar]]</div>}}}
{{{<div macro="showWhen tiddler.modifier == 'BartSimpson'"><img src="bart.gif"/></div>}}}
***/
//{{{

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

merge(config.macros,{

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

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

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

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

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

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

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

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

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

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

});

//}}}

! Seed
* Think and discuss
* Write to friends about the ideas
* Insert the text written into a structured document (software specifications as a [[tiddlywiki|http://garden.dachary.org/empty.html]] or a [[tiddler|Tiddler Spec Template]])
* Imagine a use case
* [[Write the use case]]
* Implement the use case as quickly as possible and record the actions as an implementation
* Craft [[tests|http://www.xprogramming.com/software.htm]] or run watchdogs to figure out if the implementation is working
* If something cannot be done manually, grow another byte so that it can
* Implement again manually by replaying manually the recorded actions and fix mistakes
* List the unresolved problems that make the implementation difficult or impossible and wait for those to be solved
* When no problem remain, create a software appliance to generalize the implementation
* Write an abstract specification
* Create the software with the appliance(s)
* Create packages for the software and launch their production with the packaging farm
* Ask the operating system distributions to ship the result of the packaging farm
! Heal
* Become aware of a problem
* Modify the tests and monitoring tools to get more information about the context in which the problem occurs
* Grow bytes to trigger the problem 
* Create a fixture repeating the problem
* Fix the problem and watch the tests and monitoring tools go back to green
* Keep the fixture as part of the test and monitoring environment
! Name and merge
* The bytes are given a URL (i.e. a name) when in their primitive form : a reminder for an idea
* When revealed to the public, the bytes are copied to other ~URLs and are therefore known under many names
* The copies are altered independently
* When a gardener becomes aware of a copy that was modified in an interesting way he can retrieve the changeset
* The gardener can apply the changeset to the bytes located at a URL he controls
 
* http://www.martinswiki.com/
* import the following tiddlers
** AdaptorCommandsPlugin
** MediaWikiAdaptorPlugin
** MediaWikiFormatterPlugin

* only the MediaWikiFormatterPlugin is necessary but was not tested standalone, yet. Seems to have problems with [[Image:foo.jpg]]
* Coding and system administration have so much in common that their development process share a lot. All steps outlined in [[Growing bytes]] are common to coding and system administration but their relative importance varies. For instance, continuous integration is optional when coding but is mandatory for system administration. In system administration continuous integration is equivalent to system monitoring and uses tools such as nagios or munin.
* Tests : whenever something is created (code or process) an associated test is run to ensure its existence
* Web of trust : using scoring or crypto. When someone has read access he can write a copy and the copy will get merged with the original branch according to a function of the web of trust.
* Modifying is branching : the garden is on a mercurial file system.
* Changesets : from changing a letter in a wiki to patching a driver, think of the modifications involved as a changeset that can be manipulated with tools like mercurial
* Virtual currencies : every quantity that can be obtained from a service and checked for unicity is a currency http://svn.gna.org/viewcvs/pokersource/trunk/poker-network/pokerweb/pages/currency.php
* Using software appliances to overcome the difficulties of multiple applications running in the same operating system http://garden.dachary.org/universe.html
* Create a rss feed from URL changes http://specs.dachary.org/fam2rss/
* [[Scrapbook iceweasel extension]] seems too slow
* For any given subject create a URL
* The minimal URL is a URL that points to nothing and indicates an absence
* Create a mean to convert a non existent URL into a text (bug report, tracker, wiki, tarbal, static text...)
* Convert a text into a directory
** copy http://garden.dachary.org/empty.html
** cut/paste the text in the summary
** chose a URL 
** change the site title + URL
* Convert a text or directory into a [[software appliance|http://garden.dachary.org/universe.html]]
** [[DAV Tiddlywiki home page]]
** copy the text or directory to the home page
* Convert a text to a text (i.e. move a text elsewhere)
** remove the text from the previous location
** redirect the URL to the new location
<<<
{{{
in /etc/apache2/sites-available/default
Redirect /gamenexus/scoredlinks-interaction.html http://www.tm-rating.org/wiki/scoredlinks-interaction.html
}}}
<<<
** add the text to the new location
* Convert a software appliance into a text (archival / compression) http://garden.dachary.org/universe.html#%5B%5Bdowngrade%20a%20software%20appliance%20to%20a%20directory%5D%5D
* Visualy represent the proximity of two URL in time, based on the navigation history (bash, firefox): a flower with URL at the center, petals being previous/next ~URLs in time.
irc://irc.freenode.net/dachary/

Loic Dachary <mailto:loic@dachary.org>
Johan Euphrosine <mailto:proppy@aminche.com>

http://garden.dachary.org/empty.html
http://garden.dachary.org/universe.html
http://garden.dachary.org/tiddlyforge.html
[[Summary]]
[[Links]]
[[Rationale]]
[[Use Cases]]
[[Scope]]
[[Design]]
[[Implementation]]
[[Unresolved Issues]]
[img[Passion Flower P. foetida hirsuta|images/small/Passion Flower P. foetida hirsuta.gif][images/large/Passion Flower P. foetida hirsuta.jpg]]<<imagebox>>
add http://username:password@hostname:port/dav-directory/ to Network places
* port number is mandatory (even if 80)
Load the [[http://tiddlylab.bidix.info/#GenerateRssByTag]] plugin to control which tiddlers are included in the RSS feed. Including all the tiddlers makes a lot of noise.
Get the plugin from http://tiddlylab.bidix.info/#RSSReaderPluginDescription with 
http://tiddlylab.bidix.info/#ProxyService which is a proxy.php script overcoming the security limitations of javascript in a controled environment (i.e. the list of sites from which URL are proxied is limited).

This is no agregator and http://planetplanet.org/ can be used to agregate before integration in tiddlywiki.
More often than not, modifying a set of bytes is impossible or very impractical. This is painfull in general and a daily annoyance to the Free Software developer in particular. 

How could you modify http://anpe.fr/ to add an index ? How do you become a Debian developer to upload a well formed software package to http://debian.org/ ? How do you fix a bug in the gcc compiler ? Why exciting discussions on IRC lead to nothing ? Why Free Software based persistant universes projects end with great plans, a remarkable burst of activity and a freeze ? Why the majority of Free Software projects freeze despite the good will of many ? Why a widely used software is paralyzed and even the most talented developer can't fix a bug ? 

A collection of common sense receipes and a selection of technical tool would be very helpfull to improve the process of changing bytes in situations where it is currently unpractical.
The use cases collection should cover an increasing diversity of people willing to garden bytes. From the passive visitors who merely leave a trace of his visit (or lack of such trace because (s)he wants to remain anonymous) to the developer modifying the behaviour of processes.

Garden does not implement tools, it describes how existing tools can be used for gardening and highlight the lack of usefull tools by explaining how they could help the gardener.
continuous gardening of bytes and processes
Garden
http://garden.dachary.org/index.html
Tools and methods to lower the obstacles to modify the bytes in a system (''gardening'') in a meaningfull way.The purpose of a ''gardener'' is to talk, using bytes and processes and to listen to the bytes and processes grown by other people.
The life cycle of bytes starts as a phrase (i.e. a sequence of bytes) which turns into a file, grows as a directory and becomes a software appliance when it starts running software. It will occasionaly hibernate and regress to become a directory with no dedicated software running on top of it or a single file archive that cannot be easily modified.
<html>
<table>
<tr><th></td>
<th>phrase</th>
<th>file</th>
<th>directory</th>
<th>software appliance</th>
</tr>

<tr><th>phrase</th>
<th></th> <!-- phrase -->
<td><a href="empty.html">cut/paste in a tiddlywiki</td> <!-- file -->
<th></th> <!-- directory --> 
<th></th> <!-- software appliance --> 
</tr>

<tr><th>file</th>
<th>N/A</th> <!-- phrase -->
<th></th> <!-- file -->
<td><a href="tiddlyforge.html">mkdir + tiddlywiki + mercurial + DAV</td> <!-- directory --> 
<td><a href="http://trac.dunnewind.net/dunnewind/wiki/GanetiOspfHowto">ganeti kvm</a></td> <!-- software appliance -->
<tr>

<tr><th>directory</th>
<th>N/A</th> <!-- phrase -->
<td>tarbal</td> <!-- file -->
<th></th> <!-- directory --> 
<td><a href="http://trac.dunnewind.net/dunnewind/wiki/GanetiOspfHowto">ganeti kvm</a> + <a href="http://garden.dachary.org/universe.html#[[create%20a%20trac%20%2B%20mercurial%20%2B%20openid%20repository]]">trac</a></td> <!-- software appliance -->
<tr>

<tr><th>software appliance</th>
<th>N/A</th> <!-- phrase -->
<td>tarbal or disk image with partition table in a file</td> <!-- file -->
<td>gnt-instance activate-disk appliance.vm.gnt</td> <!-- directory --> 
<th></th> <!-- software appliance -->
<tr>


</table>
</html>
/***
| Name|TagglyTaggingPlugin|
| Description|tagglyTagging macro is a replacement for the builtin tagging macro in your ViewTemplate|
| Version|3.1 ($Rev: 2341 $)|
| Date|$Date: 2007-07-05 10:02:27 +1000 (Thu, 05 Jul 2007) $|
| Source|http://mptw.tiddlyspot.com/#TagglyTaggingPlugin|
| Author|Simon Baird <simon.baird@gmail.com>|
| License|http://mptw.tiddlyspot.com/#TheBSDLicense|
!Notes
See http://mptw.tiddlyspot.com/#TagglyTagging
***/
//{{{
config.taggly = {

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

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

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

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

	setTagglyOpt: function(title,opt,value) {
		if (!store.tiddlerExists(title))
			// create it silently
			store.saveTiddler(title,title,config.views.editor.defaultText.format([title]),config.options.txtUserName,new Date(),null);
		// if value is default then remove it to save space
		return store.setValue(title,
			this.config.valuePrefix+opt,
			value == this.config.listOpts[opt][0] ? null : value);
	},

	getNextValue: function(title,opt) {
		var current = this.getTagglyOpt(title,opt);
		var pos = this.config.listOpts[opt].indexOf(current);
		// a little usability enhancement. actually it doesn't work right for grouped or sitemap
		var limit = (opt == "numCols" ? store.getTaggedTiddlers(title).length : this.config.listOpts[opt].length);
		var newPos = (pos + 1) % limit;
		return this.config.listOpts[opt][newPos];
	},

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

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

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

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

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

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

		var upperColsize = colSize;
		var lowerColsize = colSize;

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

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

		return output;
	},

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

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

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

	getExcerpt: function(inTiddlerTitle,title,indent) {
    if (!indent)
			indent = 1;
		if (this.getTagglyOpt(inTiddlerTitle,"excerpts") == "excerpts") {
			var t = store.getTiddler(title);
			if (t) {
				var text = t.text.replace(/\n/," ");
				var marker = text.indexOf(this.config.excerptMarker);
				if (marker != -1) {
					return " {{excerpt{<nowiki>" + text.substr(0,marker) + "</nowiki>}}}";
				}
				else if (text.length < this.config.excerptSize) {
					return " {{excerpt{<nowiki>" + t.text + "</nowiki>}}}";
				}
				else {
					return " {{excerpt{<nowiki>" + t.text.substr(0,this.config.excerptSize) + "..." + "</nowiki>}}}";
				}
			}
		}
		else if (this.getTagglyOpt(inTiddlerTitle,"excerpts") == "contents") {
			var t = store.getTiddler(title);
			if (t) {
				return "\n{{contents indent"+indent+"{\n" + t.text + "\n}}}";
			}
		}
		else if (this.getTagglyOpt(inTiddlerTitle,"excerpts") == "sliders") {
			var t = store.getTiddler(title);
			if (t) {
				return "<slider slide>\n{{contents{\n" + t.text + "\n}}}\n</slider>";
			}
		}
		return "";
	},

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

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

		var list = store.getTaggedTiddlers(title,this.getTagglyOpt(title,"sortBy"));

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

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

				first = false;
			}
		}

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

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

		var list = store.getTaggedTiddlers(title,sortBy);

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

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

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

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

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

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

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

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

						}
					}
				}
			}
		}

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

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

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

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

		var output = [];

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

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

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

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

	},

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

		var list = store.getTaggedTiddlers(title,sortBy);
		if (sortOrder == "desc")
			list.reverse();

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

		var childOutput = "";
		for (var i=0;i<list.length;i++)
			if (list[i].title != title)
				if (this.notHidden(list[i].title,this.config.inTiddler))
					childOutput += this.treeTraverse(list[i].title,depth+1,sortBy,sortOrder);

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

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

	macros: {
		tagglyTagging: {
			handler: function (place,macroName,params,wikifier,paramString,tiddler) {
				var refreshContainer = createTiddlyElement(place,"div");
				// do some refresh magic to make it keep the list fresh - thanks Saq
				refreshContainer.setAttribute("refresh","macro");
				refreshContainer.setAttribute("macroName",macroName);
        			refreshContainer.setAttribute("title",tiddler.title);
				this.refresh(refreshContainer);
			},

			refresh: function(place) {
				var title = place.getAttribute("title");
				removeChildren(place);
				if (store.getTaggedTiddlers(title).length > 0) {
					var lingo = config.taggly.lingo;
					config.taggly.createListControl(place,title,"hideState");
					if (config.taggly.getTagglyOpt(title,"hideState") == "show") {
						createTiddlyElement(place,"span",null,"tagglyLabel",lingo.labels.label.format([title]));
						config.taggly.createListControl(place,title,"title");
						config.taggly.createListControl(place,title,"modified");
						config.taggly.createListControl(place,title,"created");
						config.taggly.createListControl(place,title,"listMode");
						config.taggly.createListControl(place,title,"excerpts");
						config.taggly.createListControl(place,title,"numCols");
						config.taggly.createTagglyList(place,title);
					}
				}
			}
		}
	},

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

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

config.taggly.init();

//}}}

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

// syntax adjusted to not clash with NestedSlidersPlugin

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

//}}}

!Summary
What is being specified?
!Links
The list of URLs that do not yet exist. A specification must be associated to at least one URL. When the specification is implemented, bytes can be accessed with these URL and they are removed from this list.

The liste URLs of interest to the specifications, including email addresses of participants or irc channels.
!Rationale
Why is this being defined?
!Use Cases
* Describe the current user scenario.
* Describe the desired user scenario.
* List the steps (technical commands like ls ; cp ) to implement the desired scenario
* Create tiddlers for complex use cases and tag them "Use Cases" so that they are listed automatically
!Scope
Limits of this spec application.
!Design
Design guidelines.
!Implementation
Implementation steps and considerations.
!Unresolved Issues
Discuss problems and unresolved issues.
/***
|''Name:''|TiddlyLightBox|
|''Date:''|Jan 1, 2006|
|''Version:''|1.0 beta|
|''Author:''|Saq Imtiaz|
|''Location:''|http://tw.lewcid.org/#TiddlyLightBoxPlugin|
|''Documentation:''|http://tw.lewcid.org/#TiddlyLightBoxDocs|
|''License:''|[[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|''Based on:''|DC3.LightBox<br>Light Box Gone Wild <br>Ibox|

!!Code
***/
//{{{
config.macros.imagebox ={};
config.macros.imagebox.handler = function (place,macroName,params,wikifier,paramString,tiddler)
{
    var e = place.lastChild;
    e.onclick = function(){TiddlyLightBox.initBox('image',this,params[1],params[2],params[0]);return false;};
}

config.macros.divbox ={};
config.macros.divbox.handler = function (place,macroName,params,wikifier,paramString,tiddler)
{
    if (params[0]!=".")
        createTiddlyButton(place,params[0],params[0],function(){TiddlyLightBox.initBox('html',params[1],params[3],params[4],params[2]);return false;});
    else
        {
        var e = place.lastChild;
        e.onclick = function(){TiddlyLightBox.initBox('html',params[1],params[3],params[4],params[2]);return false;};
        }
}

config.macros.tiddlerbox ={}
config.macros.tiddlerbox.handler = function (place,macroName,params,wikifier,paramString,tiddler)
{
    config.macros.divbox.handler(place,macroName,[params[0],"tiddler:"+params[1],params[2],params[3],params[4]]);
    return false;
}

store.addNotification("TiddlyLightBoxStyles",refreshStyles);

if (!window.TiddlyLightBox)
    window.TiddlyLightBox = {};
    var loadingImage = "indicator.gif";
    window.TiddlyLightBox =
    {
    _curBox: null, // [sentinel]

    lightBoxHtml : '<div id="lightBoxOverlay" onclick="TiddlyLightBox.hideBox()" style="display:none"></div><div id="lightboxprogress" style="display:none;"><img src=\''+loadingImage+'\' alt=\'loading\' style="width:128px;height:128px;"></div><div class="lightBox" id="lightBox" style="display:none"><div id="lightBoxContent"></div><div id="lightBoxTitle">This is a title</div><div id="lightBoxClose"><a href:"#" onclick="TiddlyLightBox.hideBox();return false;">Click to close</a></div></div>',

    createBoxWrapper : function()
        {
        var wrapper = createTiddlyElement(document.getElementsByTagName("body")[0],"div","tiddlyLightBoxWrapper");
        wrapper.innerHTML = this.lightBoxHtml;
        },

    initBox : function(contentType,url,w,h,text)
        {
        if (this._curBox)
            return;
        this.showProgress();
        this.hideSelects("hidden");
        this.showBg();
        this._curBox = true;
        this.sizeTheBox(contentType,w,h);
        if (contentType == 'image')
            this.showImage(url,text);
        else if (contentType == 'html')
            this.showHtml(url,text);
        return false;
        },
        
    sizeTheBox : function(contentType,w,h)
        {
        var box = document.getElementById("lightBoxContent");
        if (w && isNaN(parseInt(w)))
            {
            addClass(box,w);
            }
        else if (w ||h || contentType == 'html')
            {
            box.style.width = w? w+ "px" : "450px";
            box.style.height = h? h+ "px" : "280px";
            if (contentType=='image')
                setStylesheet("#lightBoxContent img{height:100%;width:100%;}","lightBoxImageSizeHack");
            }
        },

    showProgress : function()
        {
        var progress = document.getElementById("lightboxprogress");
        progress.style.display='';
        this._center(progress);
        },
    
    hideProgress: function()
        {
        var progress = document.getElementById("lightboxprogress");
        progress.style.display='none';
        },

    //this function lifted from Lightbox Gone Wild
    hideSelects: function(visibility)
        {
        var selects = document.getElementsByTagName('select');
        for(i = 0; i < selects.length; i++)
            {
            selects[i].style.visibility = visibility;
            }
        },

    showBg: function()
        {
        var overlay = document.getElementById('lightBoxOverlay');
        if (config.browser.isIE)
            {
            overlay.style.height = Math.max(document.documentElement.scrollHeight,document.documentElement.offsetHeight);
            overlay.style.width = document.documentElement.scrollWidth;
            }
        overlay.style.display = 'block';
        },

    showImage: function (url,text)
        {
        imgPreloader = new Image();
        imgPreloader.onload = function ()
            {
            var lb = document.getElementById("lightBoxContent");
            lb.innerHTML = "<img src="+url+">";
            lb.onclick = function(){TiddlyLightBox.hideBox();return false;};
            TiddlyLightBox.posBox(text);
            };
        imgPreloader.src = url;
        },
        
    showHtml : function(theID,text)
        {
        var lb = document.getElementById("lightBoxContent");
        if (theID.indexOf("tiddler:")==-1)
             lb.innerHTML = document.getElementById(theID).innerHTML;
        else
            { 
             wikify(store.getTiddlerText(theID.replace("tiddler:","")),lb);
             lb.className='tiddler';
            }
        lb.style.overflow = "auto";
        this.posBox(text);
        },

    posBox: function(text)
       {
       this.setTitle(text);
       this.hideProgress();
       var lb = document.getElementById("lightBox");
       lb.style.display = "";
       lb.style.visibilty = "hidden";
       lb.style.position = "absolute";
       this._center(lb);
       if(!TiddlyLightBox._curBox) return;
       lb.style.visibility = "visible";
       lb.style.display = "block";
       },

     setTitle: function(text)
        {
        document.getElementById("lightBoxTitle").innerHTML=  (text==undefined)? '': text;
        },

    _center: function(lb)
       {
       var lbSize = new TiddlyLightBox.getElementSize(lb);
       lb.style.left = (Math.round(findWindowWidth()/2) - (lbSize.width /2) + findScrollX())+'px';
       lb.style.top = (Math.round(findWindowHeight()/2) - (lbSize.height /2) + findScrollY())+'px';
       },

    //this function lifted from Ibox
    getElementSize : function(elem)
       {
       this.width = elem.offsetWidth || elem.style.pixelWidth;
       this.height = elem.offsetHeight || elem.style.pixelHeight;
       },

     hideBox: function()
         {
         if(!this._curBox)
             return;
         document.getElementById("tiddlyLightBoxWrapper").innerHTML= this.lightBoxHtml;
         setStylesheet("","lightBoxImageSizeHack");
         this._curBox = null;
         return false;
         }
}

TiddlyLightBox.createBoxWrapper();

Story.prototype.findContainingTiddler = function(e)
{
    while(e && (!hasClass(e,"tiddler") || !e.getAttribute("tiddler")))
        e = e.parentNode;
    return(e);
}

config.shadowTiddlers.TiddlyLightBoxStyles="/*{{{*/\n#lightBoxOverlay {\n position:absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 90; \n background-color: #000;\n -moz-opacity: 0.75;\n opacity: .75;\n filter: alpha(opacity=75);\n}\n#lightBoxOverlay[id]{ \n position: fixed;\n}\n\n#lightboxprogress { \n margin:0;padding:0;\n position: absolute;\n z-index:95;\n}\n\ndiv.lightBox {\n background: #fff;\n color: #fff;\n border: 4px solid #525252;\npadding:20px 20px 25px 20px; position:absolute; z-index:99;\n}\n\n#lightBoxClose {text-align:right; color:#000; font-size:1.0em; position:absolute; bottom:6px; right:20px;}\n#lightBoxClose a{color:#666; border-bottom:1px solid #666;cursor:pointer;}\n#lightBoxClose a:hover {color:#111; border-bottom:1px solid #666; cursor:pointer; background:transparent;}\n\n#lightBoxContent {border:1px solid #525252;color:#000; background:#fff;}\n#lightBox .tiddler {background:#fff;}\n\n#lightBoxContent img {border:0;margin:0;padding:0;display:block;cursor:pointer;}\n\n#lightBoxTitle {padding:0px; font-weight:bold; position:absolute; left:20px;bottom:6px; font-size:1.1em; color:#000;}\n\n/*}}}*/";
//}}}
/***
|''Name:''|TiddlyStaticDavHooksPlugin|
|''Description:''|Enable DAV saving for static file and sitemap|
|''Author:''|Saq Imtiaz ( lewcid@gmail.com )|
|''Source:''|http://tw.lewcid.org/#TiddlyStaticDavHooksPlugin|
|''Code Repository:''|http://tw.lewcid.org/svn/plugins|
|''Version:''|1.0|
|''Date:''|13-09-2007|
|''Copyright holder:''|Loic Dachary|
|''License:''|[[Creative Commons Attribution-ShareAlike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]]|
|''~CoreVersion:''|2.2.3|
|''Requires:''|WebDAVSavingPlugin TiddlyStaticPlugin|

***/
// /%
//!BEGIN-PLUGIN-CODE
//{{{
if (bidix && bidix.WebDAVSaving){	
	bidix.WebDAVSaving.saveMain = function(url,original,posDiv){
		var callback = function(status,params,responseText,url,xhr) {
			if(status) {
				url = (url.indexOf("nocache=") < 0 ? url : url.substring(0,url.indexOf("nocache=")-1));
				displayMessage(config.messages.mainSaved,url);
				store.setDirty(false);
				bidix.WebDAVSaving.saveStatic(params[0],params[1],params[2]);
			} else 
				alert(config.messages.mainFailed);
		};	
		// Save new file
		var revised = updateOriginal(original,posDiv);
		bidix.httpPut(url,revised,callback,Array(url,original,posDiv));
	};
	
	bidix.WebDAVSaving.saveStatic = function(url,original,posDiv){
		var callback = function(status,params,responseText,url,xhr) {
			if (!status) {
				alert(TiddlyStatic.messages.staticError);
				return;
			}
			url = (url.indexOf("nocache=") < 0 ? url : url.substring(0,url.indexOf("nocache=")-1));
			displayMessage(TiddlyStatic.messages.staticSaved,url);
			bidix.WebDAVSaving.saveSiteMap(params[0],params[1],params[2]);
		};
		if(config.options.chkSaveStatic) {
			var path = url.substr(0,url.lastIndexOf(".")) + "-static.html";
			bidix.httpPut(path,convertUnicodeToUTF8(TiddlyStatic.generateStatic()),callback,Array(url,original,posDiv));
		} else {
			bidix.WebDAVSaving.saveSiteMap(url,original,posDiv);
		}
	};
	
	bidix.WebDAVSaving.saveSiteMap = function(url,original,posDiv){
		var callback = function(status,params,responseText,url,xhr) {
			if (!status) {
				alert(TiddlyStatic.messages.siteMapError);
				return;
			}
			url = (url.indexOf("nocache=") < 0 ? url : url.substring(0,url.indexOf("nocache=")-1));
			displayMessage(TiddlyStatic.messages.siteMapSaved,url);
		};
		if(config.options.chkSaveSiteMap) {
	    		var sep = (url[1] == ':' ? "\\" : "/");
			var path = url.substr(0,url.lastIndexOf(sep)+1) + "sitemap.xml";
			bidix.httpPut(path,convertUnicodeToUTF8(TiddlyStatic.createSiteMap(url,sep)),callback,Array(url,original,posDiv));
		}
	};
}
//}}}
//!END-PLUGIN-CODE
// %/
/***
|''Name:''|TiddlyStatic|
|''Description:''|Create a static file with the content of the TiddlyWiki, and a sitemap file|
|''Author:''|Saq Imtiaz ( lewcid@gmail.com )|
|''Source:''|http://tw.lewcid.org/#TiddlyStaticPlugin|
|''Code Repository:''|http://tw.lewcid.org/svn/plugins|
|''Version:''|1.0|
|''Date:''|13-09-2007|
|''Copyright holder:''|Loic Dachary|
|''License:''|[[Creative Commons Attribution-ShareAlike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]]|
|''~CoreVersion:''|2.2.3|

|>| !Configuration Options|
|<<option chkSaveStatic>>|save file with static content |
|<<option chkSaveSiteMap>>|save sitemap file |
|<<option txtTiddlyStaticIncludeTags>>| Include tiddlers with these tags, written as bracketed list|
|<<option txtTiddlyStaticExcludeTags>>| Exclude tiddlers with these tags, written as bracketed list|

Example of a bracketed list: {{{Tag1 Tag2 [[Tag With Spaces]]}}}
If a tiddler both as an include tag, and an exclude tag, the exclude tag takes priority.

The notice at the top of the static page can be customized via the TiddlyStaticBanner shadow tiddler.
You can insert custom markup or scripts into the head of the static page, via the TiddlyStaticMarkupHead tiddler.

***/
// /%
//!BEGIN-PLUGIN-CODE
//{{{
if (!config.options.chkSaveStatic) 
	config.options.chkSaveStatic = true;
if (!config.options.chkSaveSiteMap)
	config.options.chkSaveSiteMap = true;
if(!config.options.txtTiddlyStaticIncludeTags)
	config.options.txtTiddlyStaticIncludeTags = '';
if(!config.options.txtTiddlyStaticExcludeTags)
	config.options.txtTiddlyStaticExcludeTags = '';

TiddlyStatic = {
	
	messages:{
		staticSaved: "Static file saved",
		staticError: "There was an error saving the static file",
		siteMapSaved: "Sitemap saved",
		siteMapError: "There was an error saving the sitemap file"
	},
	
	saveStatic : function(localPath){
		var path = localPath.substr(0,localPath.lastIndexOf(".")) + "-static.html";
		var save = saveFile(path,convertUnicodeToUTF8(this.generateStatic()));
		if(save)
			displayMessage(this.messages.staticSaved,"file://" + path);
		else
			alert(this.messages.staticError);
	},
	
	generateStatic : function(){
		var tiddlers = this.getOutputTiddlers();
		var s = [];
		s.push('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"\n"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">');
		s.push("<html>\n<head>");
		var title = wikifyPlain("SiteTitle").htmlEncode()+" - " + wikifyPlain("SiteSubtitle").htmlEncode();
		s.push("<title>"+ title +"</title>");
		var styles=document.getElementsByTagName("style");
		s.push("<style type='text/css'>");
		for(var i=0; i < styles.length; i++)
				s.push(styles[i].innerHTML+"\n\n");
		s.push("</style>");
		s.push(store.getTiddlerText("TiddlyStaticMarkupHead"));
		s.push("</head>");
		var u = store.getTiddlerText("SiteUrl");
		s.push("<body>");
		s.push("<h1 class='siteTitle'>" + wikifyStatic(store.getTiddlerText("TiddlyStaticBanner")) + "</h1>");
		for (var t=tiddlers.length-1; t>=0; t--)
			s.push(tiddlers[t].generateStatic(u));
		s.push("</body>\n</html>");
		return s.join("\n");
	},
	
	getFilteredTiddlers : function(field,excludeTags,includeTags){
		var results = [];
		store.forEachTiddler(function(title,tiddler){
			if(!excludeTags.length || !tiddler.tags.containsAny(excludeTags)){
				if(!includeTags.length || tiddler.tags.containsAny(includeTags))
					results.push(tiddler);
			}
		});
		if(field)
			results.sort(function (a,b) {if(a[field] == b[field]) return(0); else return (a[field] < b[field]) ? -1 : +1; });
		return results;
	},
	
	getOutputTiddlers : function(){
		return this.getFilteredTiddlers("modified",config.options.txtTiddlyStaticExcludeTags.readBracketedList(),config.options.txtTiddlyStaticIncludeTags.readBracketedList());
	},
	
	saveSiteMap : function(localPath){
	    var sep = (localPath[1] == ':' ? "\\" : "/");
		var path = localPath.substr(0,localPath.lastIndexOf(sep)+1) + "sitemap.xml";
		var save = saveFile(path,convertUnicodeToUTF8(this.createSiteMap(localPath,sep)));
		if(save)
			displayMessage(this.messages.siteMapSaved,"file://" + path);
		else
			alert(this.messages.siteMapError);
	},
	
	createSiteMap : function (localPath,sep){
		var tiddlers = this.getOutputTiddlers();
		var s = [];
		var url = store.getTiddlerText('SiteUrl');
		var staticpath = localPath.substr(0,localPath.lastIndexOf(".")) + "-static.html";
		var filename = staticpath.substr(staticpath.lastIndexOf(sep)+1);
		s.push('<?xml version="1.0" encoding="UTF-8"?>');
		s.push('<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">');
		if(config.options.chkSaveStatic)
			s.push('<url>\n<loc>'+ url.substr(0,url.lastIndexOf("/"))+ "/" + filename +'</loc>\n</url>');
		for(var i=0; i<tiddlers.length; i++){
			s.push('<url>\n<loc>'+ url+ '#'+ encodeURIComponent(String.encodeTiddlyLink(tiddlers[i].title))+'</loc>\n</url>');
		}
		s.push('</urlset>');
		return s.join('\n');
	},

	oldSaveMain : window.saveMain	
		
};

window.saveMain = function(localPath,original,posDiv){
	if(config.options.chkSaveStatic)
		TiddlyStatic.saveStatic(localPath);
	if(config.options.chkSaveSiteMap)
		TiddlyStatic.saveSiteMap(localPath);
	TiddlyStatic.oldSaveMain.apply(this,arguments);
};

Tiddler.prototype.generateStatic = function(url){
	var s = [];
	s.push("<div class='tiddler'>");
	s.push("<h2 class='title'>" + "<a href='"+url + "#" + encodeURIComponent(String.encodeTiddlyLink(this.title))+"'>"+this.title.htmlEncode()+"</a>" + "</h2>" );
	s.push("<h3 class='tagged cat_tag'>Tags: " + this.tags.join(" ,") +"</h3>");
	s.push("<div class='viewer'>" + wikifyStatic(this.text,null,this)+"</div>");
	s.push("</div>");
	return s.join("\n");
};

config.shadowTiddlers.TiddlyStaticBanner = 'This file is a static version of the website ' + store.getTiddlerText('SiteTitle') + '.\n [[Please click here to visit the site instead.|' + store.getTiddlerText('SiteUrl')+']]';

config.shadowTiddlers.TiddlyStaticMarkupHead = '';
//}}}
//!END-PLUGIN-CODE
// %/
* a [[fsview|http://docs.kde.org/stable/en/kdeaddons/konq-plugins/fsview/index.html]] like vizualisation
* ~URLs instead of files
* updates thru rss feeds
* each square is larger if it was modified recently
* ~URLs not modified during a long time shrink
* The color depends on the size of the changeset of the last modification
* On etch, when mount on DAV says that it will use fuser instead of coda the following fails : wget -O index.html http://tiddlywiki.com/empty.html, open file://.../index.html, import rsshijack from tiddlywiki.bidix.com and save.
* There seem to be a limit to the number of coda fs mounted and when this limit is reached a davfs on etch uses fuser which seems to have other problems.
* Tiddlywiki does not work with mozilla-1.7.5 http://trac.tiddlywiki.org/ticket/341
* Tiddlywiki interupt while saving over DAV corrupts http://trac.tiddlywiki.org/ticket/342
* What exactly is "large files" ? apache2-2.2.3/modules/dav/fs/repos.c
{{{
    /* ### this does not handle large files. but this is test code anyway */
    bkt = apr_bucket_file_create(fd, 0,
                                 (apr_size_t)resource->info->finfo.size,
                                 pool, output->c->bucket_alloc);
    APR_BRIGADE_INSERT_TAIL(bb, bkt);
}}}
* Mount the DAV directory localy and copy the page to a regular directory to avoid DAV lossage
* Save the old page
{{{
cp page.html page-2.1.html
}}}
* Load the tiddlywiki-2.1 page into the web browser
* Override with tiddlwiki-2.2
{{{
wget -O page.html http://tiddlywiki.com/beta/empty.html
}}}
* Save the page loaded into the browser by using {{{save changes}}}. Beware, will not work with ''bidix upload plugin''. Saving actually reads back the new page and inserts the content into it.
* Reload the page to make sure it's properly upgraded.
* Click {{{backstage}}} in the upper right corner
* Import the {{{webdavsavingplugin}}} tiddler from http://tiddlylab.bidix.info/
* Save
* Edit and set {{{chkHttpReadOnly: true}}} to allow online editing by default
* Each use case is the URL of the software appliance containing the use case or from which the use case history can be accessed in detail.
<!--{{{-->
<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler > fields syncing permalink references jump'></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="tagglyTagging" macro="tagglyTagging"></div>
<div class='tagged' macro='tags'></div>
<div macro="hideWhen tiddler.tags.containsAny(['css','html','pre','systemConfig']) && !tiddler.text.match('{{'+'{')">
<div class='viewer' macro='view text wikified'></div>
</div>
<div macro="showWhen tiddler.tags.containsAny(['css','html','pre','systemConfig']) && !tiddler.text.match('{{'+'{')">
<div class='viewer'><pre macro='view text'></pre></div>
</div>
<div class='tagClear'></div>
<!--}}}-->
/***
|''Name:''|WebDavPlugin|
|''Description:''|Allows a TiddlyWiki to be saved to a WebDav server|
|''Author:''|Saq Imtiaz ( lewcid@gmail.com )|
|''Co-author:''|Loic Dachary|
|''Source:''|http://tw.lewcid.org/#WebDavPlugin|
|''Code Repository:''|http://tw.lewcid.org/svn/plugins|
|''Version:''|1.0|
|''Date:''|17/11/2007|
|''License:''|[[Creative Commons Attribution-ShareAlike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]]|
|''~CoreVersion:''|2.2.3|

***/
// /%
//!BEGIN-PLUGIN-CODE
DAV = {
	run : function(type,u,data,cb,params,headers){
		var callback = function(status,params,responseText,url,xhr) {
			url = url.indexOf("nocache=") < 0 ? url : url.substring(0,url.indexOf("nocache=")-1);
			if(params.length){
				params.shift().apply(this,[status,params,responseText,url,xhr]);
			}
		};	
		params = params||[];
		params.unshift(cb);		
		var r = doHttp.apply(this,[type,u,data,null,null,null,callback,params,headers]);
		if (typeof r == "string")
			alert(r);
		return r;
	},
	
	get : function(url,cb,params){
		return DAV.run("GET",url,null,cb,params,null);
	},

	put : function(url,cb,params,data) {
		return DAV.run("PUT",url,data,cb,params,null);
	},

	move : function(url,cb,params,destination) {
		return DAV.run("MOVE",url,null,cb,params,{"Destination":destination,"Overwrite":"T"});
	}, 

	copy : function(url,cb,params,destination) {
		return DAV.run("COPY",url,null,cb,params,{"Destination":destination,"Overwrite":"T","Depth":0});
	},
	
	propfind : function(url,cb,params,prop,depth){	// !!!
		var xml = '<?xml version="1.0" encoding="UTF-8" ?>' +
			'<D:propfind xmlns:D="DAV:">' +
			'<D:prop>'+
			'<D:'+prop+'/>'+
			'</D:prop>'+
			'</D:propfind>';
		return DAV.run("PROPFIND",url,xml,cb,params,{"Content-type":"text/xml","Depth":depth?depth:0});
	},
	
	makeDir : function(url,cb,params){
		return DAV.run("MKCOL",url,null,cb,params,null);
	},

	options : function(url,cb,params){
		return DAV.run("OPTIONS",url,null,cb,params,null);
	},
	
	safeput : function(url,cb,params,data){
		firstcb = function(status,p,responseText,u,xhr){
			if(status)
				DAV.move(u,cb,p,u.replace("-davsavingtemp",""));
			else
				cb.apply(firstcb,arguments);
		};
		return DAV.put(url+"-davsavingtemp",firstcb,params,data);
	}	
};

config.DavSaver = {
	defaultFileName : 'index.html',
	messages : {
		startSaveMessage : 'saving to server...',
		endSaveMessage : 'TiddlyWiki successfuly saved to server',
		overwriteNewerPrompt : 'The remote file has changed since you last loaded or saved it.\nDo you wish to overwrite it?',
		getModDateError : "Could not get last modified date",
		raceError: "Save failed because the remote file is newer than the one you are trying to save"
	},
	errors:{
		raceconflict : 'The save failed because your file is out of date',
		getlastmodified : 'The save was aborted because the last modified date of the file could not be retrieved',
		davenabled : 'The server does not support WebDav',
		saverss : 'There was an error saving the rss file, the save was aborted',
		saveempty: 'There was an error saving the empty file, the save was aborted',
		savemain : 'Save failed! There was an error saving the main TW file',
		savebackup: 'Save failed! There was an error creating a backup file',
		makebackupdir: 'Save failed! The backup directory could not be created'		
	},
	timestamp: new Date().toGMTString(),
	orig_saveChanges: saveChanges,
	saver : null
};

DavSaver = function(){
	
	this.Q = [];	

	this.reset = function(){
		config.DavSaver.saver = null;
	};
	this.runQ = function(){
		if(this.Q.length){
			this.Q.shift().apply(this,arguments);
		}
		else
			this.reset();
	};
	this.posDiv = null;
	this.original = null;
	
	this.getOriginalPath = function(){
		var p = document.location.toString();
		p = convertUriToUTF8(p,config.options.txtFileSystemCharSet);
		var argPos = p.indexOf("?");
		if(argPos != -1)
			p = p.substr(0,argPos);
		var hashPos = p.indexOf("#");
		if(hashPos != -1)
			p = p.substr(0,hashPos);		
		if (p.charAt(p.length-1) == "/")
			p = p + config.DavSaver.defaultFileName;
		return p;
	};
	
	this.originalPath = this.getOriginalPath();
	this.backupPath = null;
	this.emptyPath = null;
	this.rssPath = null;
	this.throwError = function(er){
		clearMessage();
		this.reset();
		alert(config.DavSaver.errors[er]);   //clear message, reset and delete
	};
};

DavSaver.prototype.getOriginal = function(){
	var	callback = function(status,params,original,url,xhr) {
		var that = params[0];
		if(status){
			var p = that.posDiv = locateStoreArea(original);
			if(!p || (p[0] == -1) || (p[1] == -1)) {
				alert(config.messages.invalidFileError.format([url]));
				return;
			}
			that.original = original;
			that.runQ();
		}
		else
			that.throwError('getOriginal');
	};
	
	DAV.get(this.originalPath,callback,[this]);	
};

DavSaver.prototype.checkRace = function(){
	var callback = function(status,params,responseText,url,xhr){
		var that = params[0];
		if(status){
			var lastmod = responseText.match(/<(\w*?):getlastmodified>(.*?)<\/\1:getlastmodified>/)[2];
			if(Date.parse(lastmod) > Date.parse(config.DavSaver.timestamp)){
				if (confirm(config.DavSaver.messages.overwriteNewerPrompt))
					that.runQ();
				else
					that.throwError('raceconflict');
			}
			else	
				that.runQ();
		}
		else
			that.throwError('getlastmodified');
	};
	
	DAV.propfind(this.originalPath,callback,[this],"getlastmodified",0);	
};

DavSaver.prototype.isDavEnabled = function(){
	var callback = function(status,params,responseText,url,xhr){
		that = params[0];
		if(status && !! xhr.getResponseHeader("DAV")){
			that.runQ();
			}
		else
			that.throwError('davenabled');
	};
	DAV.options(this.originalPath,callback,[this]);		
};

DavSaver.prototype.saveRss = function(){
	var callback = function(status,params,responseText,url,xhr){
		var that = params[0];
		if(status){
			displayMessage(config.messages.rssSaved,that.rssPath);
			that.runQ();
		}
		else
			that.throwError('saverss');
	};
	var u = this.originalPath;
	var rssPath = this.rssPath = u.substr(0,u.lastIndexOf(".")) + ".xml";
	DAV.safeput(rssPath,callback,[this],convertUnicodeToUTF8(generateRss()));	
};

DavSaver.prototype.saveEmpty = function(){
	var callback = function(status,params,responseText,url,xhr){
		var that = params[0];
		if(status){
			displayMessage(config.messages.emptySaved,that.emptyPath);
			that.runQ();
		}
		else
			that.throwError('saveempty');
	};
	var u = this.originalPath;
	var emptyPath,p;
	if((p = u.lastIndexOf("/")) != -1)
		emptyPath = u.substr(0,p) + "/empty.html";
	else
		emptyPath = u + ".empty.html";
	this.emptyPath = emptyPath;
	var empty = this.original.substr(0,this.posDiv[0] + startSaveArea.length) + this.original.substr(this.posDiv[1]);
	DAV.safeput(emptyPath,callback,[this],empty);		
};

DavSaver.prototype.saveMain = function(){
	var callback = function(status,params,responseText,url,xhr){
		var that = params[0];
		if(status){
			config.DavSaver.timestamp = xhr.getResponseHeader('Date');
			displayMessage(config.messages.mainSaved,that.originalPath);
			store.setDirty(false);
			that.runQ();
		}
		else
			that.throwError('savemain');		
	};
	var revised = updateOriginal(this.original,this.posDiv);
	DAV.safeput(this.originalPath,callback,[this],revised);
};

DavSaver.prototype.saveBackup = function(){	
	var callback = function(status,params,responseText,url,xhr){
		var that = params[0];
		if(status){
			clearMessage();
			displayMessage(config.messages.backupSaved,that.backupPath);
			that.runQ();
		}
		else
			that.throwError('savebackup');
	};
			
	var backupPath = this.backupPath = getBackupPath(this.originalPath);
	DAV.copy(this.originalPath,callback,[this],this.backupPath);		
};

DavSaver.prototype.makeBackupDir = function(){
	var callback = function(status,params,responseText,url,xhr){
		var that = params[0];
		if(status)
			that.runQ();
		else
			that.throwError('makebackupdir');
	};
	var u = getBackupPath(this.originalPath);
	var backupDirPath = u.substr(0,u.lastIndexOf("/"));
	DAV.makeDir(backupDirPath,callback,[this]); 
};

DavSaver.prototype.save = function(onlyIfDirty,tiddlers){
	if(onlyIfDirty && !store.isDirty())
		return;
	clearMessage();
	displayMessage(config.DavSaver.messages.startSaveMessage);
	var Q = this.Q =[];
	Q.push(this.isDavEnabled);
	Q.push(this.getOriginal);
	Q.push(this.checkRace);
	if (config.options.chkSaveBackups){
		if (config.options.txtBackupFolder!='')
			Q.push(this.makeBackupDir);
		Q.push(this.saveBackup);
	}
	Q.push(this.saveMain);
	if (config.options.chkGenerateAnRssFeed)
		Q.push(this.saveRss);
	if (config.options.chkSaveEmptyTemplate)
		Q.push(this.saveEmpty);
	//Q.push(this.reset);
	this.runQ();
};

window.saveChanges = function(onlyIfDirty,tiddlers)
{	
	var c = config.DavSaver;
	if (document.location.protocol.toString() == "http:"){
		var saver = c.saver = new DavSaver();
		saver.save(onlyIfDirty,tiddlers);		
	}
	else
		return c.orig_saveChanges(onlyIfDirty,tiddlers);
};
//!END-PLUGIN-CODE
// %/
* The simplest use case is as described in http://garden.dachary.org/empty.html#%5B%5BUse%20Cases%5D%5D
* The interaction path of a web site can mimic the page mockups at http://specs.dachary.org/gamenexus/scoredlinks-interaction.html
* A complex use case can be described with statecharts
* grommit and dachary discuss on IRC about fam2rss
* grommit starts scripting something on his machine
* dachary creates a software appliance
* dachary adds the ssh public key of grommit so that he can log in
* dachary research the net looking for answers and saves the visited pages in a directory
* dachary has better things to do for the next N days/weeks/months

* after a while dachary realize that this idea is a degenerate of a broader idea : a versioned file system where the version system provides rss feeds
* dachary downgrades the appliance to a directory
* Using gaim
* Activate logs of all conversations
* Chose to share a private conversation with proppy in a publicly accessible directory
{{{
a=$(echo ~/.gaim/logs/yahoo/ldachary/*/$(date '+%Y-%m-%d')*.txt) ; b=$(basename $a) ; cat $a | gpg --recipient proppy@aminche.com --recipient loic@senga.org --encrypt | ssh -p 3022 root@javapoker.pokersource.info "cat > $b.pgp ; hg add ; hg commit -m 'private conversation' ."
}}}
* installation
{{{
app=screen ; author=loic@dachary.org ; apt-get install -y --force-yes $app && hg addremove && hg commit -u $author -m "apt-get install $app"
echo '^]a' > /root/.screenrc
}}}
* first to join
{{{screen}}}
* second to join
{{{screen -x}}}
* dachary is about to release a new version of a poker server
* the release changes the protocol, hence the client and the server must be upgraded simultaneously
* dachary creates a software appliance to install the new server
* the software appliance will be used for testing and for archiving, hence the name carries the version number
* dachary grants access to the software appliance to mike who is also interested in this new version
* mike copies the entire software appliance on his computer and plays with it
* dachary is invited to the copy of the software appliance by mike
* dachary watches the file modification history and discuss them with mike
The hg sign extension from Benoit Boissinot <benoit.boissinot@ens-lyon.org> lacks a few factilities that would make it convenient to use.
* using mount fails completly (reported on: https://bugs.launchpad.net/ubuntu/+source/davfs2/+bug/115487). The report is cryptic and deserves plain english explanation about why it is not a server side bug. (proppy> report updated with server log, waiting for the "plain english explanation").
* using nautilus fails on writing index.html
* With ubuntu feisty add http://fam2rss.dachary.org/ as a DAV network server, save a modification in index.html and see in the logs of the server what seems to be a content negotiation failure. The same works good with etch and fstab mounted davfs2 directory.