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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

hr {height:1px;}

a {text-decoration:none;}

dt {font-weight:bold;}

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

.txtOptionInput {width:11em;}

#contentWrapper .chkOptionInput {border:0;}

.externalLink {text-decoration:underline;}

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

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

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

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

.header {position:relative;}
.header a:hover {background:transparent;}
.headerShadow {position:relative; padding:4.5em 0 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:4.5em 0 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: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: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 0; font-size:.9em;}
.editorFooter .button {padding-top:0px; padding-bottom:0px;}

.fieldsetFix {border:0; padding:0; margin: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;}
#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>>
/***
|''Name:''|ArchivePlugin|
|''Version:''|2.4.0 (2 Jun 2008)|
|''Source''|http://jackparke.googlepages.com/jtw.html#ArchivePlugin ([[del.icio.us|http://del.icio.us/post?url=http://jackparke.googlepages.com/jtw.html%23ArchivePlugin]])|
|''Author:''|[[Jack]]|
|''Type:''|Plugin|
!Description
The archive plugin allows you to store tiddler text outside of the tiddlywiki file.
Typically you would tag bulky tiddlers or those with infrequently needed content as "Archive" and these would
then be archived as separate html files in the sub folder called "archive".
!Usage
#Create a folder "archive" in the same folder as your tiddlywiki file.
#Install the Archive Plugin and reload your tiddlywiki
#Tag your bulky tiddlers with "Archive"
#Save your tiddlywiki file
!To Do
* Synchronize tiddler renames/deletions with file system
* Lazy loading of archived files via HTTP
!Code
***/
//{{{
version.extensions.ArchivePlugin = {major: 2, minor: 4, revision: 0, date: new Date("Jun 6, 2008")};
config.macros.ArchivePlugin = {};
config.macros.ArchivePlugin.init = function () {
 this.archivePath = getWikiPath('archive');
}

// Hijacking the built-in functions
TW21Saver.prototype.externalizeTiddler = function(store,tiddler)
{
	try {
		var extendedAttributes = "";
		var usePre = config.options.chkUsePreForStorage;
		store.forEachField(tiddler,
			function(tiddler,fieldName,value) {
				// don't store stuff from the temp namespace
				if(typeof value != "string")
					value = "";
				if (!fieldName.match(/^temp\./))
					extendedAttributes += ' %0="%1"'.format([fieldName,value.escapeLineBreaks().htmlEncode()]);
			},true);
		var created = tiddler.created.convertToYYYYMMDDHHMM();
		var modified = tiddler.modified.convertToYYYYMMDDHHMM();
		var vdate = version.date.convertToYYYYMMDDHHMM();
		var attributes = tiddler.modifier ? ' modifier="' + tiddler.modifier.htmlEncode() + '"' : "";
		attributes += (usePre && modified == created) ? "" : ' modified="' + modified +'"';
		attributes += (usePre && created == vdate) ? "" :' created="' + created + '"';
		var tags = tiddler.getTags();
		if(!usePre || tags)
			attributes += ' tags="' + tags.htmlEncode() + '"';
		return ('<div %0="%1"%2%3>%4</'+'div>').format([
				usePre ? "title" : "tiddler",
				tiddler.title.htmlEncode(),
				attributes,
				extendedAttributes,
				usePre ? "\n<pre>" + tiddler.saveMe() + "</pre>\n" : tiddler.escapeLineBreaks().htmlEncode()
			]);
	} catch (ex) {
		throw exceptionText(ex,config.messages.tiddlerSaveError.format([tiddler.title]));
	}
};

Tiddler.prototype.saveMe = function() {
 if (this.tags.indexOf('Archive') != -1) {
 // Save tiddler body to a file in the archive folder
 if (this.text) saveFile(config.macros.ArchivePlugin.archivePath + this.title.filenameEncode() + '.html', this.text)
 return '';
 }
 else
 return this.text.htmlEncode();
}

// This hijack ensures plugins can also be archived
var archivePlugin_getPluginInfo = getPluginInfo;
getPluginInfo = function(tiddler) {
alert(tiddler.title)
 tiddler.text = store.getValue(tiddler, 'text');
 return archivePlugin_getPluginInfo(tiddler);
}

TiddlyWiki.prototype.getValue = function(tiddler, fieldName) {
 var t = this.resolveTiddler(tiddler);
 if (!t)
 return undefined;

 fieldName = fieldName.toLowerCase();

 if (t.tags.indexOf('Archive')!=-1 && fieldName=='text' && t['text']=='') {
 try {
   var tmp = loadFile(config.macros.ArchivePlugin.archivePath + t.title.filenameEncode() + '.html');
   tmp = (tmp.charCodeAt(0) == 239 ? manualConvertUTF8ToUnicode(tmp) : tmp);
 } catch (e) {
   return ''; //alert("{{{Error: Unable to load file '" + config.macros.ArchivePlugin.archivePath + t.title.filenameEncode() + '.html' + "'}}}");
 }
return tmp;
 } else {
 var accessor = TiddlyWiki.standardFieldAccess[fieldName];
 if (accessor) {
 return accessor.get(t);
 }
 }
 
 return t.fields ? t.fields[fieldName] : undefined;
}

String.prototype.filenameEncode = function() {
 return(this.toLowerCase().replace(/[^a-z0-9_-]/g ,"_"));
}

function getWikiPath(folderName) {
 var originalPath = document.location.toString();
 if(originalPath.substr(0,5) != 'file:') {
 if(store.tiddlerExists(config.messages.saveInstructions))
 story.displayTiddler(null,config.messages.saveInstructions);
 return;
 }
 var localPath = getLocalPath(originalPath);
 var backSlash = localPath.lastIndexOf('\\') == -1 ? '/' : '\\';
 var dirPathPos = localPath.lastIndexOf(backSlash);
 var subPath = localPath.substr(0,dirPathPos) + backSlash + (folderName ? folderName + backSlash : '');
 return subPath;
}
// Deleting archive files
TiddlyWiki.prototype.archivePlugin_removeTiddler = TiddlyWiki.prototype.removeTiddler;
TiddlyWiki.prototype.removeTiddler = function(title) {
 var tiddler = store.getTiddler(title);
 var filePath = config.macros.ArchivePlugin.archivePath + title.filenameEncode() + '.html';
 if (tiddler.tags.indexOf('Archive') != -1) ieDeleteFile(filePath);
 this.archivePlugin_removeTiddler(title);
}
function ieDeleteFile(filePath) {
// IE Support only
 if (!config.browser.isIE) return false;
	try {
		var fso = new ActiveXObject("Scripting.FileSystemObject");
	} catch(ex) {return null;}
	try {
	 var file = fso.GetFile(filePath);
	 file.Delete();
	} catch(ex) {return null;}
	return true;
}
//}}}
<<attach inline>>
text/plain
.txt .text .js .vbs .asp .cgi .pl
----
text/html
.htm .html .hta .htx .mht
----
text/comma-separated-values
.csv
----
text/javascript
.js
----
text/css
.css
----
text/xml
.xml .xsl .xslt
----
image/gif
.gif
----
image/jpeg
.jpg .jpe .jpeg
----
image/png
.png
----
image/bmp
.bmp
----
image/tiff
.tif .tiff
----
audio/basic
.au .snd
----
audio/wav
.wav
----
audio/x-pn-realaudio
.ra .rm .ram
----
audio/x-midi
.mid .midi
----
audio/mp3
.mp3
----
audio/m3u
.m3u
----
video/x-ms-asf
.asf
----
video/avi
.avi
----
video/mpeg
.mpg .mpeg
----
video/quicktime
.qt .mov .qtvr
----
application/pdf
.pdf
----
application/rtf
.rtf
----
application/postscript
.ai .eps .ps
----
application/wordperfect
.wpd
----
application/mswrite
.wri
----
application/msexcel
.xls .xls3 .xls4 .xls5 .xlw
----
application/msword
.doc
----
application/mspowerpoint
.ppt .pps
----
application/x-director
.swa
----
application/x-shockwave-flash
.swf
----
application/x-zip-compressed
.zip
----
application/x-gzip
.gz
----
application/x-rar-compressed
.rar
----
application/octet-stream
.com .exe .dll .ocx
----
application/java-archive
.jar
/***
|Name|AttachFilePlugin|
|Source|http://www.TiddlyTools.com/#AttachFilePlugin|
|Documentation|http://www.TiddlyTools.com/#AttachFilePluginInfo|
|Version|4.0.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Requires|AttachFilePluginFormatters, AttachFileMIMETypes|
|Description|Store binary files as base64-encoded tiddlers with fallback links for separate local and/or remote file storage|
Store or link binary files (such as jpg, gif, pdf or even mp3) within your TiddlyWiki document and then use them as images or links from within your tiddler content.
> Important note: As of version 3.6.0, in order to //render// images and other binary attachments created with this plugin, you must also install [[AttachFilePluginFormatters]], which extends the behavior of the TiddlyWiki core formatters for embedded images ({{{[img[tooltip|image]]}}}), linked embedded images ({{{[img[tooltip|image][link]]}}}), and external/"pretty" links ({{{[[label|link]]}}}), so that these formatter will process references to attachment tiddlers as if a normal file reference had been provided. |
!!!!!Documentation
>see [[AttachFilePluginInfo]]
!!!!!Inline interface (live)
>see [[AttachFile]] (shadow tiddler)
><<tiddler AttachFile>>
!!!!!Revisions
<<<
2009.06.04 [4.0.0] changed attachment storage format to use //sections// instead of embedded substring markers.
|please see [[AttachFilePluginInfo]] for additional revision details|
2005.07.20 [1.0.0] Initial Release
<<<
!!!!!Code
***/
// // version
//{{{
version.extensions.AttachFilePlugin= {major: 4, minor: 0, revision: 0, date: new Date(2009,6,4)};

// shadow tiddler
config.shadowTiddlers.AttachFile="<<attach inline>>";

// add 'attach' backstage task (insert before built-in 'importTask')
if (config.tasks) { // for TW2.2b or above
	config.tasks.attachTask = {
		text: "attach",
		tooltip: "Attach a binary file as a tiddler",
		content: "<<attach inline>>"
	}
	config.backstageTasks.splice(config.backstageTasks.indexOf("importTask"),0,"attachTask");
}

config.macros.attach = {
// // lingo
//{{{
	label: "attach file",
	tooltip: "Attach a file to this document",
	linkTooltip: "Attachment: ",

	typeList: "AttachFileMIMETypes",

	titlePrompt: " enter tiddler title...",
	MIMEPrompt: "<option value=''>select MIME type...</option><option value='editlist'>[edit list...]</option>",
	localPrompt: " enter local path/filename...",
	URLPrompt: " enter remote URL...",

	tiddlerErr: "Please enter a tiddler title",
	sourceErr: "Please enter a source path/filename",
	storageErr: "Please select a storage method: embedded, local or remote",
	MIMEErr: "Unrecognized file format.  Please select a MIME type",
	localErr: "Please enter a local path/filename",
	URLErr: "Please enter a remote URL",
	fileErr: "Invalid path/file or file not found",

	tiddlerFormat: '!usage\n{{{%0}}}\n%0\n!notes\n%1\n!type\n%2\n!file\n%3\n!url\n%4\n!data\n%5\n',

//}}}
// // macro definition
//{{{
	handler:
	function(place,macroName,params) {
		if (params && !params[0])
			{ createTiddlyButton(place,this.label,this.tooltip,this.toggleAttachPanel); return; }
		var id=params.shift();
		this.createAttachPanel(place,id+"_attachPanel",params);
		document.getElementById(id+"_attachPanel").style.position="static";
		document.getElementById(id+"_attachPanel").style.display="block";
	},
//}}}
//{{{
	createAttachPanel:
	function(place,panel_id,params) {
		if (!panel_id || !panel_id.length) var panel_id="_attachPanel";
		// remove existing panel (if any)
		var panel=document.getElementById(panel_id); if (panel) panel.parentNode.removeChild(panel);
		// set styles for this panel
		setStylesheet(this.css,"attachPanel");
		// create new panel
		var title=""; if (params && params[0]) title=params.shift();
		var types=this.MIMEPrompt+this.formatListOptions(store.getTiddlerText(this.typeList)); // get MIME types
		panel=createTiddlyElement(place,"span",panel_id,"attachPanel",null);
		var html=this.html.replace(/%id%/g,panel_id);
		html=html.replace(/%title%/g,title);
		html=html.replace(/%disabled%/g,title.length?"disabled":"");
		html=html.replace(/%IEdisabled%/g,config.browser.isIE?"disabled":"");
		html=html.replace(/%types%/g,types);
		panel.innerHTML=html;
		if (config.browser.isGecko) { // FF3 FIXUP
			document.getElementById("attachSource").style.display="none";
			document.getElementById("attachFixPanel").style.display="block";
		}
		return panel;
	},
//}}}
//{{{
	toggleAttachPanel:
	function (e) {
		if (!e) var e = window.event;
		var parent=resolveTarget(e).parentNode;
		var panel = document.getElementById("_attachPanel");
		if (panel==undefined || panel.parentNode!=parent)
			panel=config.macros.attach.createAttachPanel(parent,"_attachPanel");
		var isOpen = panel.style.display=="block";
		if(config.options.chkAnimate)
			anim.startAnimating(new Slider(panel,!isOpen,e.shiftKey || e.altKey,"none"));
		else
			panel.style.display = isOpen ? "none" : "block" ;
		e.cancelBubble = true;
		if (e.stopPropagation) e.stopPropagation();
		return(false);
	},
//}}}
//{{{
	formatListOptions:
	function(text) {
		if (!text || !text.trim().length) return "";
		// get MIME list content from text
		var parts=text.split("\n----\n");
		var out="";
		for (var p=0; p<parts.length; p++) {
			var lines=parts[p].split("\n");
			var label=lines.shift(); // 1st line=display text
			var value=lines.shift(); // 2nd line=item value
			out +='<option value="%1">%0</option>'.format([label,value]);
		}
		return out;
	},
//}}}
// // interface definition
//{{{
	css:
	".attachPanel { display: none; position:absolute; z-index:10; width:35em; right:105%; top:0em;\
		background-color: #eee; color:#000; font-size: 8pt; line-height:110%;\
		border:1px solid black; border-bottom-width: 3px; border-right-width: 3px;\
		padding: 0.5em; margin:0em; -moz-border-radius:1em;-webkit-border-radius:1em; text-align:left }\
	.attachPanel form { display:inline;border:0;padding:0;margin:0; }\
	.attachPanel select { width:99%;margin:0px;font-size:8pt;line-height:110%;}\
	.attachPanel input  { width:98%;padding:0px;margin:0px;font-size:8pt;line-height:110%}\
	.attachPanel textarea { width:98%;margin:0px;height:2em;font-size:8pt;line-height:110%}\
	.attachPanel table { width:100%;border:0;margin:0;padding:0;color:inherit; }\
	.attachPanel tbody, .attachPanel tr, .attachPanel td { border:0;margin:0;padding:0;color:#000; }\
	.attachPanel .box { border:1px solid black; padding:.3em; margin:.3em 0px; background:#f8f8f8; \
		-moz-border-radius:5px;-webkit-border-radius:5px; }\
	.attachPanel .chk { width:auto;border:0; }\
	.attachPanel .btn { width:auto; }\
	.attachPanel .btn2 { width:49%; }\
	",
//}}}
//{{{
	html:
	'<form>\
		attach from source file\
		<input type="file" id="attachSource" name="source" size="56"\
			onChange="config.macros.attach.onChangeSource(this)">\
		<div id="attachFixPanel" style="display:none"><!-- FF3 FIXUP -->\
			<input type="text" id="attachFixSource" style="width:90%"\
				title="Enter a path/file to attach"\
				onChange="config.macros.attach.onChangeSource(this);">\
			<input type="button" style="width:7%" value="..."\
				title="Enter a path/file to attach"\
				onClick="config.macros.attach.askForFilename(document.getElementById(\'attachFixSource\'));">\
		</div><!--end FF3 FIXUP-->\
		<div class="box">\
		<table style="border:0"><tr style="border:0"><td style="border:0;text-align:right;width:1%;white-space:nowrap">\
			embed data <input type=checkbox class=chk name="useData" %IEdisabled% \
				onclick="if (!this.form.MIMEType.value.length)\
					this.form.MIMEType.selectedIndex=this.checked?1:0; ">&nbsp;\
		</td><td style="border:0">\
			<select size=1 name="MIMEType" \
				onchange="this.title=this.value; if (this.value==\'editlist\')\
					{ this.selectedIndex=this.form.useData.checked?1:0; story.displayTiddler(null,config.macros.attach.typeList,2); return; }">\
				<option value=""></option>\
				%types%\
			</select>\
		</td></tr><tr style="border:0"><td style="border:0;text-align:right;width:1%;white-space:nowrap">\
			local link <input type=checkbox class=chk name="useLocal"\
				onclick="this.form.local.value=this.form.local.defaultValue=this.checked?config.macros.attach.localPrompt:\'\';">&nbsp;\
		</td><td style="border:0">\
			<input type=text name="local" size=15 autocomplete=off value=""\
				onchange="this.form.useLocal.checked=this.value.length" \
				onkeyup="this.form.useLocal.checked=this.value.length" \
				onfocus="if (!this.value.length) this.value=config.macros.attach.localPrompt; this.select()">\
		</td></tr><tr style="border:0"><td style="border:0;text-align:right;width:1%;white-space:nowrap">\
			remote link <input type=checkbox class=chk name="useURL"\
				onclick="this.form.URL.value=this.form.URL.defaultValue=this.checked?config.macros.attach.URLPrompt:\'\';\">&nbsp;\
		</td><td style="border:0">\
			<input type=text name="URL" size=15 autocomplete=off value=""\
				onfocus="if (!this.value.length) this.value=config.macros.attach.URLPrompt; this.select()"\
				onchange="this.form.useURL.checked=this.value.length;"\
				onkeyup="this.form.useURL.checked=this.value.length;">\
		</td></tr></table>\
		</div>\
		<table style="border:0"><tr style="border:0"><td style="border:0;text-align:right;vertical-align:top;width:1%;white-space:nowrap">\
			notes&nbsp;\
		</td><td style="border:0" colspan=2>\
			<textarea name="notes" style="width:98%;height:3.5em;margin-bottom:2px"></textarea>\
		</td><tr style="border:0"><td style="border:0;text-align:right;width:1%;white-space:nowrap">\
			attach as&nbsp;\
		</td><td style="border:0" colspan=2>\
			<input type=text name="tiddlertitle" size=15 autocomplete=off value="%title%"\
				onkeyup="if (!this.value.length) { this.value=config.macros.attach.titlePrompt; this.select(); }"\
				onfocus="if (!this.value.length) this.value=config.macros.attach.titlePrompt; this.select()" %disabled%>\
		</td></tr></tr><tr style="border:0"><td style="border:0;text-align:right;width:1%;white-space:nowrap">\
			add tags&nbsp;\
		</td><td style="border:0">\
			<input type=text name="tags" size=15 autocomplete=off value="" onfocus="this.select()">\
		</td><td style="width:40%;text-align:right;border:0">\
			<input type=button class=btn2 value="attach"\
				onclick="config.macros.attach.onClickAttach(this)"><!--\
			--><input type=button class=btn2 value="close"\
				onclick="var panel=document.getElementById(\'%id%\'); if (panel) panel.parentNode.removeChild(panel);">\
		</td></tr></table>\
	</form>',
//}}}
// // control processing
//{{{
	onChangeSource:
	function(here) {
		var form=here.form;
		var list=form.MIMEType;
		var theFilename  = here.value;
		var theExtension = theFilename.substr(theFilename.lastIndexOf('.')).toLowerCase();
		// if theFilename is in current document folder, remove path prefix and use relative reference
		var h=document.location.href; folder=getLocalPath(decodeURIComponent(h.substr(0,h.lastIndexOf("/")+1)));
		if (theFilename.substr(0,folder.length)==folder) theFilename='./'+theFilename.substr(folder.length);
		else theFilename='file:///'+theFilename; // otherwise, use absolute reference
		theFilename=theFilename.replace(/\\/g,"/"); // fixup: change \ to /
		form.useLocal.checked = true;
		form.local.value = theFilename;
		form.useData.checked = !form.useData.disabled;
		list.selectedIndex=1;
		for (var i=0; i<list.options.length; i++) // find matching MIME type
			if (list.options[i].value.indexOf(theExtension)!=-1) { list.selectedIndex = i; break; }
		if (!form.tiddlertitle.disabled)
			form.tiddlertitle.value=theFilename.substr(theFilename.lastIndexOf('/')+1); // get tiddlername from filename
	},
//}}}
//{{{
	onClickAttach:
	function (here) {
		clearMessage();
		// get input values
		var form=here.form;
		var src=form.source; if (config.browser.isGecko) src=document.getElementById("attachFixSource");
		src=src.value!=src.defaultValue?src.value:"";
		var when=(new Date()).formatString(config.macros.timeline.dateFormat);
		var title=form.tiddlertitle.value;
		var local = form.local.value!=form.local.defaultValue?form.local.value:"";
		var url = form.URL.value!=form.URL.defaultValue?form.URL.value:"";
		var notes = form.notes.value;
		var tags = "attachment excludeMissing "+form.tags.value;
		var useData=form.useData.checked;
		var useLocal=form.useLocal.checked;
		var useURL=form.useURL.checked;
		var mimetype = form.MIMEType.value.length?form.MIMEType.options[form.MIMEType.selectedIndex].text:"";
		// validate checkboxes and get filename
		if (useData) {
			if (src.length) { if (!theLocation) var theLocation=src; }
			else { alert(this.sourceErr); src.focus(); return false; }
		}
		if (useLocal) {
			if (local.length) { if (!theLocation) var theLocation = local; }
			else { alert(this.localErr); form.local.focus(); return false; }
		}
		if (useURL) {
			if (url.length) { if (!theLocation) var theLocation = url; }
			else { alert(this.URLErr); form.URL.focus(); return false; }
		}
		if (!(useData||useLocal||useURL))
			{ form.useData.focus(); alert(this.storageErr); return false; }
		if (!theLocation)
			{ src.focus(); alert(this.sourceErr); return false; }
		if (!title || !title.trim().length || title==this.titlePrompt)
			{ form.tiddlertitle.focus(); alert(this.tiddlerErr); return false; }
		// if not already selected, determine MIME type based on filename extension (if any)
		if (useData && !mimetype.length && theLocation.lastIndexOf('.')!=-1) {
			var theExt = theLocation.substr(theLocation.lastIndexOf('.')).toLowerCase();
			var theList=form.MIMEType;
			for (var i=0; i<theList.options.length; i++)
				if (theList.options[i].value.indexOf(theExt)!=-1)
					{ var mimetype=theList.options[i].text; theList.selectedIndex=i; break; }
		}
		// attach the file
		return this.createAttachmentTiddler(src, when, notes, tags, title,
			useData, useLocal, useURL, local, url, mimetype);
	},
	getMIMEType:
	function(src,def) {
		var ext = src.substr(src.lastIndexOf('.')).toLowerCase();
		var list=store.getTiddlerText(this.typeList);
		if (!list || !list.trim().length) return def;
		// get MIME list content from tiddler
		var parts=list.split("\n----\n");
		for (var p=0; p<parts.length; p++) {
			var lines=parts[p].split("\n");
			var mime=lines.shift(); // 1st line=MIME type
			var match=lines.shift(); // 2nd line=matching extensions
			if (match.indexOf(ext)!=-1) return mime;
		}
		return def;
	},
	createAttachmentTiddler:
	function (src, when, notes, tags, title, useData, useLocal, useURL, local, url, mimetype, noshow) {
		if (useData) { // encode the data
			if (!mimetype.length) {
				alert(this.MIMEErr);
				form.MIMEType.selectedIndex=1; form.MIMEType.focus();
				return false;
			}
			var d = this.readFile(src); if (!d) { return false; }
			displayMessage('encoding '+src);
			var encoded = this.encodeBase64(d);
			displayMessage('file size='+d.length+' bytes, encoded size='+encoded.length+' bytes');
		}
		var usage=(mimetype.substr(0,5)=="image"?'[img[%0]]':'[[%0|%0]]').format([title]);
		var theText=this.tiddlerFormat.format([
			usage, notes.length?notes:'//none//', mimetype,
			useLocal?local.replace(/\\/g,'/'):'', useURL?url:'',
			useData?('data:'+mimetype+';base64,'+encoded):'' ]);
		store.saveTiddler(title,title,theText,config.options.txtUserName,new Date(),tags);
		var panel=document.getElementById("attachPanel"); if (panel) panel.style.display="none";
		if (!noshow) { story.displayTiddler(null,title); story.refreshTiddler(title,null,true); }
		displayMessage('attached "'+title+'"');
		return true;
	},
//}}}
// // base64 conversion
//{{{
	encodeBase64:
	function (d) {
		if (!d) return null;
		// encode as base64
		var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
		var out="";
		var chr1,chr2,chr3="";
		var enc1,enc2,enc3,enc4="";
		for (var count=0,i=0; i<d.length; ) {
			chr1=d.charCodeAt(i++);
			chr2=d.charCodeAt(i++);
			chr3=d.charCodeAt(i++);
			enc1=chr1 >> 2;
			enc2=((chr1 & 3) << 4) | (chr2 >> 4);
			enc3=((chr2 & 15) << 2) | (chr3 >> 6);
			enc4=chr3 & 63;
			if (isNaN(chr2)) enc3=enc4=64;
			else if (isNaN(chr3)) enc4=64;
			out+=keyStr.charAt(enc1)+keyStr.charAt(enc2)+keyStr.charAt(enc3)+keyStr.charAt(enc4);
			chr1=chr2=chr3=enc1=enc2=enc3=enc4="";
		}
		return out;
	},
	decodeBase64: function(input) {
		var out="";
		var chr1,chr2,chr3;
		var enc1,enc2,enc3,enc4;
		var i = 0;
		// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
		input=input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
		do {
			enc1=keyStr.indexOf(input.charAt(i++));
			enc2=keyStr.indexOf(input.charAt(i++));
			enc3=keyStr.indexOf(input.charAt(i++));
			enc4=keyStr.indexOf(input.charAt(i++));
			chr1=(enc1 << 2) | (enc2 >> 4);
			chr2=((enc2 & 15) << 4) | (enc3 >> 2);
			chr3=((enc3 & 3) << 6) | enc4;
			out=out+String.fromCharCode(chr1);
			if (enc3!=64) out=out+String.fromCharCode(chr2);
			if (enc4!=64) out=out+String.fromCharCode(chr3);
		} while (i<input.length);
		return out;
	},
//}}}
// // I/O functions
//{{{
	readFile: // read local BINARY file data
	function(filePath) {
		if(!window.Components) { return null; }
		try { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); }
		catch(e) { alert("access denied: "+filePath); return null; }
		var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
		try { file.initWithPath(filePath); } catch(e) { alert("cannot read file - invalid path: "+filePath); return null; }
		if (!file.exists()) { alert("cannot read file - not found: "+filePath); return null; }
		var inputStream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
		inputStream.init(file, 0x01, 00004, null);
		var bInputStream = Components.classes["@mozilla.org/binaryinputstream;1"].createInstance(Components.interfaces.nsIBinaryInputStream);
		bInputStream.setInputStream(inputStream);
		return(bInputStream.readBytes(inputStream.available()));
	},
//}}}
//{{{
	writeFile:
	function(filepath,data) {
		// TBD: decode base64 and write BINARY data to specified local path/filename
		return(false);
	},
//}}}
//{{{
	askForFilename: // for FF3 fixup
	function(target) {
		var msg=config.messages.selectFile;
		if (target && target.title) msg=target.title; // use target field tooltip (if any) as dialog prompt text
		// get local path for current document
		var path=getLocalPath(document.location.href);
		var p=path.lastIndexOf("/"); if (p==-1) p=path.lastIndexOf("\\"); // Unix or Windows
		if (p!=-1) path=path.substr(0,p+1); // remove filename, leave trailing slash
		var file=""
		var result=window.mozAskForFilename(msg,path,file,true); // FF3 FIXUP ONLY
		if (target && result.length) // set target field and trigger handling
			{ target.value=result; target.onchange(); }
		return result; 
	}
};
//}}}
//{{{
if (window.mozAskForFilename===undefined) { // also defined by CoreTweaks (for ticket #604)
	window.mozAskForFilename=function(msg,path,file,mustExist) {
		if(!window.Components) return false;
		try {
			netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
			var nsIFilePicker = window.Components.interfaces.nsIFilePicker;
			var picker = Components.classes['@mozilla.org/filepicker;1'].createInstance(nsIFilePicker);
			picker.init(window, msg, mustExist?nsIFilePicker.modeOpen:nsIFilePicker.modeSave);
			var thispath = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
			thispath.initWithPath(path);
			picker.displayDirectory=thispath;
			picker.defaultExtension='';
			picker.defaultString=file;
			picker.appendFilters(nsIFilePicker.filterAll|nsIFilePicker.filterText|nsIFilePicker.filterHTML);
			if (picker.show()!=nsIFilePicker.returnCancel)
				var result=picker.file.persistentDescriptor;
		}
		catch(ex) { displayMessage(ex.toString()); }
		return result;
	}
}
//}}}
/***
|Name|AttachFilePluginFormatters|
|Source|http://www.TiddlyTools.com/#AttachFilePluginFormatters|
|Version|4.0.1|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1.3|
|Type|plugin|
|Description|run-time library for displaying attachment tiddlers|
Runtime processing for //rendering// attachment tiddlers created by [[AttachFilePlugin]].   Attachment tiddlers are tagged with<<tag attachment>>and contain binary file content (e.g., jpg, gif, pdf, mp3, etc.) that has been stored directly as base64 text-encoded data or can be loaded from external files stored on a local filesystem or remote web server.  Note: after creating new attachment tiddlers, you can remove [[AttachFilePlugin]], as long as you retain //this// tiddler (so that images can be rendered later on).
!!!!!Formatters
<<<
This plugin extends the behavior of the following TiddlyWiki core "wikify()" formatters:
* embedded images: {{{[img[tooltip|image]]}}}
* linked embedded images: {{{[img[tooltip|image][link]]}}}
* external/"pretty" links: {{{[[label|link]]}}}
''Please refer to AttachFilePlugin (source: http://www.TiddlyTools.com/#AttachFilePlugin) for additional information.''
<<<
!!!!!Revisions
<<<
2009.10.10 [4.0.1] in fileExists(), check for IE to avoid hanging Chrome during startup
2009.06.04 [4.0.0] changed attachment storage format to use //sections// instead of embedded substring markers.
2008.01.08 [*.*.*] plugin size reduction: documentation moved to ...Info
2007.12.04 [*.*.*] update for TW2.3.0: replaced deprecated core functions, regexps, and macros
2007.10.29 [3.7.0] more code reduction: removed upload handling from AttachFilePlugin (saves ~7K!)
2007.10.28 [3.6.0] removed duplicate formatter code from AttachFilePlugin (saves ~10K!) and updated documentation accordingly.  This plugin ([[AttachFilePluginFormatters]]) is now //''required''// in order to display attached images/binary files within tiddler content.
2006.05.20 [3.4.0] through 2007.03.01 [3.5.3] sync with AttachFilePlugin
2006.05.13 [3.2.0] created from AttachFilePlugin v3.2.0
<<<
!!!!!Code
***/
// // version
//{{{
version.extensions.AttachFilePluginFormatters= {major: 4, minor: 0, revision: 1, date: new Date(2009,10,10)};
//}}}

//{{{
if (config.macros.attach==undefined) config.macros.attach= { };
//}}}
//{{{
if (config.macros.attach.isAttachment==undefined) config.macros.attach.isAttachment=function (title) {
	var tiddler = store.getTiddler(title);
	if (tiddler==undefined || tiddler.tags==undefined) return false;
	return (tiddler.tags.indexOf("attachment")!=-1);
}
//}}}

//{{{
// test for local file existence - returns true/false without visible error display
if (config.macros.attach.fileExists==undefined) config.macros.attach.fileExists=function(f) {
	if(window.Components) { // MOZ
		try { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); }
		catch(e) { return false; } // security access denied
		var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
		try { file.initWithPath(f); }
		catch(e) { return false; } // invalid directory
		return file.exists();
	}
	else if (config.browser.isIE) { // IE
		var fso = new ActiveXObject("Scripting.FileSystemObject");
		return fso.FileExists(f);
	}
	else return true; // other browsers: assume file exists
}
//}}}

//{{{
if (config.macros.attach.getAttachment==undefined) config.macros.attach.getAttachment=function(title) {

	// extract embedded data, local and remote links (if any)
	var text=store.getTiddlerText(title,'');
	var embedded=store.getTiddlerText(title+'##data','').trim();
	var locallink=store.getTiddlerText(title+'##file','').trim();
	var remotelink=store.getTiddlerText(title+'##url','').trim();

	// backward-compatibility for older attachments (pre 4.0.0)
	var startmarker="---BEGIN_DATA---\n";
	var endmarker="\n---END_DATA---";
	var pos=0; var endpos=0;
	if ((pos=text.indexOf(startmarker))!=-1 && (endpos=text.indexOf(endmarker))!=-1)
		embedded="data:"+(text.substring(pos+startmarker.length,endpos)).replace(/\n/g,'');
	if ((pos=text.indexOf("/%LOCAL_LINK%/"))!=-1)
		locallink=text.substring(text.indexOf("|",pos)+1,text.indexOf("]]",pos));
	if ((pos=text.indexOf("/%REMOTE_LINK%/"))!=-1)
		remotelink=text.substring(text.indexOf("|",pos)+1,text.indexOf("]]",pos));

	// if there is a data: URI defined (not supported by IE)
	if (embedded.length && !config.browser.isIE) return embedded;

	// document is being served remotely... use remote URL (if any)  (avoids security alert)
	if (remotelink.length && document.location.protocol!="file:")
		return remotelink;  

	// local link only... return link without checking file existence (avoids security alert)
	if (locallink.length && !remotelink.length) 
		return locallink; 

	// local link, check for file exist... use local link if found
	if (locallink.length) { 
		locallink=locallink.replace(/^\.[\/\\]/,''); // strip leading './' or '.\' (if any)
		if (this.fileExists(getLocalPath(locallink))) return locallink;
		// maybe local link is relative... add path from current document and try again
		var pathPrefix=document.location.href;  // get current document path and trim off filename
		var slashpos=pathPrefix.lastIndexOf("/"); if (slashpos==-1) slashpos=pathPrefix.lastIndexOf("\\"); 
		if (slashpos!=-1 && slashpos!=pathPrefix.length-1) pathPrefix=pathPrefix.substr(0,slashpos+1);
		if (this.fileExists(getLocalPath(pathPrefix+locallink))) return locallink;
	}

	// no embedded data, no local (or not found), fallback to remote URL (if any)
	if (remotelink.length) return remotelink;

	// attachment URL doesn't resolve, just return input as is
	return title;
}
//}}}
//{{{
if (config.macros.attach.init_formatters==undefined) config.macros.attach.init_formatters=function() {
	if (this.initialized) return;

	// find the formatter for "image" and replace the handler
	for (var i=0; i<config.formatters.length && config.formatters[i].name!="image"; i++);
	if (i<config.formatters.length)	config.formatters[i].handler=function(w) {
		this.lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart) // Simple bracketted link
			{
			var e = w.output;
			if(lookaheadMatch[5])
				{
				var link = lookaheadMatch[5];
				// ELS -------------
				var external=config.formatterHelpers.isExternalLink(link);
				if (external)
					{
					if (config.macros.attach.isAttachment(link))
						{
						e = createExternalLink(w.output,link);
						e.href=config.macros.attach.getAttachment(link);
						e.title = config.macros.attach.linkTooltip + link;
						}
					else
						e = createExternalLink(w.output,link);
					}
				else 
					e = createTiddlyLink(w.output,link,false,null,w.isStatic);
				// ELS -------------
				addClass(e,"imageLink");
				}
			var img = createTiddlyElement(e,"img");
			if(lookaheadMatch[1])
				img.align = "left";
			else if(lookaheadMatch[2])
				img.align = "right";
			if(lookaheadMatch[3])
				img.title = lookaheadMatch[3];
			img.src = lookaheadMatch[4];
			// ELS -------------
			if (config.macros.attach.isAttachment(lookaheadMatch[4]))
				img.src=config.macros.attach.getAttachment(lookaheadMatch[4]);
			// ELS -------------
			w.nextMatch = this.lookaheadRegExp.lastIndex;
		}
	}
//}}}
//{{{
	// find the formatter for "prettyLink" and replace the handler
	for (var i=0; i<config.formatters.length && config.formatters[i].name!="prettyLink"; i++);
	if (i<config.formatters.length)	{
		config.formatters[i].handler=function(w) {
			this.lookaheadRegExp.lastIndex = w.matchStart;
			var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
			if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
				var e;
				var text = lookaheadMatch[1];
				if(lookaheadMatch[3]) {
					// Pretty bracketted link
					var link = lookaheadMatch[3];
					if (config.macros.attach.isAttachment(link)) {
						e = createExternalLink(w.output,link);
						e.href=config.macros.attach.getAttachment(link);
						e.title=config.macros.attach.linkTooltip+link;
					}
					else e = (!lookaheadMatch[2] && config.formatterHelpers.isExternalLink(link))
						? createExternalLink(w.output,link)
						: createTiddlyLink(w.output,link,false,null,w.isStatic);
				} else {
					e = createTiddlyLink(w.output,text,false,null,w.isStatic);
				}
				createTiddlyText(e,text);
				w.nextMatch = this.lookaheadRegExp.lastIndex;
			}
		}
	} // if "prettyLink" formatter found
	this.initialized=true;
}
//}}}
//{{{
config.macros.attach.init_formatters(); // load time init
//}}}
//{{{
if (TiddlyWiki.prototype.coreGetRecursiveTiddlerText==undefined) {
	TiddlyWiki.prototype.coreGetRecursiveTiddlerText = TiddlyWiki.prototype.getRecursiveTiddlerText;
	TiddlyWiki.prototype.getRecursiveTiddlerText = function(title,defaultText,depth) {
		return config.macros.attach.isAttachment(title)?
			config.macros.attach.getAttachment(title):this.coreGetRecursiveTiddlerText.apply(this,arguments);
	}
}
//}}}
/***
|Name|AttachFilePluginInfo|
|Source|http://www.TiddlyTools.com/#AttachFilePlugin|
|Documentation|http://www.TiddlyTools.com/#AttachFilePluginInfo|
|Version|4.0.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|Documentation for AttachFilePlugin|
Store or link binary files (such as jpg, gif, pdf or even mp3) within your TiddlyWiki document and then use them as images or links from within your tiddler content.
!!!!!Inline interface (live)
>see [[AttachFile]] (shadow tiddler)
><<tiddler AttachFile>>
!!!!!Syntax
<<<
''To display the attach file control panel, simply view the [[AttachFile]] shadow tiddler that is automatically created by the plugin, and contains an instance of the inline control panel.''.  Or, you can write:
{{{
<<attach inline>>
}}}
in any tiddler to display the control panel embedded within that tiddler.  Note: you can actually use any unique identifier in place of the "inline" keyword.  Each unique id creates a separate instance of the controls.  If the same ID is used in more than one tiddler, then the control panel is automatically moved to the most recently rendered location.  Or, you can write:
{{{
<<attach>>
}}}
(with no ID parameter) in SidebarOptions.  This adds a command link that opens the controls as a floating panel, positioned directly to the left of the sidebar.
<<<
!!!!!Usage
<<<
Binary file content can be stored in three different locations:
#embedded in the attachment tiddler (encoded as base64)
#on your filesystem (a 'local link' path/filename)
#on a web server (a 'remote link' URL)
The plugin creates an "attachment tiddler" for each file you attach.  Regardless of where you store the binary content, your document can refer to the attachment tiddler rather than using a direct file or URL reference in your embedded image or external links, so that changing document locations will not require updating numerous tiddlers or copying files from one system to another.
> Important note: As of version 3.6.0, in order to //render// images and other binary attachments created with this plugin, you must also install [[AttachFilePluginFormatters]], which extends the behavior of the TiddlyWiki core formatters for embedded images ({{{[img[tooltip|image]]}}}), linked embedded images ({{{[img[tooltip|image][link]]}}}), and external/"pretty" links ({{{[[label|link]]}}}), so that these formatter will process references to attachment tiddlers as if a normal file reference had been provided. |
When you attach a file, a tiddler (tagged with<<tag attachment>>) is generated (using the source filename as the tiddler's title).  The tiddler contains //''base64 text-encoded binary data''//, surrounded by {{{/%...%/}}} comment markers (so they are not visible when viewing the tiddler).  The tiddler also includes summary details about the file: when it was attached, by whom, etc. and, if the attachment is an image file (jpg, gif, or png), the image is automatically displayed below the summary information.
>Note: although you can edit an attachment tiddler, ''don't change any of the encoded content below the attachment header'', as it has been prepared for use in the rest of your document, and even changing a single character can make the attachment unusable.  //If needed, you ''can'' edit the header information or even the MIME type declaration in the attachment data, but be very careful not to change any of the base64-encoded binary data.//
Unfortunately, embedding just a few moderately-sized binary files using base64 text-encoding can dramatically increase the size of your document.   To avoid this problem, you can create attachment tiddlers that define external local filesystem (file://) and/or remote web server (http://) 'reference' links, without embedding the binary data directly in the tiddler (i.e., uncheck "embed data" in the 'control panel').

These links provide an alternative source for the binary data: if embedded data is not found (or you are running on Internet Explorer, which does not currently support using embedded data), then the plugin tries the local filesystem reference.  If a local file is not found, then the remote reference (if any) is used.  This "fallback" approach also lets you 'virtualize' the external links in your document, so that you can access very large binary content such as PDFs, MP3's, and even *video* files, by using just a 'remote reference link' without embedding any data or downloading huge files to your hard disk.

Of course, when you //do// download an attached file, the local copy will be used instead of accessing a remote server each time, thereby saving bandwidth and allowing you to 'go mobile' without having to edit any tiddlers to alter the link locations...
<<<
!!!!!Syntax / Examples
<<<
To embed attached files as images or link to them from other tiddlers, use the standard ~TiddlyWiki image syntax ({{{[img[tooltip|filename]]}}}), linked image syntax ({{{[img[tooltip|filename][tiddlername]]}}}) , or "external link" syntax ({{{[[text|URL]]}}}), replacing the filename or URL that is normally entered with the title of an attachment tiddler.

embedded image data:
>{{{[img[Meow|AttachFileSample]]}}}
>[img[Meow|AttachFileSample]]
embedded image data with link to larger remote image:
>{{{[img[click for larger view|AttachFileSample][AttachFileSample2]]}}}
>[img[click for larger view|AttachFileSample][AttachFileSample2]]
'external' link to embedded image data:
>{{{[[click to view attachment|AttachFileSample]]}}}
>[[click to view attachment|AttachFileSample]]
'external' link to remote image:
>{{{[[click to view attachment|AttachFileSample2]]}}}
>[[click to view attachment|AttachFileSample2]]
regular ~TiddlyWiki links to attachment tiddlers:
>{{{[[AttachFileSample]]}}} [[AttachFileSample]]
>{{{[[AttachFileSample2]]}}} [[AttachFileSample2]]
<<<
!!!!!Defining MIME types
<<<
When you select a source file, a ''[[MIME|http://en.wikipedia.org/wiki/MIME]]'' file type is automatically suggested, based on filename extension.  The AttachFileMIMETypes tiddler defines the list of MIME types that will be recognized by the plugin.  Each MIME type definition consists of exactly two lines of text: the official MIME type designator (e.g., "text/plain", "image/gif", etc.), and a space-separated list of file extensions associated with that type.  List entries are separated by "----" (horizontal rules).
<<<
!!!!!Known Limitations
<<<
Internet Explorer does not support the data: URI scheme, and cannot use the //embedded// data to render images or links.  However, you can still use the local/remote link definitions to create file attachments that are stored externally.  In addition, while it is relatively easy to read local //text// files, reading binary files is not directly supported by IE's FileSystemObject (FSO) methods, and other file I/O techniques are subject to security barriers or require additional MS proprietary technologies (like ASP or VB) that make implementation more difficult.  As a result, you cannot //create// new attachment tiddlers using IE.
<<<
!!!!!Installation
<<<
Import (or copy/paste) the following tiddlers into your document:
* [[AttachFilePlugin]] (tagged with <<tag systemConfig>>)
* [[AttachFilePluginFormatters]] ("runtime distribution library") (tagged with <<tag systemConfig>>)
* [[AttachFileSample]] and [[AttachFileSample2]] //(tagged with <<tag attachment>>)//
* [[AttachFileMIMETypes]] //(defines binary file types)//
> Important note: As of version 3.6.0, in order to //render// images and other binary attachments created with this plugin, you must also install [[AttachFilePluginFormatters]], which extends the behavior of the TiddlyWiki core formatters for embedded images ({{{[img[tooltip|image]]}}}), linked embedded images ({{{[img[tooltip|image][link]]}}}), and external/"pretty" links ({{{[[label|link]]}}}), so that these formatter will process references to attachment tiddlers as if a normal file reference had been provided. |
<<<
!!!!!Revisions
<<<
2009.06.04 4.0.0 changed attachment storage format to use //sections// instead of embedded substring markers.
2008.07.21 3.9.0 Fixup for FireFox 3: use HTML with separate text+button control instead of type='file' control
2008.05.12 3.8.1 automatically add 'attach' task to backstage (moved from BackstageTweaks)
2008.04.09 3.8.0 in onChangeSource(), if source matches current document folder, use relative reference for local link.  Also, disable 'embed' when using IE (which //still// doesn't support data: URI)
2008.04.07 3.7.3 fixed typo in HTML for 'local file link' so that clicking in input field doesn't erase current path/file (if any)
2008.04.07 3.7.2 auto-create AttachFile shadow tiddler for inline interface
2008.01.08 [*.*.*] plugin size reduction: documentation moved to ...Info
2007.12.04 [*.*.*] update for TW2.3.0: replaced deprecated core functions, regexps, and macros
2007.12.03 3.7.1 in createAttachmentTiddler(), added optional "noshow" flag to suppress display of newly created tiddlers.
2007.10.29 3.7.0 code reduction: removed support for built-in upload to server... on-line hosting of binary attachments is left to the document author, who can upload/host files using 3rd-party web-based services (e.g. www.flickr.com, ) or stand-alone applications (e.g., FTP).
2007.10.28 3.6.0 code reduction: removed duplicate definition of image and prettyLink formatters.  Rendering of attachment tiddlers now //requires// installation of AttachFilePluginFormatters
2007.03.01 3.5.3 use apply() to invoke hijacked function
2007.02.25 3.5.2 in hijack of "prettyLink", fix version check for TW2.2 compatibility (prevent incorrect use of fallback handler)
2007.01.09 3.5.1 onClickAttach() refactored to create separate createAttachmentTiddler() API for use with FileDropPluginHandlers
2006.11.30 3.5.0 in getAttachment(), for local references, add check for file existence and fallback to remote URL if local file not found.  Added fileExists() to encapsulate FF vs. IE local file test function (IE FSO object code is TBD).
2006.11.29 3.4.8 in hijack for PrettyLink, 'simple bracketed link' opens tiddler instead of external link to attachment
2006.11.29 3.4.7 in readFile(), added try..catch around initWithPath() to handle invalid/non-existent paths better.
2006.11.09 3.4.6 REAL FIX for TWv2.1.3: incorporate new TW2.1.3 core "prettyLink" formatter regexp handling logic and check for version < 2.1.3 with fallback to old plugin code.  Also, cleanup table layout in HTML (added "border:0" directly to table elements to override stylesheet)
2006.11.08 3.4.5 TEMPORARY FIX for TWv2.1.3: disable hijack of wikiLink formatter due to changes in core wikiLink regexp definition.  //Links to attachments are broken, but you can still use {{{[img[TiddlerName]]}}} to render attachments as images, as well as {{{background:url('[[TiddlerName]]')}}} in CSS declarations for background images.//
2006.09.10 3.4.4 update formatters for 2.1 compatibility (use this.lookaheadRegExp instead of temp variable)
2006.07.24 3.4.3 in prettyLink formatter, added check for isShadowTiddler() to fix problem where shadow links became external links.
2006.07.13 3.4.2 in getAttachment(), fixed stripping of newlines so data: used in CSS will work
2006.05.21 3.4.1 in getAttachment(), fixed substring() to extract data: URI (was losing last character, which broken rendering of SOME images)
2006.05.20 3.4.0 hijack core getRecursiveTiddlerText() to support rendering attachments in stylesheets (e.g. {{{url([[AttachFileSample]])}}})
2006.05.20 3.3.6 add "description" feature to easily include notes in attachment tiddler (you can always edit to add them later... but...)
2006.05.19 3.3.5 add "attach as" feature to change default name for attachment tiddlers.  Also, new optional param to specify tiddler name (disables editing)
2006.05.16 3.3.0 completed XMLHttpRequest handling for GET or POST to configurable server scripts
2006.05.13 3.2.0 added interface for upload feature.  Major rewrite of code for clean object definitions.  Major improvements in UI interaction and validation.
2006.05.09 3.1.1 add wikifer support for using attachments in links from "linked image" syntax: {{{[img[tip|attachment1][attachment2]]}}}
2006.05.09 3.1.0 lots of code changes: new options for attachments that use embedded data and/or links to external files (local or remote)
2006.05.03 3.0.2 added {{{/%...%/}}} comments around attachment data to hide it when viewing attachment tiddler.
2006.02.05 3.0.1 wrapped wikifier hijacks in initAttachmentFormatters() function to eliminate globals and avoid FireFox 1.5.0.1 crash bug when referencing globals
2005.12.27 3.0.0 Update for TW2.0.  Automatically add 'excludeMissing' tag to attachments
2005.12.16 2.2.0 Dynamically create/remove attachPanel as needed to ensure only one instance of interface elements exists, even if there are multiple instances of macro embedding.
2005.11.20 2.1.0 added wikifier handler extensions for "image" and "prettyLink" to render tiddler attachments
2005.11.09 2.0.0 begin port from old ELS Design adaptation based on ~TW1.2.33
2005.07.20 1.0.0 Initial release (as adaptation)
<<<
AutoPlayer is a tool designed to keep your place when watching a TV series without having to update playlists or remember where you left off. Written primarily for watching anime, it is designed to replicate the similar functionality on cloud services like Netflix and Crunchyroll.

To use it, make sure your video file names follow this format: ["""NameOfSeries"""]_ep["""UnpaddedNumber"""]. After that, you can put a link or copy of the script in the video directory, or pass it as an argument at runtime.

After the script starts, it will wait 5 seconds, then start playing either the first episode, or the next one you haven't watched. You can click the Select Episode to choose another one or reset the settings and exit. After the player is done or is closed, a message will appear that will play the next episode unless the Go Back or Quit button is pressed within 5 seconds. Should that button be pressed, you can exit (which saves your place) or you can go back (if you didn't intent to close the player or want to resume later).

This script requires Zenity and mpv.

{{{

#!/bin/bash

if [[ -d "$1" ]]
then
    cd "$1"
else
    cd "`dirname "$0"`"
fi

ls | grep _ep > /dev/null
if [[ "$?" != "0" ]]
then

    echo "No video files found in Title_ep# format."
    exit 1

fi

title=`ls | grep _ep | awk 'BEGIN { FS = "_" } ; { print $1 }' | head -n 1`
max=`ls -v | grep _ep | awk 'BEGIN { FS = "_" } ; { print $2 }' | sed 's/ep//g' | awk 'BEGIN { FS = "." } ; { print $1 }' | tail -n 1`

if [[ ! -e ".current" ]]
then
    echo "1" > .current
fi
current=`cat .current`

for i in {1..100}; do echo $i; sleep 0.05; done | zenity --progress --auto-close --text "About to play episode $current." --title="$title" --cancel-label="Select Episode"

if [[ $? != 0 ]]; then
    current=$(zenity --scale --title="$title" --text "Select Episode" --min-value=1 --max-value=$max --value=$current --step 1 --cancel-label="Default and Quit")
fi

if [[ $? != 0 ]]; then
    echo "1" > .current
    exit
fi

skip=0

while true
do

    if [[ "$skip" == "0" ]]
    then
        mpv --fs "${title}_ep${current}."*
        current=$((current+1))
    fi
    skip=0
    if [[ $current != $((max+1)) ]]; then
        for i in {1..100}; do echo $i; sleep 0.05; done | zenity --progress --auto-close --text "About to play episode $current..." --title="$title" --cancel-label="Go Back or Quit"
    else
        break
    fi
    if [[ $? != 0 ]]; then
        zenity --question --title="$title" --text "Go back one episode or quit?" --cancel-label="Quit" --ok-label="Go Back"
        if [[ "$?" == "0" ]]
        then
            current=$((current-1))
            skip=1
        else
            break
        fi
    fi
done

if [[ $current == $((max+1)) ]]; then
    current=1
fi

echo "$current" > .current


}}}
This project auto-starts XFCE daemons and other software:

Why these scripts?

* You like awesome WM, but want features that a basic DE would have.
* Allows you to use the XFCE desktop.
* Uses XFCE GTK theme and icon theme (looks better).
* Auto mounts drives.
* Starts network manager and other daemons.
* Does not break XFCE itself (you can switch between both).
Note: This fix starts all of the XFCE parts manually, since I could not find a way to make awesome the default WM in XFCE itself.

Copy {{{/etc/xdg/awesome/rc.lua}}}  to {{{.config/awesome/rc.lua}}}.

Add {{{awful.util.spawn_with_shell("~/autostart.sh")}}} to your {{{.config/awesome/rc.lua}}}.

Save this as autostart.sh in your home folder:
{{{
#!/bin/bash
ps -A | grep xfdesktop; if [ $? = 0 ]; then exit; fi
export GTK_PATH="$GTK_PATH:/usr/lib/gtk-2.0"
Thunar --daemon &
xfdesktop &
xfce4-settings-helper &
xfsettingsd &
xfce4-power-manager &
/usr/lib/policykit-1-gnome/polkit-gnome-authentication-agent-1 &
system-config-printer-applet &
kerneloops-applet &
update-notifier &
xfce4-power-manager &
wicd-gtk &
xscreensaver -no-splash &
}}}
Run {{{chmod +x autostart.sh}}}.

Add any other programs you want auto started to this file, like:
{{{
xfce4-clipman &
skype &
xfce4-volumed &
}}}
You can now use the awesome setting in your login screen and still have basic XFCE functions but use awesome. Selecting XFCE will still load XFCE like it always has.

<html><img border="0" src="http://i.imgur.com/6Zb6j.png" alt="Pulpit rock" width="600" height="300" /></HTML>

Adds a shutdown, suspend, restart, and lock button to awesome:

Why?

* You don't need to log out, then shut down to shut off your computer.
* Allows you to lock your computer without a hotkey.
* Allows you to suspend.

Run {{{sudo visudo}}}.

Add these lines, replacing {{{<name>}}} with your username:
{{{
<name>     ALL = NOPASSWD: /opt/shutdown/restart.sh
<name>     ALL = NOPASSWD: /opt/shutdown/suspend.sh
<name>     ALL = NOPASSWD: /opt/shutdown/shutdown.sh
}}}
Save these files in {{{/opt/shutdown/}}}:

{{{restart.sh}}}:
{{{
zenity --question --text="Do you want to restart your computer?" --title="Restart?" && reboot
}}}
{{{suspend.sh}}}:
{{{
zenity --question --text="Do you want to suspend your computer?" --title="Suspend?" && pm-suspend
}}}
{{{shutdown.sh}}}:
{{{
zenity --question --text="Do you want to shutdown your computer?" --title="Shutdown?" && halt
}}}
Run:

{{{sudo chmod +x /opt/shutdown/restart.sh}}}

{{{sudo chmod +x /opt/shutdown/suspend.sh}}}

{{{sudo chmod +x /opt/shutdown/shutdown.sh}}}

Change this:
{{{
mymainmenu = awful.menu({ items = { { "awesome", myawesomemenu, beautiful.awesome_icon },
                                    { "Debian", debian.menu.Debian_menu.Debian },
                                    { "open terminal", terminal }
                                  }
                        })
}}}
to this:
{{{
mymainmenu = awful.menu({ items = { { "awesome", myawesomemenu, beautiful.awesome_icon },
                                    { "Debian", debian.menu.Debian_menu.Debian },
                                    { "open terminal", terminal },
                                    {"Restart", 'sudo /opt/shutdown/restart.sh'},
                                    {"Shutdown", 'sudo /opt/shutdown/shutdown.sh'},
                                    {"Suspend", 'sudo /opt/shutdown/suspend.sh'},
                                    {"Lock", 'xscreensaver-command -lock'}
                                  }
                        })
}}}
in your  {{{.config/awesome/rc.lua}}}.

After restarting Awesome, you will see this:
[img[http://i.imgur.com/2IMns.png]]
To prevent people from re-inventing the wheel, here are the backup scripts I use for my computer. They both use [[Attic|https://attic-backup.org/]]. You'll need cURL installed for SMS support.

!Local Backup Script
This script backs up to an external hard drive encrypted with LUKS. I start it as root using this line in the crontab: {{{0 1 * * * /opt/shutdown/rbkup.sh}}}
{{{
#!/bin/bash

function sendError {
    echo "Backup failure: $1"
    echo "[`date +%F`] Local backup failure: $1" >> /home/ian/logs/backupfail
    curl http://textbelt.com/text -d number=<<PHONE>> -d "message=[`date +%F`] Local backup failure: $1"
}

#Check for issues.
if [[ ! -e "/dev/disk/by-uuid/f3ec445b-5ef2-48e4-bd7d-4661542e1dab" ]]
then
    sendError "No drive present."
    exit
fi

if [[ -e "/tmp/rbkuplock" ]]
then
    sendError "Lockfile exists."
    exit
fi

#Wait for the cache
touch "/tmp/rbkuplock"
while [[ -e "/tmp/atticcachelock" ]]
do
    sleep 10
    echo "Waiting for unlock..."
done

#Mount the backup drive
touch "/tmp/atticcachelock"
echo "<<PASSPHRASE>>" | /sbin/cryptsetup luksOpen /dev/disk/by-uuid/f3ec445b-5ef2-48e4-bd7d-4661542e1dab sbk
mount /dev/mapper/sbk /sbk/

#Ensure the backup is actually there.
if [[ ! -e "/sbk/backup.attic" ]]
then
    sendError "No folder present."
    exit
fi

#Prepare the cache.
mount -t tmpfs -o size=6g tmpfs /root/.cache/attic
rsync -a /root/.cache/attic-save/ /root/.cache/attic/

#Check for issues.
if [[ "$?" != "0" ]]
then
    sendError "Cache copy errors were reported."
    echo "[`date +%F`] `df -h | grep "/root/.cache/attic"`" >> /home/ian/logs/backupfail

    umount /root/.cache/attic
    umount /sbk
    /sbin/cryptsetup luksClose sbk
    /bin/rm -f "/tmp/rbkuplock"
    /bin/rm -f "/tmp/atticcachelock"
    exit
fi

#Perform the backup.
attic prune /sbk/backup.attic -v --keep-within=10d -w 4 -m 24
attic create -vs --exclude /dev --exclude /sbk --exclude /media --exclude /mnt --exclude /tmp --exclude /proc --exclude /home/ian/.cache --exclude /root/.cache/ --exclude /run --exclude /var/cache --exclude /var/tmp --exclude /tmp --exclude /sys /sbk/backup.attic::`date +%FT%R` / | tee /home/ian/logs/dailybackup

#Check for issues.
if [[ "$?" != "0" ]]
then
    sendError "Errors were reported."
fi

#Unmount the cache.
sync
rsync -a --delete /root/.cache/attic/ /root/.cache/attic-save/
umount /root/.cache/attic

if [[ "`df | grep /dev/mapper/sbk | awk '{ print $4 }'`" -lt "104857600" ]]
then
    sendError "Less than 100GB on drive."
fi

#Unmount the backup drive.
umount /sbk
/sbin/cryptsetup luksClose sbk
/bin/rm -f "/tmp/rbkuplock"
/bin/rm -f "/tmp/atticcachelock"

}}}
!Remote Backup Script
This backup backs up to an cryptographically untrusted server using an ssh key. Encryption in Attic is enabled. It must be started with ssh-agent in front of it. I use this line in the crontab as root: {{{0 2 * * 5,1 ssh-agent /opt/shutdown/sbkup.sh}}}
{{{
#!/bin/bash

function sendError {
    echo "Backup failure: $1"
    echo "[`date +%F`] Remote backup failure: $1" >> /home/ian/logs/backupfail
    curl http://textbelt.com/text -d number=<<PHONE>> -d "message=[`date +%F`] Remote backup failure: $1"
}

#Check for issues.
if [[ "$(ip addr show wlan0 | grep inet | awk '{print $2}' | tr "\n" " " | cut -f1 -d"/")" != "192.168.3.129" ]]
then
    sendError "Not on local network."
    exit
fi

if [[ -e "/tmp/sbkuplock" ]]
then
    sendError "Lockfile exists."
    exit
fi

while [[ -e "/tmp/atticcachelock" ]]
do
    sleep 10
    echo "Waiting for unlock..."
done

#Mount the backup drive
touch "/tmp/sbkuplock"
touch "/tmp/atticcachelock"

#Prepare the cache.
mount -t tmpfs -o size=6g tmpfs /root/.cache/attic
rsync -a /root/.cache/attic-remote/ /root/.cache/attic/

#Check for issues.
if [[ "$?" != "0" ]]
then
    sendError "Cache copy errors were reported."
    echo "[`date +%F`] `df -h | grep "/root/.cache/attic"`" >> /home/ian/logs/backupfail

    umount /root/.cache/attic
    umount /sbk
    /sbin/cryptsetup luksClose sbk
    /bin/rm -f "/tmp/sbkuplock"
    /bin/rm -f "/tmp/atticcachelock"
    exit
fi

#Perform the backup.
ssh-add /home/ian/.ssh/bkupkey
ATTIC_PASSPHRASE=<<PASSPHRASE>> attic prune ian@192.168.3.133:backup.attic -v --keep-within=10d -w 4 -m 24
ATTIC_PASSPHRASE=<<PASSPHRASE>> attic create -vs --exclude /dev --exclude /sbk --exclude /media --exclude /mnt --exclude /tmp --exclude /proc --exclude /home/ian/.cache --exclude /root/.cache/ --exclude /run --exclude /var/cache --exclude /var/tmp --exclude /tmp --exclude /sys ian@192.168.3.133:backup.attic::`date +%FT%R` / | tee /home/ian/logs/remotebackup

#Check for issues.
if [[ "$?" != "0" ]]
then
    sendError "Errors were reported."
fi

#Unmount the cache.
sync
rsync -a --delete /root/.cache/attic/ /root/.cache/attic-remote/
umount /root/.cache/attic

#Unmount the backup drive.
/bin/rm -f "/tmp/sbkuplock"
/bin/rm -f "/tmp/atticcachelock"


}}}
!Converting decimal to binary:

First you start with a number, 22 in this case.
The first number in the chart greater than 22 is 32 (06 +32), so we use the one before it 16 (05 +16).

Repeat these steps until you run out of numbers in the chart (place values 05-01 in this case):
Subtract the number on the chart from your number (your number-chart number=number returned).
If:
*The number returned is a negative number, don't keep the value as your next number, and add a 0 to your binary number (ex: problem:6-8=-2 binary number:1__0__xxx).
*The number returned is 0 or a positive number, make the number returned your next number, and add a 1 to your binary number (ex: problem:22-16=6 binary number:__1__xxxx).

Here is an example of these rules
|!problem|!number returned|!binary number|
|22-16|6|__1__xxxx|
|6-8|-2|1__0__xxx|
|6-4|2|10__1__xx|
|2-2|0|101__1__x|
|0-1|-1|1011__0__|

10110 is your result.

!Converting binary to decimal:

First you start with a number, 10110 in this case.
Count out the digits in the number like this:
|binary number|1|0|1|1|0|
|2^X|4|3|2|1|0|
|Power of 2|8|4|2|1|0|

Add all the powers of 2 representing place values under the 1's on the chart like this:
2+4+16=22

22 is your result.
!Hexadecimal

Hexadecimal is used because it is much easier to convert between binary and hexadecimal. Here is a chart:
|!Decimal|!Hexadecimal|!Binary|
|0|0|0000|
|1|1|0001|
|2|2|0010|
|3|3|0011|
|4|4|0100|
|5|5|0101|
|6|6|0110|
|7|7|0111|
|8|8|1000|
|9|9|1001|
|10|A|1010|
|11|B|1011|
|12|C|1100|
|13|D|1101|
|14|E|1110|
|15|F|1111|

Converting is as easy as this:

|!Hexadecimal|!Binary|
|__1__"""C"""__5__F|__0001__1100__0101__1111|

(The 4 bit groups are called a nibble. 8 bit groups are called a byte.)
/***
|Name|CalendarPlugin|
|Source|http://www.TiddlyTools.com/#CalendarPlugin|
|Version|1.5.0|
|Author|Eric Shulman|
|Original Author|SteveRumsby|
|License|unknown|
|~CoreVersion|2.1|
|Type|plugin|
|Description|display monthly and yearly calendars|
NOTE: For //enhanced// date popup display, optionally install [[DatePlugin]] and [[ReminderMacros]]
!!!Usage:
<<<
|{{{<<calendar>>}}}|full-year calendar for the current year|
|{{{<<calendar year>>}}}|full-year calendar for the specified year|
|{{{<<calendar year month>>}}}|one month calendar for the specified month and year|
|{{{<<calendar thismonth>>}}}|one month calendar for the current month|
|{{{<<calendar lastmonth>>}}}|one month calendar for last month|
|{{{<<calendar nextmonth>>}}}|one month calendar for next month|
|{{{<<calendar +n>>}}}<br>{{{<<calendar -n>>}}}|one month calendar for a month +/- 'n' months from now|
<<<
!!!Configuration:
<<<
|''First day of week:''<br>{{{config.options.txtCalFirstDay}}}|<<option txtCalFirstDay>>|(Monday = 0, Sunday = 6)|
|''First day of weekend:''<br>{{{config.options.txtCalStartOfWeekend}}}|<<option txtCalStartOfWeekend>>|(Monday = 0, Sunday = 6)|

<<option chkDisplayWeekNumbers>> Display week numbers //(note: Monday will be used as the start of the week)//
|''Week number display format:''<br>{{{config.options.txtWeekNumberDisplayFormat }}}|<<option txtWeekNumberDisplayFormat >>|
|''Week number link format:''<br>{{{config.options.txtWeekNumberLinkFormat }}}|<<option txtWeekNumberLinkFormat >>|
<<<
!!!Revisions
<<<
2009.04.31 [1.5.0] rewrote onClickCalendarDate() (popup handler) and added config.options.txtCalendarReminderTags.  Partial code reduction/cleanup.  Assigned true version number (1.5.0)
2008.09.10 added '+n' (and '-n') param to permit display of relative months (e.g., '+6' means 'six months from now', '-3' means 'three months ago'.  Based on suggestion from Jean.
2008.06.17 added support for config.macros.calendar.todaybg
2008.02.27 in handler(), DON'T set hard-coded default date format, so that *customized* value (pre-defined in config.macros.calendar.journalDateFmt is used.
2008.02.17 in createCalendarYear(), fix next/previous year calculation (use parseInt() to convert to numeric value).  Also, use journalDateFmt for date linking when NOT using [[DatePlugin]].
2008.02.16 in createCalendarDay(), week numbers now created as TiddlyLinks, allowing quick creation/navigation to 'weekly' journals (based on request from Kashgarinn)
2008.01.08 in createCalendarMonthHeader(), 'month year' heading is now created as TiddlyLink, allowing quick creation/navigation to 'month-at-a-time' journals
2007.11.30 added 'return false' to onclick handlers (prevent IE from opening blank pages)
2006.08.23 added handling for weeknumbers (code supplied by Martin Budden (see 'wn**' comment marks).  Also, incorporated updated by Jeremy Sheeley to add caching for reminders (see [[ReminderMacros]], if installed)
2005.10.30 in config.macros.calendar.handler(), use 'tbody' element for IE compatibility.  Also, fix year calculation for IE's getYear() function (which returns '2005' instead of '105'). Also, in createCalendarDays(), use showDate() function (see [[DatePlugin]], if installed) to render autostyled date with linked popup.  Updated calendar stylesheet definition: use .calendar class-specific selectors, add text centering and margin settings
2006.05.29 added journalDateFmt handling
<<<
!!!Code
***/
//{{{
version.extensions.CalendarPlugin= { major: 1, minor: 5, revision: 0, date: new Date(2009,5,31)};
//}}}
//{{{
if(config.options.txtCalFirstDay == undefined)
	config.options.txtCalFirstDay = 0;
if(config.options.txtCalStartOfWeekend == undefined)
	config.options.txtCalStartOfWeekend = 5;
if(config.options.chkDisplayWeekNumbers == undefined)
	config.options.chkDisplayWeekNumbers = false;
if(config.options.chkDisplayWeekNumbers)
	config.options.txtCalFirstDay = 0;
if(config.options.txtWeekNumberDisplayFormat == undefined)
	config.options.txtWeekNumberDisplayFormat = 'w0WW';
if(config.options.txtWeekNumberLinkFormat == undefined)
	config.options.txtWeekNumberLinkFormat = 'YYYY-w0WW';
if(config.options.txtCalendarReminderTags == undefined)
	config.options.txtCalendarReminderTags = 'reminder';

config.macros.calendar = {
	monthnames:['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
	daynames:['M','T','W','T','F','S','S'],
	todaybg:'#ccccff',
	weekendbg:'#c0c0c0',
	monthbg:'#e0e0e0',
	holidaybg:'#ffc0c0',
	journalDateFmt:'DD MMM YYYY',
	monthdays:[31,28,31,30,31,30,31,31,30,31,30,31],
	holidays:[ ] // for customization see [[CalendarPluginConfig]]
};
//}}}
//{{{
function calendarIsHoliday(date)
{
	var longHoliday = date.formatString('0DD/0MM/YYYY');
	var shortHoliday = date.formatString('0DD/0MM');
	for(var i = 0; i < config.macros.calendar.holidays.length; i++) {
		if(   config.macros.calendar.holidays[i]==longHoliday
		   || config.macros.calendar.holidays[i]==shortHoliday)
			return true;
	}
	return false;
}
//}}}
//{{{
config.macros.calendar.handler = function(place,macroName,params) {
	var calendar = createTiddlyElement(place, 'table', null, 'calendar', null);
	var tbody = createTiddlyElement(calendar, 'tbody');
	var today = new Date();
	var year = today.getYear();
	if (year<1900) year+=1900;

 	// get journal format from SideBarOptions (ELS 5/29/06 - suggested by MartinBudden)
	var text = store.getTiddlerText('SideBarOptions');
	var re = new RegExp('<<(?:newJournal)([^>]*)>>','mg'); var fm = re.exec(text);
	if (fm && fm[1]!=null) { var pa=fm[1].readMacroParams(); if (pa[0]) this.journalDateFmt = pa[0]; }

	var month=-1;
	if (params[0] == 'thismonth') {
		var month=today.getMonth();
	} else if (params[0] == 'lastmonth') {
		var month = today.getMonth()-1; if (month==-1) { month=11; year--; }
	} else if (params[0] == 'nextmonth') {
		var month = today.getMonth()+1; if (month>11) { month=0; year++; }
	} else if (params[0]&&'+-'.indexOf(params[0].substr(0,1))!=-1) {
		var month = today.getMonth()+parseInt(params[0]);
		if (month>11) { year+=Math.floor(month/12); month%=12; };
		if (month<0)  { year+=Math.floor(month/12); month=12+month%12; }
	} else if (params[0]) {
		year = params[0];
		if(params[1]) month=parseInt(params[1])-1;
		if (month>11) month=11; if (month<0) month=0;
	}

	if (month!=-1) {
		cacheReminders(new Date(year, month, 1, 0, 0), 31);
		createCalendarOneMonth(tbody, year, month);
	} else {
		cacheReminders(new Date(year, 0, 1, 0, 0), 366);
		createCalendarYear(tbody, year);
	}
	window.reminderCacheForCalendar = null;
}
//}}}
//{{{
// cache used to store reminders while the calendar is being rendered
// it will be renulled after the calendar is fully rendered.
window.reminderCacheForCalendar = null;
//}}}
//{{{
function cacheReminders(date, leadtime)
{
	if (window.findTiddlersWithReminders == null) return;
	window.reminderCacheForCalendar = {};
	var leadtimeHash = [];
	leadtimeHash [0] = 0;
	leadtimeHash [1] = leadtime;
	var t = findTiddlersWithReminders(date, leadtimeHash, null, 1);
	for(var i = 0; i < t.length; i++) {
		//just tag it in the cache, so that when we're drawing days, we can bold this one.
		window.reminderCacheForCalendar[t[i]['matchedDate']] = 'reminder:' + t[i]['params']['title']; 
	}
}
//}}}
//{{{
function createCalendarOneMonth(calendar, year, mon)
{
	var row = createTiddlyElement(calendar, 'tr');
	createCalendarMonthHeader(calendar, row, config.macros.calendar.monthnames[mon]+' '+year, true, year, mon);
	row = createTiddlyElement(calendar, 'tr');
	createCalendarDayHeader(row, 1);
	createCalendarDayRowsSingle(calendar, year, mon);
}
//}}}
//{{{
function createCalendarMonth(calendar, year, mon)
{
	var row = createTiddlyElement(calendar, 'tr');
	createCalendarMonthHeader(calendar, row, config.macros.calendar.monthnames[mon]+' '+ year, false, year, mon);
	row = createTiddlyElement(calendar, 'tr');
	createCalendarDayHeader(row, 1);
	createCalendarDayRowsSingle(calendar, year, mon);
}
//}}}
//{{{
function createCalendarYear(calendar, year)
{
	var row;
	row = createTiddlyElement(calendar, 'tr');
	var back = createTiddlyElement(row, 'td');
	var backHandler = function() {
		removeChildren(calendar);
		createCalendarYear(calendar, parseInt(year)-1);
		return false; // consume click
	};
	createTiddlyButton(back, '<', 'Previous year', backHandler);
	back.align = 'center';
	var yearHeader = createTiddlyElement(row, 'td', null, 'calendarYear', year);
	yearHeader.align = 'center';
	yearHeader.setAttribute('colSpan',config.options.chkDisplayWeekNumbers?22:19);//wn**
	var fwd = createTiddlyElement(row, 'td');
	var fwdHandler = function() {
		removeChildren(calendar);
		createCalendarYear(calendar, parseInt(year)+1);
		return false; // consume click
	};
	createTiddlyButton(fwd, '>', 'Next year', fwdHandler);
	fwd.align = 'center';
	createCalendarMonthRow(calendar, year, 0);
	createCalendarMonthRow(calendar, year, 3);
	createCalendarMonthRow(calendar, year, 6);
	createCalendarMonthRow(calendar, year, 9);
}
//}}}
//{{{
function createCalendarMonthRow(cal, year, mon)
{
	var row = createTiddlyElement(cal, 'tr');
	createCalendarMonthHeader(cal, row, config.macros.calendar.monthnames[mon], false, year, mon);
	createCalendarMonthHeader(cal, row, config.macros.calendar.monthnames[mon+1], false, year, mon);
	createCalendarMonthHeader(cal, row, config.macros.calendar.monthnames[mon+2], false, year, mon);
	row = createTiddlyElement(cal, 'tr');
	createCalendarDayHeader(row, 3);
	createCalendarDayRows(cal, year, mon);
}
//}}}
//{{{
function createCalendarMonthHeader(cal, row, name, nav, year, mon)
{
	var month;
	if (nav) {
		var back = createTiddlyElement(row, 'td');
		back.align = 'center';
		back.style.background = config.macros.calendar.monthbg;

		var backMonHandler = function() {
			var newyear = year;
			var newmon = mon-1;
			if(newmon == -1) { newmon = 11; newyear = newyear-1;}
			removeChildren(cal);
			cacheReminders(new Date(newyear, newmon , 1, 0, 0), 31);
			createCalendarOneMonth(cal, newyear, newmon);
			return false; // consume click
		};
		createTiddlyButton(back, '<', 'Previous month', backMonHandler);
		month = createTiddlyElement(row, 'td', null, 'calendarMonthname')
		createTiddlyLink(month,name,true);
		month.setAttribute('colSpan', config.options.chkDisplayWeekNumbers?6:5);//wn**
		var fwd = createTiddlyElement(row, 'td');
		fwd.align = 'center';
		fwd.style.background = config.macros.calendar.monthbg; 

		var fwdMonHandler = function() {
			var newyear = year;
			var newmon = mon+1;
			if(newmon == 12) { newmon = 0; newyear = newyear+1;}
			removeChildren(cal);
			cacheReminders(new Date(newyear, newmon , 1, 0, 0), 31);
			createCalendarOneMonth(cal, newyear, newmon);
			return false; // consume click
		};
		createTiddlyButton(fwd, '>', 'Next month', fwdMonHandler);
	} else {
		month = createTiddlyElement(row, 'td', null, 'calendarMonthname', name)
		month.setAttribute('colSpan',config.options.chkDisplayWeekNumbers?8:7);//wn**
	}
	month.align = 'center';
	month.style.background = config.macros.calendar.monthbg;
}
//}}}
//{{{
function createCalendarDayHeader(row, num)
{
	var cell;
	for(var i = 0; i < num; i++) {
		if (config.options.chkDisplayWeekNumbers) createTiddlyElement(row, 'td');//wn**
		for(var j = 0; j < 7; j++) {
			var d = j + (config.options.txtCalFirstDay - 0);
			if(d > 6) d = d - 7;
			cell = createTiddlyElement(row, 'td', null, null, config.macros.calendar.daynames[d]);
			if(d == (config.options.txtCalStartOfWeekend-0) || d == (config.options.txtCalStartOfWeekend-0+1))
				cell.style.background = config.macros.calendar.weekendbg;
		}
	}
}
//}}}
//{{{
function createCalendarDays(row, col, first, max, year, mon) {
	var i;
	if (config.options.chkDisplayWeekNumbers){
		if (first<=max) {
			var ww = new Date(year,mon,first);
			var td=createTiddlyElement(row, 'td');//wn**
			var link=createTiddlyLink(td,ww.formatString(config.options.txtWeekNumberLinkFormat),false);
			link.appendChild(document.createTextNode(
				ww.formatString(config.options.txtWeekNumberDisplayFormat)));
		}
		else createTiddlyElement(row, 'td');//wn**
	}
	for(i = 0; i < col; i++)
		createTiddlyElement(row, 'td');
	var day = first;
	for(i = col; i < 7; i++) {
		var d = i + (config.options.txtCalFirstDay - 0);
		if(d > 6) d = d - 7;
		var daycell = createTiddlyElement(row, 'td');
		var isaWeekend=((d==(config.options.txtCalStartOfWeekend-0)
			|| d==(config.options.txtCalStartOfWeekend-0+1))?true:false);
		if(day > 0 && day <= max) {
			var celldate = new Date(year, mon, day);
			// ELS 10/30/05 - use <<date>> macro's showDate() function to create popup
			// ELS 05/29/06 - use journalDateFmt 
			if (window.showDate) showDate(daycell,celldate,'popup','DD',
				config.macros.calendar.journalDateFmt,true, isaWeekend);
			else {
				if(isaWeekend) daycell.style.background = config.macros.calendar.weekendbg;
				var title = celldate.formatString(config.macros.calendar.journalDateFmt);
				if(calendarIsHoliday(celldate))
					daycell.style.background = config.macros.calendar.holidaybg;
				var now=new Date();
				if ((now-celldate>=0) && (now-celldate<86400000)) // is today?
					daycell.style.background = config.macros.calendar.todaybg;
				if(window.findTiddlersWithReminders == null) {
					var link = createTiddlyLink(daycell, title, false);
					link.appendChild(document.createTextNode(day));
				} else
					var button = createTiddlyButton(daycell, day, title, onClickCalendarDate);
			}
		}
		day++;
	}
}
//}}}
//{{{
// Create a pop-up containing:
// * a link to a tiddler for this date
// * a 'new tiddler' link to add a reminder for this date
// * links to current reminders for this date
// NOTE: this code is only used if [[ReminderMacros]] is installed AND [[DatePlugin]] is //not// installed.
function onClickCalendarDate(ev) { ev=ev||window.event;
	var d=new Date(this.getAttribute('title')); var date=d.formatString(config.macros.calendar.journalDateFmt);
	var p=Popup.create(this);  if (!p) return;
	createTiddlyLink(createTiddlyElement(p,'li'),date,true);
	var rem='\\n\\<\\<reminder day:%0 month:%1 year:%2 title: \\>\\>';
	rem=rem.format([d.getDate(),d.getMonth()+1,d.getYear()+1900]);
	var cmd="<<newTiddler label:[[new reminder...]] prompt:[[add a new reminder to '%0']]"
		+" title:[[%0]] text:{{store.getTiddlerText('%0','')+'%1'}} tag:%2>>";
	wikify(cmd.format([date,rem,config.options.txtCalendarReminderTags]),p);
	createTiddlyElement(p,'hr');
	var t=findTiddlersWithReminders(d,[0,31],null,1);
	for(var i=0; i<t.length; i++) {
		var link=createTiddlyLink(createTiddlyElement(p,'li'), t[i].tiddler, false);
		link.appendChild(document.createTextNode(t[i]['params']['title']));
	}
	Popup.show(); ev.cancelBubble=true; if (ev.stopPropagation) ev.stopPropagation(); return false;
}
//}}}
//{{{
function calendarMaxDays(year, mon)
{
	var max = config.macros.calendar.monthdays[mon];
	if(mon == 1 && (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) max++;
	return max;
}
//}}}
//{{{
function createCalendarDayRows(cal, year, mon)
{
	var row = createTiddlyElement(cal, 'tr');
	var first1 = (new Date(year, mon, 1)).getDay() -1 - (config.options.txtCalFirstDay-0);
	if(first1 < 0) first1 = first1 + 7;
	var day1 = -first1 + 1;
	var first2 = (new Date(year, mon+1, 1)).getDay() -1 - (config.options.txtCalFirstDay-0);
	if(first2 < 0) first2 = first2 + 7;
	var day2 = -first2 + 1;
	var first3 = (new Date(year, mon+2, 1)).getDay() -1 - (config.options.txtCalFirstDay-0);
	if(first3 < 0) first3 = first3 + 7;
	var day3 = -first3 + 1;

	var max1 = calendarMaxDays(year, mon);
	var max2 = calendarMaxDays(year, mon+1);
	var max3 = calendarMaxDays(year, mon+2);

	while(day1 <= max1 || day2 <= max2 || day3 <= max3) {
		row = createTiddlyElement(cal, 'tr');
		createCalendarDays(row, 0, day1, max1, year, mon); day1 += 7;
		createCalendarDays(row, 0, day2, max2, year, mon+1); day2 += 7;
		createCalendarDays(row, 0, day3, max3, year, mon+2); day3 += 7;
	}
}
//}}}
//{{{
function createCalendarDayRowsSingle(cal, year, mon)
{
	var row = createTiddlyElement(cal, 'tr');
	var first1 = (new Date(year, mon, 1)).getDay() -1 - (config.options.txtCalFirstDay-0);
	if(first1 < 0) first1 = first1+ 7;
	var day1 = -first1 + 1;
	var max1 = calendarMaxDays(year, mon);
	while(day1 <= max1) {
		row = createTiddlyElement(cal, 'tr');
		createCalendarDays(row, 0, day1, max1, year, mon); day1 += 7;
	}
}
//}}}
//{{{
setStylesheet('.calendar, .calendar table, .calendar th, .calendar tr, .calendar td { text-align:center; } .calendar, .calendar a { margin:0px !important; padding:0px !important; }', 'calendarStyles');
//}}}
This is a simple script that allows for the creation of interactive stories. There are multiple files that are each named a number. Each numbered file is a chapter in the story. The chapter can have a question at the bottom and a list of options. The user types the number of the option (a different chapter number) and they are directed to that chapter. The script also checks for the end of the story and offers options when you reach the end.

[[>>Download<<|https://www.dropbox.com/s/jts4j4t017oxf7l/choose%20your%20own%20adventure.zip]]
<<clock2 120>>
/***
| Name:|Clock2|
| Author:|Simon Baird|
| Description:|A skinnable, sizeable analog clock|
| Source:|http://tiddlyspot.com/mptw/#Clock2|
| Requires:|Firefox 1.5.x or maybe Safari|
| Version:|1.0.6|
| Date:|8-Jul-2008|
!!Note
* Does not work in IE or Opera due to lack of canvas support.
* If you make a nice skin send it to me and I will include it here.
*I'm not actively maintaining this plugin
* See also http://randomibis.com/coolclock/
!!Ideas
* Can we support IE with this? http://sourceforge.net/projects/excanvas
* Skin should specify order of drawing so things can be on top of other things
* Fix it so we can have filled and/or stroked elements
* Skin should allow any number of moving and static elements
* Make download and example for non-TW use
* Make floating draggable?
!!Examples
{{{
<<clock2 fancy>><<clock2 120>>
<<clock2 chunkySwiss>> <<clock2 60 chunkySwiss noSeconds>><<clock2 '{
	outerBorder: { lineWidth: 60, radius:55, color: "#dd8877", alpha: 1 },
	smallIndicator: { lineWidth: 4, startAt: 80, endAt: 95, color: "white", alpha: 1 },
	largeIndicator: { lineWidth: 12, startAt: 77, endAt: 89, color: "#dd8877", alpha: 1 },
	hourHand: { lineWidth: 15, startAt: -15, endAt: 50, color: "white", alpha: 1 },
	minuteHand: { lineWidth: 10, startAt: 24, endAt: 200, color: "#771100", alpha: 0.6 },
	secondHand: { lineWidth: 3, startAt: 22, endAt: 83, color: "green", alpha: 0 },
	secondDecoration: { lineWidth: 1, startAt: 52, radius: 26, fillColor: "white", color: "red", alpha: 0.2 }
}'>>

}}}
<<clock2 fancy>><<clock2 120>>
<<clock2 chunkySwiss>> <<clock2 60 chunkySwiss noSeconds>><<clock2 '{
	outerBorder: { lineWidth: 60, radius:55, color: "#dd8877", alpha: 1 },
	smallIndicator: { lineWidth: 4, startAt: 80, endAt: 95, color: "white", alpha: 1 },
	largeIndicator: { lineWidth: 12, startAt: 77, endAt: 89, color: "#dd8877", alpha: 1 },
	hourHand: { lineWidth: 15, startAt: -15, endAt: 50, color: "white", alpha: 1 },
	minuteHand: { lineWidth: 10, startAt: 24, endAt: 200, color: "#771100", alpha: 0.6 },
	secondHand: { lineWidth: 3, startAt: 22, endAt: 83, color: "green", alpha: 0 },
	secondDecoration: { lineWidth: 1, startAt: 52, radius: 26, fillColor: "white", color: "red", alpha: 0.2 }
}'>>

See also BigClock.
!!Code
***/
//{{{

window.CoolClock = function(canvasId,displayRadius,skinId,showSecondHand) {
	return this.init(canvasId,displayRadius,skinId,showSecondHand);
}

CoolClock.config = {
	clockTracker: {},
	tickDelay: 1000,
	longTickDelay: 15000,
	defaultRadius: 85,
	renderRadius: 100,
	defaultSkin: "swissRail",
	skins:	{
		// try making your own...
		swissRail: {
			outerBorder: { lineWidth: 1, radius:95, color: "black", alpha: 1 },
			smallIndicator: { lineWidth: 2, startAt: 89, endAt: 93, color: "black", alpha: 1 },
			largeIndicator: { lineWidth: 4, startAt: 80, endAt: 93, color: "black", alpha: 1 },
			hourHand: { lineWidth: 8, startAt: -15, endAt: 50, color: "black", alpha: 1 },
			minuteHand: { lineWidth: 7, startAt: -15, endAt: 75, color: "black", alpha: 1 },
			secondHand: { lineWidth: 1, startAt: -20, endAt: 85, color: "red", alpha: 1 },
			secondDecoration: { lineWidth: 1, startAt: 70, radius: 4, fillColor: "red", color: "red", alpha: 1 }
		},
		chunkySwiss: {
			outerBorder: { lineWidth: 5, radius:97, color: "black", alpha: 1 },
			smallIndicator: { lineWidth: 4, startAt: 89, endAt: 93, color: "black", alpha: 1 },
			largeIndicator: { lineWidth: 8, startAt: 80, endAt: 93, color: "black", alpha: 1 },
			hourHand: { lineWidth: 12, startAt: -15, endAt: 60, color: "black", alpha: 1 },
			minuteHand: { lineWidth: 10, startAt: -15, endAt: 85, color: "black", alpha: 1 },
			secondHand: { lineWidth: 4, startAt: -20, endAt: 85, color: "red", alpha: 1 },
			secondDecoration: { lineWidth: 2, startAt: 70, radius: 8, fillColor: "red", color: "red", alpha: 1 }
		},
		fancy: {
			outerBorder: { lineWidth: 5, radius:95, color: "green", alpha: 0.7 },
			smallIndicator: { lineWidth: 1, startAt: 80, endAt: 93, color: "black", alpha: 0.4 },
			largeIndicator: { lineWidth: 1, startAt: 30, endAt: 93, color: "black", alpha: 0.5 },
			hourHand: { lineWidth: 8, startAt: -15, endAt: 50, color: "blue", alpha: 0.7 },
			minuteHand: { lineWidth: 7, startAt: -15, endAt: 92, color: "red", alpha: 0.7 },
			secondHand: { lineWidth: 10, startAt: 80, endAt: 85, color: "blue", alpha: 0.3 },
			secondDecoration: { lineWidth: 1, startAt: 30, radius: 50, fillColor: "blue", color: "red", alpha: 0.15 }
		}
	}
};

CoolClock.prototype = {
	init: function(canvasId,displayRadius,skinId,showSecondHand) {
		this.canvasId = canvasId;
		this.displayRadius = displayRadius || CoolClock.config.defaultRadius;
		this.skinId = skinId || CoolClock.config.defaultSkin;
		this.showSecondHand = typeof showSecondHand == "boolean" ? showSecondHand : true;
		this.tickDelay = CoolClock.config[ this.showSecondHand ? "tickDelay" : "longTickDelay"];

		this.canvas = document.getElementById(canvasId);
		this.canvas.setAttribute("width",this.displayRadius*2);
		this.canvas.setAttribute("height",this.displayRadius*2);

		this.renderRadius = CoolClock.config.renderRadius; 

		var scale = this.displayRadius / this.renderRadius;
		this.ctx = this.canvas.getContext("2d");
		this.ctx.scale(scale,scale);

		CoolClock.config.clockTracker[canvasId] = this;
		this.tick();
		return this;
	},

	fullCircle: function(skin) {
		this.fullCircleAt(this.renderRadius,this.renderRadius,skin);
	},

	fullCircleAt: function(x,y,skin) {
		with (this.ctx) {
			save();
			globalAlpha = skin.alpha;
			lineWidth = skin.lineWidth;
			if (!document.all)
				beginPath();
			arc(x, y, skin.radius, 0, 2*Math.PI, false);
			if (skin.fillColor) {
				fillStyle = skin.fillColor
				fill();
			}
			else {
				// XXX why not stroke and fill
				strokeStyle = skin.color;
				stroke();
			}
			restore();
		}
	},

	radialLineAtAngle: function(angleFraction,skin) {
		with (this.ctx) {
			save();
			translate(this.renderRadius,this.renderRadius);
			rotate(Math.PI * (2 * angleFraction - 0.5));
			globalAlpha = skin.alpha;
			strokeStyle = skin.color;
			lineWidth = skin.lineWidth;
			if (skin.radius) {
				this.fullCircleAt(skin.startAt,0,skin)
			}
			else {
				beginPath();
				moveTo(skin.startAt,0)
				lineTo(skin.endAt,0);
				stroke();
			}
			restore();
		}
	},

	render: function(hour,min,sec) {
		var skin = CoolClock.config.skins[this.skinId];
		this.ctx.clearRect(0,0,this.renderRadius*2,this.renderRadius*2);

		this.fullCircle(skin.outerBorder);

		for (var i=0;i<60;i++)
			this.radialLineAtAngle(i/60,skin[ i%5 ? "smallIndicator" : "largeIndicator"]);
				
		this.radialLineAtAngle((hour+min/60)/12,skin.hourHand);
		this.radialLineAtAngle((min+sec/60)/60,skin.minuteHand);
		if (this.showSecondHand) {
			this.radialLineAtAngle(sec/60,skin.secondHand);
			this.radialLineAtAngle(sec/60,skin.secondDecoration);
		}
	},


	nextTick: function() {
		setTimeout("CoolClock.config.clockTracker['"+this.canvasId+"'].tick()",this.tickDelay);
	},

	stillHere: function() {
		return document.getElementById(this.canvasId) != null;
	},

	refreshDisplay: function() {
		var now = new Date();
		this.render(now.getHours(),now.getMinutes(),now.getSeconds());
	},

	tick: function() {
		if (this.stillHere()) {
			this.refreshDisplay()
			this.nextTick();
		}
	}
}



config.macros.clock2 = {
	counter: 0,
	handler: function (place,macroName,params,wikifier,paramString,tiddler) {
		var size,skin,seconds,skinData;
		for (var i=0;i<params.length;i++)
			if (/^\d+$/.exec(params[i]))
				size = params[i];
			else if (params[i] == "noSeconds")
				seconds = false;
			else if (/^\{/.exec(params[i]))
				eval("skinData = " + params[i]);
			else
				skin = params[i];
		if (skinData) {
			CoolClock.config.skins.customSkin = skinData;
			skin = "customSkin";
		}
		var canvas = createTiddlyElement(place,"canvas","clockcanvas"+this.counter);
		var clock = new CoolClock("clockcanvas"+this.counter,size,skin,seconds);
		this.counter++;
	}
}

//}}}
I created a fairly fast auto typer/program paster using python and a client program. It is linux only, but can be ported to windows. It relies on the clipboard. This is helpful for smp servers where http is disabled, and can paste the entire redworks os in ~10 minutes.

Files:
*netinst.py: Paste a program to download the redworks installer from a file server in computercraft.
*pate.py: Paste a program into the hex converter.
*fromhex.py: Paste the hex converter program into a program. Run this before paste.py
*redworks: SFX and autoinstaller for redworks that includes some server software and is compacted for pasting use (so it excludes minepedia and other bloat)

[[>>Download<<|https://www.dropbox.com/s/fqimnvzzo5zi872/compcraft.zip]] [[[M]|http://yunpan.cn/QDeBDmGywtFCG]]

How to create a minecraft redworks server with this:
*Start minecraft and click into the computer you want to set up as a fileserver. Add a modem to said computer on the right.
*Run fromhex.py and click into the computercraft window. It will automatically type the program to send redworks.
*Run paste.py, drag the redworks file into the command line window and hit enter, then hit enter again if you want to continue. Make sure to click into minecraft!
*When it is finished pasting, type redworks into the computer.
*Type "rm startup".
*Then type install, then redbus, then fileserver, and say yes for each prompt.

How to install redworks on a computer with netinstall after creating a server (faster):
*Go into the computer in minecraft that you want to install redworks on. It must be near the server. Make sure to *Install a modem on it's right side.
*Run the netinst.py program, then click into the minecraft window.
*Type the file server number when netinst finishes (The server you hosted earlier will tell you it's id.)

Bonus: The getfile command can be used to upload or download programs or files from your redworks server (or other server).
<html>
<style>
.rolodex table {
border: 0px solid;
}

.rolodex tr, .rolodex td {
border: 0px solid;
}

</style>
</html>
<<tabs addressBookTabs
Overview "Email Addresses, Homepage URL, etc." TwabTabParts/tab1form
Home "Home Address/Phone, etc." TwabTabParts/tab2form
Business "Business Address/Phone, etc." TwabTabParts/tab3form
Misc "Notes, Birthday, SSN, etc." TwabTabParts/tab4form
>>
This script will batch convert a folder of media files to any format supported by ffmpeg.

To use:
Start the program. (Use {{{chmod +x [file]}}} to allow to execute.)
Select folder with files.
Type the extension (wav. ogg. mp3, avi, etc...)
The files will be converted in the same folder.

Note: You will need ffmpeg. Install it with this [[Debian post install tool|https://www.dropbox.com/s/f8h8b05b8iqd0f2/debian-postinst.zip]] [[[M]|http://yunpan.cn/QDeMdv6XSYfyq]].
[[>>Universal version (lower quality)<<|https://www.dropbox.com/s/40dkd6lguzvlzt3/converter.sh]] [[[M]|http://yunpan.cn/QDeM3hPdFUsda]]
[[>>MP4 version (high quality)<<|https://www.dropbox.com/s/uy7utivvlg6gznq/makeitmp4.sh]] [[[M]|http://yunpan.cn/QDeMPIAQTk2u3]]
[[>>MP3 version (high quality)<<|https://www.dropbox.com/s/fmjv4ofabk9cwjd/makeitmp3.sh]] [[[M]|http://yunpan.cn/QDeMMuDaDtxH3]]
!Extra Converters
I wrote 3 other scripts using the base shown above. They aren't useful to most people, but I will post them anyway.

These scripts depend on [[mo3enc or unmo3|http://www.un4seen.com/mo3.html]]:
[[makeitmo3.sh|https://www.dropbox.com/s/o86xq3pxrzv4j01/makeitmo3.sh]] [[[M]|http://yunpan.cn/QDeMn2C2XcZ8T]]: Converts module files to compressed mo3 files.
[[makeitunmo3.sh|https://www.dropbox.com/s/ybxmg168q2k1kw4/makeitunmo3.sh]] [[[M]|http://yunpan.cn/QDeM9aE9UP5XY]]: Converts compressed mo3 files to modules.

This script depends on sox and ffmpeg:
[[makeitnightcore.sh|https://www.dropbox.com/s/alwpdzv028dtmee/makeitnightcore.sh]] [[[M]|http://yunpan.cn/QDeM4zpp4fPDG]]: This script will change the speed of songs by %25 in either direction. This is often used to apply or remove a [[nightcore|http://www.nightcoreuniverse.net/viewtopic.php?f=17&t=930]] effect.

!Legal
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
I don't know anyone to give these to, so I'll just post them here.
These get you a 48 hours of Crunchyroll Premium (all-access) without putting in credit card information.

|!Expiration|!Code|
|2014-09-02|[[KM8895G5VCJ|http://www.crunchyroll.com/guest_pass?code=KM8895G5VCJ]]|
|2014-09-27|[[HEQFGDSPGXG|http://www.crunchyroll.com/guest_pass?code=HEQFGDSPGXG]]|
|2014-10-27|[[VCKCQL5LY7T|http://www.crunchyroll.com/guest_pass?code=VCKCQL5LY7T]]|
|2014-11-26|[[9RKW8DU4F8N|http://www.crunchyroll.com/guest_pass?code=9RKW8DU4F8N]]|
Using the computer for extended periods of time creates eye fatigue. This is only amplified at night. While f.lux or redshift help, a [[dark system theme|http://xfce-look.org/content/show.php/Xfce-Darkness+dark+GTK2+and+GTK%2B+theme?content=156807]] and dark website theme can lessen eye fatigue and issues sleeping.

!New Solution

After searching for a better solution, I was made aware of [[this userstyle on reddit|http://www.reddit.com/r/firefox/comments/2a0rvw/any_good_solution_for_making_all_websites_use_a/ciqey97?context=3]]. It works very effectively, eliminating many of the exceptions for the script below.<<slider DarkStyle DarkStyle "Click here to show the userstyle.">> Here are some suggestions for some websites that have issues:

*reddit.com: [[Userstyle|https://userstyles.org/styles/62209/reddit-dust]] (The userstyle fixes some isssues with RES and the global userstyle.)
*myanimelist.net: [[Userstyle|https://userstyles.org/styles/7678/myanimelist-net-night-theme]] (The userstyle zaps the logo that is broken by the global userstyle.)
*4chan.org: [[Appchan X|https://zixaphir.github.io/appchan-x/]] (Still recommend this userscript.)
*amazon.com: [[Userstyle|https://userstyles.org/styles/56809/amazon-dark]] (The userstyle fixes the search bar that remains white.)
*[[This theme is helpful.|https://addons.mozilla.org/En-us/firefox/addon/black-15433/]]

As per the reddit thread, you can use [[custom buttons|https://addons.mozilla.org/en-US/firefox/addon/custom-buttons/?src=userprofile]] to enable and disable all userstyles. You set the code for the button to {{{stylishOverlay.turnOnOff(!cbu.ps.getBoolPref("extensions.stylish.styleRegistrationEnabled"))}}}. I found that [[this tango icon|http://imgur.com/Xpi0n4R]] works well.

!User Script (Not Recommended)

Install this in chrome with [[Tamper Monkey|https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo?hl=en]] or in firefox with [[Grease Monkey|https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/]]. This will change all websites except the ones listed with @exclude before them. My solution for those sites is listed below. Based on [[Dark Night Reader|https://userscripts.org/scripts/show/159901]].

{{{
// ==UserScript==
// @name         Universal Dark Theme
// @description  This script darkens pages to save your eyes at night,
// @include      *
// @exclude      *reddit.com*
// @exclude      *mail.google.com*
// @exclude      *hackaday.com*
// @exclude      *youtube.com*
// @exclude      *ycombinator.com*
// @exclude      *drive.google.com*
// @exclude      *google.com/analytics*
// @exclude      *imgur.com*
// @exclude      *amazon.com*
// @exclude      *instagram.com*
// @exclude      *twitter.com*
// @exclude      *flickr.com*
// @exclude      *maps.google.com*
// @exclude      *tohtml.com*
// @exclude      *docs.google.com*
// @exclude      *repl.it*
// @exclude      *myanimelist.net*
// @exclude      *4chan.org*
// @exclude      *crunchyroll.com*
// @exclude      *chia-anime.com*
// @exclude      *jwz.org*
// @exclude      *duckduckgo.com*
// @exclude      ftp://*
// @exclude      *addons.mozilla.org*
// @exclude      *userstyles.org*
// @exclude      *crunchbang.org*
// @exclude      *hummingbird.me*
// @exclude      *tvtropes.org*
// ==/UserScript==

(function(){
    var css
    ,   style='* {background:#000000!important; color:#ccc!important }'
    +   ':link, :link * {color:#575757!important } '
    +   ':visited, :visited * { color: #1F1F1F!important } ';
    if(document.createStyleSheet) {
        document.createStyleSheet("javascript:'"+style+"'");
    } else {
        css=document.createElement('link');
        css.rel='stylesheet';
        css.href='data:text/css,'+escape(style);
        document.getElementsByTagName("head")[0].appendChild(css);
    }
})();

}}}

!Site-specific solutions

I use other solutions on the websites that don't work well with the script above.
 
*reddit.com: [[Userstyle|https://userstyles.org/styles/62209/reddit-dust]]
*mail.google.com: Built-in theme
*hackaday.com: Already dark
*youtube.com: [[Userstyle|https://userstyles.org/styles/62289/black-youtube-by-panos]]
*ycombinator.com: [[Userstyle|https://userstyles.org/styles/46143/a-dark-hacker-news-fixed-width]]
*imgur.com: Already dark
*twitter.com: [[Userstyle|https://userstyles.org/styles/97767/dark-twitter-modified]]
*myanimelist.net: [[Userstyle|https://userstyles.org/styles/7678/myanimelist-net-night-theme]]
*4chan.org: [[Appchan X has a dark theme.|https://zixaphir.github.io/appchan-x/]]
*crunchyroll.com: [[Userstyle|https://userstyles.org/styles/68293/crunchyroll-dark-theme]]
*chia-anime.com: Already dark
*jwz.org: Already dark
*duckduckgo.com: Built-in theme
*ftp: [[GTK theme|http://xfce-look.org/content/show.php/Xfce-Darkness+dark+GTK2+and+GTK%2B+theme?content=156807]]
*addons.mozilla.org: [[Userstyle|https://userstyles.org/styles/72084/firefox-dark-addons-fixed]]
*userstyles.org: [[Userstyle|https://userstyles.org/styles/56792/userstyles-tableview-enhancer-dark-grey-v-10]]
*crunchbang.org: Already dark
*tvtropes.org: [[Userstyle|https://userstyles.org/styles/32478/tv-tropes-another-dark-theme-updated]]
*lifehacker.com: [[Userstyle|https://userstyles.org/styles/46651/darker-gawker]]
*wikipedia.org: [[Userstyle|https://userstyles.org/styles/47161/dark-wikipedia-rounded]]
*amazon.com: [[Userstyle|https://userstyles.org/styles/56809/amazon-dark]]

!Bookmarklet

Dark theme bookmarklet (doesn't work in IE):

{{{
javascript:(function(){var css,style='* {background:#000000!important; color:#ccc!important }'+':link, :link * {color:#575757!important } '+':visited, :visited * { color: #1F1F1F!important } ';if(document.createStyleSheet){document.createStyleSheet("javascript:'"+style+"'");}else{css=document.createElement('link');css.rel='stylesheet';css.href='data:text/css,'+escape(style);document.getElementsByTagName("head")[0].appendChild(css);}})();
}}}
This was copy-pasted directly from the reddit thread. It is here for archival purposes.

{{{
@namespace html url(http://www.w3.org/1999/xhtml);
@namespace xul url(http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul);

/* browser background */
browser[type="content-primary"] {background-color: #141414 !important;}
 
@-moz-document url-prefix(http://), url-prefix(https://), url-prefix(ftp://), url-prefix(file://) {


/*no background images. try to exclude icons, other misc items. */

*:not(:empty):not([onclick*="open"]):not(span):not([class*="stars"]):not([id*="stars"]):not([id="rating"]):not([class="rating"]):not([class*="sprite"]):not([id*="sprite"]):not([id*="lbimage"])

{ background-image: none !important }

/* basic bodies */
html, body { background: none #141414 !important }

/* make descendents of the body element transparent.  formerly "div + span" rules. */
body * {background-color: transparent !important}

/* give id's bg hopefully */
div[id] {background-color: inherit !important}

/* filter non-icons */
span:not(:empty):not([class*="icon"]):not([id*="icon"]):not([class*="star"]):not([id*="star"]):not([id*="rating"]):not([class*="rating"]):not([class*="sprite"]):not([class*="sprite"]) {

background: none transparent !important;
border-color: #000 !important}

/* try to contrast containers */
html:root > body > * > * > * > *:not(input):not([onclick]) > div:not(:empty):not([id])

{background: none #1c1c1c !important}


/* :::::::: text presentation :::::::: */

summary, details {background-color: inherit !important}

abbr, progress, time, label,
.date {color: #cdefc2 !important}

mark,
code, pre,
blockquote,
[class*="quote"],
td[style*="inset"][class="alt2"]   { background-color: #00090f !important }


/* :::::::: headings + header :::::::: */

/* header gradient rules */

header, #header {background: -moz-linear-gradient(#333,#141414) transparent !important;}
#header h1 {background-color: transparent !important;}

h1, h2  { 

background: none #28313e !important;
border-radius: 5px !important;
-moz-border-radius: 5px !important;
-webkit-border-radius: 5px !important;} 


h3, h4 {

background: none #2a3731 !important;
border-radius: 5px !important;
-moz-border-radius: 5px !important;
-webkit-border-radius: 5px !important;} 

h5, h6 {background: none #372a2a !important}


/* :::::::: lists :::::::: */

dt     {background-color: #2b3135 !important}
dl, dd {background-color: #232323 !important}
li, ul {background-color: inherit !important}

li a:not([class*="icon"]):not([id*="icon"]):not([onclick]),
dt a:not([class*="icon"]):not([id*="icon"]):not([onclick])

{background-image: none !important; text-indent: 0 !important}

/* :::::::: list item highlight :::::::: */

li[class*="item"] a:hover,
li[class*="item"]:hover,

[class*="menuitem"]:hover  /* not list item, but still useful*/

{background-color: #2e2b2f !important}

/* :::::::: tables, cells :::::::: */

table {background-color: #232323 !important; border-color: #333 !important}
table table {background: #191919 !important;}

th, caption {background-color: #353535 !important}

/* :::::::: input :::::::: */

/*basic*/

input *, textarea * {color: #ddd !important;} /* anonymous divs */

html body input:not([type="image"]), button,
html body textarea {

background: none #353535 !important; 
-moz-appearance: none !important;
-webkit-appearance: none !important;
color:  #ddd  !important;
border: solid 1px #777 !important;
border-radius: 0 !important;
-moz-border-radius: 0 !important;
-webkit-border-radius: 0 !important;
opacity: 1 !important;}

/* style reset. */

html body input[type="checkbox"] {-moz-appearance: checkbox !important; -webkit-appearance: checkbox !important;}
html body input[type="radio"]    {-moz-appearance: radio !important; -webkit-appearance: radio !important;}

/* :::::::: custom styling :::::::: */

html:root input[type="button"],
html:root input[type="submit"],
html:root input[type="reset"],
html:root button {

color: #eee !important;
background-color: #222437 !important;

-moz-box-shadow: inset 0 1px rgba(255,255,255,0.2), inset 0 10px rgba(255,255,255,0.1), inset 0 10px 20px rgba(255,255,255,0.15), inset 0 -15px 30px rgba(0,0,0,0.2) !important;
-webkit-box-shadow: inset 0 1px rgba(255,255,255,0.2), inset 0 10px rgba(255,255,255,0.1), inset 0 10px 20px rgba(255,255,255,0.15), inset 0 -15px 30px rgba(0,0,0,0.2) !important;}

html:root input[type="button"]:hover,
html:root input[type="submit"]:hover,
html:root input[type="reset"]:hover,
html:root button:hover {

color: #fff !important;
background-color: #31344f !important;
border-color: #5f687f !important;

-moz-box-shadow: inset 0 1px rgba(255,255,255,0.3), inset 0 10px rgba(255,255,255,0.2), inset 0 10px 20px rgba(255,255,255,0.25), inset 0 -15px 30px rgba(0,0,0,0.3) !important;
-webkit-box-shadow:  inset 0 1px rgba(255,255,255,0.3), inset 0 10px rgba(255,255,255,0.2), inset 0 10px 20px rgba(255,255,255,0.25), inset 0 -15px 30px rgba(0,0,0,0.3) !important;}

html:root input[type="image"] {opacity: .85 !important}
html:root input[type="image"]:hover {opacity: .95 !important}

/* drop-down menu */

select, option, optgroup{

background: none #383838 !important;
border-color:#555 !important;
color:#f1f1f1 !important;
-moz-appearance: none !important;}

/* :::::::: misc :::::::: */

address {background: none #333 !important}
hr   {background: none #444 !important}
.current {color: #fff !important;} /*currently viewed page*/

/* remove rounded corners, borders, spacer, padding images */

img[src*="spacer"]:empty,
[id*="round"]:empty,
[id*="bottom"]:empty, [class*="bottom"]:empty, [class*="bottom"]:empty,
[id*="top"]:empty, [class*="top"]:empty, [class*="top"]:empty,
[class*="spacer"]:empty

{background-image: none !important;}

/* menus and navigation */

nav, 
menu,

/*common naming conventions - in case previous declarations fail to give solid bg*/

html body [class*="open"],
html body [id*="dropdown"],
html body [id*="dropdown"],
html body [class*="dropdown"],
html body [class*="dropdown"],
html body [id*="menu"]:not(select),
html body [class*="menu"]:not(select),
html body [class*="tooltip"],
html body [class*="popup"], 
html body [id*="popup"],

/* notes, details, etc.  maybe useful */

html body [class*="note"],
html body [class*="detail"],
html body [class*="description"]

{background-color: #232323 !important}

/* also common */
[class*="content"], [class*="container"] {background-color: #1c1c1c !important}

/* headers, logos */

[id*="masthead"] a,[id*="header"] a,
[id*="logo"] a, [class*="logo"] a

{text-indent: 0 !important;}

/* instead of increasing specificity rating by using :not, set rules separately */

html:root body [class*="layer"],
html:root body #lightbox-nav,
html:root body #imagecontainer {background-color: transparent !important}

/*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/

/* universal - sets color of text, border */

* {
color: #ccc !important; 
border-color: #444 !important;
outline-color: #444 !important;
text-shadow: none !important;}

html *:after,
html *:before

{color: #ccc !important;
border-color: #666 !important;
background: none transparent !important}


/* link */

a:link,
a:link *:not(img){ 

color: #b1cbf7 !important;
background-color: transparent !important;
border-color: #666 !important;}

a:visited, 
a:visited * {color: #cdb4e7 !important;}

a:hover,
a:hover *:not(img){

color: #ffe900 !important; 
background-color: #363037 !important;
border-color: #999 !important}

html [href*="#"]:hover {color: #ffe900 !important; background-color: transparent !important;}


/* event handlers/attributes */

[onclick],
[ondblclick],
[onmousedown]

{color: #dfd5bc !important;
text-indent: 0 !important}

[onclick]:hover,
[ondblclick]:hover,
[onmousedown]:hover

{color: #feff97 !important;}

/* make images transparent */

img  { opacity: .75 !important;}
img:hover  { opacity: 1 !important; background-color: #888 !important; }
svg {background: none #666 !important;}

/* highlight */
::-moz-selection {background-color: #626f61 !important; color: #f6f7b9 !important;}

/* :::::::: specific fixes :::::::: */

/* google search link fix */
.g .r {background-color: transparent !important;}

/* google result hover highlight*/
div.vsc:hover > .vspi, div.vso > .vspi {background: none transparent !important; border: none !important;}

}

/* :::::::: About... :::::::: */

@-moz-document url(about:newtab) {

window {background: #141414 !important;}

#newtab-scrollbox {
background-color: transparent !important;
background-image: 
    url("chrome://browser/skin/newtab/noise.png"),
    -moz-linear-gradient(transparent,transparent) !important }

.newtab-title {background-color: rgba(0,0,0,.75) !important; color: #eee !important;}

}

@-moz-document url(about:blank) {

html, html * {
  background: none #141414 !important;
  color: #CCC !important;}

}

@-moz-document url-prefix("about:neterror") {

html, body {background-color: #353535 !important; color: #CCC !important}
#errorPageContainer {background-color: #222 !important; border-color: #666 !important}
#errorPageContainer button {opacity: .8 !important}

/*resurrect pages FF extension*/
#resurrect {background-color: #333 !important; border-color: #000 !important} 

}

@-moz-document url(about:privatebrowsing) {

html, body {background-color: #353535 !important; color: #CCC !important}
#errorPageContainer {background-color: #222 !important; border-color: #666 !important}

}

@-moz-document url(about:about) {

html, body {background-color: #353535 !important; color: #CCC !important}
#abouts {background-color: #222 !important; border-color: #666 !important} 

}

@-moz-document url(about:home) {

html, body {background-color: #000000 !important; color: #000000 !important}
#topSection {background-color: #353535 !important; border-color: #353535 !important} 
#launcher {background-color: #353535 !important; border-color: #353535 !important} 

}

@-moz-document url(about:sync-tabs) {

#tabsList {background-color: #222 !important}

}
}}}
/***
|''Name:''|DataTiddlerPlugin|
|''Version:''|1.0.6 (2006-08-26)|
|''Source:''|http://tiddlywiki.abego-software.de/#DataTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license]]|
|''TiddlyWiki:''|1.2.38+, 2.0|
|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
!Description
Enhance your tiddlers with structured data (such as strings, booleans, numbers, or even arrays and compound objects) that can be easily accessed and modified through named fields (in JavaScript code).

Such tiddler data can be used in various applications. E.g. you may create tables that collect data from various tiddlers. 

''//Example: "Table with all December Expenses"//''
{{{
<<forEachTiddler
    where
        'tiddler.tags.contains("expense") && tiddler.data("month") == "Dec"'
    write
        '"|[["+tiddler.title+"]]|"+tiddler.data("descr")+"| "+tiddler.data("amount")+"|\n"'
>>
}}}
//(This assumes that expenses are stored in tiddlers tagged with "expense".)//
<<forEachTiddler
    where
        'tiddler.tags.contains("expense") && tiddler.data("month") == "Dec"'
    write
        '"|[["+tiddler.title+"]]|"+tiddler.data("descr")+"| "+tiddler.data("amount")+"|\n"'
>>
For other examples see DataTiddlerExamples.




''Access and Modify Tiddler Data''

You can "attach" data to every tiddler by assigning a JavaScript value (such as a string, boolean, number, or even arrays and compound objects) to named fields. 

These values can be accessed and modified through the following Tiddler methods:
|!Method|!Example|!Description|
|{{{data(field)}}}|{{{t.data("age")}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined {{{undefined}}} is returned.|
|{{{data(field,defaultValue)}}}|{{{t.data("isVIP",false)}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined the defaultValue is returned.|
|{{{data()}}}|{{{t.data()}}}|Returns the data object of the tiddler, with a property for every field. The properties of the returned data object may only be read and not be modified. To modify the data use DataTiddler.setData(...) or the corresponding Tiddler method.|
|{{{setData(field,value)}}}|{{{t.setData("age",42)}}}|Sets the value of the given data field of the tiddler to the value. When the value is {{{undefined}}} the field is removed.|
|{{{setData(field,value,defaultValue)}}}|{{{t.setData("isVIP",flag,false)}}}|Sets the value of the given data field of the tiddler to the value. When the value is equal to the defaultValue no value is set (and the field is removed).|

Alternatively you may use the following functions to access and modify the data. In this case the tiddler argument is either a tiddler or the name of a tiddler.
|!Method|!Description|
|{{{DataTiddler.getData(tiddler,field)}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined {{{undefined}}} is returned.|
|{{{DataTiddler.getData(tiddler,field,defaultValue)}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined the defaultValue is returned.|
|{{{DataTiddler.getDataObject(tiddler)}}}|Returns the data object of the tiddler, with a property for every field. The properties of the returned data object may only be read and not be modified. To modify the data use DataTiddler.setData(...) or the corresponding Tiddler method.|
|{{{DataTiddler.setData(tiddler,field,value)}}}|Sets the value of the given data field of the tiddler to the value. When the value is {{{undefined}}} the field is removed.|
|{{{DataTiddler.setData(tiddler,field,value,defaultValue)}}}|Sets the value of the given data field of the tiddler to the value. When the value is equal to the defaultValue no value is set (and the field is removed).|
//(For details on the various functions see the detailed comments in the source code.)//


''Data Representation in a Tiddler''

The data of a tiddler is stored as plain text in the tiddler's content/text, inside a "data" section that is framed by a {{{<data>...</data>}}} block. Inside the data section the information is stored in the [[JSON format|http://www.crockford.com/JSON/index.html]]. 

//''Data Section Example:''//
{{{
<data>{"isVIP":true,"user":"John Brown","age":34}</data>
}}}

The data section is not displayed when viewing the tiddler (see also "The showData Macro").

Beside the data section a tiddler may have all kind of other content.

Typically you will not access the data section text directly but use the methods given above. Nevertheless you may retrieve the text of the data section's content through the {{{DataTiddler.getDataText(tiddler)}}} function.


''Saving Changes''

The "setData" methods respect the "ForceMinorUpdate" and "AutoSave" configuration values. I.e. when "ForceMinorUpdate" is true changing a value using setData will not affect the "modifier" and "modified" attributes. With "AutoSave" set to true every setData will directly save the changes after a setData.


''Notifications''

No notifications are sent when a tiddler's data value is changed through the "setData" methods. 

''Escape Data Section''
In case that you want to use the text {{{<data>}}} or {{{</data>}}} in a tiddler text you must prefix the text with a tilde ('~'). Otherwise it may be wrongly considered as the data section. The tiddler text {{{~<data>}}} is displayed as {{{<data>}}}.


''The showData Macro''

By default the data of a tiddler (that is stored in the {{{<data>...</data>}}} section of the tiddler) is not displayed. If you want to display this data you may used the {{{<<showData ...>>}}} macro:

''Syntax:'' 
|>|{{{<<}}}''showData '' [''JSON''] [//tiddlerName//] {{{>>}}}|
|''JSON''|By default the data is rendered as a table with a "Name" and "Value" column. When defining ''JSON'' the data is rendered in JSON format|
|//tiddlerName//|Defines the tiddler holding the data to be displayed. When no tiddler is given the tiddler containing the showData macro is used. When the tiddler name contains spaces you must quote the name (or use the {{{[[...]]}}} syntax.)|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|


!Revision history
* v1.0.6 (2006-08-26) 
** Removed misleading comment
* v1.0.5 (2006-02-27) (Internal Release Only)
** Internal
*** Make "JSLint" conform
* v1.0.4 (2006-02-05)
** Bugfix: showData fails in TiddlyWiki 2.0
* v1.0.3 (2006-01-06)
** Support TiddlyWiki 2.0
* v1.0.2 (2005-12-22)
** Enhancements:
*** Handle texts "<data>" or "</data>" more robust when used in a tiddler text or as a field value.
*** Improved (JSON) error messages.
** Bugs fixed: 
*** References are not updated when using the DataTiddler.
*** Changes to compound objects are not always saved.
*** "~</data>" is not rendered correctly (expected "</data>")
* v1.0.1 (2005-12-13)
** Features: 
*** The showData macro supports an optional "tiddlername" argument to specify the tiddler containing the data to be displayed
** Bugs fixed: 
*** A script immediately following a data section is deleted when the data is changed. (Thanks to GeoffS for reporting.)
* v1.0.0 (2005-12-12)
** initial version

!Code
***/
//{{{
//============================================================================
//============================================================================
//                           DataTiddlerPlugin
//============================================================================
//============================================================================

// Ensure that the DataTiddler Plugin is only installed once.
//
if (!version.extensions.DataTiddlerPlugin) {



version.extensions.DataTiddlerPlugin = {
    major: 1, minor: 0, revision: 6,
    date: new Date(2006, 7, 26), 
    type: 'plugin',
    source: "http://tiddlywiki.abego-software.de/#DataTiddlerPlugin"
};

// For backward compatibility with v1.2.x
//
if (!window.story) window.story=window; 
if (!TiddlyWiki.prototype.getTiddler) {
	TiddlyWiki.prototype.getTiddler = function(title) { 
		var t = this.tiddlers[title]; 
		return (t !== undefined && t instanceof Tiddler) ? t : null; 
	};
}

//============================================================================
// DataTiddler Class
//============================================================================

// ---------------------------------------------------------------------------
// Configurations and constants 
// ---------------------------------------------------------------------------

function DataTiddler() {
}

DataTiddler = {
    // Function to stringify a JavaScript value, producing the text for the data section content.
    // (Must match the implementation of DataTiddler.parse.)
    //
    stringify : null,
    

    // Function to parse the text for the data section content, producing a JavaScript value.
    // (Must match the implementation of DataTiddler.stringify.)
    //
    parse : null
};

// Ensure access for IE
window.DataTiddler = DataTiddler;

// ---------------------------------------------------------------------------
// Data Accessor and Mutator
// ---------------------------------------------------------------------------


// Returns the value of the given data field of the tiddler.
// When no such field is defined or its value is undefined
// the defaultValue is returned.
// 
// @param tiddler either a tiddler name or a tiddler
//
DataTiddler.getData = function(tiddler, field, defaultValue) {
    var t = (typeof tiddler == "string") ? store.getTiddler(tiddler) : tiddler;
    if (!(t instanceof Tiddler)) {
        throw "Tiddler expected. Got "+tiddler;
    }

    return DataTiddler.getTiddlerDataValue(t, field, defaultValue);
};


// Sets the value of the given data field of the tiddler to
// the value. When the value is equal to the defaultValue
// no value is set (and the field is removed)
//
// Changing data of a tiddler will not trigger notifications.
// 
// @param tiddler either a tiddler name or a tiddler
//
DataTiddler.setData = function(tiddler, field, value, defaultValue) {
    var t = (typeof tiddler == "string") ? store.getTiddler(tiddler) : tiddler;
    if (!(t instanceof Tiddler)) {
        throw "Tiddler expected. Got "+tiddler+ "("+t+")";
    }

    DataTiddler.setTiddlerDataValue(t, field, value, defaultValue);
};


// Returns the data object of the tiddler, with a property for every field.
//
// The properties of the returned data object may only be read and
// not be modified. To modify the data use DataTiddler.setData(...) 
// or the corresponding Tiddler method.
//
// If no data section is defined a new (empty) object is returned.
//
// @param tiddler either a tiddler name or a Tiddler
//
DataTiddler.getDataObject = function(tiddler) {
    var t = (typeof tiddler == "string") ? store.getTiddler(tiddler) : tiddler;
    if (!(t instanceof Tiddler)) {
        throw "Tiddler expected. Got "+tiddler;
    }

    return DataTiddler.getTiddlerDataObject(t);
};

// Returns the text of the content of the data section of the tiddler.
//
// When no data section is defined for the tiddler null is returned 
//
// @param tiddler either a tiddler name or a Tiddler
// @return [may be null]
//
DataTiddler.getDataText = function(tiddler) {
    var t = (typeof tiddler == "string") ? store.getTiddler(tiddler) : tiddler;
    if (!(t instanceof Tiddler)) {
        throw "Tiddler expected. Got "+tiddler;
    }

    return DataTiddler.readDataSectionText(t);
};


// ---------------------------------------------------------------------------
// Internal helper methods (must not be used by code from outside this plugin)
// ---------------------------------------------------------------------------

// Internal.
//
// The original JSONError is not very user friendly, 
// especially it does not define a toString() method
// Therefore we extend it here.
//
DataTiddler.extendJSONError = function(ex) {
	if (ex.name == 'JSONError') {
        ex.toString = function() {
			return ex.name + ": "+ex.message+" ("+ex.text+")";
		};
	}
	return ex;
};

// Internal.
//
// @param t a Tiddler
//
DataTiddler.getTiddlerDataObject = function(t) {
    if (t.dataObject === undefined) {
        var data = DataTiddler.readData(t);
        t.dataObject = (data) ? data : {};
    }
    
    return t.dataObject;
};


// Internal.
//
// @param tiddler a Tiddler
//
DataTiddler.getTiddlerDataValue = function(tiddler, field, defaultValue) {
    var value = DataTiddler.getTiddlerDataObject(tiddler)[field];
    return (value === undefined) ? defaultValue : value;
};


// Internal.
//
// @param tiddler a Tiddler
//
DataTiddler.setTiddlerDataValue = function(tiddler, field, value, defaultValue) {
    var data = DataTiddler.getTiddlerDataObject(tiddler);
    var oldValue = data[field];
	
    if (value == defaultValue) {
        if (oldValue !== undefined) {
            delete data[field];
            DataTiddler.save(tiddler);
        }
        return;
    }
    data[field] = value;
    DataTiddler.save(tiddler);
};

// Internal.
//
// Reads the data section from the tiddler's content and returns its text
// (as a String).
//
// Returns null when no data is defined.
//
// @param tiddler a Tiddler
// @return [may be null]
//
DataTiddler.readDataSectionText = function(tiddler) {
    var matches = DataTiddler.getDataTiddlerMatches(tiddler);
    if (matches === null || !matches[2]) {
        return null;
    }
    return matches[2];
};

// Internal.
//
// Reads the data section from the tiddler's content and returns it
// (as an internalized object).
//
// Returns null when no data is defined.
//
// @param tiddler a Tiddler
// @return [may be null]
//
DataTiddler.readData = function(tiddler) {
    var text = DataTiddler.readDataSectionText(tiddler);
	try {
	    return text ? DataTiddler.parse(text) : null;
	} catch(ex) {
		throw DataTiddler.extendJSONError(ex);
	}
};

// Internal.
// 
// Returns the serialized text of the data of the given tiddler, as it
// should be stored in the data section.
//
// @param tiddler a Tiddler
//
DataTiddler.getDataTextOfTiddler = function(tiddler) {
    var data = DataTiddler.getTiddlerDataObject(tiddler);
    return DataTiddler.stringify(data);
};


// Internal.
// 
DataTiddler.indexOfNonEscapedText = function(s, subString, startIndex) {
	var index = s.indexOf(subString, startIndex);
	while ((index > 0) && (s[index-1] == '~')) { 
		index = s.indexOf(subString, index+1);
	}
	return index;
};

// Internal.
//
DataTiddler.getDataSectionInfo = function(text) {
	// Special care must be taken to handle "<data>" and "</data>" texts inside
	// a data section. 
	// Also take care not to use an escaped <data> (i.e. "~<data>") as the start 
	// of a data section. (Same for </data>)

    // NOTE: we are explicitly searching for a data section that contains a JSON
    // string, i.e. framed with braces. This way we are little bit more robust in
    // case the tiddler contains unescaped texts "<data>" or "</data>". This must
    // be changed when using a different stringifier.

	var startTagText = "<data>{";
	var endTagText = "}</data>";

	var startPos = 0;

	// Find the first not escaped "<data>".
	var startDataTagIndex = DataTiddler.indexOfNonEscapedText(text, startTagText, 0);
	if (startDataTagIndex < 0) {
		return null;
	}

	// Find the *last* not escaped "</data>".
	var endDataTagIndex = text.indexOf(endTagText, startDataTagIndex);
	if (endDataTagIndex < 0) {
		return null;
	}
	var nextEndDataTagIndex;
	while ((nextEndDataTagIndex = text.indexOf(endTagText, endDataTagIndex+1)) >= 0) {
		endDataTagIndex = nextEndDataTagIndex;
	}

	return {
		prefixEnd: startDataTagIndex, 
		dataStart: startDataTagIndex+(startTagText.length)-1, 
		dataEnd: endDataTagIndex, 
		suffixStart: endDataTagIndex+(endTagText.length)
	};
};

// Internal.
// 
// Returns the "matches" of a content of a DataTiddler on the
// "data" regular expression. Return null when no data is defined
// in the tiddler content.
//
// Group 1: text before data section (prefix)
// Group 2: content of data section
// Group 3: text behind data section (suffix)
//
// @param tiddler a Tiddler
// @return [may be null] null when the tiddler contains no data section, otherwise see above.
//
DataTiddler.getDataTiddlerMatches = function(tiddler) {
	var text = tiddler.text;
	var info = DataTiddler.getDataSectionInfo(text);
	if (!info) {
		return null;
	}

	var prefix = text.substr(0,info.prefixEnd);
	var data = text.substr(info.dataStart, info.dataEnd-info.dataStart+1);
	var suffix = text.substr(info.suffixStart);
	
	return [text, prefix, data, suffix];
};


// Internal.
//
// Saves the data in a <data> block of the given tiddler (as a minor change). 
//
// The "chkAutoSave" and "chkForceMinorUpdate" options are respected. 
// I.e. the TiddlyWiki *file* is only saved when AutoSave is on.
//
// Notifications are not send. 
//
// This method should only be called when the data really has changed. 
//
// @param tiddler
//             the tiddler to be saved.
//
DataTiddler.save = function(tiddler) {

    var matches = DataTiddler.getDataTiddlerMatches(tiddler);

    var prefix;
    var suffix;
    if (matches === null) {
        prefix = tiddler.text;
        suffix = "";
    } else {
        prefix = matches[1];
        suffix = matches[3];
    }

    var dataText = DataTiddler.getDataTextOfTiddler(tiddler);
    var newText = 
            (dataText !== null) 
                ? prefix + "<data>" + dataText + "</data>" + suffix
                : prefix + suffix;
    if (newText != tiddler.text) {
        // make the change in the tiddlers text
        
        // ... see DataTiddler.MyTiddlerChangedFunction
        tiddler.isDataTiddlerChange = true;
        
        // ... do the action change
        tiddler.set(
                tiddler.title,
                newText,
                config.options.txtUserName, 
                config.options.chkForceMinorUpdate? undefined : new Date(),
                tiddler.tags);

        // ... see DataTiddler.MyTiddlerChangedFunction
        delete tiddler.isDataTiddlerChange;

        // Mark the store as dirty.
        store.dirty = true;
 
        // AutoSave if option is selected
        if(config.options.chkAutoSave) {
           saveChanges();
        }
    }
};

// Internal.
//
DataTiddler.MyTiddlerChangedFunction = function() {
    // Remove the data object from the tiddler when the tiddler is changed
    // by code other than DataTiddler code. 
    //
    // This is necessary since the data object is just a "cached version" 
    // of the data defined in the data section of the tiddler and the 
    // "external" change may have changed the content of the data section.
    // Thus we are not sure if the data object reflects the data section 
    // contents. 
    // 
    // By deleting the data object we ensure that the data object is 
    // reconstructed the next time it is needed, with the data defined by
    // the data section in the tiddler's text.
    
    // To indicate that a change is a "DataTiddler change" a temporary
    // property "isDataTiddlerChange" is added to the tiddler.
    if (this.dataObject && !this.isDataTiddlerChange) {
        delete this.dataObject;
    }
    
    // call the original code.
	DataTiddler.originalTiddlerChangedFunction.apply(this, arguments);
};


//============================================================================
// Formatters
//============================================================================

// This formatter ensures that "~<data>" is rendered as "<data>". This is used to 
// escape the "<data>" of a data section, just in case someone really wants to use
// "<data>" as a text in a tiddler and not start a data section.
//
// Same for </data>.
//
config.formatters.push( {
    name: "data-escape",
    match: "~<\\/?data>",

    handler: function(w) {
            w.outputText(w.output,w.matchStart + 1,w.nextMatch);
    }
} );


// This formatter ensures that <data>...</data> sections are not rendered.
//
config.formatters.push( {
    name: "data",
    match: "<data>",

    handler: function(w) {
		var info = DataTiddler.getDataSectionInfo(w.source);
		if (info && info.prefixEnd == w.matchStart) {
            w.nextMatch = info.suffixStart;
		} else {
			w.outputText(w.output,w.matchStart,w.nextMatch);
		}
    }
} );


//============================================================================
// Tiddler Class Extension
//============================================================================

// "Hijack" the changed method ---------------------------------------------------

DataTiddler.originalTiddlerChangedFunction = Tiddler.prototype.changed;
Tiddler.prototype.changed = DataTiddler.MyTiddlerChangedFunction;

// Define accessor methods -------------------------------------------------------

// Returns the value of the given data field of the tiddler. When no such field 
// is defined or its value is undefined the defaultValue is returned.
//
// When field is undefined (or null) the data object is returned. (See 
// DataTiddler.getDataObject.)
//
// @param field [may be null, undefined]
// @param defaultValue [may be null, undefined]
// @return [may be null, undefined]
//
Tiddler.prototype.data = function(field, defaultValue) {
    return (field) 
         ? DataTiddler.getTiddlerDataValue(this, field, defaultValue)
         : DataTiddler.getTiddlerDataObject(this);
};

// Sets the value of the given data field of the tiddler to the value. When the 
// value is equal to the defaultValue no value is set (and the field is removed).
//
// @param value [may be null, undefined]
// @param defaultValue [may be null, undefined]
//
Tiddler.prototype.setData = function(field, value, defaultValue) {
    DataTiddler.setTiddlerDataValue(this, field, value, defaultValue);
};


//============================================================================
// showData Macro
//============================================================================

config.macros.showData = {
     // Standard Properties
     label: "showData",
     prompt: "Display the values stored in the data section of the tiddler"
};

config.macros.showData.handler = function(place,macroName,params) {
    // --- Parsing ------------------------------------------

    var i = 0; // index running over the params
    // Parse the optional "JSON"
    var showInJSONFormat = false;
    if ((i < params.length) && params[i] == "JSON") {
        i++;
        showInJSONFormat = true;
    }
    
    var tiddlerName = story.findContainingTiddler(place).id.substr(7);
    if (i < params.length) {
        tiddlerName = params[i];
        i++;
    }

    // --- Processing ------------------------------------------
    try {
        if (showInJSONFormat) {
            this.renderDataInJSONFormat(place, tiddlerName);
        } else {
            this.renderDataAsTable(place, tiddlerName);
        }
    } catch (e) {
        this.createErrorElement(place, e);
    }
};

config.macros.showData.renderDataInJSONFormat = function(place,tiddlerName) {
    var text = DataTiddler.getDataText(tiddlerName);
    if (text) {
        createTiddlyElement(place,"pre",null,null,text);
    }
};

config.macros.showData.renderDataAsTable = function(place,tiddlerName) {
    var text = "|!Name|!Value|\n";
    var data = DataTiddler.getDataObject(tiddlerName);
    if (data) {
        for (var i in data) {
            var value = data[i];
            text += "|"+i+"|"+DataTiddler.stringify(value)+"|\n";
        }
    }
    
    wikify(text, place);
};


// Internal.
//
// Creates an element that holds an error message
// 
config.macros.showData.createErrorElement = function(place, exception) {
    var message = (exception.description) ? exception.description : exception.toString();
    return createTiddlyElement(place,"span",null,"showDataError","<<showData ...>>: "+message);
};

// ---------------------------------------------------------------------------
// Stylesheet Extensions (may be overridden by local StyleSheet)
// ---------------------------------------------------------------------------
//
setStylesheet(
    ".showDataError{color: #ffffff;background-color: #880000;}",
    "showData");


} // of "install only once"
// Used Globals (for JSLint) ==============

// ... TiddlyWiki Core
/*global 	createTiddlyElement, saveChanges, store, story, wikify */
// ... DataTiddler
/*global 	DataTiddler */
// ... JSON
/*global 	JSON */
			

/***
!JSON Code, used to serialize the data
***/
/*
Copyright (c) 2005 JSON.org

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The Software shall be used for Good, not Evil.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

/*
    The global object JSON contains two methods.

    JSON.stringify(value) takes a JavaScript value and produces a JSON text.
    The value must not be cyclical.

    JSON.parse(text) takes a JSON text and produces a JavaScript value. It will
    throw a 'JSONError' exception if there is an error.
*/
var JSON = {
    copyright: '(c)2005 JSON.org',
    license: 'http://www.crockford.com/JSON/license.html',
/*
    Stringify a JavaScript value, producing a JSON text.
*/
    stringify: function (v) {
        var a = [];

/*
    Emit a string.
*/
        function e(s) {
            a[a.length] = s;
        }

/*
    Convert a value.
*/
        function g(x) {
            var c, i, l, v;

            switch (typeof x) {
            case 'object':
                if (x) {
                    if (x instanceof Array) {
                        e('[');
                        l = a.length;
                        for (i = 0; i < x.length; i += 1) {
                            v = x[i];
                            if (typeof v != 'undefined' &&
                                    typeof v != 'function') {
                                if (l < a.length) {
                                    e(',');
                                }
                                g(v);
                            }
                        }
                        e(']');
                        return;
                    } else if (typeof x.toString != 'undefined') {
                        e('{');
                        l = a.length;
                        for (i in x) {
                            v = x[i];
                            if (x.hasOwnProperty(i) &&
                                    typeof v != 'undefined' &&
                                    typeof v != 'function') {
                                if (l < a.length) {
                                    e(',');
                                }
                                g(i);
                                e(':');
                                g(v);
                            }
                        }
                        return e('}');
                    }
                }
                e('null');
                return;
            case 'number':
                e(isFinite(x) ? +x : 'null');
                return;
            case 'string':
                l = x.length;
                e('"');
                for (i = 0; i < l; i += 1) {
                    c = x.charAt(i);
                    if (c >= ' ') {
                        if (c == '\\' || c == '"') {
                            e('\\');
                        }
                        e(c);
                    } else {
                        switch (c) {
                            case '\b':
                                e('\\b');
                                break;
                            case '\f':
                                e('\\f');
                                break;
                            case '\n':
                                e('\\n');
                                break;
                            case '\r':
                                e('\\r');
                                break;
                            case '\t':
                                e('\\t');
                                break;
                            default:
                                c = c.charCodeAt();
                                e('\\u00' + Math.floor(c / 16).toString(16) +
                                    (c % 16).toString(16));
                        }
                    }
                }
                e('"');
                return;
            case 'boolean':
                e(String(x));
                return;
            default:
                e('null');
                return;
            }
        }
        g(v);
        return a.join('');
    },
/*
    Parse a JSON text, producing a JavaScript value.
*/
    parse: function (text) {
        var p = /^\s*(([,:{}\[\]])|"(\\.|[^\x00-\x1f"\\])*"|-?\d+(\.\d*)?([eE][+-]?\d+)?|true|false|null)\s*/,
            token,
            operator;

        function error(m, t) {
            throw {
                name: 'JSONError',
                message: m,
                text: t || operator || token
            };
        }

        function next(b) {
            if (b && b != operator) {
                error("Expected '" + b + "'");
            }
            if (text) {
                var t = p.exec(text);
                if (t) {
                    if (t[2]) {
                        token = null;
                        operator = t[2];
                    } else {
                        operator = null;
                        try {
                            token = eval(t[1]);
                        } catch (e) {
                            error("Bad token", t[1]);
                        }
                    }
                    text = text.substring(t[0].length);
                } else {
                    error("Unrecognized token", text);
                }
            } else {
                token = operator = undefined;
            }
        }


        function val() {
            var k, o;
            switch (operator) {
            case '{':
                next('{');
                o = {};
                if (operator != '}') {
                    for (;;) {
                        if (operator || typeof token != 'string') {
                            error("Missing key");
                        }
                        k = token;
                        next();
                        next(':');
                        o[k] = val();
                        if (operator != ',') {
                            break;
                        }
                        next(',');
                    }
                }
                next('}');
                return o;
            case '[':
                next('[');
                o = [];
                if (operator != ']') {
                    for (;;) {
                        o.push(val());
                        if (operator != ',') {
                            break;
                        }
                        next(',');
                    }
                }
                next(']');
                return o;
            default:
                if (operator !== null) {
                    error("Missing value");
                }
                k = token;
                next();
                return k;
            }
        }
        next();
        return val();
    }
};

/***
!Setup the data serialization
***/

DataTiddler.format = "JSON";
DataTiddler.stringify = JSON.stringify;
DataTiddler.parse = JSON.parse;

//}}}
<html><iframe src="https://docs.google.com/presentation/d/1n-bH14Z2RbhzqGu1kgMRSOuAJJRNkz_9INjPK65Flgw/embed?start=false&loop=false&delayms=3000" frameborder="0" width="960" height="749" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"></iframe></html>
[[Home]]
[[XFCE|http://xfce.org/]]:
<html><img src="http://i.imgur.com/1TyvK.png" width="500"></html>

[[Cinnamon|http://www.linuxmint.com/index.php]]:
<html><img src="http://i.imgur.com/SCUzMK5.jpg" width="500"></html>

[[KDE|http://www.kde.org/]]:
<html><img src="http://i.imgur.com/Zw2bH.jpg" width="500"></html>

Awesome WM:
<html><img src="http://i.imgur.com/2skZT33.png" width="500"></html>
Here is the link to [[customize awesome WM|Awesome window manager tutorials]] and [[get the command line info bar|Terminal Prompt 3]].
<html><iframe src="http://pastehtml.com/view/av2hlgw9y.html" width="100%" height="350" frameBorder="0">
</iframe></html>
/***
|''Name:''|ForEachTiddlerPlugin|
|''Version:''|1.0.8 (2007-04-12)|
|''Source:''|http://tiddlywiki.abego-software.de/#ForEachTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]|
|''Copyright:''|&copy; 2005-2007 [[abego Software|http://www.abego-software.de]]|
|''TiddlyWiki:''|1.2.38+, 2.0|
|''Browser:''|Firefox 1.0.4+; Firefox 1.5; InternetExplorer 6.0|
!Description

Create customizable lists, tables etc. for your selections of tiddlers. Specify the tiddlers to include and their order through a powerful language.

''Syntax:'' 
|>|{{{<<}}}''forEachTiddler'' [''in'' //tiddlyWikiPath//] [''where'' //whereCondition//] [''sortBy'' //sortExpression// [''ascending'' //or// ''descending'']] [''script'' //scriptText//] [//action// [//actionParameters//]]{{{>>}}}|
|//tiddlyWikiPath//|The filepath to the TiddlyWiki the macro should work on. When missing the current TiddlyWiki is used.|
|//whereCondition//|(quoted) JavaScript boolean expression. May refer to the build-in variables {{{tiddler}}} and  {{{context}}}.|
|//sortExpression//|(quoted) JavaScript expression returning "comparable" objects (using '{{{<}}}','{{{>}}}','{{{==}}}'. May refer to the build-in variables {{{tiddler}}} and  {{{context}}}.|
|//scriptText//|(quoted) JavaScript text. Typically defines JavaScript functions that are called by the various JavaScript expressions (whereClause, sortClause, action arguments,...)|
|//action//|The action that should be performed on every selected tiddler, in the given order. By default the actions [[addToList|AddToListAction]] and [[write|WriteAction]] are supported. When no action is specified [[addToList|AddToListAction]]  is used.|
|//actionParameters//|(action specific) parameters the action may refer while processing the tiddlers (see action descriptions for details). <<tiddler [[JavaScript in actionParameters]]>>|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|

See details see [[ForEachTiddlerMacro]] and [[ForEachTiddlerExamples]].

!Revision history
* v1.0.8 (2007-04-12)
** Adapted to latest TiddlyWiki 2.2 Beta importTiddlyWiki API (introduced with changeset 2004). TiddlyWiki 2.2 Beta builds prior to changeset 2004 are no longer supported (but TiddlyWiki 2.1 and earlier, of cause)
* v1.0.7 (2007-03-28)
** Also support "pre" formatted TiddlyWikis (introduced with TW 2.2) (when using "in" clause to work on external tiddlers)
* v1.0.6 (2006-09-16)
** Context provides "viewerTiddler", i.e. the tiddler used to view the macro. Most times this is equal to the "inTiddler", but when using the "tiddler" macro both may be different.
** Support "begin", "end" and "none" expressions in "write" action
* v1.0.5 (2006-02-05)
** Pass tiddler containing the macro with wikify, context object also holds reference to tiddler containing the macro ("inTiddler"). Thanks to SimonBaird.
** Support Firefox 1.5.0.1
** Internal
*** Make "JSLint" conform
*** "Only install once"
* v1.0.4 (2006-01-06)
** Support TiddlyWiki 2.0
* v1.0.3 (2005-12-22)
** Features: 
*** Write output to a file supports multi-byte environments (Thanks to Bram Chen) 
*** Provide API to access the forEachTiddler functionality directly through JavaScript (see getTiddlers and performMacro)
** Enhancements:
*** Improved error messages on InternetExplorer.
* v1.0.2 (2005-12-10)
** Features: 
*** context object also holds reference to store (TiddlyWiki)
** Fixed Bugs: 
*** ForEachTiddler 1.0.1 has broken support on win32 Opera 8.51 (Thanks to BrunoSabin for reporting)
* v1.0.1 (2005-12-08)
** Features: 
*** Access tiddlers stored in separated TiddlyWikis through the "in" option. I.e. you are no longer limited to only work on the "current TiddlyWiki".
*** Write output to an external file using the "toFile" option of the "write" action. With this option you may write your customized tiddler exports.
*** Use the "script" section to define "helper" JavaScript functions etc. to be used in the various JavaScript expressions (whereClause, sortClause, action arguments,...).
*** Access and store context information for the current forEachTiddler invocation (through the build-in "context" object) .
*** Improved script evaluation (for where/sort clause and write scripts).
* v1.0.0 (2005-11-20)
** initial version

!Code
***/
//{{{

	
//============================================================================
//============================================================================
//		   ForEachTiddlerPlugin
//============================================================================
//============================================================================

// Only install once
if (!version.extensions.ForEachTiddlerPlugin) {

if (!window.abego) window.abego = {};

version.extensions.ForEachTiddlerPlugin = {
	major: 1, minor: 0, revision: 8, 
	date: new Date(2007,3,12), 
	source: "http://tiddlywiki.abego-software.de/#ForEachTiddlerPlugin",
	licence: "[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]",
	copyright: "Copyright (c) abego Software GmbH, 2005-2007 (www.abego-software.de)"
};

// For backward compatibility with TW 1.2.x
//
if (!TiddlyWiki.prototype.forEachTiddler) {
	TiddlyWiki.prototype.forEachTiddler = function(callback) {
		for(var t in this.tiddlers) {
			callback.call(this,t,this.tiddlers[t]);
		}
	};
}

//============================================================================
// forEachTiddler Macro
//============================================================================

version.extensions.forEachTiddler = {
	major: 1, minor: 0, revision: 8, date: new Date(2007,3,12), provider: "http://tiddlywiki.abego-software.de"};

// ---------------------------------------------------------------------------
// Configurations and constants 
// ---------------------------------------------------------------------------

config.macros.forEachTiddler = {
	 // Standard Properties
	 label: "forEachTiddler",
	 prompt: "Perform actions on a (sorted) selection of tiddlers",

	 // actions
	 actions: {
		 addToList: {},
		 write: {}
	 }
};

// ---------------------------------------------------------------------------
//  The forEachTiddler Macro Handler 
// ---------------------------------------------------------------------------

config.macros.forEachTiddler.getContainingTiddler = function(e) {
	while(e && !hasClass(e,"tiddler"))
		e = e.parentNode;
	var title = e ? e.getAttribute("tiddler") : null; 
	return title ? store.getTiddler(title) : null;
};

config.macros.forEachTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
	// config.macros.forEachTiddler.traceMacroCall(place,macroName,params,wikifier,paramString,tiddler);

	if (!tiddler) tiddler = config.macros.forEachTiddler.getContainingTiddler(place);
	// --- Parsing ------------------------------------------

	var i = 0; // index running over the params
	// Parse the "in" clause
	var tiddlyWikiPath = undefined;
	if ((i < params.length) && params[i] == "in") {
		i++;
		if (i >= params.length) {
			this.handleError(place, "TiddlyWiki path expected behind 'in'.");
			return;
		}
		tiddlyWikiPath = this.paramEncode((i < params.length) ? params[i] : "");
		i++;
	}

	// Parse the where clause
	var whereClause ="true";
	if ((i < params.length) && params[i] == "where") {
		i++;
		whereClause = this.paramEncode((i < params.length) ? params[i] : "");
		i++;
	}

	// Parse the sort stuff
	var sortClause = null;
	var sortAscending = true; 
	if ((i < params.length) && params[i] == "sortBy") {
		i++;
		if (i >= params.length) {
			this.handleError(place, "sortClause missing behind 'sortBy'.");
			return;
		}
		sortClause = this.paramEncode(params[i]);
		i++;

		if ((i < params.length) && (params[i] == "ascending" || params[i] == "descending")) {
			 sortAscending = params[i] == "ascending";
			 i++;
		}
	}

	// Parse the script
	var scriptText = null;
	if ((i < params.length) && params[i] == "script") {
		i++;
		scriptText = this.paramEncode((i < params.length) ? params[i] : "");
		i++;
	}

	// Parse the action. 
	// When we are already at the end use the default action
	var actionName = "addToList";
	if (i < params.length) {
	   if (!config.macros.forEachTiddler.actions[params[i]]) {
			this.handleError(place, "Unknown action '"+params[i]+"'.");
			return;
		} else {
			actionName = params[i]; 
			i++;
		}
	} 
	
	// Get the action parameter
	// (the parsing is done inside the individual action implementation.)
	var actionParameter = params.slice(i);


	// --- Processing ------------------------------------------
	try {
		this.performMacro({
				place: place, 
				inTiddler: tiddler,
				whereClause: whereClause, 
				sortClause: sortClause, 
				sortAscending: sortAscending, 
				actionName: actionName, 
				actionParameter: actionParameter, 
				scriptText: scriptText, 
				tiddlyWikiPath: tiddlyWikiPath});

	} catch (e) {
		this.handleError(place, e);
	}
};

// Returns an object with properties "tiddlers" and "context".
// tiddlers holds the (sorted) tiddlers selected by the parameter,
// context the context of the execution of the macro.
//
// The action is not yet performed.
//
// @parameter see performMacro
//
config.macros.forEachTiddler.getTiddlersAndContext = function(parameter) {

	var context = config.macros.forEachTiddler.createContext(parameter.place, parameter.whereClause, parameter.sortClause, parameter.sortAscending, parameter.actionName, parameter.actionParameter, parameter.scriptText, parameter.tiddlyWikiPath, parameter.inTiddler);

	var tiddlyWiki = parameter.tiddlyWikiPath ? this.loadTiddlyWiki(parameter.tiddlyWikiPath) : store;
	context["tiddlyWiki"] = tiddlyWiki;
	
	// Get the tiddlers, as defined by the whereClause
	var tiddlers = this.findTiddlers(parameter.whereClause, context, tiddlyWiki);
	context["tiddlers"] = tiddlers;

	// Sort the tiddlers, when sorting is required.
	if (parameter.sortClause) {
		this.sortTiddlers(tiddlers, parameter.sortClause, parameter.sortAscending, context);
	}

	return {tiddlers: tiddlers, context: context};
};

// Returns the (sorted) tiddlers selected by the parameter.
//
// The action is not yet performed.
//
// @parameter see performMacro
//
config.macros.forEachTiddler.getTiddlers = function(parameter) {
	return this.getTiddlersAndContext(parameter).tiddlers;
};

// Performs the macros with the given parameter.
//
// @param parameter holds the parameter of the macro as separate properties.
//				  The following properties are supported:
//
//						place
//						whereClause
//						sortClause
//						sortAscending
//						actionName
//						actionParameter
//						scriptText
//						tiddlyWikiPath
//
//					All properties are optional. 
//					For most actions the place property must be defined.
//
config.macros.forEachTiddler.performMacro = function(parameter) {
	var tiddlersAndContext = this.getTiddlersAndContext(parameter);

	// Perform the action
	var actionName = parameter.actionName ? parameter.actionName : "addToList";
	var action = config.macros.forEachTiddler.actions[actionName];
	if (!action) {
		this.handleError(parameter.place, "Unknown action '"+actionName+"'.");
		return;
	}

	var actionHandler = action.handler;
	actionHandler(parameter.place, tiddlersAndContext.tiddlers, parameter.actionParameter, tiddlersAndContext.context);
};

// ---------------------------------------------------------------------------
//  The actions 
// ---------------------------------------------------------------------------

// Internal.
//
// --- The addToList Action -----------------------------------------------
//
config.macros.forEachTiddler.actions.addToList.handler = function(place, tiddlers, parameter, context) {
	// Parse the parameter
	var p = 0;

	// Check for extra parameters
	if (parameter.length > p) {
		config.macros.forEachTiddler.createExtraParameterErrorElement(place, "addToList", parameter, p);
		return;
	}

	// Perform the action.
	var list = document.createElement("ul");
	place.appendChild(list);
	for (var i = 0; i < tiddlers.length; i++) {
		var tiddler = tiddlers[i];
		var listItem = document.createElement("li");
		list.appendChild(listItem);
		createTiddlyLink(listItem, tiddler.title, true);
	}
};

abego.parseNamedParameter = function(name, parameter, i) {
	var beginExpression = null;
	if ((i < parameter.length) && parameter[i] == name) {
		i++;
		if (i >= parameter.length) {
			throw "Missing text behind '%0'".format([name]);
		}
		
		return config.macros.forEachTiddler.paramEncode(parameter[i]);
	}
	return null;
}

// Internal.
//
// --- The write Action ---------------------------------------------------
//
config.macros.forEachTiddler.actions.write.handler = function(place, tiddlers, parameter, context) {
	// Parse the parameter
	var p = 0;
	if (p >= parameter.length) {
		this.handleError(place, "Missing expression behind 'write'.");
		return;
	}

	var textExpression = config.macros.forEachTiddler.paramEncode(parameter[p]);
	p++;

	// Parse the "begin" option
	var beginExpression = abego.parseNamedParameter("begin", parameter, p);
	if (beginExpression !== null) 
		p += 2;
	var endExpression = abego.parseNamedParameter("end", parameter, p);
	if (endExpression !== null) 
		p += 2;
	var noneExpression = abego.parseNamedParameter("none", parameter, p);
	if (noneExpression !== null) 
		p += 2;

	// Parse the "toFile" option
	var filename = null;
	var lineSeparator = undefined;
	if ((p < parameter.length) && parameter[p] == "toFile") {
		p++;
		if (p >= parameter.length) {
			this.handleError(place, "Filename expected behind 'toFile' of 'write' action.");
			return;
		}
		
		filename = config.macros.forEachTiddler.getLocalPath(config.macros.forEachTiddler.paramEncode(parameter[p]));
		p++;
		if ((p < parameter.length) && parameter[p] == "withLineSeparator") {
			p++;
			if (p >= parameter.length) {
				this.handleError(place, "Line separator text expected behind 'withLineSeparator' of 'write' action.");
				return;
			}
			lineSeparator = config.macros.forEachTiddler.paramEncode(parameter[p]);
			p++;
		}
	}
	
	// Check for extra parameters
	if (parameter.length > p) {
		config.macros.forEachTiddler.createExtraParameterErrorElement(place, "write", parameter, p);
		return;
	}

	// Perform the action.
	var func = config.macros.forEachTiddler.getEvalTiddlerFunction(textExpression, context);
	var count = tiddlers.length;
	var text = "";
	if (count > 0 && beginExpression)
		text += config.macros.forEachTiddler.getEvalTiddlerFunction(beginExpression, context)(undefined, context, count, undefined);
	
	for (var i = 0; i < count; i++) {
		var tiddler = tiddlers[i];
		text += func(tiddler, context, count, i);
	}
	
	if (count > 0 && endExpression)
		text += config.macros.forEachTiddler.getEvalTiddlerFunction(endExpression, context)(undefined, context, count, undefined);

	if (count == 0 && noneExpression) 
		text += config.macros.forEachTiddler.getEvalTiddlerFunction(noneExpression, context)(undefined, context, count, undefined);
		

	if (filename) {
		if (lineSeparator !== undefined) {
			lineSeparator = lineSeparator.replace(/\\n/mg, "\n").replace(/\\r/mg, "\r");
			text = text.replace(/\n/mg,lineSeparator);
		}
		saveFile(filename, convertUnicodeToUTF8(text));
	} else {
		var wrapper = createTiddlyElement(place, "span");
		wikify(text, wrapper, null/* highlightRegExp */, context.inTiddler);
	}
};


// ---------------------------------------------------------------------------
//  Helpers
// ---------------------------------------------------------------------------

// Internal.
//
config.macros.forEachTiddler.createContext = function(placeParam, whereClauseParam, sortClauseParam, sortAscendingParam, actionNameParam, actionParameterParam, scriptText, tiddlyWikiPathParam, inTiddlerParam) {
	return {
		place : placeParam, 
		whereClause : whereClauseParam, 
		sortClause : sortClauseParam, 
		sortAscending : sortAscendingParam, 
		script : scriptText,
		actionName : actionNameParam, 
		actionParameter : actionParameterParam,
		tiddlyWikiPath : tiddlyWikiPathParam,
		inTiddler : inTiddlerParam, // the tiddler containing the <<forEachTiddler ...>> macro call.
		viewerTiddler : config.macros.forEachTiddler.getContainingTiddler(placeParam) // the tiddler showing the forEachTiddler result
	};
};

// Internal.
//
// Returns a TiddlyWiki with the tiddlers loaded from the TiddlyWiki of 
// the given path.
//
config.macros.forEachTiddler.loadTiddlyWiki = function(path, idPrefix) {
	if (!idPrefix) {
		idPrefix = "store";
	}
	var lenPrefix = idPrefix.length;
	
	// Read the content of the given file
	var content = loadFile(this.getLocalPath(path));
	if(content === null) {
		throw "TiddlyWiki '"+path+"' not found.";
	}
	
	var tiddlyWiki = new TiddlyWiki();

	// Starting with TW 2.2 there is a helper function to import the tiddlers
	if (tiddlyWiki.importTiddlyWiki) {
		if (!tiddlyWiki.importTiddlyWiki(content))
			throw "File '"+path+"' is not a TiddlyWiki.";
		tiddlyWiki.dirty = false;
		return tiddlyWiki;
	}
	
	// The legacy code, for TW < 2.2
	
	// Locate the storeArea div's
	var posOpeningDiv = content.indexOf(startSaveArea);
	var posClosingDiv = content.lastIndexOf(endSaveArea);
	if((posOpeningDiv == -1) || (posClosingDiv == -1)) {
		throw "File '"+path+"' is not a TiddlyWiki.";
	}
	var storageText = content.substr(posOpeningDiv + startSaveArea.length, posClosingDiv);
	
	// Create a "div" element that contains the storage text
	var myStorageDiv = document.createElement("div");
	myStorageDiv.innerHTML = storageText;
	myStorageDiv.normalize();
	
	// Create all tiddlers in a new TiddlyWiki
	// (following code is modified copy of TiddlyWiki.prototype.loadFromDiv)
	var store = myStorageDiv.childNodes;
	for(var t = 0; t < store.length; t++) {
		var e = store[t];
		var title = null;
		if(e.getAttribute)
			title = e.getAttribute("tiddler");
		if(!title && e.id && e.id.substr(0,lenPrefix) == idPrefix)
			title = e.id.substr(lenPrefix);
		if(title && title !== "") {
			var tiddler = tiddlyWiki.createTiddler(title);
			tiddler.loadFromDiv(e,title);
		}
	}
	tiddlyWiki.dirty = false;

	return tiddlyWiki;
};


	
// Internal.
//
// Returns a function that has a function body returning the given javaScriptExpression.
// The function has the parameters:
// 
//	 (tiddler, context, count, index)
//
config.macros.forEachTiddler.getEvalTiddlerFunction = function (javaScriptExpression, context) {
	var script = context["script"];
	var functionText = "var theFunction = function(tiddler, context, count, index) { return "+javaScriptExpression+"}";
	var fullText = (script ? script+";" : "")+functionText+";theFunction;";
	return eval(fullText);
};

// Internal.
//
config.macros.forEachTiddler.findTiddlers = function(whereClause, context, tiddlyWiki) {
	var result = [];
	var func = config.macros.forEachTiddler.getEvalTiddlerFunction(whereClause, context);
	tiddlyWiki.forEachTiddler(function(title,tiddler) {
		if (func(tiddler, context, undefined, undefined)) {
			result.push(tiddler);
		}
	});
	return result;
};

// Internal.
//
config.macros.forEachTiddler.createExtraParameterErrorElement = function(place, actionName, parameter, firstUnusedIndex) {
	var message = "Extra parameter behind '"+actionName+"':";
	for (var i = firstUnusedIndex; i < parameter.length; i++) {
		message += " "+parameter[i];
	}
	this.handleError(place, message);
};

// Internal.
//
config.macros.forEachTiddler.sortAscending = function(tiddlerA, tiddlerB) {
	var result = 
		(tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue) 
			? 0
			: (tiddlerA.forEachTiddlerSortValue < tiddlerB.forEachTiddlerSortValue)
			   ? -1 
			   : +1; 
	return result;
};

// Internal.
//
config.macros.forEachTiddler.sortDescending = function(tiddlerA, tiddlerB) {
	var result = 
		(tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue) 
			? 0
			: (tiddlerA.forEachTiddlerSortValue < tiddlerB.forEachTiddlerSortValue)
			   ? +1 
			   : -1; 
	return result;
};

// Internal.
//
config.macros.forEachTiddler.sortTiddlers = function(tiddlers, sortClause, ascending, context) {
	// To avoid evaluating the sortClause whenever two items are compared 
	// we pre-calculate the sortValue for every item in the array and store it in a 
	// temporary property ("forEachTiddlerSortValue") of the tiddlers.
	var func = config.macros.forEachTiddler.getEvalTiddlerFunction(sortClause, context);
	var count = tiddlers.length;
	var i;
	for (i = 0; i < count; i++) {
		var tiddler = tiddlers[i];
		tiddler.forEachTiddlerSortValue = func(tiddler,context, undefined, undefined);
	}

	// Do the sorting
	tiddlers.sort(ascending ? this.sortAscending : this.sortDescending);

	// Delete the temporary property that holds the sortValue.	
	for (i = 0; i < tiddlers.length; i++) {
		delete tiddlers[i].forEachTiddlerSortValue;
	}
};


// Internal.
//
config.macros.forEachTiddler.trace = function(message) {
	displayMessage(message);
};

// Internal.
//
config.macros.forEachTiddler.traceMacroCall = function(place,macroName,params) {
	var message ="<<"+macroName;
	for (var i = 0; i < params.length; i++) {
		message += " "+params[i];
	}
	message += ">>";
	displayMessage(message);
};


// Internal.
//
// Creates an element that holds an error message
// 
config.macros.forEachTiddler.createErrorElement = function(place, exception) {
	var message = (exception.description) ? exception.description : exception.toString();
	return createTiddlyElement(place,"span",null,"forEachTiddlerError","<<forEachTiddler ...>>: "+message);
};

// Internal.
//
// @param place [may be null]
//
config.macros.forEachTiddler.handleError = function(place, exception) {
	if (place) {
		this.createErrorElement(place, exception);
	} else {
		throw exception;
	}
};

// Internal.
//
// Encodes the given string.
//
// Replaces 
//	 "$))" to ">>"
//	 "$)" to ">"
//
config.macros.forEachTiddler.paramEncode = function(s) {
	var reGTGT = new RegExp("\\$\\)\\)","mg");
	var reGT = new RegExp("\\$\\)","mg");
	return s.replace(reGTGT, ">>").replace(reGT, ">");
};

// Internal.
//
// Returns the given original path (that is a file path, starting with "file:")
// as a path to a local file, in the systems native file format.
//
// Location information in the originalPath (i.e. the "#" and stuff following)
// is stripped.
// 
config.macros.forEachTiddler.getLocalPath = function(originalPath) {
	// Remove any location part of the URL
	var hashPos = originalPath.indexOf("#");
	if(hashPos != -1)
		originalPath = originalPath.substr(0,hashPos);
	// Convert to a native file format assuming
	// "file:///x:/path/path/path..." - pc local file --> "x:\path\path\path..."
	// "file://///server/share/path/path/path..." - FireFox pc network file --> "\\server\share\path\path\path..."
	// "file:///path/path/path..." - mac/unix local file --> "/path/path/path..."
	// "file://server/share/path/path/path..." - pc network file --> "\\server\share\path\path\path..."
	var localPath;
	if(originalPath.charAt(9) == ":") // pc local file
		localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\");
	else if(originalPath.indexOf("file://///") === 0) // FireFox pc network file
		localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\");
	else if(originalPath.indexOf("file:///") === 0) // mac/unix local file
		localPath = unescape(originalPath.substr(7));
	else if(originalPath.indexOf("file:/") === 0) // mac/unix local file
		localPath = unescape(originalPath.substr(5));
	else // pc network file
		localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\");	
	return localPath;
};

// ---------------------------------------------------------------------------
// Stylesheet Extensions (may be overridden by local StyleSheet)
// ---------------------------------------------------------------------------
//
setStylesheet(
	".forEachTiddlerError{color: #ffffff;background-color: #880000;}",
	"forEachTiddler");

//============================================================================
// End of forEachTiddler Macro
//============================================================================


//============================================================================
// String.startsWith Function
//============================================================================
//
// Returns true if the string starts with the given prefix, false otherwise.
//
version.extensions["String.startsWith"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.startsWith = function(prefix) {
	var n =  prefix.length;
	return (this.length >= n) && (this.slice(0, n) == prefix);
};



//============================================================================
// String.endsWith Function
//============================================================================
//
// Returns true if the string ends with the given suffix, false otherwise.
//
version.extensions["String.endsWith"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.endsWith = function(suffix) {
	var n = suffix.length;
	return (this.length >= n) && (this.right(n) == suffix);
};


//============================================================================
// String.contains Function
//============================================================================
//
// Returns true when the string contains the given substring, false otherwise.
//
version.extensions["String.contains"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.contains = function(substring) {
	return this.indexOf(substring) >= 0;
};

//============================================================================
// Array.indexOf Function
//============================================================================
//
// Returns the index of the first occurance of the given item in the array or 
// -1 when no such item exists.
//
// @param item [may be null]
//
version.extensions["Array.indexOf"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.indexOf = function(item) {
	for (var i = 0; i < this.length; i++) {
		if (this[i] == item) {
			return i;
		}
	}
	return -1;
};

//============================================================================
// Array.contains Function
//============================================================================
//
// Returns true when the array contains the given item, otherwise false. 
//
// @param item [may be null]
//
version.extensions["Array.contains"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.contains = function(item) {
	return (this.indexOf(item) >= 0);
};

//============================================================================
// Array.containsAny Function
//============================================================================
//
// Returns true when the array contains at least one of the elements 
// of the item. Otherwise (or when items contains no elements) false is returned.
//
version.extensions["Array.containsAny"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.containsAny = function(items) {
	for(var i = 0; i < items.length; i++) {
		if (this.contains(items[i])) {
			return true;
		}
	}
	return false;
};


//============================================================================
// Array.containsAll Function
//============================================================================
//
// Returns true when the array contains all the items, otherwise false.
// 
// When items is null false is returned (even if the array contains a null).
//
// @param items [may be null] 
//
version.extensions["Array.containsAll"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.containsAll = function(items) {
	for(var i = 0; i < items.length; i++) {
		if (!this.contains(items[i])) {
			return false;
		}
	}
	return true;
};


} // of "install only once"

// Used Globals (for JSLint) ==============
// ... DOM
/*global 	document */
// ... TiddlyWiki Core
/*global 	convertUnicodeToUTF8, createTiddlyElement, createTiddlyLink, 
			displayMessage, endSaveArea, hasClass, loadFile, saveFile, 
			startSaveArea, store, wikify */
//}}}


/***
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2005 ([[www.abego-software.de|http://www.abego-software.de]])

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

Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.

Neither the name of abego Software nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
***/
/***
<<checkForDataTiddlerPlugin>>
|''Name:''|FormTiddlerPlugin|
|''Version:''|1.0.6 (2007-06-24)|
|''Source:''|http://tiddlywiki.abego-software.de/#FormTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license]]|
|''Macros:''|formTiddler, checkForDataTiddlerPlugin, newTiddlerWithForm|
|''Requires:''|DataTiddlerPlugin|
|''TiddlyWiki:''|1.2.38+, 2.0|
|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
!Description
Use form-based tiddlers to enter your tiddler data using text fields, listboxes, checkboxes etc. (All standard HTML Form input elements supported).

''Syntax:'' 
|>|{{{<<}}}''formTiddler'' //tiddlerName//{{{>>}}}|
|//tiddlerName//|The name of the FormTemplate tiddler to be used to edit the data of the tiddler containing the macro.|

|>|{{{<<}}}''newTiddlerWithForm'' //formTemplateName// //buttonLabel// [//titleExpression// [''askUser'']] {{{>>}}}|
|//formTemplateName//|The name of the tiddler that defines the form the new tiddler should use.|
|//buttonLabel//|The label of the button|
|//titleExpression//|A (quoted) JavaScript String expression that defines the title (/name) of the new tiddler.|
|''askUser''|Typically the user is not asked for the title when a title is specified (and not yet used). When ''askUser'' is given the user will be asked in any case. This may be used when the calculated title is just a suggestion that must be confirmed by the user|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|

For details and how to use the macros see the [[introduction|FormTiddler Introduction]] and the [[examples|FormTiddler Examples]].

!Revision history
* v1.0.6 (2007-06-24)
** Fixed problem when using SELECT component in Internet Explorer (thanks to MaikBoenig for reporting)
* v1.0.5 (2006-02-24)
** Removed "debugger;" instruction
* v1.0.4 (2006-02-07)
** Bug: On IE no data is written to data section when field values changed (thanks to KenGirard for reporting)
* v1.0.3 (2006-02-05)
** Bug: {{{"No form template specified in <<formTiddler>>"}}} when using formTiddler macro on InternetExplorer (thanks to KenGirard for reporting)
* v1.0.2 (2006-01-06)
** Support TiddlyWiki 2.0
* v1.0.1 (2005-12-22)
** Features: 
*** Support InternetExplorer
*** Added newTiddlerWithForm Macro
* v1.0.0 (2005-12-14)
** initial version

!Code
***/
//{{{

//============================================================================
//============================================================================
//						FormTiddlerPlugin
//============================================================================
//============================================================================

if (!window.abego) window.abego = {};

abego.getOptionsValue = function(element,i) {
	var v = element.options[i].value;
	if (!v && element.options[i].text)
		v = element.options[i].text;
	return v;
};

version.extensions.FormTiddlerPlugin = {
	major: 1, minor: 0, revision: 5,
	date: new Date(2006, 2, 24), 
	type: 'plugin',
	source: "http://tiddlywiki.abego-software.de/#FormTiddlerPlugin"
};

// For backward compatibility with v1.2.x
//
if (!window.story) window.story=window; 
if (!TiddlyWiki.prototype.getTiddler) TiddlyWiki.prototype.getTiddler = function(title) { return t = this.tiddlers[title]; return (t != undefined && t instanceof Tiddler) ? t : null; } 

//============================================================================
// formTiddler Macro
//============================================================================

// -------------------------------------------------------------------------------
// Configurations and constants 
// -------------------------------------------------------------------------------

config.macros.formTiddler = {
	// Standard Properties
	label: "formTiddler",
	version: {major: 1, minor: 0, revision: 4, date: new Date(2006, 2, 7)},
	prompt: "Edit tiddler data using forms",

	// Define the "setters" that set the values of INPUT elements of a given type
	// (must match the corresponding "getter")
	setter: {  
		button:				function(e, value) {/*contains no data */ },
		checkbox:			function(e, value) {e.checked = value;},
		file:				function(e, value) {try {e.value = value;} catch(e) {/* ignore, possibly security error*/}},
		hidden:				function(e, value) {e.value = value;},
		password:			function(e, value) {e.value = value;},
		radio:				function(e, value) {e.checked = (e.value == value);},
		reset:				function(e, value) {/*contains no data */ },
		"select-one":		function(e, value) {config.macros.formTiddler.setSelectOneValue(e,value);},
		"select-multiple":	function(e, value) {config.macros.formTiddler.setSelectMultipleValue(e,value);},
		submit:				function(e, value) {/*contains no data */},
		text:				function(e, value) {e.value = value;},
		textarea:			function(e, value) {e.value = value;}
	},

	// Define the "getters" that return the value of INPUT elements of a given type
	// Return undefined to not store any data.
	getter: {  
		button:				function(e, value) {return undefined;},
		checkbox:			function(e, value) {return e.checked;},
		file:				function(e, value) {return e.value;},
		hidden:				function(e, value) {return e.value;},
		password:			function(e, value) {return e.value;},
		radio:				function(e, value) {return e.checked ? e.value : undefined;},
		reset:				function(e, value) {return undefined;},
		"select-one":		function(e, value) {return config.macros.formTiddler.getSelectOneValue(e);},
		"select-multiple":	function(e, value) {return config.macros.formTiddler.getSelectMultipleValue(e);},
		submit:				function(e, value) {return undefined;},
		text:				function(e, value) {return e.value;},
		textarea:			function(e, value) {return e.value;}
	}
};


// -------------------------------------------------------------------------------
// The formTiddler Macro Handler 
// -------------------------------------------------------------------------------

config.macros.formTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
	if (!config.macros.formTiddler.checkForExtensions(place, macroName)) {
		return;
	}
	// --- Parsing ------------------------------------------

	var i = 0; // index running over the params

	// get the name of the form template tiddler
	var formTemplateName = undefined;
	if (i < params.length) {
		formTemplateName = params[i];
		i++;
	}

	if (!formTemplateName) {
		config.macros.formTiddler.createErrorElement(place, "No form template specified in <<" + macroName + ">>.");
		return;
	}


	// --- Processing ------------------------------------------

	// Get the form template text. 
	// (This contains the INPUT elements for the form.)
	var formTemplateTiddler = store.getTiddler(formTemplateName);
	if (!formTemplateTiddler) {
		config.macros.formTiddler.createErrorElement(place, "Form template '" + formTemplateName + "' not found.");
		return;
	}
	var templateText = formTemplateTiddler.text;
	if(!templateText) {
		// Shortcut: when template text is empty we do nothing.
		return;
	}

	// Get the name of the tiddler containing this "formTiddler" macro
	// (i.e. the tiddler, that will be edited and that contains the data)
	var tiddlerName = config.macros.formTiddler.getContainingTiddlerName(place);

	// Append a "form" element. 
	var formName = "form"+formTemplateName+"__"+tiddlerName;
	var e = document.createElement("form");
	e.setAttribute("name", formName);
	place.appendChild(e);

	// "Embed" the elements defined by the templateText (i.e. the INPUT elements) 
	// into the "form" element we just created
	wikify(templateText, e);

	// Initialize the INPUT elements.
	config.macros.formTiddler.initValuesAndHandlersInFormElements(formName, DataTiddler.getDataObject(tiddlerName));
}


// -------------------------------------------------------------------------------
// Form Data Access 
// -------------------------------------------------------------------------------

// Internal.
//
// Initialize the INPUT elements of the form with the values of their "matching"
// data fields in the tiddler. Also setup the onChange handler to ensure that
// changes in the INPUT elements are stored in the tiddler's data.
//
config.macros.formTiddler.initValuesAndHandlersInFormElements = function(formName, data) {
	// config.macros.formTiddler.trace("initValuesAndHandlersInFormElements(formName="+formName+", data="+data+")");

	// find the form
	var form = config.macros.formTiddler.findForm(formName);
	if (!form) {
		return;
	}

	try {
		var elems = form.elements;
		for (var i = 0; i < elems.length; i++) {
			var c = elems[i];
		
			var setter = config.macros.formTiddler.setter[c.type];
			if (setter) {
				var value = data[c.name];
				if (value != null) {
					setter(c, value);
				}
				c.onchange = onFormTiddlerChange;
			} else {
				config.macros.formTiddler.displayFormTiddlerError("No setter defined for INPUT element of type '"+c.type+"'. (Element '"+c.name+"' in form '"+formName+"')");
			}
		}
	} catch(e) {
		config.macros.formTiddler.displayFormTiddlerError("Error when updating elements with new formData. "+e);
	}
}


// Internal.
//
// @return [may be null]
//
config.macros.formTiddler.findForm = function(formName) {
	// We must manually iterate through the document's forms, since
	// IE does not support the "document[formName]" approach

	var forms = window.document.forms;
	for (var i = 0; i < forms.length; i++) {
		var form = forms[i];
		if (form.name == formName) {
			return form;
		}
	}

	return null;
}


// Internal.
//
config.macros.formTiddler.setSelectOneValue = function(element,value) {
	var n = element.options.length;
	for (var i = 0; i < n; i++) {
		element.options[i].selected = abego.getOptionsValue(element,i) == value;
	}
}

// Internal.
//
config.macros.formTiddler.setSelectMultipleValue = function(element,value) {
	var values = {};
	for (var i = 0; i < value.length; i++) {
		values[value[i]] = true;
	}
	
	var n = element.length;
	for (var i = 0; i < n; i++) {
		element.options[i].selected = !(!values[abego.getOptionsValue(element,i)]);
	}
}

// Internal.
//
config.macros.formTiddler.getSelectOneValue = function(element) {
	var i = element.selectedIndex;
	return (i >= 0) ? abego.getOptionsValue(element,i) : null;
}

// Internal.
//
config.macros.formTiddler.getSelectMultipleValue = function(element) {
	var values = [];
	var n = element.length;
	for (var i = 0; i < n; i++) {
		if (element.options[i].selected) {
			values.push(abego.getOptionsValue(element,i));
		}
	}
	return values;
}



// -------------------------------------------------------------------------------
// Helpers 
// -------------------------------------------------------------------------------

// Internal.
//
config.macros.formTiddler.checkForExtensions = function(place,macroName) {
	if (!version.extensions.DataTiddlerPlugin) {
		config.macros.formTiddler.createErrorElement(place, "<<" + macroName + ">> requires the DataTiddlerPlugin. (You can get it from http://tiddlywiki.abego-software.de/#DataTiddlerPlugin)");
		return false;
	}
	return true;
}

// Internal.
//
// Displays a trace message in the "TiddlyWiki" message pane.
// (used for debugging)
//
config.macros.formTiddler.trace = function(s) {
	displayMessage("Trace: "+s);
}

// Internal.
//
// Display some error message in the "TiddlyWiki" message pane.
//
config.macros.formTiddler.displayFormTiddlerError = function(s) {
	alert("FormTiddlerPlugin Error: "+s);
}

// Internal.
//
// Creates an element that holds an error message
// 
config.macros.formTiddler.createErrorElement = function(place, message) {
	return createTiddlyElement(place,"span",null,"formTiddlerError",message);
}

// Internal.
//
// Returns the name of the tiddler containing the given element.
// 
config.macros.formTiddler.getContainingTiddlerName = function(element) {
	return story.findContainingTiddler(element).id.substr(7);
}

// -------------------------------------------------------------------------------
// Event Handlers 
// -------------------------------------------------------------------------------

// This function must be called by the INPUT elements whenever their
// data changes. Typically this is done through an "onChange" handler.
//
function onFormTiddlerChange (e) {
	// config.macros.formTiddler.trace("onFormTiddlerChange "+e);

	if (!e) var e = window.event;

	var target = resolveTarget(e);
	var tiddlerName = config.macros.formTiddler.getContainingTiddlerName(target);
	var getter = config.macros.formTiddler.getter[target.type];
	if (getter) {
		var value = getter(target);
		DataTiddler.setData(tiddlerName, target.name, value);
	} else {
		config.macros.formTiddler.displayFormTiddlerError("No getter defined for INPUT element of type '"+target.type+"'. (Element '"+target.name+"' used in tiddler '"+tiddlerName+"')");
	}
}

// ensure that the function can be used in HTML event handler
window.onFormTiddlerChange = onFormTiddlerChange;


// -------------------------------------------------------------------------------
// Stylesheet Extensions (may be overridden by local StyleSheet)
// -------------------------------------------------------------------------------

setStylesheet(
	".formTiddlerError{color: #ffffff;background-color: #880000;}",
	"formTiddler");


//============================================================================
// checkForDataTiddlerPlugin Macro
//============================================================================

config.macros.checkForDataTiddlerPlugin = {
	// Standard Properties
	label: "checkForDataTiddlerPlugin",
	version: {major: 1, minor: 0, revision: 0, date: new Date(2005, 12, 14)},
	prompt: "Check if the DataTiddlerPlugin exists"
}

config.macros.checkForDataTiddlerPlugin.handler = function(place,macroName,params) {
	config.macros.formTiddler.checkForExtensions(place, config.macros.formTiddler.label);
}



//============================================================================
// newTiddlerWithForm Macro
//============================================================================

config.macros.newTiddlerWithForm = {
	// Standard Properties
	label: "newTiddlerWithForm",
	version: {major: 1, minor: 0, revision: 1, date: new Date(2006, 1, 6)},
	prompt: "Creates a new Tiddler with a <<formTiddler ...>> macro"
}

config.macros.newTiddlerWithForm.handler = function(place,macroName,params) {
	// --- Parsing ------------------------------------------

	var i = 0; // index running over the params

	// get the name of the form template tiddler
	var formTemplateName = undefined;
	if (i < params.length) {
		formTemplateName = params[i];
		i++;
	}

	if (!formTemplateName) {
		config.macros.formTiddler.createErrorElement(place, "No form template specified in <<" + macroName + ">>.");
		return;
	}

	// get the button label
	var buttonLabel = undefined;
	if (i < params.length) {
		buttonLabel = params[i];
		i++;
	}

	if (!buttonLabel) {
		config.macros.formTiddler.createErrorElement(place, "No button label specified in <<" + macroName + ">>.");
		return;
	}

	// get the (optional) tiddlerName script and "askUser"
	var tiddlerNameScript = undefined;
	var askUser = false;
	if (i < params.length) {
		tiddlerNameScript = params[i];
		i++;

		if (i < params.length && params[i] == "askUser") {
			askUser = true;
			i++;
		}
	}

	// --- Processing ------------------------------------------

	if(!readOnly) {
		var onClick = function() {
			var tiddlerName;
			if (tiddlerNameScript) {
				try {
					tiddlerName = eval(tiddlerNameScript);
				} catch (ex) {
				}
			}
			if (!tiddlerName || askUser) {
				tiddlerName = prompt("Please specify a tiddler name.", askUser ? tiddlerName : "");
			}
			while (tiddlerName && store.getTiddler(tiddlerName)) {
				tiddlerName = prompt("A tiddler named '"+tiddlerName+"' already exists.\n\n"+"Please specify a tiddler name.", tiddlerName);
			}

			// tiddlerName is either null (user canceled) or a name that is not yet in the store.
			if (tiddlerName) {
				var body = "<<formTiddler [["+formTemplateName+"]]>>";
				var tags = [];
				store.saveTiddler(tiddlerName,tiddlerName,body,config.options.txtUserName,new Date(),tags);
				story.displayTiddler(null,tiddlerName,1);
			}
		}

		createTiddlyButton(place,buttonLabel,buttonLabel,onClick);
    }
}

//}}}


/***
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2005 ([[www.abego-software.de|http://www.abego-software.de]])

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

Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.

Neither the name of abego Software nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
***/
This is a simple program that uses the Python Turtle library to draw geometric fractals. The program accepts input, or can be run in interactive mode. The program also has a demo mode that displays some interesting presets.

Usage: python fractal.py [OPTION]...
This program generates recursive fractals.

"""--"""iterations <NUMBER>   Set the iteration count.
"""--"""shape <NUMBER>        Set the shape by the number of sides.
"""--"""size <NUMBER>         Set the size of the sides.
"""--"""demo                  Show the automatic demo.
"""--"""help                  Show this screen

{{{
#import the turtle library
import turtle
import sys
import os

#Who knew clearing a screen is so hard...
def clearscreen():
    if os.name == "posix":
        os.system('clear')
    elif os.name in ("nt", "dos", "ce"):
        os.system('CLS')
    else:
        for i in range(50):
            print '\n'

#define the fractal function
def fractal(sides, size, iterations):
    passsize=size/2
    passiterations=iterations-1
    for i in range(sides):

        #if there are additional iterations, start a new copy of this function
        if iterations>0:
            fractal(sides, passsize, passiterations)

        turtle.forward(size)
        turtle.left(360.0/sides)

#Demo function
def fractaldemo(shape, iterations, size, comment):
    print(comment)
    print("Here is the formula for this fractal:")
    print "shape: ",shape," iterations: ",iterations," size: ",size
    turtle.reset()
    sizeinput=size*2
    turtle.speed(0)
    turtle.right(90)
    turtle.forward(400)
    turtle.right(90)
    turtle.forward(size)
    turtle.clear()
    turtle.right(180)
    turtle.hideturtle()
    turtle.tracer(0, 0)
    fractal(shape, sizeinput, iterations)
    raw_input("Done! Press enter to continue...")
    clearscreen()


#preset variables
shape=3
iterations=8
size=400

#Get user input for variables
if len(sys.argv) == 1:
    shape=int(raw_input("Shape? "))
    iterations=int(raw_input("Iterations? "))
    size=int(raw_input("Size? "))
else:
    #This huge loop parses command line inputs
    iterator=1
    while iterator < len(sys.argv):
        if sys.argv[iterator] == "--iterations":
            iterator=iterator+1
            iterations=int(sys.argv[iterator])
            iterator=iterator+1
        elif sys.argv[iterator] == "--shape":
            iterator=iterator+1
            shape=int(sys.argv[iterator])
            iterator=iterator+1
        elif sys.argv[iterator] == "--size":
            iterator=iterator+1
            size=int(sys.argv[iterator])
            iterator=iterator+1
        elif sys.argv[iterator] == "--demo":
            fractaldemo(3,8,400,"This fractal is called the Sierpinski triangle. I designed this program to draw this fractal.")
            fractaldemo(4,7,400,"This is probably the hardest way to draw a grid...")
            fractaldemo(5,6,250,"This is a 5 sided fractal. Very odd pattern")
            fractaldemo(6,5,200,"6 sided fractals are not exciting.")
            fractaldemo(7,5,200,"7 sided fractal...")
            fractaldemo(8,4,150,"8 sided fractal. Very odd pattern for an even number!")
            fractaldemo(9,4,150,"The 9 sided fractal is very odd...")
            fractaldemo(20,2,60,"Lets try something different... 20 sides!")
            fractaldemo(200,1,5,"200 sides!")
            sys.exit()
        elif sys.argv[iterator] == "--help":
            print """
Usage: python fractal.py [OPTION]...
This program generates recursive fractals.

--iterations <NUMBER>   Set the iteration count.
--shape <NUMBER>        Set the shape by the number of sides.
--size <NUMBER>         Set the size of the sides.
--demo                  Show the automatic demo.
--help                  Show this screen"""
            sys.exit()
        else:
            print sys.argv[iterator]+" is not an option. Check you options."
            sys.exit()

sizeinput=size*2

#move the turtle to the lower portion of the screen
turtle.right(90)
turtle.forward(400)
turtle.right(90)
turtle.forward(size)
turtle.clear()
turtle.right(180)

#make the turtle go as fast as possible
turtle.speed(0)
turtle.hideturtle()
turtle.tracer(0, 0)
print("Please wait for fractal to finish drawing...")

fractal(shape, sizeinput, iterations)

#prevents the window from closing
raw_input("Done! Press enter to exit!")
}}}
Here is a simple gmail notifier script. It is lightweight.

Just add your email (bla@gmail.com) and password.

This script will pop up a window that displays your inbox, and asks you if you want to open it in Google Chrome (you can change it). Regardless if you look at it or not, it will not display another window unless you get a new email. If you read an email, and a new one comes in, it will still notify you. Place the script in your start up, and it will check every 10 seconds (You don't need to set it on cron).

{{{
#!/bin/bash
cd `dirname "$0"`
lastm=0

#>>>> Edit This <<<<

email=""
password=""

#>>>> Don't touch anything else. <<<<

while true
do
data=`curl -s -u $email:$password https://mail.google.com/mail/feed/atom `
full=`echo "$data" |  grep fullcount | sed -e 's/<fullcount>//' -e 's/<\/fullcount>//'`
echo "$data" |grep title|tail -n +2|sed -e 's/<title>//' -e 's/<\/title>//' > .file1
echo "$data" |grep summary|sed -e 's/<summary>//' -e 's/<\/summary>//' > .file2
if [[ -n $full ]] && [[ $full == '1' ]]; then
	emails=`pr -m -t -s^ .file1 .file2 |while read i; do echo ${i:0:35}; done | sed 's/\^/ - /g'| tr '\n' '^' | sed 's/\^/\\n/g'`
	message="You have $full new email. Do you want to open gmail?\n$emails"
else
	emails=`pr -m -t -s^ .file1 .file2 |while read i; do echo ${i:0:35}; done | sed 's/\^/ - /g'| tr '\n' '^' | sed 's/\^/\\n/g'`
	message="You have $full new emails. Do you want to open gmail?\n$emails"
fi

if [[ ! $full == '0' ]] && [[ "$lastm" < "$full" ]]; then
    zenity --question --text "${message}"
	if [[ "$?" == "0" ]]; then
        google-chrome https://mail.google.com/
    fi
fi
lastm="$full"
sleep 10
done
}}}
This is a small card that consists of a table of numbers. To use it, find the white number for the total worth of the assignment. The remaining numbers on that row are the minimum number of problems that need to be correct for that grade. (The first number is the denominator and  the other numbers are numerators in the fraction.)

[[>>Download (PDF)<<|https://www.dropbox.com/s/rc194atdavzal8n/gradechart.pdf]] [[[M]|http://yunpan.cn/QDekeVKqvtwS9]]
[[>>Download (ODT)<<|https://www.dropbox.com/s/eua9mhwtz3k8x3w/gradechart.odt]] [[[M]|http://yunpan.cn/QDekt4FuXQQaC]]

^^*.ODT files can be opened in [[Libre Office|https://www.libreoffice.org/]] or a modern copy of Microsoft Word.
!Projects and Tutorials
[[Awesome window manager tutorials]]: These are instructions for configuring Awesome to work with XFCE and add a computer shutdown options.
[[Binary Cheat Sheet]]: This is a simple binary conversion guide.
[[Dark Theme Script]]: This bookmarklet changes the color of text on webpages to make them easier to read.
[[Macro pictures]]: These are some simple macro pictures.
[[Park Pictures]]: This is a photo album from the Metro Parks near Cleveland.
[[Vacation Pictures]]: These are some pictures from Portland and Seattle.
[[Ping)) computer security system]]: This is guide to build a automatic computer locking device (includes program).
[[Terminal Prompt 3]]: This is a bash prompt and some aliases that I made to improve the Linux terminal.
[[Linux Tutorial]]: This is a tutorial for getting started with Linux.
[[Debian Install Presentation]]: A picture-based Debian install tutorial.
[[Grade Sheet]]: This is a useful reference card to convert a fraction to a letter grade.
[[IP Subnetting]]: Tutorial for converting an IP range to usable information.
[[Crunchyroll Guest Passes]]: These give you 48 hours of premium.
[[Permissions]]: Icons to display if and when you want a document redistributed.  

!Shell Scripts (For Linux)
[[Choose Your Own Adventure]]: This is a simple system for choose your own adventure books written using shell scripts.
[[Gmail checker script]]: This is a script for checking gmail that doesn't use a half a gigabyte of ram.
[[Converter]]: This is a tool for converting a folder of media to a specific format.
[[Self extracting script maker]]: This is a tool for creating shell scripts that extract themselves and perform a task.
[[Persistent self extracting script maker]]: This tool will create a self extracting script that will update itself after performing a task.
[[text to speech]]: This tool converts text files to .wav files.
[[Minecraft Server Scripts]]: Scripts for running a Minecraft server.
[[AutoPlayer]]: A simple script to save your spot when watching a TV series offline
[[Backup Script]]: Why write a backup script when you can just adapt mine for your uses?
[[Memory Tool]]: Why use flashcards when you can have the terms read to you while you do something you enjoy?

!Python Programs
[[Fractal]]: This is a simple geometric fractal program in Python. (Uses turtle graphics.)
[[Prime Finder]]: This is a simple implementation of the Sieve of Eratosthenes in Python.
[[Computercraft Script Pasting Program]]: This is a simple program for pasting programs into [[Computercraft|http://www.computercraft.info/]] in [[Minecraft|https://minecraft.net/]]. Linux only.
[[ObfuscatedTextDocument]]: An experimental implementation of a word replacement code.

Email me at ian@waltons.org. [[Public Key|http://pool.sks-keyservers.net:11371/pks/lookup?op=get&search=0x5FF72240]]

^^Powered by [[tiddlywiki|http://www.tiddlywiki.com/]].
Please read the [[Binary Cheat Sheet]] tutorial before reading this tutorial if you don't understand binary.

We start out with an initial IP range. This example will use 192.168.33.5/21.

The first step is using the network ID length to determine the locked bits in this IP range. 21 bits will be underlined in this case.
__11111111__.__11111111__.__11111__000.00000000

To determine the subnet mask, convert the binary representation shown above to decimal. The subnet mask is 255.255.248.0.

To determine the network ID, convert the given IP to binary and set all of the unlocked bits to 0. If a byte of the IP is all locked, bring the number down from the original IP. Convert any changed bytes to decimal for the result.
"__1100000__.__1010100__.__00100__001.00000101"""" --"""> "__1100000__.__1010100__.__00100__000.00000000"
Network ID: __192__.__168__.32.0

To determine the first usable IP, add 1 to the last byte in the network ID. The first usable IP is 192.168.32.1.

To determine the broadcast IP, set all of the unlocked bits to 1. Use the converted IP from the network ID step.
"__1100000__.__1010100__.__00100__001.00000101"""" --"""> "__1100000__.__1010100__.__00100__111.11111111"
Broadcast: 192.168.39.255
/***
|Name|ImageSizePlugin|
|Source|http://www.TiddlyTools.com/#ImageSizePlugin|
|Version|1.2.2|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|adds support for resizing images|
This plugin adds optional syntax to scale an image to a specified width and height and/or interactively resize the image with the mouse.
!!!!!Usage
<<<
The extended image syntax is:
{{{
[img(w+,h+)[...][...]]
}}}
where ''(w,h)'' indicates the desired width and height (in CSS units, e.g., px, em, cm, in, or %). Use ''auto'' (or a blank value) for either dimension to scale that dimension proportionally (i.e., maintain the aspect ratio). You can also calculate a CSS value 'on-the-fly' by using a //javascript expression// enclosed between """{{""" and """}}""". Appending a plus sign (+) to a dimension enables interactive resizing in that dimension (by dragging the mouse inside the image). Use ~SHIFT-click to show the full-sized (un-scaled) image. Use ~CTRL-click to restore the starting size (either scaled or full-sized).
<<<
!!!!!Examples
<<<
{{{
[img(100px+,75px+)[images/meow2.jpg]]
}}}
[img(100px+,75px+)[images/meow2.jpg]]
{{{
[<img(34%+,+)[images/meow.gif]]
[<img(21% ,+)[images/meow.gif]]
[<img(13%+, )[images/meow.gif]]
[<img( 8%+, )[images/meow.gif]]
[<img( 5% , )[images/meow.gif]]
[<img( 3% , )[images/meow.gif]]
[<img( 2% , )[images/meow.gif]]
[img(  1%+,+)[images/meow.gif]]
}}}
[<img(34%+,+)[images/meow.gif]]
[<img(21% ,+)[images/meow.gif]]
[<img(13%+, )[images/meow.gif]]
[<img( 8%+, )[images/meow.gif]]
[<img( 5% , )[images/meow.gif]]
[<img( 3% , )[images/meow.gif]]
[<img( 2% , )[images/meow.gif]]
[img(  1%+,+)[images/meow.gif]]
{{tagClear{
}}}
<<<
!!!!!Revisions
<<<
2010.07.24 [1.2.2] moved tip/dragtip text to config.formatterHelpers.imageSize object to enable customization
2009.02.24 [1.2.1] cleanup width/height regexp, use '+' suffix for resizing
2009.02.22 [1.2.0] added stretchable images
2008.01.19 [1.1.0] added evaluated width/height values
2008.01.18 [1.0.1] regexp for "(width,height)" now passes all CSS values to browser for validation
2008.01.17 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.ImageSizePlugin= {major: 1, minor: 2, revision: 2, date: new Date(2010,7,24)};
//}}}
//{{{
var f=config.formatters[config.formatters.findByField("name","image")];
f.match="\\[[<>]?[Ii][Mm][Gg](?:\\([^,]*,[^\\)]*\\))?\\[";
f.lookaheadRegExp=/\[([<]?)(>?)[Ii][Mm][Gg](?:\(([^,]*),([^\)]*)\))?\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg;
f.handler=function(w) {
	this.lookaheadRegExp.lastIndex = w.matchStart;
	var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
	if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
		var floatLeft=lookaheadMatch[1];
		var floatRight=lookaheadMatch[2];
		var width=lookaheadMatch[3];
		var height=lookaheadMatch[4];
		var tooltip=lookaheadMatch[5];
		var src=lookaheadMatch[6];
		var link=lookaheadMatch[7];

		// Simple bracketted link
		var e = w.output;
		if(link) { // LINKED IMAGE
			if (config.formatterHelpers.isExternalLink(link)) {
				if (config.macros.attach && config.macros.attach.isAttachment(link)) {
					// see [[AttachFilePluginFormatters]]
					e = createExternalLink(w.output,link);
					e.href=config.macros.attach.getAttachment(link);
					e.title = config.macros.attach.linkTooltip + link;
				} else
					e = createExternalLink(w.output,link);
			} else 
				e = createTiddlyLink(w.output,link,false,null,w.isStatic);
			addClass(e,"imageLink");
		}

		var img = createTiddlyElement(e,"img");
		if(floatLeft) img.align="left"; else if(floatRight) img.align="right";
		if(width||height) {
			var x=width.trim(); var y=height.trim();
			var stretchW=(x.substr(x.length-1,1)=='+'); if (stretchW) x=x.substr(0,x.length-1);
			var stretchH=(y.substr(y.length-1,1)=='+'); if (stretchH) y=y.substr(0,y.length-1);
			if (x.substr(0,2)=="{{")
				{ try{x=eval(x.substr(2,x.length-4))} catch(e){displayMessage(e.description||e.toString())} }
			if (y.substr(0,2)=="{{")
				{ try{y=eval(y.substr(2,y.length-4))} catch(e){displayMessage(e.description||e.toString())} }
			img.style.width=x.trim(); img.style.height=y.trim();
			config.formatterHelpers.addStretchHandlers(img,stretchW,stretchH);
		}
		if(tooltip) img.title = tooltip;

		// GET IMAGE SOURCE
		if (config.macros.attach && config.macros.attach.isAttachment(src))
			src=config.macros.attach.getAttachment(src); // see [[AttachFilePluginFormatters]]
		else if (config.formatterHelpers.resolvePath) { // see [[ImagePathPlugin]]
			if (config.browser.isIE || config.browser.isSafari) {
				img.onerror=(function(){
					this.src=config.formatterHelpers.resolvePath(this.src,false);
					return false;
				});
			} else
				src=config.formatterHelpers.resolvePath(src,true);
		}
		img.src=src;
		w.nextMatch = this.lookaheadRegExp.lastIndex;
	}
}

config.formatterHelpers.imageSize={
	tip: 'SHIFT-CLICK=show full size, CTRL-CLICK=restore initial size',
	dragtip: 'DRAG=stretch/shrink, '
}

config.formatterHelpers.addStretchHandlers=function(e,stretchW,stretchH) {
	e.title=((stretchW||stretchH)?this.imageSize.dragtip:'')+this.imageSize.tip;
	e.statusMsg='width=%0, height=%1';
	e.style.cursor='move';
	e.originalW=e.style.width;
	e.originalH=e.style.height;
	e.minW=Math.max(e.offsetWidth/20,10);
	e.minH=Math.max(e.offsetHeight/20,10);
	e.stretchW=stretchW;
	e.stretchH=stretchH;
	e.onmousedown=function(ev) { var ev=ev||window.event;
		this.sizing=true;
		this.startX=!config.browser.isIE?ev.pageX:(ev.clientX+findScrollX());
		this.startY=!config.browser.isIE?ev.pageY:(ev.clientY+findScrollY());
		this.startW=this.offsetWidth;
		this.startH=this.offsetHeight;
		return false;
	};
	e.onmousemove=function(ev) { var ev=ev||window.event;
		if (this.sizing) {
			var s=this.style;
			var currX=!config.browser.isIE?ev.pageX:(ev.clientX+findScrollX());
			var currY=!config.browser.isIE?ev.pageY:(ev.clientY+findScrollY());
			var newW=(currX-this.offsetLeft)/(this.startX-this.offsetLeft)*this.startW;
			var newH=(currY-this.offsetTop )/(this.startY-this.offsetTop )*this.startH;
			if (this.stretchW) s.width =Math.floor(Math.max(newW,this.minW))+'px';
			if (this.stretchH) s.height=Math.floor(Math.max(newH,this.minH))+'px';
			clearMessage(); displayMessage(this.statusMsg.format([s.width,s.height]));
		}
		return false;
	};
	e.onmouseup=function(ev) { var ev=ev||window.event;
		if (ev.shiftKey) { this.style.width=this.style.height=''; }
		if (ev.ctrlKey)  { this.style.width=this.originalW; this.style.height=this.originalH; }
		this.sizing=false;
		clearMessage();
		return false;
	};
	e.onmouseout=function(ev) { var ev=ev||window.event;
		this.sizing=false;
		clearMessage();
		return false;
	};
}
//}}}
/***
|Name|InlineJavascriptPlugin|
|Source|http://www.TiddlyTools.com/#InlineJavascriptPlugin|
|Documentation|http://www.TiddlyTools.com/#InlineJavascriptPluginInfo|
|Version|1.8.1|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Description|Insert Javascript executable code directly into your tiddler content.|
''Call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
!!!!!Documentation
>see [[InlineJavascriptPluginInfo]]
!!!!!Revision History
<<<
2008.02.14 [1.8.1] added backward-compatibility for use of wikifyPlainText() in TW2.1.3 and earlier
2008.01.08 [*.*.*] plugin size reduction: documentation moved to ...Info and ...History tiddlers
2007.12.28 [1.8.0] added support for key="X" syntax to specify custom access key definitions
|please see [[InlineJavascriptPluginHistory]] for additional revision details|
2005.11.08 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.inlineJavascript= {major: 1, minor: 8, revision: 1, date: new Date(2008,2,14)};

config.formatters.push( {
	name: "inlineJavascript",
	match: "\\<script",
	lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?(?: title=\\\"((?:.|\\n)*?)\\\")?(?: key=\\\"((?:.|\\n)*?)\\\")?( show)?\\>((?:.|\\n)*?)\\</script\\>",

	handler: function(w) {
		var lookaheadRegExp = new RegExp(this.lookahead,"mg");
		lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = lookaheadRegExp.exec(w.source)
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
			var src=lookaheadMatch[1];
			var label=lookaheadMatch[2];
			var tip=lookaheadMatch[3];
			var key=lookaheadMatch[4];
			var show=lookaheadMatch[5];
			var code=lookaheadMatch[6];
			if (src) { // load a script library
				// make script tag, set src, add to body to execute, then remove for cleanup
				var script = document.createElement("script"); script.src = src;
				document.body.appendChild(script); document.body.removeChild(script);
			}
			if (code) { // there is script code
				if (show) // show inline script code in tiddler output
					wikify("{{{\n"+lookaheadMatch[0]+"\n}}}\n",w.output);
				if (label) { // create a link to an 'onclick' script
					// add a link, define click handler, save code in link (pass 'place'), set link attributes
					var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",wikifyPlainText(label));
					link.onclick=function(){try{return(eval(this.code))}catch(e){alert(e.description||e.toString())}}
					var fixup=code.replace(/document.write\s*\(/gi,'place.innerHTML+=(');
					link.code="function _out(place){"+fixup+"\n};_out(this);"
					link.setAttribute("title",tip||"");
					var URIcode='javascript:void(eval(decodeURIComponent(%22(function(){try{';
					URIcode+=encodeURIComponent(encodeURIComponent(code.replace(/\n/g,' ')));
					URIcode+='}catch(e){alert(e.description||e.toString())}})()%22)))';
					link.setAttribute("href",URIcode);
					link.style.cursor="pointer";
					if (key) link.accessKey=key.substr(0,1); // single character only
				}
				else { // run inline script code
					var fixup=code.replace(/document.write\s*\(/gi,'place.innerHTML+=(');
					var code="function _out(place){"+fixup+"\n};_out(w.output);"
					try { var out=eval(code); } catch(e) { out=e.description?e.description:e.toString(); }
					if (out && out.length) wikify(out,w.output,w.highlightRegExp,w.tiddler);
				}
			}
			w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
		}
	}
} )
//}}}

// // Backward-compatibility for TW2.1.x and earlier
//{{{
if (typeof(wikifyPlainText)=="undefined") function wikifyPlainText(text,limit,tiddler) {
	if(limit > 0) text = text.substr(0,limit);
	var wikifier = new Wikifier(text,formatter,null,tiddler);
	return wikifier.wikifyPlain();
}
//}}}
/***
|''Name:''|IntelliTaggerPlugin|
|''Version:''|1.0.2 (2007-07-25)|
|''Type:''|plugin|
|''Source:''|http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin|
|''Author:''|Udo Borkowski (ub [at] abego-software [dot] de)|
|''Documentation:''|[[IntelliTaggerPlugin Documentation]]|
|''~SourceCode:''|[[IntelliTaggerPlugin SourceCode]]|
|''Licence:''|[[BSD open source license (abego Software)]]|
|''~CoreVersion:''|2.0.8|
|''Browser:''|Firefox 1.5.0.2 or better|
***/
/***
!Version History
* 1.0.2 (2007-07-25): 
** Feature: "Return" key may be used to accept first tag suggestion (beside "Alt-1")
** Bugfix: Keyboard shortcuts (Alt+3 etc.) shifted
* 1.0.1 (2007-05-18): Improvement: Speedup when using TiddlyWikis with many tags
* 1.0.0 (2006-04-26): Initial release

***/
// /%
if(!version.extensions.IntelliTaggerPlugin){if(!window.abego){window.abego={};}if(!abego.internal){abego.internal={};}abego.alertAndThrow=function(s){alert(s);throw s;};if(version.major<2){abego.alertAndThrow("Use TiddlyWiki 2.0.8 or better to run the IntelliTagger Plugin.");}version.extensions.IntelliTaggerPlugin={major:1,minor:0,revision:2,date:new Date(2007,6,25),type:"plugin",source:"http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin",documentation:"[[IntelliTaggerPlugin Documentation]]",sourcecode:"[[IntelliTaggerPlugin SourceCode]]",author:"Udo Borkowski (ub [at] abego-software [dot] de)",licence:"[[BSD open source license (abego Software)]]",tiddlywiki:"Version 2.0.8 or better",browser:"Firefox 1.5.0.2 or better"};abego.createEllipsis=function(_2){var e=createTiddlyElement(_2,"span");e.innerHTML="&hellip;";};abego.isPopupOpen=function(_4){return _4&&_4.parentNode==document.body;};abego.openAsPopup=function(_5){if(_5.parentNode!=document.body){document.body.appendChild(_5);}};abego.closePopup=function(_6){if(abego.isPopupOpen(_6)){document.body.removeChild(_6);}};abego.getWindowRect=function(){return {left:findScrollX(),top:findScrollY(),height:findWindowHeight(),width:findWindowWidth()};};abego.moveElement=function(_7,_8,_9){_7.style.left=_8+"px";_7.style.top=_9+"px";};abego.centerOnWindow=function(_a){if(_a.style.position!="absolute"){throw "abego.centerOnWindow: element must have absolute position";}var _b=abego.getWindowRect();abego.moveElement(_a,_b.left+(_b.width-_a.offsetWidth)/2,_b.top+(_b.height-_a.offsetHeight)/2);};abego.isDescendantOrSelf=function(_c,e){while(e){if(_c==e){return true;}e=e.parentNode;}return false;};abego.toSet=function(_e){var _f={};for(var i=0;i<_e.length;i++){_f[_e[i]]=true;}return _f;};abego.filterStrings=function(_11,_12,_13){var _14=[];for(var i=0;i<_11.length&&(_13===undefined||_14.length<_13);i++){var s=_11[i];if(s.match(_12)){_14.push(s);}}return _14;};abego.arraysAreEqual=function(a,b){if(!a){return !b;}if(!b){return false;}var n=a.length;if(n!=b.length){return false;}for(var i=0;i<n;i++){if(a[i]!=b[i]){return false;}}return true;};abego.moveBelowAndClip=function(_1b,_1c){if(!_1c){return;}var _1d=findPosX(_1c);var _1e=findPosY(_1c);var _1f=_1c.offsetHeight;var _20=_1d;var _21=_1e+_1f;var _22=findWindowWidth();if(_22<_1b.offsetWidth){_1b.style.width=(_22-100)+"px";}var _23=_1b.offsetWidth;if(_20+_23>_22){_20=_22-_23-30;}if(_20<0){_20=0;}_1b.style.left=_20+"px";_1b.style.top=_21+"px";_1b.style.display="block";};abego.compareStrings=function(a,b){return (a==b)?0:(a<b)?-1:1;};abego.sortIgnoreCase=function(arr){var _27=[];var n=arr.length;for(var i=0;i<n;i++){var s=arr[i];_27.push([s.toString().toLowerCase(),s]);}_27.sort(function(a,b){return (a[0]==b[0])?0:(a[0]<b[0])?-1:1;});for(i=0;i<n;i++){arr[i]=_27[i][1];}};abego.getTiddlerField=function(_2d,_2e,_2f){var _30=document.getElementById(_2d.idPrefix+_2e);var e=null;if(_30!=null){var _32=_30.getElementsByTagName("*");for(var t=0;t<_32.length;t++){var c=_32[t];if(c.tagName.toLowerCase()=="input"||c.tagName.toLowerCase()=="textarea"){if(!e){e=c;}if(c.getAttribute("edit")==_2f){e=c;}}}}return e;};abego.setRange=function(_35,_36,end){if(_35.setSelectionRange){_35.setSelectionRange(_36,end);var max=0+_35.scrollHeight;var len=_35.textLength;var top=max*_36/len,bot=max*end/len;_35.scrollTop=Math.min(top,(bot+top-_35.clientHeight)/2);}else{if(_35.createTextRange!=undefined){var _3b=_35.createTextRange();_3b.collapse();_3b.moveEnd("character",end);_3b.moveStart("character",_36);_3b.select();}else{_35.select();}}};abego.internal.TagManager=function(){var _3c=null;var _3d=function(){if(_3c){return;}_3c={};store.forEachTiddler(function(_3e,_3f){for(var i=0;i<_3f.tags.length;i++){var tag=_3f.tags[i];var _42=_3c[tag];if(!_42){_42=_3c[tag]={count:0,tiddlers:{}};}_42.tiddlers[_3f.title]=true;_42.count+=1;}});};var _43=TiddlyWiki.prototype.saveTiddler;TiddlyWiki.prototype.saveTiddler=function(_44,_45,_46,_47,_48,_49){var _4a=this.fetchTiddler(_44);var _4b=_4a?_4a.tags:[];var _4c=(typeof _49=="string")?_49.readBracketedList():_49;_43.apply(this,arguments);if(!abego.arraysAreEqual(_4b,_4c)){abego.internal.getTagManager().reset();}};var _4d=TiddlyWiki.prototype.removeTiddler;TiddlyWiki.prototype.removeTiddler=function(_4e){var _4f=this.fetchTiddler(_4e);var _50=_4f&&_4f.tags.length>0;_4d.apply(this,arguments);if(_50){abego.internal.getTagManager().reset();}};this.reset=function(){_3c=null;};this.getTiddlersWithTag=function(tag){_3d();var _52=_3c[tag];return _52?_52.tiddlers:null;};this.getAllTags=function(_53){_3d();var _54=[];for(var i in _3c){_54.push(i);}for(i=0;_53&&i<_53.length;i++){_54.pushUnique(_53[i],true);}abego.sortIgnoreCase(_54);return _54;};this.getTagInfos=function(){_3d();var _56=[];for(var _57 in _3c){_56.push([_57,_3c[_57]]);}return _56;};var _58=function(a,b){var a1=a[1];var b1=b[1];var d=b[1].count-a[1].count;return d!=0?d:abego.compareStrings(a[0].toLowerCase(),b[0].toLowerCase());};this.getSortedTagInfos=function(){_3d();var _5e=this.getTagInfos();_5e.sort(_58);return _5e;};this.getPartnerRankedTags=function(_5f){var _60={};for(var i=0;i<_5f.length;i++){var _62=this.getTiddlersWithTag(_5f[i]);for(var _63 in _62){var _64=store.getTiddler(_63);if(!(_64 instanceof Tiddler)){continue;}for(var j=0;j<_64.tags.length;j++){var tag=_64.tags[j];var c=_60[tag];_60[tag]=c?c+1:1;}}}var _68=abego.toSet(_5f);var _69=[];for(var n in _60){if(!_68[n]){_69.push(n);}}_69.sort(function(a,b){var d=_60[b]-_60[a];return d!=0?d:abego.compareStrings(a.toLowerCase(),b.toLowerCase());});return _69;};};abego.internal.getTagManager=function(){if(!abego.internal.gTagManager){abego.internal.gTagManager=new abego.internal.TagManager();}return abego.internal.gTagManager;};(function(){var _6e=2;var _6f=1;var _70=30;var _71;var _72;var _73;var _74;var _75;var _76;if(!abego.IntelliTagger){abego.IntelliTagger={};}var _77=function(){return _72;};var _78=function(tag){return _75[tag];};var _7a=function(s){var i=s.lastIndexOf(" ");return (i>=0)?s.substr(0,i):"";};var _7d=function(_7e){var s=_7e.value;var len=s.length;return (len>0&&s[len-1]!=" ");};var _81=function(_82){var s=_82.value;var len=s.length;if(len>0&&s[len-1]!=" "){_82.value+=" ";}};var _85=function(tag,_87,_88){if(_7d(_87)){_87.value=_7a(_87.value);}story.setTiddlerTag(_88.title,tag,0);_81(_87);abego.IntelliTagger.assistTagging(_87,_88);};var _89=function(n){if(_76&&_76.length>n){return _76[n];}return (_74&&_74.length>n)?_74[n]:null;};var _8b=function(n,_8d,_8e){var _8f=_89(n);if(_8f){_85(_8f,_8d,_8e);}};var _90=function(_91){var pos=_91.value.lastIndexOf(" ");var _93=(pos>=0)?_91.value.substr(++pos,_91.value.length):_91.value;return new RegExp(_93.escapeRegExp(),"i");};var _94=function(_95,_96){var _97=0;for(var i=0;i<_95.length;i++){if(_96[_95[i]]){_97++;}}return _97;};var _99=function(_9a,_9b,_9c){var _9d=1;var c=_9a[_9b];for(var i=_9b+1;i<_9a.length;i++){if(_9a[i][1].count==c){if(_9a[i][0].match(_9c)){_9d++;}}else{break;}}return _9d;};var _a0=function(_a1,_a2){var _a3=abego.internal.getTagManager().getSortedTagInfos();var _a4=[];var _a5=0;for(var i=0;i<_a3.length;i++){var c=_a3[i][1].count;if(c!=_a5){if(_a2&&(_a4.length+_99(_a3,i,_a1)>_a2)){break;}_a5=c;}if(c==1){break;}var s=_a3[i][0];if(s.match(_a1)){_a4.push(s);}}return _a4;};var _a9=function(_aa,_ab){return abego.filterStrings(abego.internal.getTagManager().getAllTags(_ab),_aa);};var _ac=function(){if(!_71){return;}var _ad=store.getTiddlerText("IntelliTaggerMainTemplate");if(!_ad){_ad="<b>Tiddler IntelliTaggerMainTemplate not found</b>";}_71.innerHTML=_ad;applyHtmlMacros(_71,null);refreshElements(_71,null);};var _ae=function(e){if(!e){var e=window.event;}var tag=this.getAttribute("tag");if(_73){_73.call(this,tag,e);}return false;};var _b2=function(_b3){createTiddlyElement(_b3,"span",null,"tagSeparator"," | ");};var _b4=function(_b5,_b6,_b7,_b8,_b9){if(!_b6){return;}var _ba=_b8?abego.toSet(_b8):{};var n=_b6.length;var c=0;for(var i=0;i<n;i++){var tag=_b6[i];if(_ba[tag]){continue;}if(c>0){_b2(_b5);}if(_b9&&c>=_b9){abego.createEllipsis(_b5);break;}c++;var _bf="";var _c0=_b5;if(_b7<10){_c0=createTiddlyElement(_b5,"span",null,"numberedSuggestion");_b7++;var key=_b7<10?""+(_b7):"0";createTiddlyElement(_c0,"span",null,"suggestionNumber",key+") ");var _c2=_b7==1?"Return or ":"";_bf=" (Shortcut: %1Alt-%0)".format([key,_c2]);}var _c3=config.views.wikified.tag.tooltip.format([tag]);var _c4=(_78(tag)?"Remove tag '%0'%1":"Add tag '%0'%1").format([tag,_bf]);var _c5="%0; Shift-Click: %1".format([_c4,_c3]);var btn=createTiddlyButton(_c0,tag,_c5,_ae,_78(tag)?"currentTag":null);btn.setAttribute("tag",tag);}};var _c7=function(){if(_71){window.scrollTo(0,ensureVisible(_71));}if(_77()){window.scrollTo(0,ensureVisible(_77()));}};var _c8=function(e){if(!e){var e=window.event;}if(!_71){return;}var _cb=resolveTarget(e);if(_cb==_77()){return;}if(abego.isDescendantOrSelf(_71,_cb)){return;}abego.IntelliTagger.close();};addEvent(document,"click",_c8);var _cc=Story.prototype.gatherSaveFields;Story.prototype.gatherSaveFields=function(e,_ce){_cc.apply(this,arguments);var _cf=_ce.tags;if(_cf){_ce.tags=_cf.trim();}};var _d0=function(_d1){story.focusTiddler(_d1,"tags");var _d2=abego.getTiddlerField(story,_d1,"tags");if(_d2){var len=_d2.value.length;abego.setRange(_d2,len,len);window.scrollTo(0,ensureVisible(_d2));}};var _d4=config.macros.edit.handler;config.macros.edit.handler=function(_d5,_d6,_d7,_d8,_d9,_da){_d4.apply(this,arguments);var _db=_d7[0];if((_da instanceof Tiddler)&&_db=="tags"){var _dc=_d5.lastChild;_dc.onfocus=function(e){abego.IntelliTagger.assistTagging(_dc,_da);setTimeout(function(){_d0(_da.title);},100);};_dc.onkeyup=function(e){if(!e){var e=window.event;}if(e.altKey&&!e.ctrlKey&&!e.metaKey&&(e.keyCode>=48&&e.keyCode<=57)){_8b(e.keyCode==48?9:e.keyCode-49,_dc,_da);}else{if(e.ctrlKey&&e.keyCode==32){_8b(0,_dc,_da);}}if(!e.ctrlKey&&(e.keyCode==13||e.keyCode==10)){_8b(0,_dc,_da);}setTimeout(function(){abego.IntelliTagger.assistTagging(_dc,_da);},100);return false;};_81(_dc);}};var _e0=function(e){if(!e){var e=window.event;}var _e3=resolveTarget(e);var _e4=_e3.getAttribute("tiddler");if(_e4){story.displayTiddler(_e3,_e4,"IntelliTaggerEditTagsTemplate",false);_d0(_e4);}return false;};var _e5=config.macros.tags.handler;config.macros.tags.handler=function(_e6,_e7,_e8,_e9,_ea,_eb){_e5.apply(this,arguments);abego.IntelliTagger.createEditTagsButton(_eb,createTiddlyElement(_e6.lastChild,"li"));};var _ec=function(){if(_71&&_72&&!abego.isDescendantOrSelf(document,_72)){abego.IntelliTagger.close();}};setInterval(_ec,100);abego.IntelliTagger.displayTagSuggestions=function(_ed,_ee,_ef,_f0,_f1){_74=_ed;_75=abego.toSet(_ee);_76=_ef;_72=_f0;_73=_f1;if(!_71){_71=createTiddlyElement(document.body,"div",null,"intelliTaggerSuggestions");_71.style.position="absolute";}_ac();abego.openAsPopup(_71);if(_77()){var w=_77().offsetWidth;if(_71.offsetWidth<w){_71.style.width=(w-2*(_6e+_6f))+"px";}abego.moveBelowAndClip(_71,_77());}else{abego.centerOnWindow(_71);}_c7();};abego.IntelliTagger.assistTagging=function(_f3,_f4){var _f5=_90(_f3);var s=_f3.value;if(_7d(_f3)){s=_7a(s);}var _f7=s.readBracketedList();var _f8=_f7.length>0?abego.filterStrings(abego.internal.getTagManager().getPartnerRankedTags(_f7),_f5,_70):_a0(_f5,_70);abego.IntelliTagger.displayTagSuggestions(_a9(_f5,_f7),_f7,_f8,_f3,function(tag,e){if(e.shiftKey){onClickTag.call(this,e);}else{_85(tag,_f3,_f4);}});};abego.IntelliTagger.close=function(){abego.closePopup(_71);_71=null;return false;};abego.IntelliTagger.createEditTagsButton=function(_fb,_fc,_fd,_fe,_ff,id,_101){if(!_fd){_fd="[edit]";}if(!_fe){_fe="Edit the tags";}if(!_ff){_ff="editTags";}var _102=createTiddlyButton(_fc,_fd,_fe,_e0,_ff,id,_101);_102.setAttribute("tiddler",(_fb instanceof Tiddler)?_fb.title:String(_fb));return _102;};abego.IntelliTagger.getSuggestionTagsMaxCount=function(){return 100;};config.macros.intelliTagger={label:"intelliTagger",handler:function(_103,_104,_105,_106,_107,_108){var _109=_107.parseParams("list",null,true);var _10a=_109[0]["action"];for(var i=0;_10a&&i<_10a.length;i++){var _10c=_10a[i];var _10d=config.macros.intelliTagger.subhandlers[_10c];if(!_10d){abego.alertAndThrow("Unsupported action '%0'".format([_10c]));}_10d(_103,_104,_105,_106,_107,_108);}},subhandlers:{showTags:function(_10e,_10f,_110,_111,_112,_113){_b4(_10e,_74,_76?_76.length:0,_76,abego.IntelliTagger.getSuggestionTagsMaxCount());},showFavorites:function(_114,_115,_116,_117,_118,_119){_b4(_114,_76,0);},closeButton:function(_11a,_11b,_11c,_11d,_11e,_11f){var _120=createTiddlyButton(_11a,"close","Close the suggestions",abego.IntelliTagger.close);},version:function(_121){var t="IntelliTagger %0.%1.%2".format([version.extensions.IntelliTaggerPlugin.major,version.extensions.IntelliTaggerPlugin.minor,version.extensions.IntelliTaggerPlugin.revision]);var e=createTiddlyElement(_121,"a");e.setAttribute("href","http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin");e.innerHTML="<font color=\"black\" face=\"Arial, Helvetica, sans-serif\">"+t+"<font>";},copyright:function(_124){var e=createTiddlyElement(_124,"a");e.setAttribute("href","http://tiddlywiki.abego-software.de");e.innerHTML="<font color=\"black\" face=\"Arial, Helvetica, sans-serif\">&copy; 2006-2007 <b><font color=\"red\">abego</font></b> Software<font>";}}};})();config.shadowTiddlers["IntelliTaggerStyleSheet"]="/***\n"+"!~IntelliTagger Stylesheet\n"+"***/\n"+"/*{{{*/\n"+".intelliTaggerSuggestions {\n"+"\tposition: absolute;\n"+"\twidth: 600px;\n"+"\n"+"\tpadding: 2px;\n"+"\tlist-style: none;\n"+"\tmargin: 0;\n"+"\n"+"\tbackground: #eeeeee;\n"+"\tborder: 1px solid DarkGray;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .currentTag   {\n"+"\tfont-weight: bold;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .suggestionNumber {\n"+"\tcolor: #808080;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .numberedSuggestion{\n"+"\twhite-space: nowrap;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .intelliTaggerFooter {\n"+"\tmargin-top: 4px;\n"+"\tborder-top-width: thin;\n"+"\tborder-top-style: solid;\n"+"\tborder-top-color: #999999;\n"+"}\n"+".intelliTaggerSuggestions .favorites {\n"+"\tborder-bottom-width: thin;\n"+"\tborder-bottom-style: solid;\n"+"\tborder-bottom-color: #999999;\n"+"\tpadding-bottom: 2px;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .normalTags {\n"+"\tpadding-top: 2px;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .intelliTaggerFooter .button {\n"+"\tfont-size: 10px;\n"+"\n"+"\tpadding-left: 0.3em;\n"+"\tpadding-right: 0.3em;\n"+"}\n"+"\n"+"/*}}}*/\n";config.shadowTiddlers["IntelliTaggerMainTemplate"]="<!--\n"+"{{{\n"+"-->\n"+"<div class=\"favorites\" macro=\"intelliTagger action: showFavorites\"></div>\n"+"<div class=\"normalTags\" macro=\"intelliTagger action: showTags\"></div>\n"+"<!-- The Footer (with the Navigation) ============================================ -->\n"+"<table class=\"intelliTaggerFooter\" border=\"0\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\"><tbody>\n"+"  <tr>\n"+"\t<td align=\"left\">\n"+"\t\t<span macro=\"intelliTagger action: closeButton\"></span>\n"+"\t</td>\n"+"\t<td align=\"right\">\n"+"\t\t<span macro=\"intelliTagger action: version\"></span>, <span macro=\"intelliTagger action: copyright \"></span>\n"+"\t</td>\n"+"  </tr>\n"+"</tbody></table>\n"+"<!--\n"+"}}}\n"+"-->\n";config.shadowTiddlers["IntelliTaggerEditTagsTemplate"]="<!--\n"+"{{{\n"+"-->\n"+"<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler'></div>\n"+"<div class='title' macro='view title'></div>\n"+"<div class='tagged' macro='tags'></div>\n"+"<div class='viewer' macro='view text wikified'></div>\n"+"<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler'></div>\n"+"<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>\n"+"<!--\n"+"}}}\n"+"-->\n";config.shadowTiddlers["BSD open source license (abego Software)"]="See [[Licence|http://tiddlywiki.abego-software.de/#%5B%5BBSD%20open%20source%20license%5D%5D]].";config.shadowTiddlers["IntelliTaggerPlugin Documentation"]="[[Documentation on abego Software website|http://tiddlywiki.abego-software.de/doc/IntelliTagger.pdf]].";config.shadowTiddlers["IntelliTaggerPlugin SourceCode"]="[[Plugin source code on abego Software website|http://tiddlywiki.abego-software.de/archive/IntelliTaggerPlugin/Plugin-IntelliTagger-src.1.0.2.js]]\n";(function(){var _126=restart;restart=function(){setStylesheet(store.getTiddlerText("IntelliTaggerStyleSheet"),"IntelliTaggerStyleSheet");_126.apply(this,arguments);};})();}
// %/
!What is Linux?

Linux is an Operating system like Windows or Mac OS that is free and open source. It has package management and is easy to customize. Linux is used as the kernel for Android phones. Linux known for being fast and reliable. Some Linux servers can stay up for years without crashing (but that doesn't mean you shouldn't restart servers).

!Why should I bother?

Here is a list of reasons [[here|http://www.whylinuxisbetter.net/]].

Here is a short list of reasons to use Linux:
*Linux is Free. (Free as in money and free as in freedom of choice)
*Linux has tons of easy to obtain software. There are 29000 programs in [[Debian|http://www.debian.org/]] that can be instantly installed through the package manager.
*All the software installed through a package manager is updated using the package manager.
*Linux doesn't have severe malware issues. It has fewer security holes and requires the root account password or user password (through sudo) to do dangerous tasks.
*Linux has many active communities ready to help you. One example is [[/r/linux|http://reddit.com/r/linux]] or the [[Debian forums|http://forums.debian.net/]].
*Linux has a powerful command line. You can use it to perform advanced tasks quickly and make scripts to do mundane tasks like [[check Gmail|Gmail checker script]] or [[convert your music|Converter]]. Here is a tool to [[make the command line more usable|Terminal Prompt 3]].
*Linux can be customized as much as you want. You can use many different desktop environments and change almost every aspect of your computer.
*Linux is less bloated than Windows or Mac OS X (depending on what you install).
*Many devices work without 500MB device drivers you need to download from the manufacturer's website.
*Linux is developed by many people, as opposed to a single company.
*Many popular programs work on Linux (Example: Firefox, Gimp, Skype, and Google Chrome.)
*Linux allows you to encrypt everything with dm-crypt/LUKS.
*Linux is very reliable, and can run for years without restarting (Though you should restart for security updates).
*Installing most programs does NOT slow down you system. Boot times remain fast. (Unless you are installing server software like Apache.)
*Linux uses less RAM that other operating systems, and puts no limits on that RAM.
*Linux does not require defragmentation.
*Menus are organized by category.


!What is the catch?

*Linux requires some technical knowledge to install. Using some distros of Linux is easy. (This tutorial aims to make installation easier. Simple and advanced distributions are linked below.)
*Windows applications don't usually run on Linux. The application is probably available for Linux if it is open source. Some Windows applications //might// work using [[wine|http://www.winehq.org/]]. Software like [[Playonlinux|http://www.playonlinux.com/en/]] (free) or [[Crossover|http://www.codeweavers.com/products/]] (nonfree) makes this easier. An example of Microsoft Office in [[Playonlinux|http://www.playonlinux.com/en/]] is pictured below.
*Gaming on Linux isn't as good as it is on Windows. This is starting to improve, as Steam has been ported to Linux and the Humble Bundles are making cross-platform game development popular.

<html><img src="http://i.imgur.com/lMIUFCx.png" width="700"></html>
^^"""LibreOffice""" is great, but make sure to export your work as a PDF.^^

!What does Linux look like?

Linux can look like anything. It all depends on what you want. People have built various desktop environments to suit their needs. The images below are screenshots of popular desktop environments.

<<slider DesktopEnv DesktopEnv "Click here to see screenshots.">>


!I want to try Linux without harming my system or any risk. Can I?

Yes! Most Linux distributions have a live CD/DVD. This means that the OS can run in a trial mode of the CD or DVD without changing anything on the computer.

#You need to figure out if you have a 64 or 32 bit computer. If you are using Windows, right click on my computer and select properties to find out.
#You will need to download the live iso for the Linux Distribution. An iso file is an image of a CD or DVD. There are a few recommended Linux distributions you can try:
#*Linux Mint: For new users that want things to "just work". Easier to install drivers, but less stable than pure Debian. Includes some extra software and codecs by default.
##*[[32 Bit Linux Mint 16|http://linuxmint.mirrorcatalogs.com/iso//stable/16/linuxmint-16-cinnamon-dvd-32bit.iso]]
##*[[64 Bit Linux Mint 16|http://linuxmint.mirrorcatalogs.com/iso//stable/16/linuxmint-16-cinnamon-dvd-64bit.iso]]
#*Debian XFCE: For users willing to spend more time working with the system. The most stable distribution in this list, but you need to manually install high-end graphics drivers. It has an annoying dock setup with XFCE that can be removed using the Debian install tutorial below.
##*[[32 Bit Debian|http://live.debian.net/cdimage/release/stable+nonfree/i386/iso-hybrid/]]
##*[[64 Bit Dbeian|http://live.debian.net/cdimage/release/stable+nonfree/amd64/iso-hybrid/]]
##*You want to select the {{{debian-live-}}}<version and architecture>{{{-xfce-desktop+nonfree.iso}}} download.
#If you use Windows, you can download [[ImgBurn|http://download.imgburn.com/SetupImgBurn_2.5.7.0.exe]], which is a tool to burn the file to a DVD.
#You can then restart you computer with the CD/DVD in the drive. You will need to press the boot menu key (usually F12) and select the CD/optical drive option. 
#Linux will start in live mode, and you will be able to test the OS. If you like it, continue to the next tutorial.

!Generic Linux Installation Tutorial

@@Make sure to have backups before doing this. (You should have them anyway!)@@
This tutorial assumes that you have just finished or read the previous tutorial. If you wish to install Debian, read the more useful tutorial below.

#Have the CD/DVD you burned in the tutorial above in your CD/DVD drive.
#You can then restart you computer with the CD/DVD in the drive. You will need to press the boot menu key (usually F12) and select the CD/optical drive option. 
#Once the system is started, run the installer.
#Follow the prompts until you get to the partitioner part. This will determine if you want to keep another OS on your machine.
#Select the option to erase the disk if you want to delete your other OS (probably Windows), and select the install alongside option to keep it installed.
#When the install is done, when you reboot the computer, you will now be running Linux.

!Debian Install Tutorial

@@If you feel comfortable with Linux, follow these instructions. Make sure to have backups.@@
If you want a more detailed picture-based tutorial, see the newer [[Debian Install Presentation]].

#You will need your system directly connected to the internet for this to work. (The installer sometimes works with """Wi-Fi""".)
#The first thing you will want to do is download the Debian installer CD. ''Use this installer, as it has the firmware.''
#*[[Debian Netinstall|http://ftp.acc.umu.se/cdimage/unofficial/non-free/cd-including-firmware/current/multi-arch/iso-cd/]]
#Burn the iso and boot into the CD as in previous tutorials.
#Select the graphical installer option and hit TAB. Then type {{{desktop=xfce}}}.
#Do NOT set a root password. Using sudo is easier and more secure. (It is also required for the script below.)
#When you get to the partitioner, select install with encrypted LVM to erase the disk if you want only Linux installed, or use the manual option to resize the existing OS partition and set up an encrypted LVM.
#Download and run [[this script|https://www.dropbox.com/s/01gmz2vpheqkain/debian-postinst.tar.gz]]. It allows you to install various improvements:
#*Codecs: Allows you to play media files and view """DVDs""".
#*Skype: Online instant message and video call client.
#*Google Chrome: A popular browser from Google.
#*Mozilla Firefox: A popular browser from Mozilla.
#*General Applications: Includes many useful applications./%
#*Systemd: Replaces sysvinit. Allows your computer to boot faster.%/
#*XFCE Panel Fix: Makes the XFCE panel behave like the taskbar in Windows XP.
#*[[Terminal Prompt|Terminal Prompt 3]]: Improves overall bash experience with an info bar.
#If your wireless isn't working (most cards do as most firmware is in the CD I linked), run {{{aptitude search firmware}}} and install the appropriate package with {{{sudo aptitude install [package]}}}. Then reboot.
#*If you don't know what your wireless card is (and therefor don't know what firmware to install), run {{{sudo aptitude install hardinfo}}}. You can see what network controller you have in the PCI Devices section.
#If you want Awesome WM instead of XFCE, run {{{sudo aptitude install awesome}}}, then follow [[this tutorial|Awesome window manager tutorials]].
#If you are using a high end graphics card, install the drivers for them for a better gaming experience.
#*[[ATI/AMD Graphics Card Driver|https://wiki.debian.org/ATIProprietary]]
#*[[Nvidia Graphics Card Driver|https://wiki.debian.org/NvidiaGraphicsDrivers]]
#You should read this [[general tutorial|https://wiki.debian.org/DebianDesktopHowTo]].

!Linux Resources
[[Debian|http://www.debian.org/]] Debian is my Linux Distribution of choice.
[[Debian Forums|http://forums.debian.net/]] The forums for Debian.
[[Debian Documentationl|http://www.debian.org/doc/]] This is a list of manuals and resources for Debian.
[[Debian Reference|http://www.debian.org/doc/manuals/debian-reference/]] This is a good first tutorial.
[[Arch Linux Wiki|https://wiki.archlinux.org/]] While Arch Linux itself is difficult to set up, their wiki is very useful, as it documents almost everything.
[[Linux Mint|http://www.linuxmint.com/]] Linux Mint website.
[[Linux|https://www.linux.com/]] The website for the Linux Foundation. Has many great tutorials.
[[/r/Linux|http://www.reddit.com/r/linux?NERpage=2]] This is the reddit Linux community. Very helpful.
[[/r/Commandline|http://www.reddit.com/r/commandline]] This is the reddit command line community. Very useful for learning about the Linux command line.
[[/r/scriptswap|http://www.reddit.com/r/ScriptSwap]] This is a good place to find useful bash scripts.
[[Kris Occhipinti's Youtube Channel|https://www.youtube.com/user/metalx1000/videos]] These videos teach you lots about shell scripting.
/***
|''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);
}
//}}}
<html><iframe class="imgur-album" width="100%" height="550" frameborder="0" src="http://imgur.com/a/eWZ5Z/embed"></iframe></html>

Here are the rest of the images I took and the ones shown in the album at a higher resolution (3264x2448 instead of 1024x768): [[Link|https://www.dropbox.com/s/gpmronb8mtljikr/Macros.zip]] [[[M]|http://yunpan.cn/QDeWX24G88rYE]] (267 MB)

All the images that are shown were created by me and are licensed [[Attribution-ShareAlike|https://creativecommons.org/licenses/by-sa/3.0/us/]].
[[Home]]<<tag Projects>><<tag "Shell Script">> <<tag Python>> <<tag Minecraft>>

<title>Ian's site - My site for projects, apps, and other stuff!</title>
<!--{{{-->
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' />
<!--}}}-->
<link rel="icon" type="image/png" href="http://i.imgur.com/Ec8aG.png" />
<title>Ian's site - My site for projects, apps, and other stuff!</title>

<style type="text/css">#contentWrapper {display:none;}</style><div id="SplashScreen" style="border: 3px solid #fff; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 20px; font-family:Tahoma; background-color:#fff;"><img src="http://i.imgur.com/nWaRVe5.gif"></img></div>

This tool will speak facts while playing music in the background. It requires espeak, mplayer, and pulseaudio. This allows you to spend your time doing something more pleasant, like browsing the internet or lying in bed thinking about how annoying those fill-in questions were in the first place.

Usage: ./memorytool.sh [fact file] [music] [options]
* -l will play the facts in order instead of randomly.
* -n will disable smooth volume changes.

{{{
#!/bin/bash
if [[ "`echo $* | grep "\-l"`" != "" ]]
then
    echo "Thank you for using memory tool. The specified facts, $1, will play in order every 10 seconds."
    espeak "Thank you for using memory tool. The specified facts, $1, will play in order every 10 seconds."
else
    echo "Thank you for using memory tool. The specified facts, $1, will play randomly every 10 seconds."
    espeak "Thank you for using memory tool. The specified facts, $1, will play randomly every 10 seconds."
fi
mplayer -quiet -loop 0 $2 < /dev/null &> /dev/null &
sleep 10
time="0"
iterator=0
length=`wc -l $1 | awk '{ print $1 }'`
tempfile="/tmp/$RANDOM$RANDOM.txt"
cat $1 | shuf > "$tempfile"
if [[ "`echo $* | grep "\-n"`" != "-l" ]]
then
    file="$tempfile"
else
    file="$1"
fi
while true;
do
    iterator=$((iterator+1))
    if [[ "$iterator" -gt "$length" ]]
    then
        iterator=1
        cat $1 | shuf > "$tempfile"
    fi
    LINE="`sed -n $((iterator))p "$file"`"
    id=`pacmd list-sink-inputs | tr '\n' ' ' | sed 's/index/\nindex/g' | grep "application.name = \"mplayer2\"" | grep index | awk '{print $2}'`
    if [[ "`echo $* | grep "\-n"`" == "" ]]
    then
        for i in `seq 65536 -5120 32768`
        do
            pacmd set-sink-input-volume $id $i
            sleep 0.1
        done
    fi
    pacmd set-sink-input-volume $id 32768
    echo
    echo "$LINE"
    espeak "$LINE"
    id=`pacmd list-sink-inputs | tr '\n' ' ' | sed 's/index/\nindex/g' | grep "application.name = \"mplayer2\"" | grep index | awk '{print $2}'`
    if [[ "`echo $* | grep "\-n"`" == "" ]]
    then
        for i in `seq 32768 5120 65536`
        do
            pacmd set-sink-input-volume $id $i
            sleep 0.1
        done
    fi
    pacmd set-sink-input-volume $id 65536
    sleep 10
done
rm "$tempfile"
}}}
!Small singleplayer stations (just pics)
These are a good way to make an interactive railroad in little space.
[img[http://i.imgur.com/pJMeR.png]]
These two minecart stations are small, reliable, and have many features.

[img[http://i.imgur.com/oPKcC.png]]
The smaller station is an endpoint station. Used at end of tracks to make turns.
This can:
*make turns
*accept and deploy in one direction
*remain completely flat
BUILT FOR SINGLEPLAYER!

[img[http://i.imgur.com/hmpiT.png]]
The larger station is a midpoint station.
This can:
*make turns
*continue in same direction
*accept and deploy in both directions
*remain completely flat
BUILT FOR SINGLEPLAYER!
The purpose of this script is to make running a Minecraft server on Linux easier. It performs mundane tasks automatically, and allow for easier server administration. Server world backups use [[Attic|https://attic-backup.org/]], so a long Minecraft world history can be saved with minimal disk space cost. Here is the helptext:

{{{
Usage: ./mcs <command>
This is a general minecraft server admin tool.

This script must reside in the root of the server folder.
The world should be in the folder world.
Please ensure that attic and screen are installed.

  start           Start the minecraft server.
  stop [-f]       Stop the minecraft server.
                  -f stops the server without warning.
  restart [-f]    Restart the minecraft server.
                  -f restarts the server without warning.
  backup          Create a backup of the server world.
  restore         Stop the server, restore a backup, then start it again.
  send <file>     Broadcast the file to people on the server.
  command <text>  Send the command to the server.
  console         Show the server console in screen.
                  Use CTRL+A D to exit.
  daemon [-f]     Start the server directly. Use start instead.


By default, the server jar is minecraft_server.jar and the screen name
is minecraft. Change the variables at the start of the script to change this.

This script is useful for automation. Here is an example crontab:

0 * * * * "/path/to/server/mcs" backup
7 7 * * * "/path/to/server/mcs" restart
0 */2 * * * "/path/to/server/mcs" send info.txt

It will do backups every hour, restart the server every day at 7:07 AM,
and send server info every 2 hours.
}}}

The script runs Minecraft in screen, so the console can be accessed with {{{screen -x}}}. This is used by the [[Minecraft Servers]].
Dependencies: [[Attic|https://attic-backup.org/]] and [[GNU screen|https://www.gnu.org/software/screen/]]. Install them on Debian with {{{sudo aptitude install attic screen}}}.
[[>>Download<<|https://www.dropbox.com/s/blwcx09m67z6wd8/mcs]] or <<slider minecraftScript minecraftScript "Click to show code.">>

!Mod Installer
These scripts will copy the contents of the {{{ModFiles}}} folder (in the same folder with the scripts) to the Minecraft folder. A custom Minecraft folder can be selected. There is a version for Windows, Linux, and Mac. These installer scripts are intended to be executed after running the Minecraft Forge [[installer|http://files.minecraftforge.net/]].
!!!windows.bat
{{{
@echo off
echo Mod Package Installer for Windows
echo.
echo Drag custom minecraft folder to screen and hit enter.
echo To use the default minecraft folder, hit enter.
set /p mcpath="> "
if %mcpath%=="" (
set mcpath=%appdata%\.minecraft
)
echo Installing...
xcopy /q /s /e /y ModFiles\* %mcpath%
}}}
!!!linux.sh
{{{
#!/bin/bash
cd `dirname "$0"`
echo Mod Package Installer for Linux
echo
echo Drag custom minecraft folder to screen and hit enter.
echo To use the default minecraft folder, hit enter.
read -p "> " mcpath
if [[ "$mcpath" == "" ]]; then
    mcpath=~/.minecraft
fi
echo Installing...
cp -rf ModFiles/* "$mcpath"/
}}}
!!!mac.sh
{{{
#!/bin/bash
cd `dirname "$0"`
echo Mod Package Installer for Mac
echo
echo Drag custom minecraft folder to screen and hit enter.
echo To use the default minecraft folder, hit enter.
read -p "> " mcpath
if [[ "$mcpath" == "" ]]; then
    mcpath=~/Library/Application Support/minecraft
fi
echo Installing...
cp -rf ModFiles/* "$mcpath"/
}}}
/%I am currently hosting 2 Minecraft servers. They are both running on the same machine, which is almost always on. The servers use the [[mcs|Minecraft Server Scripts]], and is fully automated.
%/I am currently hosting a dedicated Minecraft server. The server uses the [[Minecraft Server Scripts]]./%
No Minecraft servers are currently being hosted. If you wish to play on one, please contact me via email at ian@waltons.org.%/
!Server Rules
#''Respect all users.''
##''No profanity.'' Any use of profanity will result in a ''warning''.
##''No killing of other users.'' Any killing of other users on the server will result in a ''warning''.
##''No inappropriate behavior towards other users.'' Any inappropriate behavior will result in a ''warning''.
#''Respect all constructions.''
##''No griefing.'' Any reported griefing will be resolved with an ''indefinite ban'' and a ''rollback to the last server world''.
##''No stealing.'' Any reported stealing will be resolved with a ''request to return said item(s)'' and a ''warning''. Failure to comply will result in an ''indefinite ban''.
##''Don't modify other people's constructions without permission.'' The penalty for modifying constructions is determined on a case by case basis.
#''Respect the server and it's administrators.''
##''No inappropriate constructions.'' Inappropriate constructions will be demolished and any users creating them receive a ''warning''.
##''No inappropriate skins.'' Users of inappropriate skins will receive a ''request to change their skin'' and a ''warning''. Failure to comply will result in an ''indefinite ban''.
##''No hacking of any kind.'' Any attempt to obtain an advantage will result in a ''warning''. Any attempt to cause damage to the server will result in an ''indefinite ban''. Rei's mini map and Inventory Tweaks are allowed.
##''No spamming.'' Spammers will receive a ''request to stop'' and a ''warning''. Failure to comply will result in an ''indefinite ban''.
##''No server advertising.'' Any people who advertise a server will receive a ''warning''.
##''Do not ask to become an administrator.'' Any requests will be ''denied'', and you will ensure that you ''never become an administrator'' in the future.
''Three warnings results in a one week ban. After six warnings, the ban is indefinite''
!Vanilla Minecraft Server
This server has no mods and runs the current version of Minecraft. The server is currently running Minecraft 1.8. World backups are saved every hour. The administrator is iwalton3. The address for this server is {{{iwalton.us.to}}}./%
!Big Dig Server
This server is running the [[Big Dig|http://www.technicpack.net/big-dig]] modpack and runs the current development build. You will need to update to [[this|http://bit.ly/18qRjpt]] version of [[Dimensional Doors|http://www.minecraftforum.net/topic/1650007-16x-betauniversal-dimensional-doors-v141-eyes-in-the-dark/]]. The server is currently running Minecraft 1.5.2. World backups are saved every three hours. This server was requested by [[middaughr|http://i.imgur.com/6mSVYS6.jpg]]. The administrators for this server are iwalton3 and [[middaughr|http://i.imgur.com/6mSVYS6.jpg]]. The address for this server is {{{iwalton.no-ip.org:30000}}}.%//%
!Direwolf20 Server
This server is running the [[Direwolf20|http://feed-the-beast.com/]] modpack and runs the current recommended build. The server is currently running Minecraft 1.6.4. World backups are saved every three hours. This server was requested by roboto15. The administrators for this server are iwalton3 and roboto15. The address for this server is {{{iwalton.no-ip.org:30000}}}.%//%
!Tech World Server
This server is running the [[Tech World|http://ftbwiki.org/Tech_World]] modpack and runs the recommended build. World backups are saved every three hours. This server was requested by [[middaughr|http://i.imgur.com/6mSVYS6.jpg]]. The administrators for this server are iwalton3 and [[middaughr|http://i.imgur.com/6mSVYS6.jpg]]. The address for this server is {{{iwalton.no-ip.org}}}.%/
/%!Yogcast Complete Server
This server is running the [[Yogscast Complete|http://www.atlauncher.com/]] modpack and version 2872 with the recommended mods with MystCraft and Logistics Pipes enabled. World backups are saved every three hours. This server was requested [[here|http://www.reddit.com/r/ATLauncher/comments/25o9fv/any_yogscast_complete_private_servers_with/]]. The administrator for this server are iwalton3. The address for this server is {{{iwalton.no-ip.org}}}.
%//%!Resonant Rise Mainline Server
This server is running the [[Resonant Rise|http://www.atlauncher.com/]] modpack and runs the current version with the recommended mods enabled. World backups are saved every three hours. The administrator for this server is iwalton3. The address for this server is {{{iwalton.no-ip.org}}}.%/
/%!Scheduled Downtime
These servers are running on hardware that is taken out of service occasionally. If I am aware beforehand, I will post the downtime below.
|!Date|!Reason|!Time Down|
|2014-05-29|Internship|6:20 - 19:00|
I currently have a new server available. Should this downtime cause issues, I can switch to the dedicated box at the cost of performance.%/
This is an ''experimental'' program to encrypt text using a shuffled dictionary. It ''does not'' encrypt names and numbers. Don't use it for anything important.

[[>>Download<<|https://www.dropbox.com/s/065jtsfvl8rx8mf/otd.zip]] [[[M]|http://yunpan.cn/QDeB2EShbYuvK]]

!Usage
{{{python otd.py <INPUT FILE> <OUTPUT FILE> [DICTIONARY FILE]}}}
This program encrypts and decrypts text using a dictionary file.

"""    --"""help      Show this screen.

If the dictionary file isn't specified, it assumes it is a file named {{{cdict}}} in the current directory.
You can create a dictionary file with:
{{{sort -R /etc/dictionaries-common/words > cdict}}}
<<tiddler TspotOptions>>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]]
<!--{{{-->
<div class='header'>
     <div class='gradient' macro='gradient vert #FF8614 #DA4A0D '>
	<div class='titleLine' >
                <span class='searchBar' macro='search'></span>
		<span class='siteTitle' refresh='content' tiddler='imgtitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div id='topMenu' refresh='content' tiddler='MainMenu'></div>
    </div>
</div>
<div id='bodywrapper'>
<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 id='contentFooter' refresh='content' tiddler='contentFooter'></div>
</div>

<!--}}}-->
<html><iframe class="imgur-album" width="100%" height="550" frameborder="0" src="http://imgur.com/a/ozLrU/embed"></iframe></html>

Download these and many more in full resolution: [[Link|https://www.dropbox.com/s/581omqo7sxapdbr/nat.zip]] [[[M]|http://yunpan.cn/QDeW5zg2KwGKV]](279 MB)

All the images that are shown were created by me and are licensed [[Attribution-ShareAlike|https://creativecommons.org/licenses/by-sa/3.0/us/]].
/***
|<html><a name="Top"/></html>''Name:''|PartTiddlerPlugin|
|''Version:''|1.0.9 (2007-07-14)|
|''Source:''|http://tiddlywiki.abego-software.de/#PartTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license]]|
|''CoreVersion:''|2.1.3|
|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
!Table of Content<html><a name="TOC"/></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Description',null, event)">Description, Syntax</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Applications',null, event)">Applications</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('LongTiddler',null, event)">Refering to Paragraphs of a Longer Tiddler</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Citation',null, event)">Citation Index</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('TableCells',null, event)">Creating "multi-line" Table Cells</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Tabs',null, event)">Creating Tabs</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Sliders',null, event)">Using Sliders</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Revisions',null, event)">Revision History</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Code',null, event)">Code</a></html>
!Description<html><a name="Description"/></html>
With the {{{<part aPartName> ... </part>}}} feature you can structure your tiddler text into separate (named) parts. 
Each part can be referenced as a "normal" tiddler, using the "//tiddlerName//''/''//partName//" syntax (e.g. "About/Features").  E.g. you may create links to the parts (e.g. {{{[[Quotes/BAX95]]}}} or {{{[[Hobbies|AboutMe/Hobbies]]}}}), use it in {{{<<tiddler...>>}}} or {{{<<tabs...>>}}} macros etc.


''Syntax:'' 
|>|''<part'' //partName// [''hidden''] ''>'' //any tiddler content// ''</part>''|
|//partName//|The name of the part. You may reference a part tiddler with the combined tiddler name "//nameOfContainerTidder//''/''//partName//. <<br>>If you use a partName containing spaces you need to quote it (e.g. {{{"Major Overview"}}} or {{{[[Shortcut List]]}}}).|
|''hidden''|When defined the content of the part is not displayed in the container tiddler. But when the part is explicitly referenced (e.g. in a {{{<<tiddler...>>}}} macro or in a link) the part's content is displayed.|
|<html><i>any&nbsp;tiddler&nbsp;content</i></html>|<html>The content of the part.<br>A part can have any content that a "normal" tiddler may have, e.g. you may use all the formattings and macros defined.</html>|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!Applications<html><a name="Applications"/></html>
!!Refering to Paragraphs of a Longer Tiddler<html><a name="LongTiddler"/></html>
Assume you have written a long description in a tiddler and now you want to refer to the content of a certain paragraph in that tiddler (e.g. some definition.) Just wrap the text with a ''part'' block, give it a nice name, create a "pretty link" (like {{{[[Discussion Groups|Introduction/DiscussionGroups]]}}}) and you are done.

Notice this complements the approach to first writing a lot of small tiddlers and combine these tiddlers to one larger tiddler in a second step (e.g. using the {{{<<tiddler...>>}}} macro). Using the ''part'' feature you can first write a "classic" (longer) text that can be read "from top to bottom" and later "reuse" parts of this text for some more "non-linear" reading.

<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!!Citation Index<html><a name="Citation"/></html>
Create a tiddler "Citations" that contains your "citations". 
Wrap every citation with a part and a proper name. 

''Example''
{{{
<part BAX98>Baxter, Ira D. et al: //Clone Detection Using Abstract Syntax Trees.// 
in //Proc. ICSM//, 1998.</part>

<part BEL02>Bellon, Stefan: //Vergleich von Techniken zur Erkennung duplizierten Quellcodes.// 
Thesis, Uni Stuttgart, 2002.</part>

<part DUC99>Ducasse, Stéfane et al: //A Language Independent Approach for Detecting Duplicated Code.// 
in //Proc. ICSM//, 1999.</part>
}}}

You may now "cite" them just by using a pretty link like {{{[[Citations/BAX98]]}}} or even more pretty, like this {{{[[BAX98|Citations/BAX98]]}}}.

<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!!Creating "multi-line" Table Cells<html><a name="TableCells"/></html>
You may have noticed that it is hard to create table cells with "multi-line" content. E.g. if you want to create a bullet list inside a table cell you cannot just write the bullet list
{{{
* Item 1
* Item 2
* Item 3
}}}
into a table cell (i.e. between the | ... | bars) because every bullet item must start in a new line but all cells of a table row must be in one line.

Using the ''part'' feature this problem can be solved. Just create a hidden part that contains the cells content and use a {{{<<tiddler >>}}} macro to include its content in the table's cell.

''Example''
{{{
|!Subject|!Items|
|subject1|<<tiddler ./Cell1>>|
|subject2|<<tiddler ./Cell2>>|

<part Cell1 hidden>
* Item 1
* Item 2
* Item 3
</part>
...
}}}

Notice that inside the {{{<<tiddler ...>>}}} macro you may refer to the "current tiddler" using the ".".

BTW: The same approach can be used to create bullet lists with items that contain more than one line.

<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!!Creating Tabs<html><a name="Tabs"/></html>
The build-in {{{<<tabs ...>>}}} macro requires that you defined an additional tiddler for every tab it displays. When you want to have "nested" tabs you need to define a tiddler for the "main tab" and one for every tab it contains. I.e. the definition of a set of tabs that is visually displayed at one place is distributed across multiple tiddlers.

With the ''part'' feature you can put the complete definition in one tiddler, making it easier to keep an overview and maintain the tab sets.

''Example''
The standard tabs at the sidebar are defined by the following eight tiddlers:
* SideBarTabs
* TabAll
* TabMore
* TabMoreMissing
* TabMoreOrphans
* TabMoreShadowed
* TabTags
* TabTimeline

Instead of these eight tiddlers one could define the following SideBarTabs tiddler that uses the ''part'' feature:
{{{
<<tabs txtMainTab 
    Timeline Timeline SideBarTabs/Timeline 
    All 'All tiddlers' SideBarTabs/All 
    Tags 'All tags' SideBarTabs/Tags 
    More 'More lists' SideBarTabs/More>>
<part Timeline hidden><<timeline>></part>
<part All hidden><<list all>></part>
<part Tags hidden><<allTags>></part>
<part More hidden><<tabs txtMoreTab 
    Missing 'Missing tiddlers' SideBarTabs/Missing 
    Orphans 'Orphaned tiddlers' SideBarTabs/Orphans 
    Shadowed 'Shadowed tiddlers' SideBarTabs/Shadowed>></part>
<part Missing hidden><<list missing>></part>
<part Orphans hidden><<list orphans>></part>
<part Shadowed hidden><<list shadowed>></part>
}}}

Notice that you can easily "overwrite" individual parts in separate tiddlers that have the full name of the part.

E.g. if you don't like the classic timeline tab but only want to see the 100 most recent tiddlers you could create a tiddler "~SideBarTabs/Timeline" with the following content:
{{{
<<forEachTiddler 
		sortBy 'tiddler.modified' descending 
		write '(index < 100) ? "* [["+tiddler.title+"]]\n":""'>>
}}}
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!!Using Sliders<html><a name="Sliders"/></html>
Very similar to the build-in {{{<<tabs ...>>}}} macro (see above) the {{{<<slider ...>>}}} macro requires that you defined an additional tiddler that holds the content "to be slid". You can avoid creating this extra tiddler by using the ''part'' feature

''Example''
In a tiddler "About" we may use the slider to show some details that are documented in the tiddler's "Details" part.
{{{
...
<<slider chkAboutDetails About/Details details "Click here to see more details">>
<part Details hidden>
To give you a better overview ...
</part>
...
}}}

Notice that putting the content of the slider into the slider's tiddler also has an extra benefit: When you decide you need to edit the content of the slider you can just doubleclick the content, the tiddler opens for editing and you can directly start editing the content (in the part section). In the "old" approach you would doubleclick the tiddler, see that the slider is using tiddler X, have to look for the tiddler X and can finally open it for editing. So using the ''part'' approach results in a much short workflow.

<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!Revision history<html><a name="Revisions"/></html>
* v1.0.9 (2007-07-14)
** Bugfix: Error when using the SideBarTabs example and switching between "More" and "Shadow". Thanks to cmari for reporting the issue.
* v1.0.8 (2007-06-16)
** Speeding up display of tiddlers containing multiple pard definitions. Thanks to Paco Rivière for reporting the issue.
** Support "./partName" syntax inside <<tabs ...>> macro
* v1.0.7 (2007-03-07)
** Bugfix: <<tiddler "./partName">> does not always render correctly after a refresh (e.g. like it happens when using the "Include" plugin). Thanks to Morris Gray for reporting the bug.
* v1.0.6 (2006-11-07)
** Bugfix: cannot edit tiddler when UploadPlugin by Bidix is installed. Thanks to José Luis González Castro for reporting the bug.
* v1.0.5 (2006-03-02)
** Bugfix: Example with multi-line table cells does not work in IE6. Thanks to Paulo Soares for reporting the bug.
* v1.0.4 (2006-02-28)
** Bugfix: Shadow tiddlers cannot be edited (in TW 2.0.6). Thanks to Torsten Vanek for reporting the bug.
* v1.0.3 (2006-02-26)
** Adapt code to newly introduced Tiddler.prototype.isReadOnly() function (in TW 2.0.6). Thanks to Paulo Soares for reporting the problem.
* v1.0.2 (2006-02-05)
** Also allow other macros than the "tiddler" macro use the "." in the part reference (to refer to "this" tiddler)
* v1.0.1 (2006-01-27)
** Added Table of Content for plugin documentation. Thanks to RichCarrillo for suggesting.
** Bugfix: newReminder plugin does not work when PartTiddler is installed. Thanks to PauloSoares for reporting.
* v1.0.0 (2006-01-25)
** initial version
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!Code<html><a name="Code"/></html>
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
***/
//{{{
//============================================================================
//                           PartTiddlerPlugin

// Ensure that the PartTiddler Plugin is only installed once.
//
if (!version.extensions.PartTiddlerPlugin) {



version.extensions.PartTiddlerPlugin = {
    major: 1, minor: 0, revision: 9,
    date: new Date(2007, 6, 14), 
    type: 'plugin',
    source: "http://tiddlywiki.abego-software.de/#PartTiddlerPlugin"
};

if (!window.abego) window.abego = {};
if (version.major < 2) alertAndThrow("PartTiddlerPlugin requires TiddlyWiki 2.0 or newer.");

//============================================================================
// Common Helpers

// Looks for the next newline, starting at the index-th char of text. 
//
// If there are only whitespaces between index and the newline 
// the index behind the newline is returned, 
// otherwise (or when no newline is found) index is returned.
//
var skipEmptyEndOfLine = function(text, index) {
	var re = /(\n|[^\s])/g;
	re.lastIndex = index;
	var result = re.exec(text);
	return (result && text.charAt(result.index) == '\n') 
			? result.index+1
			: index;
}


//============================================================================
// Constants

var partEndOrStartTagRE = /(<\/part>)|(<part(?:\s+)((?:[^>])+)>)/mg;
var partEndTagREString = "<\\/part>";
var partEndTagString = "</part>";

//============================================================================
// Plugin Specific Helpers

// Parse the parameters inside a <part ...> tag and return the result.
//
// @return [may be null] {partName: ..., isHidden: ...}
//
var parseStartTagParams = function(paramText) {
	var params = paramText.readMacroParams();
	if (params.length == 0 || params[0].length == 0) return null;
	
	var name = params[0];
	var paramsIndex = 1;
	var hidden = false;
	if (paramsIndex < params.length) {
		hidden = params[paramsIndex] == "hidden";
		paramsIndex++;
	}
	
	return {
		partName: name, 
		isHidden: hidden
	};
}

// Returns the match to the next (end or start) part tag in the text, 
// starting the search at startIndex.
// 
// When no such tag is found null is returned, otherwise a "Match" is returned:
// [0]: full match
// [1]: matched "end" tag (or null when no end tag match)
// [2]: matched "start" tag (or null when no start tag match)
// [3]: content of start tag (or null if no start tag match)
//
var findNextPartEndOrStartTagMatch = function(text, startIndex) {
	var re = new RegExp(partEndOrStartTagRE);
	re.lastIndex = startIndex;
	var match = re.exec(text);
	return match;
}

//============================================================================
// Formatter

// Process the <part ...> ... </part> starting at (w.source, w.matchStart) for formatting.
//
// @return true if a complete part section (including the end tag) could be processed, false otherwise.
//
var handlePartSection = function(w) {
	var tagMatch = findNextPartEndOrStartTagMatch(w.source, w.matchStart);
	if (!tagMatch) return false;
	if (tagMatch.index != w.matchStart || !tagMatch[2]) return false;

	// Parse the start tag parameters
	var arguments = parseStartTagParams(tagMatch[3]);
	if (!arguments) return false;
	
	// Continue processing
	var startTagEndIndex = skipEmptyEndOfLine(w.source, tagMatch.index + tagMatch[0].length);
	var endMatch = findNextPartEndOrStartTagMatch(w.source, startTagEndIndex);
	if (endMatch && endMatch[1]) {
		if (!arguments.isHidden) {
			w.nextMatch = startTagEndIndex;
			w.subWikify(w.output,partEndTagREString);
		}
		w.nextMatch = skipEmptyEndOfLine(w.source, endMatch.index + endMatch[0].length);
		
		return true;
	}
	return false;
}

config.formatters.push( {
    name: "part",
    match: "<part\\s+[^>]+>",
	
	handler: function(w) {
		if (!handlePartSection(w)) {
			w.outputText(w.output,w.matchStart,w.matchStart+w.matchLength);
		}
	}
} )

//============================================================================
// Extend "fetchTiddler" functionality to also recognize "part"s of tiddlers 
// as tiddlers.

var currentParent = null; // used for the "." parent (e.g. in the "tiddler" macro)

// Return the match to the first <part ...> tag of the text that has the
// requrest partName.
//
// @return [may be null]
//
var findPartStartTagByName = function(text, partName) {
	var i = 0;
	
	while (true) {
		var tagMatch = findNextPartEndOrStartTagMatch(text, i);
		if (!tagMatch) return null;

		if (tagMatch[2]) {
			// Is start tag
	
			// Check the name
			var arguments = parseStartTagParams(tagMatch[3]);
			if (arguments && arguments.partName == partName) {
				return tagMatch;
			}
		}
		i = tagMatch.index+tagMatch[0].length;
	}
}

// Return the part "partName" of the given parentTiddler as a "readOnly" Tiddler 
// object, using fullName as the Tiddler's title. 
//
// All remaining properties of the new Tiddler (tags etc.) are inherited from 
// the parentTiddler.
// 
// @return [may be null]
//
var getPart = function(parentTiddler, partName, fullName) {
	var text = parentTiddler.text;
	var startTag = findPartStartTagByName(text, partName);
	if (!startTag) return null;
	
	var endIndexOfStartTag = skipEmptyEndOfLine(text, startTag.index+startTag[0].length);
	var indexOfEndTag = text.indexOf(partEndTagString, endIndexOfStartTag);

	if (indexOfEndTag >= 0) {
		var partTiddlerText = text.substring(endIndexOfStartTag,indexOfEndTag);
		var partTiddler = new Tiddler();
		partTiddler.set(
						fullName,
						partTiddlerText,
						parentTiddler.modifier,
						parentTiddler.modified,
						parentTiddler.tags,
						parentTiddler.created);
		partTiddler.abegoIsPartTiddler = true;
		return partTiddler;
	}
	
	return null;
}

// Hijack the store.fetchTiddler to recognize the "part" addresses.
//
var hijackFetchTiddler = function() {
	var oldFetchTiddler = store.fetchTiddler ;
	store.fetchTiddler = function(title) {
		var result = oldFetchTiddler.apply(this, arguments);
		if (!result && title) {
			var i = title.lastIndexOf('/');
			if (i > 0) {
				var parentName = title.substring(0, i);
				var partName = title.substring(i+1);
				var parent = (parentName == ".") 
						? store.resolveTiddler(currentParent)
						: oldFetchTiddler.apply(this, [parentName]);
				if (parent) {
					return getPart(parent, partName, parent.title+"/"+partName);
				}
			}
		}
		return result;	
	};
};

// for debugging the plugin is not loaded through the systemConfig mechanism but via a script tag. 
// At that point in the "store" is not yet defined. In that case hijackFetchTiddler through the restart function.
// Otherwise hijack now.
if (!store) {
	var oldRestartFunc = restart;
	window.restart = function() {
		hijackFetchTiddler();
		oldRestartFunc.apply(this,arguments);
	};
} else
	hijackFetchTiddler();




// The user must not edit a readOnly/partTiddler
//

config.commands.editTiddler.oldIsReadOnlyFunction = Tiddler.prototype.isReadOnly;

Tiddler.prototype.isReadOnly = function() {
	// Tiddler.isReadOnly was introduced with TW 2.0.6.
	// For older version we explicitly check the global readOnly flag
	if (config.commands.editTiddler.oldIsReadOnlyFunction) {
		if (config.commands.editTiddler.oldIsReadOnlyFunction.apply(this, arguments)) return true;
	} else {
		if (readOnly) return true;
	}

	return this.abegoIsPartTiddler;
}

config.commands.editTiddler.handler = function(event,src,title)
{
	var t = store.getTiddler(title);
	// Edit the tiddler if it either is not a tiddler (but a shadowTiddler)
	// or the tiddler is not readOnly
	if(!t || !t.abegoIsPartTiddler)
		{
		clearMessage();
		story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
		story.focusTiddler(title,"text");
		return false;
		}
}

// To allow the "./partName" syntax in macros we need to hijack 
// the invokeMacro to define the "currentParent" while it is running.
// 
var oldInvokeMacro = window.invokeMacro;
function myInvokeMacro(place,macro,params,wikifier,tiddler) {
	var oldCurrentParent = currentParent;
	if (tiddler) currentParent = tiddler;
	try {
		oldInvokeMacro.apply(this, arguments);
	} finally {
		currentParent = oldCurrentParent;
	}
}
window.invokeMacro = myInvokeMacro;

// To correctly support the "./partName" syntax while refreshing we need to hijack 
// the config.refreshers.tiddlers to define the "currentParent" while it is running.
// 
(function() {
	var oldTiddlerRefresher= config.refreshers.tiddler;
	config.refreshers.tiddler = function(e,changeList) {
		var oldCurrentParent = currentParent;
		try {
			currentParent = e.getAttribute("tiddler");
			return oldTiddlerRefresher.apply(this,arguments);
		} finally {
			currentParent = oldCurrentParent;
		}
	};
})();

// Support "./partName" syntax inside <<tabs ...>> macro
(function() {
	var extendRelativeNames = function(e, title) {
		var nodes = e.getElementsByTagName("a");
		for(var i=0; i<nodes.length; i++) {
			var node = nodes[i];
			var s = node.getAttribute("content");
			if (s && s.indexOf("./") == 0)
				node.setAttribute("content",title+s.substr(1));
		}
	};
	var oldHandler = config.macros.tabs.handler;
	config.macros.tabs.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
		var result = oldHandler.apply(this,arguments);
		if (tiddler)
			extendRelativeNames(place, tiddler.title);
		return result;
	};
})();

// Scroll the anchor anchorName in the viewer of the given tiddler visible.
// When no tiddler is defined use the tiddler of the target given event is used.
window.scrollAnchorVisible = function(anchorName, tiddler, evt) {
	var tiddlerElem = null;
	if (tiddler) {
		tiddlerElem = document.getElementById(story.idPrefix + tiddler);
	}
	if (!tiddlerElem && evt) {
		var target = resolveTarget(evt);
		tiddlerElem = story.findContainingTiddler(target);
	}
	if (!tiddlerElem) return;

	var children = tiddlerElem.getElementsByTagName("a");
	for (var i = 0; i < children.length; i++) {
		var child = children[i];
		var name = child.getAttribute("name");
		if (name == anchorName) {
			var y = findPosY(child);
			window.scrollTo(0,y);
			return;
		}
	}
}

} // of "install only once"
//}}}

/***
<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2006 ([[www.abego-software.de|http://www.abego-software.de]])

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

Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.

Neither the name of abego Software nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.

<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
***/
/***
|''Name:''|PasswordOptionPlugin|
|''Description:''|Extends TiddlyWiki options with non encrypted password option.|
|''Version:''|1.0.2|
|''Date:''|Apr 19, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#PasswordOptionPlugin|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0 (Beta 5)|
***/
//{{{
version.extensions.PasswordOptionPlugin = {
	major: 1, minor: 0, revision: 2, 
	date: new Date("Apr 19, 2007"),
	source: 'http://tiddlywiki.bidix.info/#PasswordOptionPlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	license: '[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D]]',
	coreVersion: '2.2.0 (Beta 5)'
};

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

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

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

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

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

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

merge(config.optionsDesc,{
		pasPassword: "Test password"
	});
*/
//}}}
These icons and banners are used to display the desired treatment for the document you are attaching them to. These are not a legally binding, but they could be used to summarize license terms.

|!Icon|!Banner|!Description|!Example|
|<html><img height="64" src="http://iwalton.us.to/theme/permissions/0.svg"></html>|<html><img height="64" src="http://iwalton.us.to/theme/permissions/0-full.svg"></html>|This banner is intended for documents that should never be shared for personal or legal reasons. These documents should never be stored unencrypted.|Personal Information, Passwords, Credit Card Numbers, Trade Secrets|
|<html><img height="64" src="http://iwalton.us.to/theme/permissions/1.svg"></html>|<html><img height="64" src="http://iwalton.us.to/theme/permissions/1-full.svg"></html>|This banner is intended for documents that you intend to keep secret, but are willing to give up if interrogated. These types of items might be hidden well enough to give the impression that items from the previous category don't exist.|Questionable Computer Files, Semiprivate Information|
|<html><img height="64" src="http://iwalton.us.to/theme/permissions/2.svg"></html>|<html><img height="64" src="http://iwalton.us.to/theme/permissions/2-full.svg"></html>|This banner is intended for documents that have limited interest or use for the general population. They shouldn't be forced on people unless they show interest or are trying to find it.|Technical Guides, Niche Interest Content|
|<html><img height="64" src="http://iwalton.us.to/theme/permissions/3.svg"></html>|<html><img height="64" src="http://iwalton.us.to/theme/permissions/3-full.svg"></html>|This banner is intended for documents that anyone may take interest in. These include general news articles and contact information, such as a public encryption key.|News Articles, Contact Information, Public Keys|
This is a useful script for bundling a folder into a self extracting script. When you are done with the files, it will repack them as a new self extractor, allowing persistent self extracting scripts. It also supports encryption and starting a program in the archive automatically.

With it you can:

* Use it on a flash drive as a substitute for truecrypt (for small files).
* Distribute files in an encrypted form that automatically extracts itself.
* Package installers or scripts (even if the script needs to change the contents of the archive).
* Package a portable version of an application as one file (like appimage with persistance).

To use:

* Put a copy of the script below into the folder you want to archive.
* Run it. It will guide you through the rest of the process.

{{{
#!/bin/bash
password="0"
skip=0
cd `dirname "$0"`
if [ ! -f .gen.sh ]; then
    zenity --question --text="You are about to archive and delete\n`pwd`\nAre you sure you want to continue?"
    if [ "$?" != "0" ]; then
        exit
    fi
    pw1=`zenity --entry --text="Enter a password to encrypt with.\n(blank for none)" --hide-text`
    if [ "$pw1" != "" ]; then
        pw2=`zenity --entry --text="Type the password in again." --hide-text`
        if [ "$pw1" != "$pw2" ]; then
            while [ "$pw1" != "$pw2" ]; do
                pw1=`zenity --entry --text="You entered the password wrong!\nEnter a password to encrypt with." --hide-text`
                pw2=`zenity --entry --text="Type the password in again." --hide-text`
            done
        fi
        echo "$pw1" > .password
    fi
    command=`zenity --entry --text="Execute a program?\n(blank for standard message)"`
    if [ "$command" != "" ]; then
        echo "$command" > .command
        chmod +x .command
    fi
    mv `basename "$0"` .gen.sh
    skip=1
fi
filename=${PWD##*/}
rm ../"$filename.sh"
if [ $skip == 0 ]; then
    if [ -f .command ]; then
        ./.command
    else
        zenity --info --text="Close when done."
    fi
fi
cd ../
function ccmd {
    echo cd "\"$filename"\" >> "$filename.sh"
    echo "$cmd" >> "$filename.sh"
}
function password {
    local pw1=`cat "$filename/.password"`
    password="1"
    gpg -c --no-use-agent --passphrase "$pw1" a.tar.gz | zenity --progress --auto-close --text="Encrypting..." --pulsate
}
echo "#!/bin/bash" >> "$filename.sh"
echo "cd \`dirname \"\$0\"\`" >> "$filename.sh"
echo "ARCHIVE=\$(awk '/^__ARCHIVE_BELOW__/{print NR + 1;exit;0;}' \"\$0\")" >> "$filename.sh"
tar czvf a.tar.gz "$filename" | zenity --progress --auto-close --text="Compressing..." --pulsate
if [ -f $filename/.password ]; then
    password
fi
if [ "$password" == "1" ]; then
    echo "tail -n+\$ARCHIVE \"\$0\" > a.tar.gz.gpg" >> "$filename.sh"
    echo "pw1=\`zenity --entry --text=\"Enter your _password:\" --hide-text\`" >> "$filename.sh"
    echo "gpg --no-use-agent --passphrase \"\$pw1\" a.tar.gz.gpg | zenity --progress --auto-close --text=\"Extracting...\" --pulsate" >> "$filename.sh"
    echo "tar -zxvf a.tar.gz | zenity --progress --auto-close --text=\"Extracting...\" --pulsate" >> "$filename.sh"
    echo "rm -f a.tar.gz" >> "$filename.sh"
    echo "rm -f a.tar.gz.gpg" >> "$filename.sh"
else
    echo "tail -n+\$ARCHIVE \"\$0\" > a.tar.gz" >> "$filename.sh"
    echo "tar -zxvf a.tar.gz | zenity --progress --auto-close --text=\"Extracting...\" --pulsate" >> "$filename.sh"
    echo "rm -f a.tar.gz" >> "$filename.sh"
fi
cmd="bash .gen.sh"
ccmd
echo "exit" >> "$filename.sh"
echo "__ARCHIVE_BELOW__" >> "$filename.sh"
if [ "$password" == "1" ]; then
    cat a.tar.gz.gpg >> "$filename.sh" | zenity --progress --auto-close --text="Packaging..." --pulsate
    rm a.tar.gz.gpg | zenity --progress --auto-close --text="Packaging..." --pulsate
else
    cat a.tar.gz >> "$filename.sh" | zenity --progress --auto-close --text="Packaging..." --pulsate
fi
rm a.tar.gz | zenity --progress --auto-close --text="Packaging..." --pulsate
chmod +x "$filename.sh" | zenity --progress --auto-close --text="Packaging..." --pulsate
rm -rf "$filename"
exit

}}}
!The Problem:
Many people leave their computers unlocked for a few minutes every time they leave to do quick tasks. This is an easy way for someone to steal vital information or deface your computer.

!The solution:
This device is placed in front of a computer, when it detects you leaving your desk, it automatically locks your computer.

Note: Right now, this is Linux only, and needs a special script to work. Later it will also be compatible with windows, and be plug and play. It will use a keyboard module to "press" Win-L key combo. On linux, you can configure it to use Win-L. The device does not unlock the computer when you come back, because it does not know who it is.

Price (with future keyboard module) : 60$

!Parts
Arduino
Ping)) sensor
led (optional, there is one on the Arduino, but this led is more noticeable.)

!Wiring

[LED]
Wire negative (shorter lead) to GND
Wire positive (longer lead) to DIGITAL 13

[Ping))]
Wire 5V pin to 5V
Wire GND to GND
Wire SIG to DIGITAL 7

[USB]
Plug the USB cable into the computer.

Software for computer:
Install this:
{{{sudo apt-get install libdevice-serialport-perl}}}
Run this:
{{{
#!/usr/bin/perl -w

use Device::SerialPort;

my $port = Device::SerialPort->new("/dev/ttyUSB0");
$port->databits(8);
$port->baudrate(9600);
$port->parity("none");
$port->stopbits(1);

$number_of_chars_to_read=1;

$command4="xscreensaver-command -lock";

$command5="xscreensaver-command -lock";

# $time="1";

while(1)
{

my $code=$port->read($number_of_chars_to_read);

print "$code";

system("$command4") if($code eq "a");
system("$command5") if($code eq "b");


sleep(1);
}
}}}
This code must ALWAYS be running when the computer is on.
If you don't use xscreensaver, change this line to match your screen locker.
{{{$command4="xscreensaver-command -lock";}}}

!What it does:
This script watches the arduino for the signal, when it gets the signal, it locks your computer.

Arduino code:
{{{
char N;
int I;
int ByteVar;

int NN;
int Remainder;
int Num_5;
const int pingPin = 7;
void setup()
{
  Serial.begin(9600);
pinMode(13, OUTPUT);
}

void loop()
{
  long duration, inches, cm;
  pinMode(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin, LOW);
  pinMode(pingPin, INPUT);
  duration = pulseIn(pingPin, HIGH);
  inches = microsecondsToInches(duration);
  cm = microsecondsToCentimeters(duration);
  if ( inches > 10 ) {
      digitalWrite(13, HIGH);   // set the LED on
  delay(100);              // wait for a second
  digitalWrite(13, LOW);    // set the LED off
  delay(100);              // wait for a second
    delay(1000);
        long duration, inches, cm;
  pinMode(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin, LOW);
  pinMode(pingPin, INPUT);
  duration = pulseIn(pingPin, HIGH);
  inches = microsecondsToInches(duration);
  cm = microsecondsToCentimeters(duration);
    if ( inches > 10 ) {
        digitalWrite(13, HIGH);   // set the LED on
  delay(100);              // wait for a second
  digitalWrite(13, LOW);    // set the LED off
  delay(100);              // wait for a second
    digitalWrite(13, HIGH);   // set the LED on
  delay(100);              // wait for a second
  digitalWrite(13, LOW);    // set the LED off
  delay(100);              // wait for a second
    Serial.print("a");
          while ( inches > 10 ) {
    delay(1000);
    long duration, inches, cm;
  pinMode(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin, LOW);
  pinMode(pingPin, INPUT);
  duration = pulseIn(pingPin, HIGH);
  inches = microsecondsToInches(duration);
  cm = microsecondsToCentimeters(duration);
      if ( inches < 10 ) {
  digitalWrite(13, HIGH);   // set the LED on
  delay(100);              // wait for a second
  digitalWrite(13, LOW);    // set the LED off
  delay(100);              // wait for a second
    digitalWrite(13, HIGH);   // set the LED on
  delay(100);              // wait for a second
  digitalWrite(13, LOW);    // set the LED off
  delay(100);              // wait for a second
    digitalWrite(13, HIGH);   // set the LED on
  delay(100);              // wait for a second
  digitalWrite(13, LOW);    // set the LED off
  delay(1000);              // wait for a second
      long duration, inches, cm;
  pinMode(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin, LOW);
  pinMode(pingPin, INPUT);
  duration = pulseIn(pingPin, HIGH);
  inches = microsecondsToInches(duration);
  cm = microsecondsToCentimeters(duration);
        if ( inches < 10 ) {
  digitalWrite(13, HIGH);   // set the LED on
  delay(100);
  digitalWrite(13, LOW);    // set the LED off
  delay(100);
    digitalWrite(13, HIGH);   // set the LED on
  delay(100);
  digitalWrite(13, LOW);    // set the LED off
  delay(100);
    digitalWrite(13, HIGH);   // set the LED on
  delay(100);
  digitalWrite(13, LOW);    // set the LED off
  delay(100);
    digitalWrite(13, HIGH);   // set the LED on
  delay(100);
  digitalWrite(13, LOW);    // set the LED off
  delay(100);
        break;
        }
      }
  }
    } 
  } 
  delay(1000);


}
long microsecondsToInches(long microseconds)
{
  return microseconds / 74 / 2;
}
long microsecondsToCentimeters(long microseconds)
{
  return microseconds / 29 / 2;
}
}}}
Program this code to your arduino.

!What it does:
This code checks the ping)) every second. When someone is within 10 inches, it keeps the computer unlocked. When someone walks away, it blinks the led and waits 1 second. It then performs the check again. If no one is there, it blinks the led twice, and sends the lock signal. The script waits for you to return, then when it detects your presence, it blinks the led 3 times. It then waits 1 second, if someone is still there, it breaks out of the loop, and starts checking if you leave. The device checks twice to prevent unintended screen locking. The device also checks to see when the individual returns to prevent lock screen command spamming.
Note: The device does not unlock the computer when you come back, because it can't verify the identity of the person.

!Using it:
Place the Ping)) sensor on the very front of your desk where you sit (it must be within 10 inches of you). It is more reliable to put it on your table edge, not your monitor. Plug your Arduino in, and make your connections. Program the Arduino, and run the code (I encourage adding it to startup). When you get up, the LED will flash once, then twice. Your computer will lock. Then sit back down. The LED will flash three times, then four. Type your password. When you get up, the process repeats. If you walk in front of the device, it may blink 3 times, but not four. This prevents spamming of the lock command. If you move your hand in front of the sensor while sitting, or do something (sometimes it may happen randomly) the led may blink once, but not twice. This prevents your computer from locking when you don't leave.
/***
|Name|PlayerPlugin|
|Source|http://www.TiddlyTools.com/#PlayerPlugin|
|Version|1.1.4|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|Embed a media player in a tiddler|
!!!!!Usage
<<<
{{{<<player [id=xxx] [type] [URL] [width] [height] [autoplay|true|false] [showcontrols|true|false] [extras]>>}}}

''id=xxx'' is optional, and specifies a unique identifier for each embedded player.  note: this is required if you intend to display more than one player at the same time.

''type'' is optional, and is one of the following: ''windows'', ''realone'', ''quicktime'', ''flash'', ''image'' or ''iframe''.  If the media type is not specified, the plugin automatically detects Windows, Real, QuickTime, Flash video or JPG/GIF images by matching known file extensions and/or specialized streaming-media transfer protocols (such as RTSP:).  For unrecognized media types, the plugin displays an error message.

''URL'' is the location of the media content

''width'' and ''height'' are the dimensions of the video display area (in pixels)

''autoplay'' or ''true'' or ''false'' is optional, and specifies whether the media content should begin playing as soon as it is loaded, or wait for the user to press the "play" button.  Default is //not// to autoplay.

''showcontrols'' or ''true'' or ''false'' is optional, and specifies whether the embedded media player should display its built-in control panel (e.g., play, pause, stop, rewind, etc), if any.  Default is to display the player controls.

''extras'' are optional //pairs// of parameters that can be passed to the embedded player, using the {{{<param name=xxx value=yyy>}}} HTML syntax.

''If you use [[AttachFilePlugin]] to encode and store a media file within your document, you can play embedded media content by using the title of the //attachment tiddler//'' as a parameter in place of the usual reference to an external URL.  When playing an attached media content, you should always explicitly specify the media type parameter, because the name used for the attachment tiddler may not contain a known file extension from which a default media type can be readily determined.
<<<
!!!!!Configuration
<<<
Default player size:
width: <<option txtPlayerDefaultWidth>> height: <<option txtPlayerDefaultHeight>>
<<<
!!!!!Examples
<<<
+++[Windows Media]...
Times Square Live Webcam
{{{<<player id=1 http://www.earthcam.com/usa/newyork/timessquare/asx/tsq_stream.asx>>}}}
<<player id=1 http://www.earthcam.com/usa/newyork/timessquare/asx/tsq_stream.asx>>
===
+++[RealOne]...
BBC London: Live and Recorded news
{{{<<player id=2 http://www.bbc.co.uk/london/realmedia/news/tvnews.ram>>}}}
<<player id=2 http://www.bbc.co.uk/london/realmedia/news/tvnews.ram>>
===
+++[Quicktime]...
America Free TV: Classic Comedy
{{{<<player id=3 http://www.americafree.tv/unicast_mov/AmericaFreeTVComedy.mov>>}}}
<<player id=3 http://www.americafree.tv/unicast_mov/AmericaFreeTVComedy.mov>>
===
+++[Flash]...
Asteroids arcade game
{{{<<player id=4 http://www.80smusiclyrics.com/games/asteroids/asteroids.swf 400 300>>}}}
<<player id=4 http://www.80smusiclyrics.com/games/asteroids/asteroids.swf 400 300>>
Google Video
{{{<<player id=5 flash http://video.google.com/googleplayer.swf?videoUrl=http%3A%2F%2Fvp.video.google.com%2Fvideodownload%3Fversion%3D0%26secureurl%3DoQAAAIVnUNP6GYRY8YnIRNPe4Uk5-j1q1MVpJIW4uyEFpq5Si0hcSDuig_JZcB9nNpAhbScm9W_8y_vDJQBw1DRdCVbXl-wwm5dyUiiStl_rXt0ATlstVzrUNC4fkgK_j7nmse7kxojRj1M3eo3jXKm2V8pQjWk97GcksMFFwg7BRAXmRSERexR210Amar5LYzlo9_k2AGUWPLyRhMJS4v5KtDSvNK0neL83ZjlHlSECYXyk%26sigh%3Dmpt2EOr86OAUNnPQ3b9Tr0wnDms%26begin%3D0%26len%3D429700%26docid%3D-914679554478687740&thumbnailUrl=http%3A%2F%2Fvideo.google.com%2FThumbnailServer%3Fcontentid%3De7e77162deb04c42%26second%3D5%26itag%3Dw320%26urlcreated%3D1144620753%26sigh%3DC3fqXPPS1tFiUqLzmkX3pdgYc2Y&playerId=-91467955447868774               400 326>>}}}
<<player id=5 flash http://video.google.com/googleplayer.swf?videoUrl=http%3A%2F%2Fvp.video.google.com%2Fvideodownload%3Fversion%3D0%26secureurl%3DoQAAAIVnUNP6GYRY8YnIRNPe4Uk5-j1q1MVpJIW4uyEFpq5Si0hcSDuig_JZcB9nNpAhbScm9W_8y_vDJQBw1DRdCVbXl-wwm5dyUiiStl_rXt0ATlstVzrUNC4fkgK_j7nmse7kxojRj1M3eo3jXKm2V8pQjWk97GcksMFFwg7BRAXmRSERexR210Amar5LYzlo9_k2AGUWPLyRhMJS4v5KtDSvNK0neL83ZjlHlSECYXyk%26sigh%3Dmpt2EOr86OAUNnPQ3b9Tr0wnDms%26begin%3D0%26len%3D429700%26docid%3D-914679554478687740&thumbnailUrl=http%3A%2F%2Fvideo.google.com%2FThumbnailServer%3Fcontentid%3De7e77162deb04c42%26second%3D5%26itag%3Dw320%26urlcreated%3D1144620753%26sigh%3DC3fqXPPS1tFiUqLzmkX3pdgYc2Y&playerId=-91467955447868774               400 326>>
YouTube Video
{{{<<player id=6 flash http://www.youtube.com/v/OdT9z-JjtJk 400 300>>}}}
<<player id=6 flash http://www.youtube.com/v/OdT9z-JjtJk 400 300>>
===
+++[Still Images]...
GIF (best for illustrations, animations, diagrams, etc.)
{{{<<player id=7 image images/meow.gif auto auto>>}}}
<<player id=7 image images/meow.gif auto auto>>
JPG (best for photographs, scanned images, etc.)
{{{<<player id=8 image images/meow2.jpg 200 150>>}}}
<<player id=8 image images/meow2.jpg 200 150>>
===
<<<
!!!!!Revisions
<<<
2008.05.10 [1.1.4] in handlers(), immediately return if no params (prevents error in macro).  Also, refactored auto-detect code to make type mapping configurable.
2007.10.15 [1.1.3] in loadURL(), add recognition for .PNG (still image), fallback to iframe for unrecognized media types
2007.08.31 [1.1.2] added 'click-through' link for JPG/GIF images
2007.06.21 [1.1.1] changed "hidecontrols" param to "showcontrols" and recognize true/false values in addition to 'showcontrols', added "autoplay" param (also recognize true/false values), allow "auto" as value for type param
2007.05.22 [1.1.0] added support for type=="iframe" (displays src URL in an IFRAME)
2006.12.06 [1.0.1] in handler(), corrected check for config.macros.attach (instead of config.macros.attach.getAttachment) so that player plugin will work when AttachFilePlugin is NOT installed.  (Thanks to Phillip Ehses for bug report)
2006.11.30 [1.0.0] support embedded media content using getAttachment() API defined by AttachFilePlugin or AttachFilePluginFormatters.  Also added support for 'image' type to render JPG/GIF still images
2006.02.26 [0.7.0] major re-write.  handles default params better.  create/recreate player objects via loadURL() API for use with interactive forms and scripts.
2006.01.27 [0.6.0] added support for 'extra' macro params to pass through to object parameters
2006.01.19 [0.5.0] Initial ALPHA release
2005.12.23 [0.0.0] Started
<<<
!!!!!Code
***/
//{{{
version.extensions.PlayerPlugin= {major: 1, minor: 1, revision: 4, date: new Date(2008,5,10)};

config.macros.player = {};
config.macros.player.html = {};
config.macros.player.handler= function(place,macroName,params) {
	if (!params.length) return; // missing parameters - do nothing
	var id=null;
	if (params[0].substr(0,3)=="id=") id=params.shift().substr(3);
	var type="";
	if (!params.length) return; // missing parameters - do nothing
	var p=params[0].toLowerCase();
	if (p=="auto" || p=="windows" || p=="realone" || p=="quicktime" || p=="flash" || p=="image" || p=="iframe")
		type=params.shift().toLowerCase();
	var url=params.shift(); if (!url || !url.trim().length) url="";
	if (url.length && config.macros.attach!=undefined) // if AttachFilePlugin is installed
		if ((tid=store.getTiddler(url))!=null && tid.isTagged("attachment")) // if URL is attachment
			url=config.macros.attach.getAttachment(url); // replace TiddlerTitle with URL
	var width=params.shift();
	var height=params.shift();
	var autoplay=false;
	if (params[0]=='autoplay'||params[0]=='true'||params[0]=='false')
		autoplay=(params.shift()!='false');
	var show=true;
	if (params[0]=='showcontrols'||params[0]=='true'||params[0]=='false')
		show=(params.shift()!='false');
	var extras="";
	while (params[0]!=undefined)
		extras+="<param name='"+params.shift()+"' value='"+params.shift()+"'> ";
	this.loadURL(place,id,type,url,width,height,autoplay,show,extras);
}

if (config.options.txtPlayerDefaultWidth==undefined) config.options.txtPlayerDefaultWidth="100%";
if (config.options.txtPlayerDefaultHeight==undefined) config.options.txtPlayerDefaultHeight="480"; // can't use "100%"... player height doesn't stretch right :-(

config.macros.player.typeMap={
	windows: ['mms', '.asx', '.wvx', '.wmv', '.mp3'],
	realone: ['rtsp', '.ram', '.rpm', '.rm', '.ra'],
	quicktime: ['.mov', '.qt'],
	flash: ['.swf', '.flv'],
	image: ['.jpg', '.gif', '.png'],
	iframe: ['.htm', '.html', '.shtml', '.php']
};

config.macros.player.loadURL=function(place,id,type,url,width,height,autoplay,show,extras) {

	if (id==undefined) id="tiddlyPlayer";
	if (!width) var width=config.options.txtPlayerDefaultWidth;
	if (!height) var height=config.options.txtPlayerDefaultHeight;
	if (url && (!type || !type.length || type=="auto")) { // determine type from URL
		u=url.toLowerCase();
		var map=config.macros.player.typeMap;
		for (var t in map) for (var i=0; i<map[t].length; i++)
			if (u.indexOf(map[t][i])!=-1) var type=t;
	}
	if (!type || !config.macros.player.html[type]) var type="none";
	if (!url) var url="";
	if (show===undefined) var show=true;
	if (!extras) var extras="";
	if (type=="none" && url.trim().length) type="iframe"; // fallback to iframe for unrecognized media types

	// adjust parameter values for player-specific embedded HTML
	switch (type) {
		case "windows":
			autoplay=autoplay?"1":"0"; // player-specific param value
			show=show?"1":"0"; // player-specific param value
			break;
		case "realone":
			autoplay=autoplay?"true":"false";
			show=show?"block":"none";
			height-=show?60:0; // leave room for controls
			break;
		case "quicktime":
			autoplay=autoplay?"true":"false";
			show=show?"true":"false";
			break;
		case "image":
			show=show?"block":"none";
			break;
		case "iframe":
			show=show?"block":"none";
			break;
	}

	// create containing div for player HTML
	// and add or replace player in TW DOM structure
	var newplayer = document.createElement("div");
	newplayer.playerType=type;
	newplayer.setAttribute("id",id+"_div");
	var existing = document.getElementById(id+"_div");
	if (existing && !place) place=existing.parentNode;
	if (!existing)
		place.appendChild(newplayer);
	else {
		if (place==existing.parentNode) place.replaceChild(newplayer,existing)
		else { existing.parentNode.removeChild(existing); place.appendChild(newplayer); }
	}

	var html=config.macros.player.html[type];
	html=html.replace(/%i%/mg,id);
	html=html.replace(/%w%/mg,width);
	html=html.replace(/%h%/mg,height);
	html=html.replace(/%u%/mg,url);
	html=html.replace(/%a%/mg,autoplay);
	html=html.replace(/%s%/mg,show);
	html=html.replace(/%x%/mg,extras);
	newplayer.innerHTML=html;
}
//}}}

// // Player-specific API functions: isReady(id), isPlaying(id), toggleControls(id), showControls(id,flag)

//{{{
// status values:
// Windows: 0=Undefined, 1=Stopped, 2=Paused, 3=Playing, 4=ScanForward, 5=ScanReverse
//          6=Buffering, 7=Waiting, 8=MediaEnded, 9=Transitioning, 10=Ready, 11=Reconnecting
// RealOne: 0=Stopped, 1=Contacting, 2=Buffering, 3=Playing, 4=Paused, 5=Seeking
// QuickTime: 'Waiting', 'Loading', 'Playable', 'Complete', 'Error:###'
// Flash: 0=Loading, 1=Uninitialized, 2=Loaded, 3=Interactive, 4=Complete
config.macros.player.isReady=function(id)
{
	var d=document.getElementById(id+"_div"); if (!d) return false;
	var p=document.getElementById(id); if (!p) return false;
	if (d.playerType=='windows') return !((p.playState==0)||(p.playState==7)||(p.playState==9)||(p.playState==11));
	if (d.playerType=='realone') return (p.GetPlayState()>1);
	if (d.playerType=='quicktime') return !((p.getPluginStatus()=='Waiting')||(p.getPluginStatus()=='Loading'));
	if (d.playerType=='flash') return (p.ReadyState>2);
	return true;
}
config.macros.player.isPlaying=function(id)
{
	var d=document.getElementById(id+"_div"); if (!d) return false;
	var p=document.getElementById(id); if (!p) return false;
	if (d.playerType=='windows') return (p.playState==3);
	if (d.playerType=='realone') return (p.GetPlayState()==3);
	if (d.playerType=='quicktime') return (p.getPluginStatus()=='Complete');
	if (d.playerType=='flash') return (p.ReadyState<4);
	return false;
}
config.macros.player.showControls=function(id,flag) {
	var d=document.getElementById(id+"_div"); if (!d) return false;
	var p=document.getElementById(id); if (!p) return false;
	if (d.playerType=='windows') { p.ShowControls=flag; p.ShowStatusBar=flag; }
	if (d.playerType=='realone') { alert('show/hide controls not available'); }
	if (d.playerType=='quicktime')      // if player not ready, retry in one second
		{ if (this.isReady(id)) p.setControllerVisible(flag); else setTimeout('config.macros.player.showControls("'+id+'",'+flag+')',1000); }
	if (d.playerType=='flash') { alert('show/hide controls not available'); }
}
config.macros.player.toggleControls=function(id) {
	var d=document.getElementById(id+"_div"); if (!d) return false;
	var p=document.getElementById(id); if (!p) return false;
	if (d.playerType=='windows') var flag=!p.ShowControls;
	if (d.playerType=='realone') var flag=true; // TBD
	if (d.playerType=='quicktime') var flag=!p.getControllerVisible();
	if (d.playerType=='flash') var flag=true; // TBD
	this.showControls(id,flag);
}
config.macros.player.fullScreen=function(id) {
	var d=document.getElementById(id+"_div"); if (!d) return false;
	var p=document.getElementById(id); if (!p) return false;
	if (d.playerType=='windows') p.DisplaySize=3;
	if (d.playerType=='realone') p.SetFullScreen();
	if (d.playerType=='quicktime') { alert('full screen not available'); }
	if (d.playerType=='flash') { alert('full screen not available'); }
}
//}}}

// // Player HTML

//{{{
// placeholder (no player)
config.macros.player.html.none=' \
	<table id="%i%" width="%w%" height="%h%" style="background-color:#111;border:0;margin:0;padding:0;"> \
	<tr style="background-color:#111;border:0;margin:0;padding:0;"> \
	<td width="%w%" height="%h%" style="background-color:#111;color:#ccc;border:0;margin:0;padding:0;text-align:center;"> \
	&nbsp; \
	%u% \
	&nbsp; \
	</td></tr></table>';
//}}}

//{{{
// JPG/GIF/PNG still images
config.macros.player.html.image='\
	<a href="%u%" target="_blank"><img width="%w%" height="%h%" style="display:%s%;" src="%u%"></a>';
//}}}

//{{{
// IFRAME web page viewer
config.macros.player.html.iframe='\
	<iframe id="%i%" width="%w%" height="%h%" style="display:%s%;background:#fff;" src="%u%"></iframe>';
//}}}

//{{{
// Windows Media Player
// v7.1 ID: classid=CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6
// v9	ID: classid=CLSID:22d6f312-b0f6-11d0-94ab-0080c74c7e95
config.macros.player.html.windows=' \
	<object id="%i%" width="%w%" height="%h%" style="margin:0;padding:0;width:%w%;height:%h%px;" \
		classid="CLSID:22d6f312-b0f6-11d0-94ab-0080c74c7e95" \
		codebase="http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=6,4,5,715" \
		align="baseline" border="0" \
		standby="Loading Microsoft Windows Media Player components..." \
		type="application/x-oleobject"> \
		<param name="FileName" value="%u%"> <param name="ShowControls" value="%s%"> \
		<param name="ShowPositionControls" value="1"> <param name="ShowAudioControls" value="1"> \
		<param name="ShowTracker" value="1"> <param name="ShowDisplay" value="0"> \
		<param name="ShowStatusBar" value="1"> <param name="AutoSize" value="1"> \
		<param name="ShowGotoBar" value="0"> <param name="ShowCaptioning" value="0"> \
		<param name="AutoStart" value="%a%"> <param name="AnimationAtStart" value="1"> \
		<param name="TransparentAtStart" value="0"> <param name="AllowScan" value="1"> \
		<param name="EnableContextMenu" value="1"> <param name="ClickToPlay" value="1"> \
		<param name="InvokeURLs" value="1"> <param name="DefaultFrame" value="datawindow"> \
		%x% \
		<embed src="%u%" style="margin:0;padding:0;width:%w%;height:%h%px;" \
			align="baseline" border="0" width="%w%" height="%h%" \
			type="application/x-mplayer2" \
			pluginspage="http://www.microsoft.com/windows/windowsmedia/download/default.asp" \
			name="%i%" showcontrols="%s%" showpositioncontrols="1" \
			showaudiocontrols="1" showtracker="1" showdisplay="0" \
			showstatusbar="%s%" autosize="1" showgotobar="0" showcaptioning="0" \
			autostart="%a%" autorewind="0" animationatstart="1" transparentatstart="0" \
			allowscan="1" enablecontextmenu="1" clicktoplay="0" invokeurls="1" \
			defaultframe="datawindow"> \
		</embed> \
	</object>';
//}}}

//{{{
// RealNetworks' RealOne Player
config.macros.player.html.realone=' \
	<table width="%w%" style="border:0;margin:0;padding:0;"><tr style="border:0;margin:0;padding:0;"><td style="border:0;margin:0;padding:0;"> \
	<object id="%i%" width="%w%" height="%h%" style="margin:0;padding:0;" \
		CLASSID="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA"> \
		<PARAM NAME="CONSOLE" VALUE="player"> \
		<PARAM NAME="CONTROLS" VALUE="ImageWindow"> \
		<PARAM NAME="AUTOSTART" Value="%a%"> \
		<PARAM NAME="MAINTAINASPECT" Value="true"> \
		<PARAM NAME="NOLOGO" Value="true"> \
		<PARAM name="BACKGROUNDCOLOR" VALUE="#333333"> \
		<PARAM NAME="SRC" VALUE="%u%"> \
		%x% \
		<EMBED width="%w%" height="%h%" controls="ImageWindow" type="audio/x-pn-realaudio-plugin" style="margin:0;padding:0;" \
			name="%i%" \
			src="%u%" \
			console=player \
			maintainaspect=true \
			nologo=true \
			backgroundcolor=#333333 \
			autostart=%a%> \
		</OBJECT> \
	</td></tr><tr style="border:0;margin:0;padding:0;"><td style="border:0;margin:0;padding:0;"> \
	<object id="%i%_controls" width="%w%" height="60" style="margin:0;padding:0;display:%s%" \
		CLASSID="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA"> \
		<PARAM NAME="CONSOLE" VALUE="player"> \
		<PARAM NAME="CONTROLS" VALUE="All"> \
		<PARAM NAME="NOJAVA" Value="true"> \
		<PARAM NAME="MAINTAINASPECT" Value="true"> \
		<PARAM NAME="NOLOGO" Value="true"> \
		<PARAM name="BACKGROUNDCOLOR" VALUE="#333333"> \
		<PARAM NAME="SRC" VALUE="%u%"> \
		%x% \
		<EMBED WIDTH="%w%" HEIGHT="60" NOJAVA="true" type="audio/x-pn-realaudio-plugin" style="margin:0;padding:0;display:%s%" \
			controls="All" \
			name="%i%_controls" \
			src="%u%" \
			console=player \
			maintainaspect=true \
			nologo=true \
			backgroundcolor=#333333> \
		</OBJECT> \
	</td></tr></table>';
//}}}

//{{{
// QuickTime Player
config.macros.player.html.quicktime=' \
	<OBJECT ID="%i%" WIDTH="%w%" HEIGHT="%h%" style="margin:0;padding:0;" \
		CLASSID="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" \
		CODEBASE="http://www.apple.com/qtactivex/qtplugin.cab"> \
		<PARAM name="SRC" VALUE="%u%"> \
		<PARAM name="AUTOPLAY" VALUE="%a%"> \
		<PARAM name="CONTROLLER" VALUE="%s%"> \
		<PARAM name="BGCOLOR" VALUE="#333333"> \
		<PARAM name="SCALE" VALUE="aspect"> \
		<PARAM name="SAVEEMBEDTAGS" VALUE="true"> \
		%x% \
		<EMBED name="%i%" WIDTH="%w%" HEIGHT="%h%" style="margin:0;padding:0;" \
			SRC="%u%" \
			AUTOPLAY="%a%" \
			SCALE="aspect" \
			CONTROLLER="%s%" \
			BGCOLOR="#333333" \
			EnableJavaSript="true" \
			PLUGINSPAGE="http://www.apple.com/quicktime/download/"> \
		</EMBED> \
	</OBJECT>';
//}}}

//{{{
// Flash Player
config.macros.player.html.flash='\
	<object id="%i%" width="%w%" height="%h%" style="margin:0;padding:0;" \
		classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" \
		codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0"> \
		<param name="movie" value="%u%"> \
		<param name="quality" value="high"> \
		<param name="SCALE" value="exactfit"> \
		<param name="bgcolor" value="333333"> \
		%x% \
		<embed name="%i%" src="%u%" style="margin:0;padding:0;" \
			height="%h%" width="%w%" quality="high" \
			pluginspage="http://www.macromedia.com/go/getflashplayer" \
			type="application/x-shockwave-flash" scale="exactfit"> \
		</embed> \
	</object>';
//}}}
<html><iframe src="http://pastehtml.com/view/1e1u81a.html" width="100%" height="4000" frameBorder="0"><a href="http://pastehtml.com/view/1e1u81a.html">Click here.</a>
</iframe></html>
Usage: python primefinder.py [OPTION] [MAXIMUM] <FILE>
This program finds a list of primes less than or equal to the upper limit. It also runs in interactive mode if you run it without any parameters.

    """--"""help      Show this screen.

{{{
import array
import sys

# Interactive goodness:
if len(sys.argv) == 1:
    checkUpTo=int(raw_input("Upper limit for prime finder? "))
    outputfile=raw_input("Output filename? ")
    if outputfile=="":
        toFile=False
    else:
        toFile=True
else:
    if sys.argv[1] == "--help":
        print """
Usage: python primefinder.py [OPTION] [MAXIMUM] <FILE>
This program finds a list of primes less than or equal to the upper limit.

    --help      Show this screen."""
        sys.exit()
    else:
        checkUpTo=int(sys.argv[1])
        if len(sys.argv) == 2:
            toFile=False
        else:
            toFile=True
            outputfile=sys.argv[2]

#Set variables and arrays
maximum = checkUpTo-2
length = maximum-3
values = array.array('h')
for i in range(0,maximum):
    values.append(0)

if toFile:
    #Calculate primes and write to file
    of = open(outputfile, 'w')
    for i in range(2,maximum):
        if values[i-2]==0:
            of.write("%s\n" % i)
            for remove in range(i**2, checkUpTo, i):
                values[remove-2] = 1
else:
    #Calculate primes and print them
    for i in range(2,maximum):
        if values[i-2]==0:
            print i
            for remove in range(i**2, checkUpTo, i):
                values[remove-2] = 1
}}}
Features:
Automatically extracts and ENTIRE folder from the created script to script location.
GUI tool! (uses zenity)
Ability to (optionally) run a command in the folder created. (e: play a song, or install somthing.)
Ability to (optionally) delete folder after running command.
NEW! encryption (optional).

How to:
Run "chmod +x [file]" on script you downloaded.
Run script.
Select folder to use.
If you want to encrypt, click yes.
if your encrypting, type your password (2 chances)
type your password again(2 chances)
If you mistyped your password, it will prompt again. (it will only do so once)
Type a command to run or click cancel to just unzip. (command is run in the extracted folder).
If you are using a command, select if you want to delete the folder after running a script.
Your sfx will be created in the same directory as the folder you used, with the same name (with .sh added).

NOTE: self extractors need zenity to extract!

[[>>Download<<|https://www.dropbox.com/s/kqklzu39wvzssbk/sfx_make.sh]] [[[M]|http://yunpan.cn/QDeMVj8wEKBnX]]

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Credit to metalx1000 for sfx script base: [[link|http://www.youtube.com/watch?v=Sy5Xf1Hzr2o&feature=feedu]].
<<closeAll>><<permaview>><<slider dev dev 'Developer »' 'Developer menu'>>
<<tabs txtMainTab "Tags" "All tags" TabTags "Timeline" "Timeline" TabTimeline "All" "All tiddlers" TabAll "More" "More lists" TabMore>>
/***
This CSS by DaveBirss.
***/
/*{{{*/


.tabSelected {
 background: #fff;
}

.tabUnselected {
 background: #eee;
}

#sidebar {
 color: #000;
 background: transparent; 
}

#sidebarOptions {
 background: #fff;
}

#sidebarOptions input {
	border: 1px solid #ccc;
}

#sidebarOptions input:hover, #sidebarOptions input:active,  #sidebarOptions input:focus {
	border: 1px solid #000;
}

#sidebarOptions .button {
 color: #999;
}

#sidebarOptions .button:hover {
 color: #000;
 background: #fff;
 border-color:white;
}

#sidebarOptions .button:active {
 color: #000;
 background: #fff;
}

#sidebarOptions .sliderPanel {
 background: transparent;
}

#sidebarOptions .sliderPanel A {
 color: #999;
}

#sidebarOptions .sliderPanel A:hover {
 color: #000;
 background: #fff;
}

#sidebarOptions .sliderPanel A:active {
 color: #000;
 background: #fff;
}

.sidebarSubHeading {
 color: #000;
}

#sidebarTabs {`
 background: #fff
}

#sidebarTabs .tabSelected {
 color: #000;
 background: #fff;
 border-top: solid 1px #ccc;
 border-left: solid 1px #ccc;
 border-right: solid 1px #ccc;
 border-bottom: none;
}

#sidebarTabs .tabUnselected {
 color: #999;
 background: #eee;
 border-top: solid 1px #ccc;
 border-left: solid 1px #ccc;
 border-right: solid 1px #ccc;
 border-bottom: none;
}

#sidebarTabs .tabContents {
 background: #fff;
}


#sidebarTabs .txtMoreTab .tabSelected {
 background: #fff;
}

#sidebarTabs .txtMoreTab .tabUnselected {
 background: #eee;
}

#sidebarTabs .txtMoreTab .tabContents {
 background: #fff;
}

#sidebarTabs .tabContents .tiddlyLink {
 color: #999;
 border:none;
}

#sidebarTabs .tabContents .tiddlyLink:hover {
 background: #fff;
 color: #000;
 border:none;
}

#sidebarTabs .tabContents {
 color: #000;
}

#sidebarTabs .button {
 color: #666;
}

#sidebarTabs .tabContents .button:hover {
 color: #000;
 background: #fff;
}

#sidebar {color:#999;}
/*}}}*/
/***
|''Name''|SimpleSearchPlugin|
|''Description''|displays search results as a simple list of matching tiddlers|
|''Authors''|FND|
|''Version''|0.4.1|
|''Status''|stable|
|''Source''|http://devpad.tiddlyspot.com/#SimpleSearchPlugin|
|''CodeRepository''|http://svn.tiddlywiki.org/Trunk/contributors/FND/plugins/SimpleSearchPlugin.js|
|''License''|[[Creative Commons Attribution-ShareAlike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]]|
|''Keywords''|search|
!Revision History
!!v0.2.0 (2008-08-18)
* initial release
!!v0.3.0 (2008-08-19)
* added Open All button (renders Classic Search option obsolete)
* sorting by relevance (title matches before content matches)
!!v0.4.0 (2008-08-26)
* added tag matching
!To Do
* tag matching optional
* animations for container creation and removal
* when clicking on search results, do not scroll to the respective tiddler (optional)
* use template for search results
!Code
***/
//{{{
if(!version.extensions.SimpleSearchPlugin) { //# ensure that the plugin is only installed once
version.extensions.SimpleSearchPlugin = { installed: true };

if(!config.extensions) { config.extensions = {}; }

config.extensions.SimpleSearchPlugin = {
	heading: "Search Results",
	containerId: "searchResults",
	btnCloseLabel: "close",
	btnCloseTooltip: "dismiss search results",
	btnCloseId: "search_close",
	btnOpenLabel: "Open all",
	btnOpenTooltip: "open all search results",
	btnOpenId: "search_open",

	displayResults: function(matches, query) {
		story.refreshAllTiddlers(true); // update highlighting within story tiddlers
		var el = document.getElementById(this.containerId);
		query = '"""' + query + '"""'; // prevent WikiLinks
		if(el) {
			removeChildren(el);
		} else { //# fallback: use displayArea as parent
			var container = document.getElementById("displayArea");
			el = document.createElement("div");
			el.id = this.containerId;
			el = container.insertBefore(el, container.firstChild);
		}
		var msg = "!" + this.heading + "\n";
		if(matches.length > 0) {
			msg += "''" + config.macros.search.successMsg.format([matches.length.toString(), query]) + ":''\n";
			this.results = [];
			for(var i = 0 ; i < matches.length; i++) {
				this.results.push(matches[i].title);
				msg += "* [[" + matches[i].title + "]]\n";
			}
		} else {
			msg += "''" + config.macros.search.failureMsg.format([query]) + "''"; // XXX: do not use bold here!?
		}
		createTiddlyButton(el, this.btnCloseLabel, this.btnCloseTooltip, config.extensions.SimpleSearchPlugin.closeResults, "button", this.btnCloseId);
		wikify(msg, el);
		if(matches.length > 0) { // XXX: redundant!?
			createTiddlyButton(el, this.btnOpenLabel, this.btnOpenTooltip, config.extensions.SimpleSearchPlugin.openAll, "button", this.btnOpenId);
		}
	},

	closeResults: function() {
		var el = document.getElementById(config.extensions.SimpleSearchPlugin.containerId);
		removeNode(el);
		config.extensions.SimpleSearchPlugin.results = null;
		highlightHack = null;
	},

	openAll: function(ev) {
		story.displayTiddlers(null, config.extensions.SimpleSearchPlugin.results);
		return false;
	}
};

config.shadowTiddlers.StyleSheetSimpleSearch = "/*{{{*/\n" +
	"#" + config.extensions.SimpleSearchPlugin.containerId + " {\n" +
	"\toverflow: auto;\n" +
	"\tpadding: 5px 1em 10px;\n" +
	"\tbackground-color: [[ColorPalette::TertiaryPale]];\n" +
	"}\n\n" +
	"#" + config.extensions.SimpleSearchPlugin.containerId + " h1 {\n" +
	"\tmargin-top: 0;\n" +
	"\tborder: none;\n" +
	"}\n\n" +
	"#" + config.extensions.SimpleSearchPlugin.containerId + " ul {\n" +
	"\tmargin: 0.5em;\n" +
	"\tpadding-left: 1.5em;\n" +
	"}\n\n" +
	"#" + config.extensions.SimpleSearchPlugin.containerId + " .button {\n" +
	"\tdisplay: block;\n" +
	"\tborder-color: [[ColorPalette::TertiaryDark]];\n" +
	"\tpadding: 5px;\n" +
	"\tbackground-color: [[ColorPalette::TertiaryLight]];\n" +
	"}\n\n" +
	"#" + config.extensions.SimpleSearchPlugin.containerId + " .button:hover {\n" +
	"\tborder-color: [[ColorPalette::SecondaryMid]];\n" +
	"\tbackground-color: [[ColorPalette::SecondaryLight]];\n" +
	"}\n\n" +
	"#" + config.extensions.SimpleSearchPlugin.btnCloseId + " {\n" +
	"\tfloat: right;\n" +
	"\tmargin: -5px -1em 5px 5px;\n" +
	"}\n\n" +
	"#" + config.extensions.SimpleSearchPlugin.btnOpenId + " {\n" +
	"\tfloat: left;\n" +
	"\tmargin-top: 5px;\n" +
	"}\n" +
	"/*}}}*/";
store.addNotification("StyleSheetSimpleSearch", refreshStyles);

// override Story.search()
Story.prototype.search = function(text, useCaseSensitive, useRegExp) {
	highlightHack = new RegExp(useRegExp ? text : text.escapeRegExp(), useCaseSensitive ? "mg" : "img");
	var matches = store.search(highlightHack, null, "excludeSearch");
	var q = useRegExp ? "/" : "'";
	config.extensions.SimpleSearchPlugin.displayResults(matches, q + text + q);
};

// override TiddlyWiki.search() to sort by relevance
TiddlyWiki.prototype.search = function(searchRegExp, sortField, excludeTag, match) {
	var candidates = this.reverseLookup("tags", excludeTag, !!match);
	var primary = [];
	var secondary = [];
	var tertiary = [];
	for(var t = 0; t < candidates.length; t++) {
		if(candidates[t].title.search(searchRegExp) != -1) {
			primary.push(candidates[t]);
		} else if(candidates[t].tags.join(" ").search(searchRegExp) != -1) {
			secondary.push(candidates[t]);
		} else if(candidates[t].text.search(searchRegExp) != -1) {
			tertiary.push(candidates[t]);
		}
	}
	var results = primary.concat(secondary).concat(tertiary);
	if(sortField) {
		results.sort(function(a, b) {
			return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);
		});
	}
	return results;
};

} //# end of "install only once"
//}}}
/% My site for news, programs, information, and more. %/
Ian's site - My site for projects, apps, and other stuff!
/***

''Inspired by [[TiddlyPom|http://www.warwick.ac.uk/~tuspam/tiddlypom.html]]''

|Name|SplashScreenPlugin|
|Created by|SaqImtiaz|
|Location|http://tw.lewcid.org/#SplashScreenPlugin|
|Version|0.21 |
|Requires|~TW2.08+|
!Description:
Provides a simple splash screen that is visible while the TW is loading.

!Installation
Copy the source text of this tiddler to your TW in a new tiddler, tag it with systemConfig and save and reload. The SplashScreen will now be installed and will be visible the next time you reload your TW.

!Customizing
Once the SplashScreen has been installed and you have reloaded your TW, the splash screen html will be present in the MarkupPreHead tiddler. You can edit it and customize to your needs.

!History
* 20-07-06 : version 0.21, modified to hide contentWrapper while SplashScreen is displayed.
* 26-06-06 : version 0.2, first release

!Code
***/
//{{{
window.old_lewcid_splash_restart=window.restart;

window.restart = function()
{   if (document.getElementById("SplashScreen"))
        document.getElementById("SplashScreen").style.display = "none";
      if (document.getElementById("contentWrapper"))
        document.getElementById("contentWrapper").style.display = "block";
    
    window.old_lewcid_splash_restart();
   
    if (splashScreenInstall)
       {if(config.options.chkAutoSave)
			{saveChanges();}
        displayMessage("TW SplashScreen has been installed, please save and refresh your TW.");
        }
}


var oldText = store.getTiddlerText("MarkupPreHead");
if (oldText.indexOf("SplashScreen")==-1)
   {var siteTitle = store.getTiddlerText("SiteTitle");
   var splasher='\n\n<style type="text/css">#contentWrapper {display:none;}</style><div id="SplashScreen" style="border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;"><b>'+siteTitle +'</b> is loading<blink> ...</blink><br><br><span style="font-size: 14px; color:red;">Requires Javascript.</span></div>';
   if (! store.tiddlerExists("MarkupPreHead"))
       {var myTiddler = store.createTiddler("MarkupPreHead");}
   else
      {var myTiddler = store.getTiddler("MarkupPreHead");}
      myTiddler.set(myTiddler.title,oldText+splasher,config.options.txtUserName,null,null);
      store.setDirty(true);
      var splashScreenInstall = true;
}
//}}}
[[SideBarWG]]

#topMen br {display:none;}
/***
!Top Menu Styles
***/
/*{{{*/
#topMenu br {display:none; }
#topMenu { background: #000 ; color:#fff;padding: 1em 1em;}
/*}}}*/

/***
!General
***/
/*{{{*/
body {
 background: #444;
 margin: 0 auto;
}

 #contentWrapper{
 background: #fff;
 border: 0;
 margin: 0 1em;

 padding:0;
}
/*}}}*/

/***
!Header rules
***/
/*{{{*/
.titleLine{
 margin: 40px 3em 0em 0em;
margin-left:1.7em;
margin-bottom: 20px;
 padding: 0;
 text-align: left;
 color: #fff;
}

.siteTitle {
	font-size: 2em;
        font-weight: bold;
}

.siteSubtitle {
	font-size: 1.1em;
        display: block;
        margin: .5em auto 1em;
}

.gradient {margin: 0 auto;}



.header {
 background: #fff; 
 margin: 0 0em;
 padding:0 12px;

}
/*}}}*/

/***
!Display Area
***/
/*{{{*/
#bodywrapper {margin:0 12px; padding:0;background:#fff; height:1%}

#displayArea{
 margin: 0em 16em 0em 1em;
 text-align: left;
}

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

h1,h2,h3,h4,h5 { color: #000; background: transparent; padding-bottom:2px; border-bottom: 1px dotted #666; }
.title {color:black; font-size:1.8em; border-bottom:1px solid #333; padding-bottom:0.3px;}
.subtitle { font-size:90%; color:#ccc; padding-left:0.25em; margin-top:0.1em; }

.shadow .title {
	color: #aaa;
}

.tagClear{
	clear: none; 
}

* html .viewer pre {
	margin-left: 0em;
}

* html .editor textarea, * html .editor input {
	width: 98%;
}

.tiddler {margin-bottom:1em; padding-bottom:0em;}


.toolbar .button {color:#bbb; border:none;}
.toolbar .button:hover, .toolbar .highlight, .toolbar .marked, .toolbar a.button:active {background:transparent; color:#111; border:none; text-decoration:underline;}

#sidebar .highlight, #sidebar .marked {background:transparent;}

.tagging, .tagged {
	border: 1px solid #eee;
	background-color: #F7F7F7;
}

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

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

.selected .tagging .listTitle, .selected .tagged .listTitle {
	color: #222; 
}


.tagging .button:hover, .tagged .button:hover {
		border: none; background:transparent; text-decoration:underline; color:#000;
}

.tagging .button, .tagged .button {
		color:#aaa;
}

.selected .tagging .button, .selected .tagged .button {
		color:#000;
}

.viewer blockquote {
	border-left: 3px solid #000;
}

.viewer pre, .viewer code {
	border: 1px dashed #ccc;
	background: #eee;}

.viewer hr {
	border: 0;
	border-top: solid 1px #333;
 margin: 0 8em;
	color: #333;
}

.highlight, .marked {background:transparent; color:#111; border:none; text-decoration:underline;}

.viewer .highlight, .viewer .marked {text-decoration:none;}

#sidebarTabs .highlight, #sidebarTabs .marked {color:#000; text-decoration:none;}

.tabSelected {
 color: #000;
 background: #fff;
 border-top: solid 1px #ccc;
 border-left: solid 1px #ccc;
 border-right: solid 1px #ccc;
 border-bottom: none;
}

.viewer .tabSelected:hover{color:#000;}

.viewer .tabSelected {font-weight:bold;}

.tabUnselected {
 color: #999;
 background: #eee;
 border-top: solid 1px #ccc;
 border-left: solid 1px #ccc;
 border-right: solid 1px #ccc;
 border-bottom: solid 1px #ccc;
 padding-bottom:1px;
}

.tabContents {
 background: #fff;
  color: #000;
}
/*}}}*/
/***
!!!Tables
***/
/*{{{*/
.viewer table {
	border: 1px solid #000;
}

.viewer th, thead td {
	background: #000;
	border: 1px solid #000;
	color: #fff;
}

.viewer td, .viewer tr {
	border: 1px solid #111; padding:4px;
}
/*}}}*/


/***
!!!Editor area
***/
/*{{{*/
.editor input, .editor textarea {
	border: 1px solid #ccc;
}

.editor {padding-top:0.3em;}

.editor textarea:focus, .editor input:focus {
	border: 1px solid #333;
}
/*}}}*/

/***
!Sidebar
***/
/*{{{*/
#sidebar{
position:relative;
float:right;
margin-bottom:1em;
display:inline;
width: 16em;
}

#sidebarOptions .sliderPanel {
	background: #eee; border:1px solid #ccc;
}

/*}}}*/

/***
!Body Footer rules
***/
/*{{{*/
#contentFooter {
 text-align: center;
 clear: both;
 color:#fff;
 background: #000;
 padding: 1em 2em;
font-weight:bold;
}

/*}}}*/
/***
!Link Styles
***/
/*{{{*/
a{
	color: #000;
}

a:hover{
        color: #FF6600;
        background:#fff;
}


.button {
	color: #000;
	border: 1px solid #fff;
}

.button:hover {
	color: #fff;
	background: #ff8614;
	border-color: #000;
}

.button:active {
	color: #fff;
	background: #ff8614;
	border: 1px solid #000;
}

.tiddlyLink {border-bottom: 1px dotted #000;}
.tiddlyLink:hover {border-bottom: 1px dotted #FF6600;} 

.titleLine a {border-bottom: 1px dotted #FF9900;}

.titleLine a:hover {border-bottom: 1px dotted #fff;}

.siteTitle a, .siteSubtitle a{
 color: #fff;
}

.viewer .button {border: 1px solid #ff8614; font-weight:bold;}
.viewer .button:hover, .viewer .marked, .viewer .highlight{background:[[ColorPalette::SecondaryLight]];}

#topMenu .button, #topMenu .tiddlyLink {
 margin-left:0.5em; margin-right:0.5em;
 padding-left:3px; padding-right:3px;
 color:white; font-weight:bold;
}
#topMenu .button:hover, #topMenu .tiddlyLink:hover { background:#000; color:#FF8814}

#topMenu a{border:none;}
/*}}}*/

/***
!Message Area /%=================================================%/
***/
/*{{{*/
#messageArea {
	border: 4px dotted #ff8614;
	background: #000;
	color: #fff;
        font-size:90%;
}

#messageArea .button {
	padding: 0.2em;
	color: #000;
	background: #fff;
        text-decoration:none;
        font-weight:bold;
        border:1px solid #000; 
}

#messageArea a {color:#fff;}

#messageArea a:hover {color:#ff8614; background:transparent;}

#messageArea .button:hover {background: #FF8614; color:#fff; border:1px solid #fff; }

/*}}}*/

/***
!Popup /%=================================================%/
***/
/*{{{*/
.popup {
	background: #ff8814;
	border: 1px solid #333;
}

.popup hr {
	color: #333;
	background: #333;
	border-bottom: 1px;
}

.popup li.disabled {
	color: #333;
}

.popup li a, .popup li a:visited {
	color: #eee;
	border: none;
}

.popup li a:hover {
	background: #ff8614;
	color: #fff;
	border: none;
        text-decoration:underline;
}

.searchBar {float:right; font-size:1em;}
.searchBar .button {display:block; border:none; color:#ccc; }
.searchBar .button:hover{border:none; color:#eee;}

.searchBar input{
 border: 1px inset #000; background:#EFDFD1; width:10em; margin:0;
}

.searchBar input:focus {
 border: 1px inset #000; background:#fff;
}

*html .titleLine {margin-right:1.3em;}

*html .searchBar .button {margin-left:1.7em;}

 .HideSideBarButton {float:right;} 
/*}}}*/

.blog h2, .blog h3, .blog h4{
  margin:0;
  padding:0;
border-bottom:none;
}
.blog {margin-left:1.5em;}  


.blog .excerpt {
  margin:0;
margin-top:0.3em;
  padding: 0;
  margin-left:1em;
  padding-left:1em;
  font-size:90%;
  border-left:1px solid #ddd;
}

#tiddlerWhatsNew h1, #tiddlerWhatsNew h2 {border-bottom:none;}
div[tags~="RecentUpdates"], div[tags~="lewcidExtension"] {margin-bottom: 2em;}

#hoverMenu  .button, #hoverMenu  .tiddlyLink {border:none; font-weight:bold; background:#f37211; color:#fff; padding:0 5px; float:right; margin-bottom:4px;}
#hoverMenu .button:hover, #hoverMenu .tiddlyLink:hover {font-weight:bold; border:none; color:#f37211; background:#000; padding:0 5px; float:right; margin-bottom:4px;}

#topMenu .fontResizer {float:right;}

#topMenu .fontResizer .button{border:1px solid #000;}
#topMenu .fontResizer .button:hover {border:1px solid #f37211; color:#fff;}
#sidebarTabs .txtMainTab .tiddlyLinkExisting {
 font-weight: normal;
 font-style: normal;
}

#sidebarTabs .txtMoreTab .tiddlyLinkExisting {
 font-weight: bold;
 font-style: normal;
}

.block a{display:block;}
.viewer .tableRWrapper {
float: right;
}

.viewer .tableLWrapper {
float: left;
}

.viewer div.centeredTable {
text-align: center;
}
.viewer div.centeredTable table {
margin: 0 auto;
text-align: left;
}

.viewer table.borderless,
.viewer table.borderless * {
border: 0;
}
{{{


# postinstall-prompt
function launch {
    ( $* &> /dev/null & )
}
function ip-get {
echo -n "Wired:    "
ip addr show eth0 | grep inet | awk '{print $2}' | tr "\n" " " | cut -f1 -d"/"
echo -n "Wireless: "
ip addr show wlan0 | grep inet | awk '{print $2}' | tr "\n" " " | cut -f1 -d"/"
echo -n "Public:   "
curl http://icanhazip.com/
}
alias sudo="sudo "
alias mirror='wget -e robots=off -m -r -np --reject html,htm'
extract() {
    local c e i

    (($#)) || return

    for i; do
        c=''
        e=1

        if [[ ! -r $i ]]; then
            echo "$0: file is unreadable: \`$i'" >&2
            continue
        fi

        case $i in
        *.t@(gz|lz|xz|b@(2|z?(2))|a@(z|r?(.@(Z|bz?(2)|gz|lzma|xz)))))
                c='tar xfvz';;
        *.7z)  c='7z x';;
        *.Z)   c='uncompress';;
        *.bz2) c='bunzip2';;
        *.exe) c='cabextract';;
        *.gz)  c='gunzip';;
        *.rar) c='unrar x';;
        *.xz)  c='unxz';;
        *.zip) c='unzip';;
        *)     echo "$0: unrecognized file extension: \`$i'" >&2
                continue;;
        esac

        command $c "$i"
        e=$?
    done

    return $e
}
function mkcd() {
    mkdir -p "$1" && cd "$1"
}
alias ..='cd ..'
alias ins='sudo aptitude install'
alias desk='cd ~/Desktop'
function nocap() {
    rename 'y/A-Z/a-z/' *
}
function nospace() {
    rename 's/ /_/g' *
}
function fixdir() {
    rename 'y/A-Z/a-z/' *
    rename 's/ /_/g' *
}
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'
if [ -x /usr/bin/dircolors ]; then
    test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
    alias ls='ls --color=auto'
    #alias dir='dir --color=auto'
    #alias vdir='vdir --color=auto'

    alias grep='grep --color=auto'
    alias fgrep='fgrep --color=auto'
    alias egrep='egrep --color=auto'
fi
HISTSIZE=10000
HISTFILESIZE=10000
function up() { pushd .. > /dev/null; }
function down() { popd > /dev/null; }
function cd() {
    builtin cd "${@}"
    if [ "$( ls | wc -l )" -gt 30 ] ; then
        ls --color=always | awk 'NR < 16 { print }; NR == 16 { print " (... snip ...)" }; { buffer[NR % 14] = $0 } END { for( i = NR + 1; i <= NR+14; i++ ) print buffer[i % 14] }'
    else
        ls
    fi
}
bind '"\e[A": history-search-backward'
bind '"\e[B": history-search-forward'
function makePrompt {
    which acpi > /dev/null
    useBattery="1"
    if [[ "$?" != "0" ]]
    then
        useBattery="0"
    fi
    local pred="\[\033[0;31m\]"
    local pyellow="\[\033[1;33m\]"

    bold=`echo -en "\e[1m"`; underline=`echo -en "\e[4m"`; dim=`echo -en "\e[2m"`; strickthrough=`echo -en "\e[9m"`; blink=`echo -en "\e[5m"`; reverse=`echo -en "\e[7m"`; hidden=`echo -en "\e[8m"`; normal=`echo -en "\e[0m"`; black=`echo -en "\e[30m"`; red=`echo -en "\e[31m"`; green=`echo -en "\e[32m"`; orange=`echo -en "\e[33m"`; blue=`echo -en "\e[34m"`; purple=`echo -en "\e[35m"`; aqua=`echo -en "\e[36m"`; gray=`echo -en "\e[37m"`; darkgray=`echo -en "\e[90m"`; lightred=`echo -en "\e[91m"`; lightgreen=`echo -en "\e[92m"`; lightyellow=`echo -en "\e[93m"`; lightblue=`echo -en "\e[94m"`; lightpurple=`echo -en "\e[95m"`; lightaqua=`echo -en "\e[96m"`; white=`echo -en "\e[97m"`; default=`echo -en "\e[39m"`; BLACK=`echo -en "\e[40m"`; RED=`echo -en "\e[41m"`; GREEN=`echo -en "\e[42m"`; ORANGE=`echo -en "\e[43m"`; BLUE=`echo -en "\e[44m"`; PURPLE=`echo -en "\e[45m"`; AQUA=`echo -en "\e[46m"`; GRAY=`echo -en "\e[47m"`; DARKGRAY=`echo -en "\e[100m"`; LIGHTRED=`echo -en "\e[101m"`; LIGHTGREEN=`echo -en "\e[102m"`; LIGHTYELLOW=`echo -en "\e[103m"`; LIGHTBLUE=`echo -en "\e[104m"`; LIGHTPURPLE=`echo -en "\e[105m"`; LIGHTAQUA=`echo -en "\e[106m"`; WHITE=`echo -en "\e[107m"`; DEFAULT=`echo -en "\e[49m"`;

    # Check the script is being run by root
    if [ "$(id -u)" != "0" ]; then
        local SYMBOL="$"
        local UNAME_COLOR="\[\033[1;32m\]"
        local FINAL_COLOR="\[\033[0m\]"
        namecolor="$green"
    else
        local SYMBOL="#"
        local UNAME_COLOR="\[\033[1;31m\]"
        local FINAL_COLOR="\[\033[0;32m\]"
        namecolor="$red"
    fi
    interface=`/usr/bin/tty | /bin/sed -e 's:/dev/::'`
    PromCurTTY=$(tty | sed -e "s/.*tty\(.*\)/\1/")
    PromBLUE_BACK="\033[44m"
    BLUE_BACK="\[\033[44m\]"
    promusername=`whoami`
    promhostname=`hostname`
    sedhome=$(printf "%s\n" "$HOME" | sed 's/[][\.*^$(){}?+|/]/\\&/g')
    function prompt_command {
        errortest=`if [ "$?" != "0" ]; then echo '! '; fi`
        currentdir=`pwd | sed "s/${sedhome}/~/g"`
        if [[ "$useBattery" == "1" ]]
        then
            battest=`acpi | awk '{print $4}' | tr -d " " | tr -d "," | tr -d "%"`
            if [[ "$battest" == "100" ]]
            then
                battery=" "
            elif [[ "$battest" -gt "89" ]]
            then
                battery="$bold$green█$normal$BLUE"
            elif [[ "$battest" -gt "79" ]]
            then
                battery="$bold$green▇$normal$BLUE"
            elif [[ "$battest" -gt "69" ]]
            then
                battery="$bold$green▆$normal$BLUE"
            elif [[ "$battest" -gt "59" ]]
            then
                battery="$bold$green▅$normal$BLUE"
            elif [[ "$battest" -gt "49" ]]
            then
                battery="$bold$green▄$normal$BLUE"
            elif [[ "$battest" -gt "39" ]]
            then
                battery="$orange▃$normal$BLUE"
            elif [[ "$battest" -gt "29" ]]
            then
                battery="$bold$orange▂$normal$BLUE"
            else
                battery="$red▁$normal$BLUE"
            fi
        else
            battery=" "
        fi

        stopped="$(jobs -s | wc -l | tr -d " ")"
        running="$(jobs -r | wc -l | tr -d " ")"
        dateget=`/bin/date +"%a %b %d %I:%M"`
        filecount=`/bin/ls -1 | /usr/bin/wc -l | /bin/sed "s: ::g"`
        size=`/bin/ls -lah | /bin/grep -m 1 total | /bin/sed "s/total //"`
        length=`echo "$promusername@$promhostname on $interface jobs:$running$stopped $filecount files $size   $dateget" | wc -c`
        fulllength=`echo "$promusername@$promhostname on $interface jobs:$running$stopped $filecount files $size $currentdir   $dateget" | wc -c`
        if [[ "$fulllength" -gt "$COLUMNS" ]]
        then
            spacecount=`expr "$COLUMNS" - "$length"`
            spaces=`for((i=1;i<=$spacecount;i++));do printf "%s" " ";done;printf "\n"`
            useline="1"
        else
            spacecount=`expr "$COLUMNS" - "$fulllength"`
            spaces=`for((i=1;i<=$spacecount;i++));do printf "%s" " ";done;printf "\n"`
            useline="0"
        fi
        if [[ "$useline" == "0" ]]
        then
            echo -en "\033[s\
            \033[H\033[K"
            echo -en "$BLUE$bold$namecolor"
            echo -en "$promusername@$promhostname$normal$white$BLUE on $bold$blue$interface$red jobs:$green$running$red$stopped$aqua $filecount files $orange$size $normal$BLUE$green$currentdir $spaces$battery $purple$dateget\
\033[K\
\033[u\033[1A\033[1B$default$DEFAULT"
        else
            echo -en "\033[s\
            \033[H\033[K"
            echo -en "$BLUE$bold$namecolor"
            echo -en "$promusername@$promhostname$normal$white$BLUE on $bold$blue$interface$red jobs:$green$running$red$stopped$aqua $filecount files $orange$size $normal$BLUE$spaces$battery $purple$dateget\033[K\n$normal$BLUE$green$currentdir \
\033[K\
\033[u\033[1A\033[1B$default$DEFAULT"
        fi
    }

    export PROMPT_COMMAND=prompt_command

    #Custom PS1 string (prompt)

    PS1="$pred\$errortest$pyellow\! $UNAME_COLOR$SYMBOL$FINAL_COLOR "

    export PS1;
}
makePrompt
#End of prompt code

}}}
This prompt takes a different route than large prompts with many lines that get in the way. The actual prompt is smaller than the Debian default, and only shows information that is relevant to the command entered (if the last command failed and the history number). Everything else is in a title bar that is not in the way. It works in ttys, ssh, screen, and most terminals. It now works in tmux, aterm, and rxvt.

Here are 2 diagrams showing how the title bar system works:
[img[http://i.imgur.com/t1vAc.png]]
[img[http://i.imgur.com/VvhZq.png]]

Add this code to your .bashrc file or use [[this script|https://www.dropbox.com/s/01gmz2vpheqkain/debian-postinst.tar.gz]].
<<slider TermPromptCode TermPromptCode "Click to show code.">>

[[Learn about additional terminal tools here.|http://amplitudeproblem.com/blog/?p=7]]

Credits:
http://www.gilesorr.com/bashprompt/prompts/sergio.html
http://www.debian-administration.org/article/205/Fancy_Bash_Prompts
http://maketecheasier.com/8-useful-and-interesting-bash-prompts/2009/09/04
/***
|Name|TiddlerEncryptionPlugin|
|Author|Lyall Pearce|
|Source|http://www.Remotely-Helpful.com/TiddlyWiki/TiddlerEncryptionPlugin.html|
|License|[[Creative Commons Attribution-Share Alike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]]|
|Version|3.2.1|
|~CoreVersion|2.4.0|
|Requires|None|
|Overrides|store.getSaver().externalizeTiddler(), store.getTiddler() and store.getTiddlerText()|
|Description|Encrypt/Decrypt Tiddlers with a Password key|

!!!!!Usage
<<<
* Tag a tiddler with Encrypt(prompt)
** Consider the 'prompt' something to help you remember the password with. If multiple tiddlers can be encrypted with the same 'prompt' and you will only be asked for the password once.
* Upon save, the Tiddler will be encrypted and the tag replaced with Decrypt(prompt).
** Failure to encrypt (by not entering a password) will leave the tiddler unencrypted and will leave the Encrypt(prompt) tag in place. This means that the next time you save, you will be asked for the password again.
** To have multiple tiddlers use the same password - simply use the same 'prompt'.
** Tiddlers that are encrypted may be automatically tagged 'excludeSearch' as there is no point in searching encrypted data - this is configurable by an option - you still may want to search the titles of encrypted tiddlers
** Tiddlers that are encrypted may be automatically tagged 'excludeLists', if you have them encrypted, you may also want to keep them 'hidden' - this is configurable by an option.
** Automatic removal of excludeLists and excludeSearch tags is performed, if the above two options are set, only if these two tags are the last 2 tags for a tiddler, if they are positioned somewhere else in the tags list, they will be left in place, meaning that the decrypted tiddler will not be searchable and/or will not appear in lists.
** Encrypted tiddlers are stored as displayable hex, to keep things visibly tidy, should you display an encrypted tiddler. There is nothing worse than seeing a pile of gobbledy gook on your screen. Additionally, the encrypted data is easily cut/paste/emailed if displayed in hex form.
* Tiddlers are decrypted only if you click the decrypt button or the decryptAll button, not when you load the TiddlyWiki
** If you don't display a tiddler, you won't have the option to decrypt it (unless you use the {{{<<EncryptionDecryptAll>>}}} macro)
** Tiddlers will re-encrypt automatically on save.
** Decryption of Tiddlers does not make your TiddlyWiki 'dirty' - you will not be asked to save if you leave the page.
* Errors are reported via diagnostic messages.
** Empty passwords, on save, will result in the tiddler being saved unencrypted - this should only occur with new tiddlers, decrypted tiddlers or with tiddlers who have had their 'prompt' tag changed.
** Encrypted tiddlers know if they are decrypted successfully - failure to decrypt a tiddler will ''not'' lose your data.
** Editing of an encrypted (that has not been unencrypted) tiddler will result in loss of that tiddler as the SHA1 checksums will no longer match, upon decryption. To this end, it is best that you do not check the option. You can, however edit an encrypted tiddler tag list - just do ''not'' change the tiddler contents.
** To change the password on a Tiddler, change the Encrypt('prompt') tag to a new prompt value, after decrypting the tiddler.
** You can edit the tags of an encrypted tiddler, so long as you do not edit the text.
** To change the password for all tiddlers of a particular prompt, use the {{{<<EncryptionChangePassword ["button text" ["tooltip text" ["prompt string" ["accessKey"]]]]>>}}} macro.
** To decrypt all tiddlers of a particular "prompt string", use the {{{<<EncryptionDecryptAll ["button text" ["tooltip text" ["prompt string" ["accessKey"]]]]>>}}} macro - this will make tiddlers encrypted with "prompt string" searchable - or prompt for all 'prompt strings', if none is supplied.
<<<
!!!!!Configuration
<<<
Useful Buttons: 
<<EncryptionChangePassword>> - Change passwords of encrypted tiddlers.
<<EncryptionDecryptAll>> - Decrypt ALL tiddlers - enables searching contents of encrypted tiddlers.
<<option chkExcludeEncryptedFromSearch>> - If set, Encrypted Tiddlers are excluded from searching by tagging with excludeSearch. If Clear, excludeSearch is not added and it is also removed from existing Encrypted Tiddlers only if it is the last Tag. Searching of Encrypted Tiddlers is only meaningful for the Title and Tags.
<<option chkExcludeEncryptedFromLists>> - If set, Encrypted Tiddlers are excluded from lists by tagging with excludeLists. If Clear, excludeLists is not added and it is also removed from existing Encrypted Tiddlers only if it is the last Tag. Preventing encrypted tiddlers from appearing in lists effectively hides them.
<<option chkShowDecryptButtonInContent>> - If set, Encrypted Tiddlers content is replaced by <<EncryptionDecryptThis>> button. This has consequences, in the current version as, if you edit the tiddler without decrypting it, you lose the contents.
<<<
!!!!!Revision History
<<<
* 3.2.1 - Returned the <<EncryptionDecryptThis>> button as an option.
* 3.2.0 - Ditched the 'Decrypt' button showing up in the tiddler contents if the tiddler is encrypted. It caused too much pain if you edit the tiddler without decrypting it - you lost your data as it was replaced by a Decrypt Macro call!  Additionally, a 'decrypt' button will now appear in the toolbar, just before the edit button, if the tiddler is encrypted. This button only appears if using core TiddlyWiki version 2.4 or above.
* 3.1.1 - Obscure bug whereby if an encrypted tiddler was a certain length, it would refuse to decrypt.
* 3.1.0 - When creating a new Encrypt(prompt) tiddler and you have not previously decrypted a tiddler with the same prompt, on save, you will be prompted for the password to encrypt the tiddler. Prior to encrypting, an attempt to decrypt all other tiddlers with the same prompt, is performed. If any tiddler fails to decrypt, the save is aborted - this is so you don't accidentally have 2 (or more!) passwords for the same prompt. Either you enter the correct password, change the prompt string and try re-saving or you cancel (and the tiddler is saved unencrypted).
* 3.0.1 - Allow Enter to be used for password entry, rather than having to press the OK button.
* 3.0.0 - Major revamp internally to support entry of passwords using forms such that passwords are no longer visible on entry. Completely backward compatible with old encrypted tiddlers. No more using the javascript prompt() function.
<<<
!!!!!Additional work

***/
//{{{
version.extensions.TiddlerEncryptionPlugin = {major: 3, minor: 2, revision: 1, date: new Date(2008,10,26)};

// where I cache the passwords - for want of a better place.
config.encryptionPasswords = new Array();
config.encryptionReEnterPasswords = false;

if(config.options.chkExcludeEncryptedFromSearch == undefined) config.options.chkExcludeEncryptedFromSearch = false;
if(config.options.chkExcludeEncryptedFromLists == undefined) config.options.chkExcludeEncryptedFromLists = false;
if(config.options.chkShowDecryptButtonInContent == undefined) config.options.chkShowDecryptButtonInContent = false;

config.macros.EncryptionChangePassword = {};
config.macros.EncryptionChangePassword.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
    var theButton = createTiddlyButton(place,
				       (params[0] && params[0].length > 0) ? params[0] : "Change Passwords", 
				       (params[1] && params[1].length > 0) ? params[1] : "Change Passwords" + (params[2] ? " for prompt "+params[2] : ""), 
				       onClickEncryptionChangePassword,
				       null,
				       null,
				       params[3]);
    if(params[2] && params[2].length > 0) {
	theButton.setAttribute("promptString", params[2]);
    }
};

config.macros.EncryptionDecryptAll = {};
config.macros.EncryptionDecryptAll.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
    var theButton = createTiddlyButton(place,
				       (params[0] && params[0].length > 0) ? params[0] : "Decrypt All", 
				       (params[1] && params[1].length > 0) ? params[1] : "Decrypt All Tiddlers" + ((params[2] && params[2].length > 0) ? " for prompt "+params[2] : " for a given 'prompt string'"), 
				       onClickEncryptionDecryptAll,
				       null,
				       null,
				       params[3]);
    if(params[2] && params[2].length > 0) {
	theButton.setAttribute("promptString", params[2]);
    }
};

config.macros.EncryptionDecryptThis = {};
config.macros.EncryptionDecryptThis.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
    var theButton = createTiddlyButton(place,
				       (params[0] && params[0].length > 0) ? params[0] : "Decrypt", 
				       (params[1] && params[1].length > 0) ? params[1] : "Decrypt this Tiddler", 
				       onClickEncryptionDecryptThis,
				       null,
				       null,
				       params[3]);
    if(params[2] && params[2].length > 0) {
	theButton.setAttribute("theTiddler", params[2]);
    }
};
// toolbar button to decrypt tiddlers.
config.commands.decryptThis = {
  text: "decrypt",
  tooltip: "Decrypt this tiddler",
  isEnabled : function(tiddler) {
	// Only show decrypt button if tiddler is tagged as Decrypt(
	if(tiddler.tags.join().indexOf('Decrypt(') == -1)  {
	    return false;
	} else {
	    return true;
	}
    },	
  handler: function(event, src, title) {
	encryptionGetAndDecryptTiddler(title);
	return false; 
    }
};
// core version 2.4 or above get a 'decrypt' button in the toolbar.
if(config.shadowTiddlers && config.shadowTiddlers.ToolbarCommands  && config.shadowTiddlers.ToolbarCommands.indexOf('decryptThis') == -1) {
    // put our toolbar button in before the edit button.
    // won't work if editTiddler is not the default item (prefixed with plus)
    config.shadowTiddlers.ToolbarCommands.replace(/\+editTiddler/,'decryptThis +editTiddler');
}


// Called by the EncryptionChangePassword macro/button
// Also invoked by the callback for password entry
function onClickEncryptionChangePassword(eventObject) {
    var promptString;
    if(!promptString && this.getAttribute) {
	promptString = this.getAttribute("promptString");
    }
    // I do call this function directly
    if(!promptString && typeof(eventObject) == "string") {
	promptString = eventObject;
    }
    if(!promptString) {
	promptString = prompt("Enter 'prompt string' to change password for:","");
    }
    if(!promptString) {
	return;
    }
    if(! config.encryptionPasswords[promptString]) {
	var changePasswordContext = {changePasswordPromptString: promptString,
				     callbackFunction: MyChangePasswordPromptCallback_TiddlerEncryptionPlugin};
	MyPrompt_TiddlerEncryptionPlugin(promptString,"",changePasswordContext);
	return;
	// Callback function will re-invoke this function
    }

    // Decrypt ALL tiddlers for that prompt
    onClickEncryptionDecryptAll(promptString);
    // Now ditch the cached password, this will force the re-request for the new password, on save.
    displayMessage("Save TiddlyWiki to set new password for '"+promptString+"'");
    config.encryptionPasswords[promptString] = null;
    // mark store as dirty so a save will be requrested.
    store.setDirty(true);
    autoSaveChanges(); 
    return;
};
// Called by the password entry form when the user clicks 'OK' button.
function MyChangePasswordPromptCallback_TiddlerEncryptionPlugin(context) {
    config.encryptionPasswords[context.passwordPrompt] = context.password;
    onClickEncryptionChangePassword(context.changePasswordPromptString);
    return;
}
// Called by the EncryptionDecryptThis macro/button
function onClickEncryptionDecryptThis() {
    var theTiddler = this.getAttribute("theTiddler");
    if(!theTiddler) {
	return;
    }
    encryptionGetAndDecryptTiddler(theTiddler);
    return;
};

function encryptionGetAndDecryptTiddler(title) {
    config.encryptionReEnterPasswords = true;
    try {
	theTiddler = store.getTiddler(title);
	config.encryptionReEnterPasswords = false;
	story.refreshAllTiddlers();
    } catch (e) {
	if(e == "DecryptionFailed") {
	    displayMessage("Decryption failed");
	    return;
	}
    } // catch
    return;
};

// called by the EncryptionDecryptAlll macro/button
// Also called by the callback after the user clicks 'OK' button on the password entry form
function onClickEncryptionDecryptAll(eventObject) {
    var promptString;
    if(!promptString && this.getAttribute) {
	promptString = this.getAttribute("promptString");
    }
    // I do call this function directly
    if(!promptString && typeof(eventObject) == "string") {
	promptString = eventObject;
    }
    if(!promptString) {
	promptString = "";
    }

    // Loop through all tiddlers, looking to see if there are any Decrypt(promptString) tagged tiddlers
    // If there are, check to see if their password has been cached.
    // If not, ask for the first one that is missing, that we find
    // the call back function will store that password then invoke this function again, 
    // which will repeat the whole process. If we find all passwords have been cached
    // then we will finally do the decryptAll functionality, which will then
    // be able to decrypt all the required tiddlers, without prompting.
    // We have to do this whole rigmarole because we are using a 'form' to enter the password
    // rather than the 'prompt()' function - which shows the value of the password.
    var tagToSearchFor="Decrypt("+promptString;
    config.encryptionReEnterPasswords = true; 
    var promptGenerated = false;
    store.forEachTiddler(function(store,tiddler) {
	    // Note, there is no way to stop the forEachTiddler iterations
	    if(!promptGenerated && tiddler && tiddler.tags) {
		for(var ix=0; ix<tiddler.tags.length && !promptGenerated; ix++) {
		    if(tiddler.tags[ix].indexOf(tagToSearchFor) == 0) {
			var tag = tiddler.tags[ix];
			var lastBracket=tag.lastIndexOf(")");
			if(lastBracket >= 0) {
			    // Ok, tagged with Encrypt(passwordPrompt)
			    // extract the passwordPrompt name
			    var passwordPromptString=tag.substring(8,lastBracket);
			    if(!config.encryptionPasswords[passwordPromptString]) {
				// no password cached, prompt and cache it, rather than decryptAll
				// callback from prompting form will resume decryptAll attempt.
				var decryptAllContext = {decryptAllPromptString: promptString,
							 callbackFunction: MyDecryptAllPromptCallback_TiddlerEncryptionPlugin};
				MyPrompt_TiddlerEncryptionPlugin(passwordPromptString,"",decryptAllContext);
				promptGenerated = true;
			    } // if(!config.encryptionPasswords
			} // if(lastBracket
		    } // if(tiddler.tags[ix]..
		} // for
	    } // if
	}); // store.forEachTiddler
    // If we get here, all passwords have been cached.
    if(!promptGenerated) {
	config.encryptionReEnterPasswords = false;
	// Now do the decrypt all functionality
	try {
	    store.forEachTiddler(function(store,tiddler) {
		    // Note, there is no way to stop the forEachTiddler iterations
		    if(tiddler && tiddler.tags) {
			for(var ix=0; ix<tiddler.tags.length; ix++) {
			    if(tiddler.tags[ix].indexOf(tagToSearchFor) == 0) {
				try {
				    CheckTiddlerForDecryption_TiddlerEncryptionPlugin(tiddler);
				} catch (e) {
				    displayMessage("Decryption of '"+tiddler.title+"' failed.");
				    // throw e;
				}
			    } // if(tiddler.tags
			} // for
		    } // if
		}); // store.forEachTiddler
	    displayMessage("All tiddlers" + (promptString != "" ? " for '"+promptString+"'" : "") + " have been decrypted");
	} catch (e) {
	    if(e == "DecryptionFailed") {
		return;
	    }
	} // catch
    }
    return;
};

function MyDecryptAllPromptCallback_TiddlerEncryptionPlugin(context) {
    config.encryptionPasswords[context.passwordPrompt] = context.password;
    // restart the decryptAll process again after the user has entered a password.
    onClickEncryptionDecryptAll(context.decryptAllPromptString);
    return;
}

saveChanges_TiddlerEncryptionPlugin = saveChanges;
saveChanges = function(onlyIfDirty,tiddlers) {
    // Loop through all tiddlers, looking to see if there are any Encrypt(string) tagged tiddlers
    // If there are, check to see if their password has been cached.
    // If not, ask for the first one that is missing, that we find
    // the call back function will store that password then invoke this function again, 
    // which will repeat the whole process. If we find all passwords have been cached
    // then we will finally call the original saveChanges() function, which will then
    // be able to save the tiddlers.
    // We have to do this whole rigmarole because we are using a 'form' to enter the password
    // rather than the 'prompt()' function - which shows the value of the password.
    config.encryptionReEnterPasswords = true; 
    var promptGenerated = false;
    store.forEachTiddler(function(store,tiddler) {
	    if(!promptGenerated && tiddler && tiddler.tags) {
		for(var ix=0; ix<tiddler.tags.length && !promptGenerated; ix++) {
		    if(tiddler.tags[ix].indexOf("Encrypt(") == 0) {
			var tag = tiddler.tags[ix];
			var lastBracket=tag.lastIndexOf(")");
			if(lastBracket >= 0) {
			    // Ok, tagged with Encrypt(passwordPrompt)
			    // extract the passwordPrompt name
			    var passwordPrompt=tag.substring(8,lastBracket);
			    if(!config.encryptionPasswords[passwordPrompt]) {
				// no password cached, prompt and cache it, rather than save
				var saveContext = {onlyIfDirty: onlyIfDirty, 
						   tiddlers: tiddlers, 
				                   callbackFunction: MySavePromptCallback_TiddlerEncryptionPlugin};
				MyPrompt_TiddlerEncryptionPlugin(passwordPrompt,"",saveContext);
				promptGenerated = true;
			    } // if(!config.encryptionPasswords
			} // if(lastBracket
		    } // if(tiddler.tags[ix]..
		} // for
	    } // if
	}); // store.forEachTiddler
    // If we get here, all passwords have been cached.
    if(!promptGenerated) {
	config.encryptionReEnterPasswords = false;
	saveChanges_TiddlerEncryptionPlugin(onlyIfDirty,tiddlers);
    }
    return;
}

function MySavePromptCallback_TiddlerEncryptionPlugin(context) {
    config.encryptionPasswords[context.passwordPrompt] = context.password;
    // validate the password entered by attempting to decrypt all tiddlers
    // with the same encryption prompt string.
    onClickEncryptionDecryptAll(context.passwordPrompt);

    // restart the save process again
    saveChanges(context.onlyIfDirty, context.tiddlers);
    return;
}

store.getSaver().externalizeTiddler_TiddlerEncryptionPlugin = store.getSaver().externalizeTiddler;
store.getSaver().externalizeTiddler = function(store, tiddler) {
    // Ok, got the tiddler, track down the passwordPrompt in the tags.
    // track down the Encrypt(passwordPrompt) tag
    if(tiddler && tiddler.tags) {
	for(var g=0; g<tiddler.tags.length; g++) {
	    var tag = tiddler.tags[g];
	    if(tag.indexOf("Encrypt(") == 0) {
		var lastBracket=tag.lastIndexOf(")");
		if(lastBracket >= 0) {
		    // Ok, tagged with Encrypt(passwordPrompt)
		    // extract the passwordPrompt name
		    var passwordPrompt=tag.substring(8,lastBracket);
		    // Ok, Encrypt this tiddler!
		    var decryptedSHA1 = Crypto.hexSha1Str(tiddler.text);
		    var password =  GetAndSetPasswordForPrompt_TiddlerEncryptionPlugin(passwordPrompt);
		    if(password) {
			var encryptedText = TEAencrypt(tiddler.text, password);
			encryptedText = StringToHext_TiddlerEncryptionPlugin(encryptedText);
			tiddler.text = "Encrypted("+decryptedSHA1+")\n"+encryptedText;
			// Replace the Tag with the Decrypt() tag
			tiddler.tags[g]="Decrypt("+passwordPrompt+")";
			// let the store know it's dirty
			store.setDirty(tiddler.title, true);
			// prevent searches on encrypted tiddlers, still nice to search on title though.
			if(config.options.chkExcludeEncryptedFromSearch == true) {
			    tiddler.tags.push("excludeSearch");
			}
			// prevent lists of encrypted tiddlers
			if(config.options.chkExcludeEncryptedFromLists == true) {
			    tiddler.tags.push("excludeLists");
			}
		    } else {
			// do not encrypt - no password entered
		    }
		    break;
		} // if (lastBracket...
	    } // if(tag.indexOf(...
	} // for(var g=0;...
    } // if(tiddler.tags...
    
    // Then, finally, do the save by calling the function we override.

    return store.getSaver().externalizeTiddler_TiddlerEncryptionPlugin(store, tiddler);
};

function CheckTiddlerForDecryption_TiddlerEncryptionPlugin(tiddler) {
    if(tiddler && tiddler.tags) {
	for(var g=0; g<tiddler.tags.length; g++) {
	    var tag = tiddler.tags[g];
	    if(tag.indexOf("Decrypt(") == 0) {
		var lastBracket=tag.lastIndexOf(")");
		if(lastBracket >= 0) {
		    if(tiddler.text.substr(0,10) == "Encrypted(") {
			var closingSHA1Bracket = tiddler.text.indexOf(")");
			var decryptedSHA1 = tiddler.text.substring(10, closingSHA1Bracket);
			// Ok, tagged with Decrypt(passwordPrompt)
			// extract the passwordPrompt name
			var passwordPrompt=tag.substring(8,lastBracket);
			// Ok, Decrypt this tiddler!
			var decryptedText = tiddler.text.substr(closingSHA1Bracket+2);
			decryptedText = HexToString_TiddlerEncryptionPlugin(decryptedText);
                        // prompt("Decryption request for Tiddler '"+tiddler.title+"'");
			var password = GetAndSetPasswordForPromptToDecrypt_TiddlerEncryptionPlugin(passwordPrompt);
			if(password) {
			    decryptedText = TEAdecrypt(decryptedText, password );
			    var thisDecryptedSHA1 = Crypto.hexSha1Str(decryptedText);
			    if(decryptedSHA1 == thisDecryptedSHA1) {
				tiddler.text = decryptedText;
				// Replace the Tag with the Encrypt() tag
				tiddler.tags[g]="Encrypt("+passwordPrompt+")";
				if(tiddler.tags[tiddler.tags.length-1] == 'excludeLists') {
				    // Remove exclude lists only if it's the last entry
				    // as it's automatically put there by encryption
				    tiddler.tags.length--;
				}
				if(tiddler.tags[tiddler.tags.length-1] == 'excludeSearch') {
				    // Remove exclude search only if it's the last entry
				    // as it's automatically put there by encryption
				    tiddler.tags.length--;
				}
			    } else {
				// Did not decrypt, discard the password from the cache
				config.encryptionPasswords[passwordPrompt] = null;
				config.encryptionReEnterPasswords = false;
				throw "DecryptionFailed";
			    }
			} else {
			    // no password supplied, dont bother trying to decrypt
			    config.encryptionReEnterPasswords = false;
			    throw "DecryptionFailed";
			}
		    } else {
			// Tagged as encrypted but not expected format, just leave it unchanged
		    }
		    break; // out of for loop
		} // if (lastBracket...
	    } // if(tag.indexOf(...
	} // for(var g=0;...
    } // if (tiddler && tags)
    return tiddler;
};

store.getTiddler_TiddlerEncryptionPlugin = store.getTiddler;
store.getTiddler = function(title) {
    var tiddler = store.getTiddler_TiddlerEncryptionPlugin(title);
    if(tiddler) { // shadow tiddlers are not expected to be encrypted.
	try {
	    return CheckTiddlerForDecryption_TiddlerEncryptionPlugin(tiddler);
	} catch (e) {
	    if (config.options.chkShowDecryptButtonInContent == true) {
		if(e == "DecryptionFailed") {
		    var tiddler = store.getTiddler("DecryptionFailed");
		    if(!tiddler) {
			tiddler = new Tiddler();
			tiddler.set(title,
				    "<<EncryptionDecryptThis \"Decrypt\" \"Decrypt this tiddler\" \""+title+"\">>",
				    config.views.wikified.shadowModifier,
				    version.date,[],version.date);
		    } 
		    return tiddler;
		} // if(e)
	    }
	    return(tiddler);
	} // catch
    } // if(tiddler) {
    return null;
};

store.getTiddlerText_TiddlerEncryptionPlugin = store.getTiddlerText;
store.getTiddlerText = function(title,defaultText) {
    // Simply retrieve the tiddler, normally, if it requires decryption, it will be decrypted
    var decryptedTiddler = store.getTiddler(title);
    if(decryptedTiddler) {
	return decryptedTiddler.text;
    }
    //Ok, rather than duplicate all the core code, the above code should fail if we reach here
    // let the core code take over.
    return  store.getTiddlerText_TiddlerEncryptionPlugin(title,defaultText);
};

// Given a prompt, search our cache to see if we have already entered the password.
// Can return null if the user enters nothing.
function MyPrompt_TiddlerEncryptionPlugin(promptString,defaultValue,context) {
    if(!context) {
	context = {};
    }
    context.passwordPrompt = promptString;
    PasswordPrompt.prompt(MyPromptCallback_TiddlerEncryptionPlugin, context);
    return;
}

function MyPromptCallback_TiddlerEncryptionPlugin(context) {
    if(context.callbackFunction) {
	context.callbackFunction(context);
    } else {
	config.encryptionPasswords[context.passwordPrompt] = context.password;
	story.refreshAllTiddlers(true);
    }
    return;
}

function GetAndSetPasswordForPrompt_TiddlerEncryptionPlugin(promptString) {
    if(!config.encryptionPasswords[promptString]) {
	config.encryptionPasswords[promptString] = MyPrompt_TiddlerEncryptionPlugin(promptString, "");
    }
    return config.encryptionPasswords[promptString]; // may be null, prompt can be cancelled.
}

function GetAndSetPasswordForPromptToDecrypt_TiddlerEncryptionPlugin(promptString) {
    if(config.encryptionReEnterPasswords) {
	return GetAndSetPasswordForPrompt_TiddlerEncryptionPlugin(promptString);
    } else {
	return config.encryptionPasswords[promptString];
    }
}

// Make the encrypted tiddlies look a little more presentable.
function StringToHext_TiddlerEncryptionPlugin(theString) {
    var theResult = "";
    for(var i=0; i<theString.length; i++) {
	var theHex = theString.charCodeAt(i).toString(16);
	if(theHex.length<2) {
	    theResult += "0"+theHex;
	} else {
	    theResult += theHex;
	}
	if(i && i % 32 == 0)
	    theResult += "\n";
    }
    return theResult;
}

function HexToString_TiddlerEncryptionPlugin(theString) {
    var theResult = "";
    for(var i=0; i<theString.length; i+=2) {
	if(theString.charAt(i) == "\n") {
	    i--;	// cause us to skip over the newline and resume
	    continue;
	}
	theResult += String.fromCharCode(parseInt(theString.substr(i, 2),16));
    }
    return theResult;
}
//
// Heavily leveraged from http://trac.tiddlywiki.org/browser/Trunk/contributors/SaqImtiaz/verticals/Hesperian/PasswordPromptPlugin.js  Revision 5635
//
PasswordPrompt ={
  prompt : function(callback,context){
	if (!context) {
	    context = {};
	}
	var box = createTiddlyElement(document.getElementById("contentWrapper"),'div','passwordPromptBox');
	box.innerHTML = store.getTiddlerText('PasswordPromptTemplate');
	box.style.position = 'absolute';
	this.center(box);
	document.getElementById('promptDisplayField').value = context.passwordPrompt;
	var passwordInputField = document.getElementById('passwordInputField');
	passwordInputField.onkeyup = function(ev) {
	    var e = ev || window.event;
	    if(e.keyCode == 10 || e.keyCode == 13) { // Enter
		PasswordPrompt.submit(callback, context);
	    }
	};
	passwordInputField.focus();
	document.getElementById('passwordPromptSubmitBtn').onclick = function(){PasswordPrompt.submit(callback,context);};
	document.getElementById('passwordPromptCancelBtn').onclick = function(){PasswordPrompt.cancel(callback,context);};
    },     
 	       
  center : function(el){
	var size = this.getsize(el);
	el.style.left = (Math.round(findWindowWidth()/2) - (size.width /2) + findScrollX())+'px';
	el.style.top = (Math.round(findWindowHeight()/2) - (size.height /2) + findScrollY())+'px';
    },
 	       
  getsize : function (el){
	var x = {};
	x.width = el.offsetWidth || el.style.pixelWidth;
	x.height = el.offsetHeight || el.style.pixelHeight;
	return x;
    },
 	       
  submit : function(cb,context){
	context.passwordPrompt = document.getElementById('promptDisplayField').value;
	context.password = document.getElementById('passwordInputField').value;
	var box = document.getElementById('passwordPromptBox');
	box.parentNode.removeChild(box);
	cb(context);
	return false;
    },

  cancel : function(cb,context){
	var box = document.getElementById('passwordPromptBox');
	box.parentNode.removeChild(box);
	return false;
    },
 	       
  setStyles : function(){
	setStylesheet(
	    "#passwordPromptBox dd.submit {margin-left:0; font-weight: bold; margin-top:1em;}\n"+
	    "#passwordPromptBox dd.submit .button {padding:0.5em 1em; border:1px solid #ccc;}\n"+
	    "#passwordPromptBox dt.heading {margin-bottom:0.5em; font-size:1.2em;}\n"+
	    "#passwordPromptBox {border:1px solid #ccc;background-color: #eee;padding:1em 2em;}",'passwordPromptStyles');
    },
 	       
  template : '<form action="" onsubmit="return false;" id="passwordPromptForm">\n'+
  '    <dl>\n'+
  '        <dt class="heading">Please enter the password:</dt>\n'+
  '        <dt>Prompt:</dt>\n'+
  '        <dd><input type="text" readonly id="promptDisplayField" class="display"/></dd>\n'+
  '        <dt>Password:</dt>\n'+
  '        <dd><input type="password" tabindex="1" class="input" id="passwordInputField"/></dd>\n'+
  '        <dd class="submit">\n'+
  '            <a tabindex="2" href="javascript:;" class="button" id="passwordPromptSubmitBtn">OK</a>\n'+
  '            <a tabindex="3" href="javascript:;" class="button" id="passwordPromptCancelBtn">Cancel</a>\n'+
  '        </dd>\n'+
  '    </dl>\n'+
  '</form>',
 	                         
  init : function(){
	config.shadowTiddlers.PasswordPromptTemplate = this.template;
	this.setStyles();
    }
};
 	
PasswordPrompt.init();

// http://www.movable-type.co.uk/scripts/tea-block.html
//
// TEAencrypt: Use Corrected Block TEA to encrypt plaintext using password
//             (note plaintext & password must be strings not string objects)
//
// Return encrypted text as string
//
function TEAencrypt(plaintext, password)
{
    if (plaintext.length == 0) return('');  // nothing to encrypt
    // 'escape' plaintext so chars outside ISO-8859-1 work in single-byte packing, but keep
    // spaces as spaces (not '%20') so encrypted text doesn't grow too long (quick & dirty)
    var asciitext = escape(plaintext).replace(/%20/g,' ');
    var v = strToLongs(asciitext);  // convert string to array of longs
    if (v.length <= 1) v[1] = 0;  // algorithm doesn't work for n<2 so fudge by adding a null
    var k = strToLongs(password.slice(0,16));  // simply convert first 16 chars of password as key
    var n = v.length;

    var z = v[n-1], y = v[0], delta = 0x9E3779B9;
    var mx, e, q = Math.floor(6 + 52/n), sum = 0;

    while (q-- > 0) {  // 6 + 52/n operations gives between 6 & 32 mixes on each word
        sum += delta;
        e = sum>>>2 & 3;
        for (var p = 0; p < n; p++) {
            y = v[(p+1)%n];
            mx = (z>>>5 ^ y<<2) + (y>>>3 ^ z<<4) ^ (sum^y) + (k[p&3 ^ e] ^ z);
            z = v[p] += mx;
        }
    }

    var ciphertext = longsToStr(v);

    return escCtrlCh(ciphertext);
}

//
// TEAdecrypt: Use Corrected Block TEA to decrypt ciphertext using password
//
function TEAdecrypt(ciphertext, password)
{
    if (ciphertext.length == 0) return('');
    var v = strToLongs(unescCtrlCh(ciphertext));
    var k = strToLongs(password.slice(0,16)); 
    var n = v.length;

    var z = v[n-1], y = v[0], delta = 0x9E3779B9;
    var mx, e, q = Math.floor(6 + 52/n), sum = q*delta;

    while (sum != 0) {
        e = sum>>>2 & 3;
        for (var p = n-1; p >= 0; p--) {
            z = v[p>0 ? p-1 : n-1];
            mx = (z>>>5 ^ y<<2) + (y>>>3 ^ z<<4) ^ (sum^y) + (k[p&3 ^ e] ^ z);
            y = v[p] -= mx;
        }
        sum -= delta;
    }

    var plaintext = longsToStr(v);

    // strip trailing null chars resulting from filling 4-char blocks:
    plaintext = plaintext.replace(/\0+$/,'');

    return unescape(plaintext);
}


// supporting functions

function strToLongs(s) {  // convert string to array of longs, each containing 4 chars
    // note chars must be within ISO-8859-1 (with Unicode code-point < 256) to fit 4/long
    var l = new Array(Math.ceil(s.length/4));
    for (var i=0; i<l.length; i++) {
        // note little-endian encoding - endianness is irrelevant as long as 
        // it is the same in longsToStr() 
        l[i] = s.charCodeAt(i*4) + (s.charCodeAt(i*4+1)<<8) + 
               (s.charCodeAt(i*4+2)<<16) + (s.charCodeAt(i*4+3)<<24);
    }
    return l;  // note running off the end of the string generates nulls since 
}              // bitwise operators treat NaN as 0

function longsToStr(l) {  // convert array of longs back to string
    var a = new Array(l.length);
    for (var i=0; i<l.length; i++) {
        a[i] = String.fromCharCode(l[i] & 0xFF, l[i]>>>8 & 0xFF, 
                                   l[i]>>>16 & 0xFF, l[i]>>>24 & 0xFF);
    }
    return a.join('');  // use Array.join() rather than repeated string appends for efficiency
}

function escCtrlCh(str) {  // escape control chars etc which might cause problems with encrypted texts
    return str.replace(/[\0\t\n\v\f\r\xa0'"!]/g, function(c) { return '!' + c.charCodeAt(0) + '!'; });
}

function unescCtrlCh(str) {  // unescape potentially problematic nulls and control characters
    return str.replace(/!\d\d?\d?!/g, function(c) { return String.fromCharCode(c.slice(1,-1)); });
}

//}}}
<<slider TabTimeline TabTimeline 'Show timeline'>>
|~ViewToolbar|closeTiddler closeOthers +editTiddler > fields syncing permalink references jump|
|~EditToolbar|+saveTiddler -cancelTiddler deleteTiddler|
| tiddlyspot password:|<<option pasUploadPassword>>|
| site management:|<<upload http://iwalton.tiddlyspot.com/store.cgi index.html . .  iwalton>>//(requires tiddlyspot password)//<br>[[control panel|http://iwalton.tiddlyspot.com/controlpanel]], [[download (go offline)|http://iwalton.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]]|
tiddlyspot password:
<<option pasUploadPassword>>
/***
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 = 'iwalton';

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

});
//}}}
<<upload http://iwalton.tiddlyspot.com/store.cgi index.html . .  iwalton>>
<html><a href='http://iwalton.tiddlyspot.com/download' class='button'>download</a></html>
first.name=first.name
last.name=last.name
email=email
other.email=other.email
business.email=business.email
webpage=webpage
phone=phone
mobile=mobile
address=address
city=city
state=state
postal=postal
country=country
company=company
job.title=job.title
business.phone=business.phone
business.mobile=business.mobile
business.fax=business.fax
business.address=business.address
business.city=business.city
business.state=business.state
business.postal=business.postal
business.country=business.country
business.webpage=business.webpage
ssn=ssn
birthday=birthday
anniversary=anniversary
children=children
notes=notes

first.name=Name
last.name=<skip>
email=E-mail Address
other.email=E-mail 2 Address
business.email=E-mail 3 Address
webpage=Web Page
phone=Home Phone
mobile=Mobile Phone
address=Home Street
city=Home City
state=Home State
postal=Home Postal Code
country=Home Country
company=Company
job.title=Job Title
business.phone=Business Phone
business.mobile=Business Phone 2
business.fax=Businss Fax
business.address=Business Street
business.city=Business City
business.state=Business State
business.postal=Business Postal Code
business.country=Business Country
business.webpage=<skip>
ssn=<skip>
birthday=Birthday
anniversary=Anniversary
children=Children
notes=Notes
unmapped=Title,Middle Name,Suffix,Department,Business Street 2,Business Street 3,Home Street 2,Home Street 3,Other Street,Other Street 2,Other Street 3,Other City,Other State,Other Postal Code,Other Country,Assistant's Phone,Callback,Car Phone,Company Main Phone,Home Fax,Home Phone 2,ISDN,Other Fax,Other Phone,Pager,Primary Phone,Radio Phone,TTY/TDD Phone,Telex,Account,Assistant's Name,Billing Information,Categories,E-mail Display Name,E-mail 2 Display Name,E-mail 3 Display Name,Gender,Government ID Number,Hobby,Initials,Keywords,Language,Location,Mileage,Office Location,Organizational ID Number,PO Box,Private,Profession,Referred By,Spouse,User 1,User 2,User 3,User 4
first.name,last.name,job.title,webpage,phone,email,company
Princess,Leia,"Leader, Rebels",http://starwars.wikia.com/wiki/Leia_Organa,,leia@alderaan.com
Darth,Vader,"Sith Lord",http://starwars.wikia.com/wiki/Darth_Vader,,darth@deathstar.com
Luke,Skywalker,"Jedi Master",http://starwars.wikia.com/wiki/Luke_Skywalker,,luke@tatooine.com
Luke,Skywalker,"Moisture Farmer",http://starwars.wikia.com/wiki/Luke_Skywalker,,luke@tatooine.com
,,,http://starwars.wikia.com/wiki/501st,,501st@deathstar.com,501st Legion
first.name=First Name
last.name=Last Name
email=E-mail Address
other.email=E-mail 2 Address
business.email=E-mail 3 Address
webpage=Web Page
phone=Home Phone
mobile=Mobile Phone
address=Home Street
city=Home City
state=Home State
postal=Home Postal Code
country=Home Country
company=Company
job.title=Job Title
business.phone=Business Phone
business.mobile=Business Phone 2
business.fax=Businss Fax
business.address=Business Street
business.city=Business City
business.state=Business State
business.postal=Business Postal Code
business.country=Business Country
business.webpage=<skip>
ssn=<skip>
birthday=Birthday
anniversary=Anniversary
children=Children
notes=Notes
unmapped=Title,Middle Name,Suffix,Department,Callback,Car Phone,Company Main Phone,Home Fax,Home Phone 2,ISDN,Other Fax,Other Phone,Pager,Primary Phone,Radio Phone,TTY/TDD Phone,Telex,Account,Assistant's Name,Billing Information,Business Address PO Box,Categories,Company Yomi,Directory Server,E-mail Type,E-mail Display Name,E-mail 2 Type,E-mail 2 Display Name,E-mail 3 Type,E-mail 3 Display Name,Gender,Given Yomi,Government ID Number,Hobby,Home Address PO Box,Initials,Internet Free Busy,Keywords,Language,Location,Manager's Name,Mileage,Office Location,Organizational ID Number,Other Address PO Box,Priority,Private,Profession,Referred By,Sensitivity,Spouse,Surname Yomi,User 1,User 2
first.name=First Name
last.name=Last Name
email=E-mail Address
other.email=E-mail 2 Address
business.email=E-mail 3 Address
webpage=Web Page
phone=Home Phone
mobile=Mobile Phone
address=Home Street
city=Home City
state=Home State
postal=Home Postal Code
country=Home Country
company=Company
job.title=Job Title
business.phone=Business Phone
business.mobile=Business Phone 2
business.fax=Businss Fax
business.address=Business Street
business.city=Business City
business.state=Business State
business.postal=Business Postal Code
business.country=Business Country
business.webpage=<skip>
ssn=<skip>
birthday=Birthday
anniversary=Anniversary
children=Children
notes=Notes
unmapped=Title,Middle Name,Suffix,Department,Business Street 2,Business Street 3,Home Street 2,Home Street 3,Other Street,Other Street 2,Other Street 3,Other City,Other State,Other Postal Code,Other Country,Assistant's Phone,Callback,Car Phone,Company Main Phone,Home Fax,Home Phone 2,ISDN,Other Fax,Other Phone,Pager,Primary Phone,Radio Phone,TTY/TDD Phone,Telex,Account,Assistant's Name,Billing Information,Categories,E-mail Display Name,E-mail 2 Display Name,E-mail 3 Display Name,Gender,Government ID Number,Hobby,Initials,Keywords,Language,Location,Mileage,Office Location,Organizational ID Number,PO Box,Private,Profession,Referred By,Spouse,User 1,User 2,User 3,User 4
/***
| Name|TwabPlugin|
| Author|Vincent ~DiBartolo ([[vadibart@gmail.com|mailto:vadibart@gmail.com]])|
| Version|2.2|
| Date|12/08/2008|
| Source|http://www.tiddly-twab.com/#TwabPlugin|
| License|BSD License - http://www.tiddly-twab.com/#TheBSDLicense|
| Requires|~TW2.x, [[DataTiddlerPlugin]], [[FormTiddlerPlugin]], [[InlineJavascriptPlugin]], [[PartTiddlerPlugin]], and any Tiddlers with the [[twab]] tag in the source file |
!Description
Elegant system for keeping your Address Book inside a TiddlyWiki document.  Supports import and export of contacts via CSV data.  Built-in support for Google, Yahoo, MSN, and Outlook CSV formats.  Supports customized formats for those not built-in.

!History
* 08-Dec-08, version 2.2 - use company name if neither first name or last name exists on import, allow for multiple contacts with same title
* 21-Jul-08, version 2.1 - use contact's first and last name in email link if it's present (Thanks to Lyall)
* 20-Feb-08, version 2.0 - import and export Google, Yahoo, MSN, Outlook, or custom CSV formats
* 29-Jun-07, version 1.1 - adding support for mailto:, http:, and map directions
* 20-Jun-07, version 1.0 - preparing for release, changed name to twab
* 19-Jun-07, version 0.4 - modified how new contacts are added
* 21-Nov-06, version 0.3 - changed name from ContactParserMacro to AddressBookMacro
* 10-Oct-06, version 0.2 - converted from regex parsing for title of contact Tiddler to JSON
* 09-Oct-06, version 0.1 - created file

!Example of Adding New Contact
Place the following code in any Tiddler:
{{{
<<twab>>
}}}
Which will result in: <<twab>>
You can add extra parameters to change the button's name as in:
{{{
<<twab press this button>>
}}}
Which will result in: <<twab press this button>>
See [[About:twab:Overview]] for more information.

!Example of Import
Place the following code in any Tiddler:
{{{
<<twab Import AddressBook>>
}}}
Which will result in: <<twab Import AddressBook>>
See [[About:twab:Import]] for more information.

!Example of Export
Place the following code in any Tiddler:
{{{
<<twab Export AddressBook>>
}}}
Which will result in: <<twab Export AddressBook>>
See [[About:twab:Export]] for more information.

! Generate Test Data for Import
Place the following code in any Tiddler:
{{{
<<twab ImportTest>>
}}}
Which will result in: <<twab ImportTest>>
See [[About:twab:Import]] for more information.

!Code
***/
//{{{


version.extensions.TwabMacro = { 
   major: 2, 
   minor: 0, 
   revision: 0, 
   date: new Date(2008,02,16), 
   source: "http://www.tiddly-twab.com"
};


config.macros.twab = {};
config.macros.twab.newButtonText    = "new contact";
config.macros.twab.importButtonText = "import contacts";
config.macros.twab.exportButtonText = "export contacts";
config.macros.twab.preClean         = true;
config.macros.twab.importTiddler    = "TwabImport";
config.macros.twab.exportTiddler    = "TwabExport";
config.macros.twab.importTags       = "AddressBook";
config.macros.twab.exportTags       = "AddressBook";
config.macros.twab.fnameField       = "first.name";
config.macros.twab.lnameField       = "last.name";
config.macros.twab.companyNameField = "company";
config.macros.twab.mapTagPrefix     = "format:";
config.macros.twab.skipFlag         = "<skip>";
config.macros.twab.unmappedFlag     = "unmapped";


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

   if( (params.length == 1) && (params[0] == "ImportTest") ){

      //if they want to create an import test button
      createTiddlyButton(place, "Populate " + config.macros.twab.importTiddler, "", this.testImport);
      return;

   } else if( (params.length >= 1) && (params[0] == "Import") ){

      //any other params are interpreted as tags to be placed on imported tiddlers
      if( params.length >= 2 ){
         config.macros.twab.importTags = "";
         for( var i=1; i<params.length; i++){
            config.macros.twab.importTags += params[i] + " ";
         }//for

      }//if

      //if they want to import data
      createTiddlyButton(place, config.macros.twab.importButtonText, "", this.importContacts);

   } else if( (params.length >= 1) && (params[0] == "Export") ){

      //the first tag of the remainder is the one to use for exports
      if( params.length >= 2 ){
         config.macros.twab.exportTags = params[1];
      }//if

      //if they want to export data
      createTiddlyButton(place, config.macros.twab.exportButtonText, "", this.exportContacts);

   } else {
   
      //assume want to create a new contact - any extra params are button name
      var buttonText = "";
      for( var i=0; i<params.length; i++){
         buttonText += " " + params[i] + " ";
      }//for

      createTiddlyButton(place, ((buttonText == "") ? config.macros.twab.newButtonText : buttonText), "", this.newContact);

   }//if-else ifs

}//function handler



//from: http://www.nicknettleton.com/zine/javascript/trim-a-string-in-javascript
config.macros.twab.trim = function(word) { return word.replace(/^\s+|\s+$/g, ''); }



//proxy that retrieves some data and sets null to empty string
config.macros.twab.getData = function(tiddlerName, fieldName){

   var data = DataTiddler.getData(tiddlerName, fieldName);

   if( data == null ){
      data = "";
   }//if

   return data;

}//function getData



config.macros.twab.testImport = function(e){

   var title = config.macros.twab.importTiddler;

   var text = "first.name,last.name,job.title,webpage,phone,email\nPrincess,Leia,\"Leader, Rebels\",http://starwars.wikia.com/wiki/Leia_Organa,,leia@alderaan.com\nDarth,Vader,\"Sith Lord\",http://starwars.wikia.com/wiki/Darth_Vader,555-1212,darth@deathstar.com\nLuke,Skywalker,Jedi Master,http://starwars.wikia.com/wiki/Luke_Skywalker,,luke@tatooine.com";

   store.saveTiddler(title, title, text, config.options.txtUserName);
   story.displayTiddler(null, title, DEFAULT_VIEW_TEMPLATE);
   return true;

}//function import



config.macros.twab.newContact = function(e){

   var title = prompt("Please enter contact's name", "");
   if( (title == null) || (title == "") ){
      return;
   }//if

   store.saveTiddler(title, title, "<<tiddler ContactsFormTemplate>><data>{}</data>", config.options.txtUserName, new Date(), config.macros.twab.importTags);

   story.displayTiddler(null, title, DEFAULT_VIEW_TEMPLATE);

}//function new



config.macros.twab.importContacts = function(e){
   config.macros.twab.deleteAll();
   config.macros.twab.parseAll( config.macros.twab.getImportedCSVText() );
   alert("Contacts successfully imported.");
   return true;
}//function importContacts



config.macros.twab.exportContacts = function(e){
   var title   = config.macros.twab.exportTiddler;
   var mapping = config.macros.twab.getMapping(title, true);
   store.saveTiddler(title, title, config.macros.twab.getExportedCSVText(mapping), config.options.txtUserName );
   story.displayTiddler(null, title, DEFAULT_VIEW_TEMPLATE);
   return true;
}//function exportContacts



config.macros.twab.deleteAll = function(){

   if( !config.macros.twab.preClean ){
      return;
   }//if

   //only remove tiddlers tagged with only first tag if contacts each get more than one
   var tags = config.macros.twab.importTags.split(" ");
   var tag  = tags[0];
   if( !confirm("Are you sure you want to clear existing Tiddlers tagged \"" + tag + "\"?") ){
      return;
   }//if

   var contacts = store.getTaggedTiddlers(tag);
   for( var i=0; i<contacts.length; i++ ){
      store.removeTiddler( contacts[i].title );
   }//for

}//function deleteAll



config.macros.twab.getImportedCSVText = function(){
   return store.getTiddler(config.macros.twab.importTiddler).text;
}//function getImportedCSVText



config.macros.twab.getExportedCSVText = function(mapping){

   var returnStr = "";

   //get the mapped header columns
   for( var i=0; i<mapping.length; i++ ){

      if( mapping[i] && (mapping[i] != config.macros.twab.unmappedFlag) ){
         var twabCol = mapping[i];
         var mapCol  = mapping[twabCol];
         returnStr  += '"' + mapCol + '",';
      }//if

   }//for

   //get the unmapped header columns
   var unmappedStr = mapping[config.macros.twab.unmappedFlag];
   if( unmappedStr ){

      var unmappedArr = unmappedStr.split(",");
      for( var i=0; i<unmappedArr.length; i++ ){
         returnStr += '"' + unmappedArr[i] + '",';
      }//for

      //strip off the last ","
      returnStr = returnStr.substring(0, returnStr.length-1);

   }//if

   returnStr += "\n";

   //get all contacts
   var tags     = config.macros.twab.exportTags.split(" ");
   var contacts = store.getTaggedTiddlers( tags[0] );
   for( var i=0; i<contacts.length; i++ ){
      returnStr += config.macros.twab.exportContact(contacts[i], mapping) + "\n";
   }//for

   return returnStr;

}//function getExportedCSVText



config.macros.twab.parseAll = function (contactStr){

   var rows = contactStr.split("\n");
   if( rows.length < 2 ){
      alert("Two or more rows must be present to parse contacts.");
      return;
   }//if

   var header = config.macros.twab.parseHeader(rows[0]);
   if( header.length == 0 ){
      return;
   }//if

   var contacts = new Array();
   for( i=1; i<rows.length; i++){
      contacts[ i-1 ] = config.macros.twab.parseContact(header, rows[i]);
   }//for

   //uncomment this to get contact-by-contact alerts
   //config.macros.twab.debugAll(contacts);
   config.macros.twab.addAll(contacts);

}//function parseAll



config.macros.twab.parseHeader = function(row){

   var mappedHeader = new Array();

   //get the raw data
   var unmappedHeader = config.macros.twab.parseCSV(row);

   //get the appropriate mapping
   var mapping = config.macros.twab.getMapping(config.macros.twab.importTiddler, false);

   //now convert the unmapped header to the mapped header
   for( var i=0; i<unmappedHeader.length; i++ ){

      var colName = unmappedHeader[i].replace(/ /g, ".").toLowerCase();
      if( mapping[colName] ){
         mappedHeader[i] = mapping[colName];
      } else {
         mappedHeader[i] = config.macros.twab.skipFlag;
      }//if

      //uncomment this to get field-by-field alerts from the header
      //alert("Header field " + i + " is '" + mappedHeader[i] + "'");

   }//for

   return mappedHeader;

}//function parseHeader



config.macros.twab.getMapping = function(tiddlerName, isExport){

   var mapTiddlerName = "";
   if( store.getTiddler(tiddlerName) ){

      //see if they've declared a mapping (preset or custom) by looking
      //at a tag on the tiddler passed in
      var tagStr = ""+store.getTiddler(tiddlerName).tags; 
      var tags   = tagStr.split(',');
   
      for( var i=0; i<tags.length; i++ ){
   
         if( tags[i].indexOf( config.macros.twab.mapTagPrefix ) == 0 ){
            mapTiddlerName = tags[i].replace(config.macros.twab.mapTagPrefix, "");
         }//if
   
      }//for
         
   }//if

   //parse the mapping Tiddler
   var mapTiddler = config.macros.twab.getMappingTiddler(mapTiddlerName);
   if( !mapTiddler ){
      alert("Import/Export Format Tiddler " + mapTiddler + " does not exist.  Can't proceed.");
      return new Array();
   }//if

   var mapText = ""+store.getTiddler(mapTiddler).text;
   var mapArr  = mapText.split("\n");

   var mapping = new Array();
   for( var i=0; i<mapArr.length; i++ ){

      var rule = mapArr[i].split("=");
      if( !rule || (rule.length < 2) ){
         continue;
      }//if

      //uncomment this to see what mapping is being applied to your import file
      //alert("Twab column '" + twabCol + "' is mapped to input tiddler column '" + mapCol + "'");

      if( isExport ){

         var twabCol = config.macros.twab.trim( rule[0] );
         var mapCol  = config.macros.twab.trim( rule[1] );
         if( mapCol == config.macros.twab.skipFlag ){
            continue;
         }//inner if

         mapping[twabCol] = mapCol;
         mapping[i]       = twabCol;

      } else {

         var twabCol = config.macros.twab.trim( rule[0].replace(/ /g, ".").toLowerCase() );
         var mapCol  = config.macros.twab.trim( rule[1].replace(/ /g, ".").toLowerCase() );
         mapping[mapCol] = twabCol;
         mapping[i]      = mapCol;

      }//outer if-else

   }//for 

   return mapping;

}//function getMapping



config.macros.twab.getMappingTiddler = function(name){

   var tiddlerName = "TwabDefaultFieldMap";

   if( name == "google" ){
      tiddlerName = "TwabGoogleFieldMap";

   } else if( name == "yahoo" ){
      tiddlerName = "TwabYahooFieldMap";

   } else if( name == "msn" ){
      tiddlerName = "TwabMSNFieldMap";

   } else if( name == "outlook" ){
      tiddlerName = "TwabOutlookFieldMap";

   } else {

      //see if a Tiddler by this name exists, if not use the default
      if( (name != "default") && store.getTiddler(name) ){
         tiddlerName = name;
      } else {
         tiddlerName = "TwabDefaultFieldMap";
      }//inner if-else

   }//outer if-else ifs

   return tiddlerName;

}//function getMappingTiddler



config.macros.twab.parseCSV = function(row){

   var scrubbed = new Array();
   var fields   = row.split(",");
   for( var i=0; i<fields.length; i++){

      //if starts with quote but doesn't end with a quote, likely had a comma in the middle
      if( (fields[i].charAt(0) == '"') && (fields[i].charAt( fields[i].length-1) != '"') ){

         //Hotmail bug: last contact doesn't have ending double-quote
         if( i == (fields.length-1) ){
            scrubbed[ scrubbed.length ] = fields[i].replace(/"/g, "");
            continue;
         }//if

         var quoted = fields[i++];
         if( !fields[i] ){
            continue;
         }//if

         while( fields[i].charAt( fields[i].length-1 ) != '"' ){
            quoted += "," + fields[i++];
         }//while

         quoted += "," + fields[i];
         scrubbed[ scrubbed.length ] = quoted.replace(/"/g, "");

      } else {
         scrubbed[ scrubbed.length ] = fields[i].replace(/"/g, "");

      }//if-else

   }//for

   return scrubbed;

}//function parseCSV



config.macros.twab.parseContact = function (header, row){

   var returnStr = "";
   var fields    = config.macros.twab.parseCSV(row);
   for( var i=0; i<fields.length; i++ ){

      if( header[i] == config.macros.twab.skipFlag ){
         continue;

      } else if( fields[i] ){
         returnStr += "\"" + header[i] + "\":\"" + fields[i] + "\",";

      }//if-else

   }//for

   return returnStr.substr(0, returnStr.length-1);

}//function parseContact



//export a particular contact to a particular mapping
config.macros.twab.exportContact = function(contactTiddler, mapping){

   var returnStr = "";
   var text      = contactTiddler.text;

   //have to strip out FormTiddler stuff
   text = text.replace(/<<tiddler ContactsFormTemplate>>/g, "");
   text = text.replace(/<data>/g, "");
   text = text.replace(/<\/data>/g, "");
   text = text.replace(/\n/g, "");

   //use JSON format to our advantage
   var contact = eval( "("+text+")" ); 

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

      var twabCol = mapping[i];
      if( !(twabCol) || 
          !(mapping[twabCol]) || 
          (twabCol == config.macros.twab.unmappedFlag) || 
          (mapping[twabCol] == config.macros.twab.skipFlag)){
         continue;
      }//if

      //Google hack - on export, "Name" should be "first.name" and "last.name" together.
      //Know this because "first.name" is mapped to "Name" and "last.name" is not mapped
      if( (twabCol == config.macros.twab.fnameField) && !mapping[config.macros.twab.lnameField] ){
         returnStr += '"' + contact[config.macros.twab.fnameField] + " " + contact[config.macros.twab.lnameField] + '",'
      } else if( contact[twabCol] ){
         returnStr += '"' + contact[twabCol] + '",';
      } else {
         returnStr += ",";
      }//if-else

   }//for

   //get the unmapped columns
   var unmappedStr = mapping[config.macros.twab.unmappedFlag];
   if( unmappedStr ){

      var unmappedArr = unmappedStr.split(",");
      for( var i=0; i<unmappedArr.length; i++ ){
         returnStr += ",";
      }//for

      //strip out the last ","
      returnStr = returnStr.substr(0, returnStr.length-1);

   }//if

   return returnStr.replace(/\n/g, "");

}//function exportContact



//add/overwrite existing contacts with the data parsed out of the import tiddler
config.macros.twab.addAll = function(contacts){

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

      if( !contacts[i] ){
         continue;
      }//if

      //use JSON format to our advantage
      var toEval  = "({" + contacts[i] + "})";  
      var contact = eval(toEval); 
      var title   = config.macros.twab.getSaveTitle(contact);
      if( title == "" ){
         continue;
      }//if

      //add it now
      var text = config.macros.twab.toTiddlyFormat(contacts[i]);
      store.saveTiddler(title, title, text, config.options.txtUserName, new Date(), config.macros.twab.importTags);

   }//for

}//function addAll



config.macros.twab.getSaveTitle = function(contact){

   var title = "";

   if( contact[config.macros.twab.fnameField] ){
      title += contact[config.macros.twab.fnameField];
   }//if

   if( contact[config.macros.twab.lnameField] ){
      title += contact[config.macros.twab.lnameField];
   }//if

   if( title == "" ){
      title = contact[config.macros.twab.companyNameField];
   }//if

   if( title == "" ){
      alert("Contact missing name field - could not be added: \n" + contacts[i]);
      return "";
   }//if

   if( store.tiddlerExists(title) ){

      //try up to 50 times
      var seqTitle = "";
      var foundOne = false;
      for( var i=2; i<51; i++){

         seqTitle = title + " (" + i + ")";
         if( !store.tiddlerExists(seqTitle) ){
            title    = seqTitle;
            foundOne = true;
            break;
         }//innermost if

      }//inner if

      //if got to 50 then there's a problem
      if( !foundOne ){
         alert("Seriously, you really have that many contacts named '" + title + "'?  Wow.");
         return "";
      }//if

   }//if

   return title;

}//function getSaveTitle



config.macros.twab.toTiddlyFormat = function(contact){

   var tpl = config.macros.twab.getContactTemplate();
   return tpl.replace( config.macros.twab.getMacroName(), contact);

}//function toTiddlyFormat



config.macros.twab.getContactTemplate = function(){
   var macroName = config.macros.twab.getMacroName();
   return "<<tiddler ContactsFormTemplate>>\n<data>{" + macroName + "}</data>";
}//function getContactTemplate



config.macros.twab.getMacroName = function(){
   return "#thisContact#";
}//function getMacroName



config.macros.twab.debugAll = function(contacts){

   for( var i=0; i<contacts.length; i++ ){
      //alert( contacts[i] );
      alert( config.macros.twab.toTiddlyFormat(contacts[i]) );
   }//for

}//function debugAll



config.macros.twab.populateLinks = function(place){
   config.macros.twab.populateEmail(place, "email");
   config.macros.twab.populateEmail(place, "other.email");
   config.macros.twab.populateEmail(place, "business.email");
   config.macros.twab.populateHref(place, "webpage");
   config.macros.twab.populateMap(place, "home");
   config.macros.twab.populateHref(place, "business.webpage");
   config.macros.twab.populateMap(place, "business");
}//function populateLinks



config.macros.twab.populateEmail = function(place, fieldName){
   var tiddlerName = config.macros.formTiddler.getContainingTiddlerName(place);
   var element     = document.getElementById("twab." + fieldName);
   if( element ){
      element.innerHTML = config.macros.twab.formatEmail(tiddlerName, fieldName);
   }//if
}//function populateEmail



//Thanks to Udo for the idea for this solution and to Lyall for the code to use display name
config.macros.twab.formatEmail = function(tiddlerName, fieldName){

   var returnStr   = "";
   var mailTo      = config.macros.twab.getData(tiddlerName, fieldName);
   var displayName = config.macros.twab.getData(tiddlerName, "first.name") + " " + config.macros.twab.getData(tiddlerName, "last.name");

   if( mailTo != "" ){

      if( displayName != "" ){
         mailTo = displayName + "<" + mailTo + ">";
      }//if

      returnStr = "<a href=\"mailto:" + mailTo + "\" content=\"\">(email)</a>";

   }//if

   return returnStr;

}//function formatEmail



config.macros.twab.populateHref = function(place, fieldName){
   var tiddlerName = config.macros.formTiddler.getContainingTiddlerName(place);
   var element = document.getElementById("twab." + fieldName);
   if( element ){
      element.innerHTML = config.macros.twab.formatHref(tiddlerName, fieldName);
   }//if
}//function populateHref



//Thanks to Udo for the idea for this solution
config.macros.twab.formatHref = function(tiddlerName, fieldName){

   var returnStr = "";
   var href     = config.macros.twab.getData(tiddlerName, fieldName);

   if( href != "" ){

      if( href.indexOf("http") != 0 ){
         href = "http://" + href;
      }//inner if

      returnStr = "<a href=\"" + href + "\" content=\"\" target=\"twab\">(visit)</a>";

   }//if

   return returnStr;

}//function formatHref



config.macros.twab.populateMap = function(place, fieldType){
   var tiddlerName = config.macros.formTiddler.getContainingTiddlerName(place);
   var element = document.getElementById("twab." + fieldType + ".map");
   if( element ){
      element.innerHTML = config.macros.twab.formatMap(tiddlerName, fieldType);
   }//if
}//function populateHref



//Thanks to Udo for the idea for this solution
config.macros.twab.formatMap = function(tiddlerName, fieldType){

   //hack for lack of planning of home versus business labels
   if( fieldType == "business"){
      fieldType += ".";
   } else if( fieldType == "home" ){
      fieldType = "";
   }//if-else if

   var returnStr = "";

   var address = "";
   if( DataTiddler.getData(tiddlerName, fieldType+"address") ){
      address += DataTiddler.getData(tiddlerName, fieldType+"address") + " ";
   }//if

   if( DataTiddler.getData(tiddlerName, fieldType+"city") ){
      address += DataTiddler.getData(tiddlerName, fieldType+"city") + " ";
   }//if

   if( DataTiddler.getData(tiddlerName, fieldType+"state") ){
      address += DataTiddler.getData(tiddlerName, fieldType+"state") + " ";
   }//if

   if( DataTiddler.getData(tiddlerName, fieldType+"postal") ){
      address += DataTiddler.getData(tiddlerName, fieldType+"postal") + " ";
   }//if

   if( address == "" ){
      return "";
   }//if

   var href  = "http://maps.google.com/maps?ie=UTF8&hl=en&q=" + address + "&f=q&sampleq=1";
   returnStr = "<a href=\"" + href + "\" content=\"\" target=\"twab\">(map)</a>";

   returnStr.replace(/ /g, "\+");
   return returnStr;

}//function formatMap

//}}}
<part tab1form>
<<formTiddler TwabTabParts/tab1>>
<script>config.macros.twab.populateLinks(place);</script>
</part>

<part tab1>
<html>
<span class="rolodex">
 <table>
 <tr>
    <td align="right"><b>First Name:</b></td>
    <td colspan="3"><input name="first.name" type="text" size="50" style="width:100%" /></td>
 </tr>
 <tr>
    <td align="right"><b>Last Name:</b></td>
    <td colspan="3"><input name="last.name" type="text" size="50" style="width:100%" /></td>
 </tr>
 <tr>
    <td align="right"><b>Email:</b></td>
    <td colspan="2"><input name="email" type="text" size="50" style="width:100%" /></td>
    <td id="twab.email"></td>
 </tr>
 <tr>
    <td align="right"><b>Alt Email:</b></td>
    <td colspan="2"><input name="other.email" type="text" size="50" style="width:100%" /></td>
    <td id="twab.other.email"></td>
 </tr>
 <tr>
    <td align="right"><b>Business Email:</b></td>
    <td colspan="2"><input name="business.email" type="text" size="50" style="width:100%" /></td>
    <td id="twab.business.email"></td>
 </tr>
 <tr>
    <td align="right"><b>Web Site:</b></td>
    <td colspan="2"><input name="webpage" type="text" size="50" style="width:100%" /></td>
    <td id="twab.webpage"></td>
 </tr>
 </table>
</span>
</html>
</part>

<part tab2form>
<<formTiddler TwabTabParts/tab2>>
<script>config.macros.twab.populateLinks(place);</script>
</part>

<part tab2>
<html>
<span class="rolodex">
 <table>
 <tr>
    <td align="right"><b>Phone:</b></td>
    <td colspan="3"><input name="phone" type="text" size="50" style="width:100%" /></td>
 </tr>
 <tr>
    <td align="right"><b>Mobile:</b></td>
    <td colspan="3"><input name="mobile" type="text" size="50" style="width:100%" /></td>
 </tr>
 <tr>
    <td align="right" valign="top"><b>Address:</b></td>
    <td colspan="3"><textarea name="address" rows="2" cols="40" style="width:100%" ></textarea></td>
 </tr>
 <tr>
    <td align="right"><b>City:</b></td>
    <td colspan="3"><input name="city" type="text" size="50" style="width:100%" /></td>
 </tr>
 <tr>
    <td align="right"><b>State/Province:</b></td>
    <td><input name="state" type="text" size="5" /></td>
    <td align="right"><b>ZIP/Postal Code:</b></td>
    <td><input name="postal" type="text" size="5" style="width:100%" /></td>
 </tr>
 <tr>
    <td align="right"><b>Country:</b></td>
    <td colspan="2"><input name="country" type="text" size="50" style="width:100%" /></td>
    <td id="twab.home.map"></td>
 </tr>
 </table>
</span>
</html>
</part>

<part tab3form>
<<formTiddler TwabTabParts/tab3>>
<script>config.macros.twab.populateLinks(place);</script>
</part>

<part tab3>
<html>
<span class="rolodex">
 <table>
 <tr>
    <td align="right"><b>Company:</b></td>
    <td colspan="3"><input name="company" type="text" size="50" style="width:100%" /></td>
 </tr>
 <tr>
    <td align="right"><b>Job Title:</b></td>
    <td colspan="3"><input name="job.title" type="text" size="50" style="width:100%" /></td>
 </tr>
 <tr>
    <td align="right"><b>Phone:</b></td>
    <td colspan="3"><input name="business.phone" type="text" size="50" style="width:100%" /></td>
 </tr>
 <tr>
    <td align="right"><b>Mobile:</b></td>
    <td colspan="3"><input name="business.mobile" type="text" size="50" style="width:100%" /></td>
 </tr>
 <tr>
    <td align="right"><b>Fax:</b></td>
    <td colspan="3"><input name="business.fax" type="text" size="50" style="width:100%" /></td>
 </tr>
 <tr>
    <td align="right" valign="top"><b>Address:</b></td>
    <td colspan="3"><textarea name="business.address" rows="2" cols="40" style="width:100%" ></textarea></td>
 </tr>
 <tr>
    <td align="right"><b>City:</b></td>
    <td colspan="3"><input name="business.city" type="text" size="50" style="width:100%" /></td>
 </tr>
 <tr>
    <td align="right"><b>State/Province:</b></td>
    <td><input name="business.state" type="text" size="5" /></td>
    <td align="right"><b>ZIP/Postal Code:</b></td>
    <td><input name="business.postal" type="text" size="5" style="width:100%" /></td>
 </tr>
 <tr>
    <td align="right"><b>Country:</b></td>
    <td colspan="2"><input name="business.country" type="text" size="50" style="width:100%" /></td>
    <td id="twab.business.map"></td>
 </tr>
 <tr>
    <td align="right"><b>Web Site:</b></td>
    <td colspan="2"><input name="business.webpage" type="text" size="50" style="width:100%" /></td>
    <td id="twab.business.webpage"></td>
 </tr>
 </table>
</span>
</html>
</part>

<part tab4form>
<<formTiddler TwabTabParts/tab4>>
<script>config.macros.twab.populateLinks(place);</script>
</part>

<part tab4>
<html>
<span class="rolodex">
 <table>
 <tr>
    <td align="right"><b>SSN:</b></td>
    <td colspan="3"><input name="ssn" type="text" size="50" style="width:100%" /></td>
 </tr>
 <tr>
    <td align="right"><b>Birthday:</b></td>
    <td colspan="3"><input name="birthday" type="text" size="50" style="width:100%" /></td>
 </tr>
 <tr>
    <td align="right"><b>Anniversary:</b></td>
    <td colspan="3"><input name="anniversary" type="text" size="50" style="width:100%" /></td>
 </tr>
 <tr>
    <td align="right"><b>Children:</b></td>
    <td colspan="3"><input name="children" type="text" size="50" style="width:100%" /></td>
 </tr>
 <tr>
    <td align="right"><b>Notes:</b></td>
    <td><textarea name=notes rows="4" cols="40" style="width:100%" ></textarea></td>
 </tr>
 </table>
</span>
</html>
</part>
first.name=First
last.name=Last
email=Email
other.email=Alternate Email 2
business.email=Alternate Email 1
webpage=Personal Website
phone=Home
mobile=Mobile
address=Home Address
city=Home City
state=Home State
postal=Home ZIP
country=Home Country
company=Company
job.title=Title
business.phone=Work
business.mobile=Other
business.fax=Fax
business.address=Work Address
business.city=Work City
business.state=Work State
business.postal=Work ZIP
business.country=Work Country
business.webpage=Business Website
ssn=<skip>
birthday=Birthday
anniversary=Anniversary
children=<skip>
notes=Comments
unmapped=Middle,Nickname,Category,Distribution Lists,Messenger ID,Pager,Yahoo! Phone,Primary,Custom 1,Custom 2,Custom 3,Custom 4,Messenger ID1,Messenger ID2,Messenger ID3,Messenger ID4,Messenger ID5,Messenger ID6,Messenger ID7,Messenger ID8,Messenger ID9,Skype ID,IRC ID,ICQ ID,Google ID,MSN ID,AIM ID,QQ ID
| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |
| 01/08/2014 23:24:11 | Ian | [[/|http://iwalton.tiddlyspot.com/#%5B%5BBackup%20Script%5D%5D]] | [[store.cgi|http://iwalton.tiddlyspot.com/store.cgi]] | . | [[index.html | http://iwalton.tiddlyspot.com/index.html]] | . |
| 01/08/2014 23:27:51 | Ian | [[/|http://iwalton.tiddlyspot.com/#%5B%5BBackup%20Script%5D%5D]] | [[store.cgi|http://iwalton.tiddlyspot.com/store.cgi]] | . | [[index.html | http://iwalton.tiddlyspot.com/index.html]] | . |
| 01/08/2014 23:29:51 | Ian | [[/|http://iwalton.tiddlyspot.com/#%5B%5BBackup%20Script%5D%5D]] | [[store.cgi|http://iwalton.tiddlyspot.com/store.cgi]] | . | [[index.html | http://iwalton.tiddlyspot.com/index.html]] | . |
| 19/08/2014 19:06:38 | Ian | [[/|http://iwalton.tiddlyspot.com/]] | [[store.cgi|http://iwalton.tiddlyspot.com/store.cgi]] | . | [[index.html | http://iwalton.tiddlyspot.com/index.html]] | . |
| 20/08/2014 22:09:48 | Ian | [[/|http://iwalton.tiddlyspot.com/]] | [[store.cgi|http://iwalton.tiddlyspot.com/store.cgi]] | . | [[index.html | http://iwalton.tiddlyspot.com/index.html]] | . |
| 25/08/2014 20:52:18 | Ian | [[/|http://iwalton.tiddlyspot.com/]] | [[store.cgi|http://iwalton.tiddlyspot.com/store.cgi]] | . | [[index.html | http://iwalton.tiddlyspot.com/index.html]] | . |
| 27/08/2014 22:34:29 | Ian | [[/|http://iwalton.tiddlyspot.com/]] | [[store.cgi|http://iwalton.tiddlyspot.com/store.cgi]] | . | [[index.html | http://iwalton.tiddlyspot.com/index.html]] | . |
| 27/08/2014 23:17:09 | Ian | [[iwalton.html|file:///home/ian/Documents/iwalton.html]] | [[store.cgi|http://iwalton.tiddlyspot.com/store.cgi]] | . | [[index.html | http://iwalton.tiddlyspot.com/index.html]] | . |
| 28/08/2014 16:16:12 | Ian | [[/|http://iwalton.tiddlyspot.com/]] | [[store.cgi|http://iwalton.tiddlyspot.com/store.cgi]] | . | [[index.html | http://iwalton.tiddlyspot.com/index.html]] | . |
| 04/09/2014 21:46:20 | Ian | [[/|http://iwalton.tiddlyspot.com/]] | [[store.cgi|http://iwalton.tiddlyspot.com/store.cgi]] | . | [[index.html | http://iwalton.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");


//}}}
<html><iframe class="imgur-album" width="100%" height="550" frameborder="0" src="http://imgur.com/a/Qw0pn/embed"></iframe></html>

Download these and many more in full resolution: [[Link|https://www.dropbox.com/s/uoyookkpokt3k94/pictures.zip]] [[[M]|http://yunpan.cn/QDeW8h3V2E5bq]] (276 MB)

All the images that are shown were created by me and are licensed [[Attribution-ShareAlike|https://creativecommons.org/licenses/by-sa/3.0/us/]].
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://iwalton.tiddlyspot.com/controlpanel]] (your control panel username is //iwalton//).
<<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.
I support adblock plus!
<html><object width="480" height="385"><param name="movie" value="http://www.youtube.com/v/oNvb2SjVjjI?fs=1&amp;hl=en_US"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/oNvb2SjVjjI?fs=1&amp;hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"></embed></object></html>
[[>DOWNLOAD HERE<|http://adblockplus.org/]]
|>| Binary Cheat Sheet |
|01 +1<br>02 +2<br>03 +4<br>04 +8<br>05 +16<br>06 +32<br>07 +64<br>08 +128<br>09 +256<br>10 +512<br>11 +1024<br>12 +2048<br>13 +4096<br>14 +8192<br>15 +16384<br>16 +32768<br>17 +65536<br>18 +131072<br>19 +262144<br>20 +524288<br>|21 +1048576<br>22 +2097152<br>23 +4194304<br>24 +8388608<br>25 +16777216<br>26 +33554432<br>27 +67108864<br>28 +134217728<br>29 +268435456<br>30 +536870912<br>31 +1073741824<br>32 +2147483648<br>33 +4294967296<br>34 +8589934592<br>35 +1717986918<br>36 +34359738368<br>37 +68719476736<br>38 +137438953472<br>39 +274877906944<br>40 +549755813888|
<<calendar>>
<<newTiddler>>
<<twab>>
<<newJournal 'DD MMM YYYY'>>
<<saveChanges>>
<<attach>>
<<EncryptionDecryptAll>>
<<EncryptionChangePassword>>
<<tiddler TspotSidebar>>
----
<<tiddler TspotOptions>>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]]
<html><iframe src="http://iwalton3.freeforums.org/index.php" width="100%" height="3000" frameBorder="0">
</iframe></html>
!!!!Menu
The [[MainMenu]] has sub menus. To add a tiddler to one just tag it with the name of the menu entry.
The tag box is at the bottom of the editor.

!!!Change
To change the title go to: [[SiteTitle]].
To change the subtitle go to: [[SiteSubtitle]].
To change the home tiddlers go to: [[DefaultTiddlers]].
To change the sidebar go to: [[SideBarOptions]].
To change the tiddler toolbar go to: [[ToolbarCommands]],WARNING: you car destroy this wiki by misusing this.

!!!Clock
Add a clock to your tiddlers.
Example:
{{{<<clock2 120>>}}}
<<clock2 120>>
For more info on clocks see: [[Clock2]]

!!!Twab
TWAB lets you create a <<twab>>.
For more info on TWAB see: http://www.tiddly-twab.com/

!!!Calender
Add a calender to your tiddlers.
|{{{<<calendar>>}}}|full-year calendar for the current year|
|{{{<<calendar year>>}}}|full-year calendar for the specified year|
|{{{<<calendar year month>>}}}|one month calendar for the specified month and year|
|{{{<<calendar thismonth>>}}}|one month calendar for the current month|
|{{{<<calendar lastmonth>>}}}|one month calendar for last month|
|{{{<<calendar nextmonth>>}}}|one month calendar for next month|
|{{{<<calendar +n>>}}}<br>{{{<<calendar -n>>}}}|one month calendar for a month +/- 'n' months from now|

!!!archive
You can archive tiddlers to a separate file.
To archive the tiddler tag it archive.
NOTE:You need to create a folder called archive with your wiki.

!!!Intelli Tags
Speed up tagging tiddlers using tag auto completion and correction, tag suggestions and context sensitive tag guessing, "edit tags only" mode and many more. This makes tags easier to use.

!!!Encryption
This wiki can encrypt data to encrypt just tag it with: Encrypt(prompt)
Consider the 'prompt' something to help you remember the password with. If multiple tiddlers can be encrypted with the same 'prompt' and you will only be asked for the password once.
There is an <<EncryptionDecryptAll>>button and<<EncryptionChangePassword>>button in the sidebar for ease of use.
For more info on encryption see: [[TiddlerEncryptionPlugin]]

!!!Attaching
You can attach a file to this wiki without external files.
Attaching keeps your wiki one file while having files!
There is a<<slider chkSliderOptionsPanel AttachFile 'attach file' 'Click here to attach a file.'>>button in the sidebar for ease of use.
For more info on Attaching see: [[AttachFilePluginInfo]]

!!!Player
There is even a player for media.
You can embed Windows Media, Real One, Quick time, Flash, Google Video, and Still Images.
For more information on the player see: [[PlayerPlugin]]

!!Markup
How to edit tiddlers.
!!!Basic Editing.
|''Bold'' | <html>''Bold''</html>|
|//Italics// | <html>//Italics//</html>|
|''//Bold Italics//'' | <html>''//Bold Italics//''</html>|
|__Underline__ | <html>__Underline__</html>|
|--Strikethrough--| <html>--Strikethrough--</html>|
|Super^^script^^ | <html>Super^^script^^</html>|
|Sub~~script~~ | <html>Sub~~script~~</html>|
|@@Highlight@@ | <html>@@Highlight@@</html>|
|{{{PlainText No ''Formatting''}}} | <html>{{{PlainText No ''Formatting''}}}</html>|

!!!Tables
|table1|table2|
|table3|table4|
is produced by
{{{
|table1|table2|
|table3|table4|
}}}
|CssClass|k
|!heading column 1|!heading column 2|h
|row 1, column 1|row 1, column 2|
|row 2, column 1|row 2, column 2|
|>|COLSPAN|
|ROWSPAN| … |
|~| … |
|CssProperty:value;…| … |
|caption|c
is produced by
{{{
|CssClass|k
|!heading column 1|!heading column 2|h
|row 1, column 1|row 1, column 2|
|row 2, column 1|row 2, column 2|
|>|COLSPAN|
|ROWSPAN| … |
|~| … |
|CssProperty:value;…| … |
|caption|c
}}}
<<slider tabhelp  tabhelp 'Advanced tables' 'Here is an in depth article about tables'>>
Annotation:
* The > marker creates a "colspan", causing the current cell to merge with the one to the right.
* The ~ marker creates a "rowspan", causing the current cell to merge with the one above. 
!!!Headings
!Heading1
is produced by
{{{
!Heading1
}}}

!!Heading2
is produced by
{{{
!!Heading2
}}}

!!!Heading3
is produced by
{{{
!!Heading3
}}}

!!!!Heading4
is produced by
{{{
!!!!Heading4
}}}

!!!!Heading5
is produced by
{{{
!!!!Heading5
}}}

!!!!!Heading6
is produced by
{{{
!!!!!Heading6
}}}

!!!Unordered Lists
* Unordered List, Level 2
** Unordered List, Level 2
*** Unordered List, Level 3
is produced by
{{{
*Unordered List, Level 2
**Unordered List, Level 2
***Unordered List, Level 3
}}}
!!!Ordered Lists 
# Ordered List, Level 1A
# Ordered List, Level 1B
## Ordered List, Level 2A
### Ordered List, Level 3A
is produced by
{{{
# Ordered List, Level 1A
# Ordered List, Level 1B
## Ordered List, Level 2A
### Ordered List, Level 3A
}}}
!!!Definition Lists 
; definition list, term
: definition list, description
is produced by
{{{
; definition list, term
: definition list, description
}}}
!!!Indents
: indent1
:: indent2
::: indent3
is produced by
{{{
: indent1
:: indent2
::: indent3
}}}
!!!Block Quotes
> blockquote, level 1
>> blockquote, level 2
>>> blockquote, level 3
is produced by
{{{
> blockquote, level 1
>> blockquote, level 2
>>> blockquote, level 3
}}}
<<<
blockquote
<<<
is produced by
{{{
<<<
blockquote
<<<
}}}
!!!Preformatted 
{{{
preformatted (e.g. code)
}}}
is produced by

"""{{{"""
preformatted (e.g. code)
"""}}}"""
!!!Links
*  Wiki Words are automatically transformed to hyperlinks to the respective tiddler
** the automatic transformation can be suppressed by preceding the respective Wiki Word with a tilde (~): ~Wiki Word 
* Pretty Links are enclosed in square brackets and contain the desired tiddler name: [[tiddler name]]
** optionally, a custom title or description can be added, separated by a pipe character (|): [[title|target]]
N.B.: In this case, the target can also be any website (i.e. URL), folder or file. 

''Examples''

* a simple website (URL) requires no markup: http://domain.tld = {{{http://domain.tld}}}
* website (URL) : [[label|http://domain.tld]] = {{{[[label|http://domain.tld]]}}}
* Unix-style folder: [[label|file:///folder/file]] = {{{[[label|file:///folder/file]]}}}
* Windows drive-mapped folder: [[label|file:///c:/folder/file]] = {{{[[label|file:///c:/folder/file]]}}}
* Windows network share: [[label|file://///server/folder/file]] = {{{[[label|file://///server/folder/file]]}}}
!!!Custom Styling
*inline styles:
@@CssProperty:value;CssProperty:value;…@@
is produced by
{{{
@@CssProperty:value;CssProperty:value;…@@
}}}
''N.B.:'' CSS color definitions should use lowercase letters to prevent the inadvertent creation of WikiWords.
*class wrapper:
{{customClass{…}}}
is produced by
{{{
{{customClass{…}}}
}}}
!!!Inserting HTML
*raw HTML can be inserted by enclosing the respective code in HTML tags {{{<html> … </html>}}}.
!!!Special Markers
*{{{<br>}}} forces a manual line break
* {{{----}}} and {{{<hr>}}} create a horizontal rule (<hr> syntax supported from v2.4.2)
* {{{<<macroName>>}}} calls the respective macro
* To hide text within a tiddler so that it is not displayed, it can be wrapped in {{{/%}}} and {{{%/}}}.This can be a useful trick for hiding drafts or annotating complex markup. 
* To prevent wiki markup from taking effect for a particular section, that section can be enclosed in three double quotes: e.g. {{{"""WikiWord"""}}}.
!!!HtmlEntities
Entities in HTML documents allow characters to be entered that can't easily be typed on an ordinary keyboard. They take the form of an ampersand (&), an identifying string, and a terminating semi-colon (;). There's a complete reference [[here|http://www.htmlhelp.com/reference/html40/entities/]]; some of the more common and useful ones are shown below. Also see [[Paul's Notepad|http://thepettersons.org/PaulsNotepad.html#GreekHtmlEntities%20HtmlEntitiesList%20LatinHtmlEntities%20MathHtmlEntities]] for a more complete list.

|>|>|>|>|>|>| !HTML Entities |
| &amp;nbsp; | &nbsp; | no-break space | &nbsp;&nbsp; | &amp;apos; | &apos; | single quote, apostrophe |
| &amp;ndash; | &ndash; | en dash |~| &amp;quot; | " | quotation mark |
| &amp;mdash; | &mdash; | em dash |~| &amp;prime; | &prime; | prime; minutes; feet |
| &amp;hellip; | &hellip; |	horizontal ellipsis |~| &amp;Prime; | &Prime; | double prime; seconds; inches |
| &amp;copy; | &copy; | Copyright symbol |~| &amp;lsquo; | &lsquo; | left single quote |
| &amp;reg; | &reg; | Registered symbol |~| &amp;rsquo; | &rsquo; | right  single quote |
| &amp;trade; | &trade; | Trademark symbol |~| &amp;ldquo; | &ldquo; | left double quote |
| &amp;dagger; | &dagger; | dagger |~| &amp;rdquo; | &rdquo; | right double quote |
| &amp;Dagger; | &Dagger; | double dagger |~| &amp;laquo; | &laquo; | left angle quote |
| &amp;para; | &para; | paragraph sign |~| &amp;raquo; | &raquo; | right angle quote |
| &amp;sect; | &sect; | section sign |~| &amp;times; | &times; | multiplication symbol |
| &amp;uarr; | &uarr; | up arrow |~| &amp;darr; | &darr; | down arrow |
| &amp;larr; | &larr; | left arrow |~| &amp;rarr; | &rarr; | right arrow |
| &amp;lArr; | &lArr; | double left arrow |~| &amp;rArr; | &rArr; | double right arrow |
| &amp;harr; | &harr; | left right arrow |~| &amp;hArr; | &hArr; | double left right arrow |

The table below shows how accented characters can be built up by subsituting a base character into the various accent entities in place of the underscore ('_'):

|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>| !Accented Characters |
| grave accent | &amp;_grave; | &Agrave; | &agrave; | &Egrave; | &egrave; | &Igrave; | &igrave; | &Ograve; | &ograve; | &Ugrave; | &ugrave; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; |
| acute accent | &amp;_acute; | &Aacute; | &aacute; | &Eacute; | &eacute; | &Iacute; | &iacute; | &Oacute; | &oacute; | &Uacute; | &uacute; | &nbsp; | &nbsp; | &Yacute; | &yacute; | &nbsp; | &nbsp; |
| circumflex accent | &amp;_circ; | &Acirc; | &acirc; | &Ecirc; | &ecirc; | &Icirc; | &icirc; | &Ocirc; | &ocirc; | &Ucirc; | &ucirc; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; |
| umlaut mark | &amp;_uml; | &Auml; | &auml; |  &Euml; | &euml; | &Iuml; | &iuml; | &Ouml; | &ouml; | &Uuml; | &uuml; | &nbsp; | &nbsp; | &Yuml; | &yuml; | &nbsp; | &nbsp; |
| tilde | &amp;_tilde; | &Atilde; | &atilde; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &Otilde; | &otilde; | &nbsp; | &nbsp; | &Ntilde; | &ntilde; | &nbsp; | &nbsp; | &nbsp; | &nbsp; |
| ring | &amp;_ring; | &Aring; | &aring; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; |
| slash | &amp;_slash; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &Oslash; | &oslash; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; |
| cedilla | &amp;_cedil; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &nbsp; | &Ccedil; | &ccedil; |
[img[http://i.imgur.com/YSwWQ3y.png]]
{{{
#!/bin/bash
cd "`dirname "$0"`"
scriptPath=`readlink -f "$0"`

#Simple Config
serverJar="minecraft_server.jar"
screenName="minecraft"

function sendCommand {
    screen -x "$screenName" -X stuff "`printf "$*\r"`"
}

if [[ "$1" == "backup" ]]
then

    echo Backing up server world.
    if [[ ! -e "backup.attic" ]]
    then
        attic init backup.attic
    fi
    echo Forcing save...
    sleep 2 # Prevents conflicts with sending files.
    sendCommand "save-off"
    sleep 2
    sendCommand "save-all"
    sleep 10
    echo Archiving...
    attic prune backup.attic -v -H 24 -d 10 -w 4 -m 24
    attic create -vs backup.attic::`date +%FT%R` world
    sendCommand "save-on"

elif [[ "$1" == "send" ]]
then

    echo "Sending $2 to minecraft."
    cat "$2" | while read line
    do
        sendCommand "say $line"
    done

elif [[ "$1" == "daemon" ]]
then

    if [[ "$2" != "-f" ]]
    then
        echo '# You should not run this command manually.'
        echo '# Use start instead.'
        echo '# To ignore this warning, run use -f.'
        exit
    fi
    echo '# Minecraft server starting...'
    while true
    do
        java -XX:+UseConcMarkSweepGC -Xms768M -Xmx2048M -jar "$serverJar" nogui
        if [ -f ./restart.conf ]
        then
            if [[ "`cat ./restart.conf`" == "terminate" ]]
            then
                echo '# Terminating...'
                rm ./restart.conf
                exit
            else
                rm ./restart.conf
                echo '# Restarting NOW!'
        fi
        else
            echo '#'
            echo '# Restarting minecraft server in 20 seconds...'
            echo '# Press CTRL+C NOW if you want to stop the server.'
            echo '#'
            sleep 20
        fi
        echo '# Minecraft server starting... (automatic)'
    done

elif [[ "$1" == "restart" ]]
then

    if [[ "$2" == "-f" ]]
    then
        echo 'Will restart NOW!'
    else
        echo 'Warning users about restart. Will restart in 5 minutes.'
        sendCommand "say Server will be restarting in 5 minutes."
        sleep 120
        echo 'Will restart in 3 minutes.'
        sendCommand "say Server will be restarting in 3 minutes."
        sleep 120
        echo 'Will restart in 1 minute.'
        sendCommand "say Server will be restarting in 1 minute."
        sleep 60
        echo 'Restarting...'
    fi
    sendCommand "say Server is restarting, please log out safely."
    sleep 10
    echo "nolag" > restart.conf
    sendCommand "stop"

elif [[ "$1" == "stop" ]]
then

    if [[ "$2" == "-f" ]]
    then
        echo 'Will stop NOW!'
    else
        echo 'Warning users about server shutdown. Will stop in 5 minutes.'
        sendCommand "say Server will be stopped in 5 minutes for maintenance."
        sleep 120
        echo 'Will stop in 3 minutes.'
        sendCommand "say Server will be stopped in 3 minutes for maintenance."
        sleep 120
        echo 'Will stop in 1 minute.'
        sendCommand "say Server will be stopped in 1 minute for maintenance."
        sleep 60
        echo 'Stopping...'
    fi
    sendCommand "say Server is now going down for maintenance, please log out safely."
    sleep 10
    echo "terminate" > restart.conf
    sendCommand "stop"

elif [[ "$1" == "start" ]]
then

    echo 'Checking to see if screen is running a minecraft instance...'
    screen -list | grep "\.$screenName" > /dev/null
    if [[ "$?" != "0" ]]
    then
        echo 'Starting minecraft screen instance...'
        screen -dmS "$screenName" bash -c "\"$scriptPath\" daemon -f"

    else
        echo 'Checking to see if minecraft is running...'
        ps -A | grep "daemon -f" > /dev/null
        if [[ "$?" != "0" ]]
        then
            echo 'Starting minecraft launch script...'
            sendCommand "\"$scriptPath\" daemon -f"
            #` This fixes a broken lexer.
        fi
    fi

elif [[ "$1" == "restore" ]]
then

    #Show a warning.
    echo "You are about to restore a backup!"
    echo "This will shut off the minecraft server!"
    read -p "Continue? [y/N]? " cont
    if [[ "$cont" != "y" ]]
    then
        exit
    fi

    #Stop the server
    sendCommand "say Server is now going down for backup restore, please log out safely."
    sleep 10
    echo "terminate" > restart.conf
    sendCommand "stop"
    time=0
    while [[ -e "restart.conf" ]]
    do
        sleep 1
        time=$((time+1))
        if [[ "$time" -gt "15" ]]
        then
            rm restart.conf
            break
        fi
    done

    #Create a backup
    attic create -vs backup.attic::`date +%FT%R`-dirty world

    #List the backups
    attic list backup.attic/ > backups.tmp
    max=`wc -l backups.tmp | awk '{ print $1 }'`
    cat backups.tmp | awk '{ print $1 }' | while read line
    do
        count=$((count+1))
        echo "$count) $line"
    done
    while [[ "$backupNumber" -gt "$max" ]] || [[ "$backupNumber" -lt "1" ]]
    do
        read -p "Which backup? [1-$max]? " backupNumber
    done
    backupName=`sed -n "$backupNumber{p;q;}" backups.tmp | awk '{ print $1 }'`
    rm -f backups.tmp

    #Restore the backup.
    rm -rf world
    mkdir world
    attic extract -v backup.attic::$backupName

    #Start the server
    "$0" start

elif [[ "$1" == "command" ]]
then

    sendCommand "$2"

elif [[ "$1" == "console" ]]
then

    screen -x "$screenName"

else

    echo "Usage: $0 <command>"
    echo "This is a general minecraft server admin tool."
    echo
    echo "This script must reside in the root of the server folder."
    echo "The world should be in the folder world."
    echo "Please ensure that attic and screen are installed."
    echo
    echo "  start           Start the minecraft server."
    echo "  stop [-f]       Stop the minecraft server."
    echo "                  -f stops the server without warning."
    echo "  restart [-f]    Restart the minecraft server."
    echo "                  -f restarts the server without warning."
    echo "  backup          Create a backup of the server world."
    echo "  restore         Stop the server, restore a backup, then start it again."
    echo "  send <file>     Broadcast the file to people on the server."
    echo "  command <text>  Send the command to the server."
    echo "  console         Show the server console in screen."
    echo "                  Use CTRL+A D to exit."
    echo "  daemon [-f]     Start the server directly. Use start instead."
    echo
    echo "By default, the server jar is minecraft_server.jar and the screen name"
    echo "is minecraft. Change the variables at the start of the script to change this."
    echo
    echo "This script is useful for automation. Here is an example crontab:"
    echo
    echo "0 * * * * \"$scriptPath\" backup"
    echo "7 7 * * * \"$scriptPath\" restart"
    echo "0 */2 * * * \"$scriptPath\" send info.txt"
    echo
    echo "It will do backups every hour, restart the server every day at 7:07 AM,"
    echo "and send server info every 2 hours."

fi
}}}
{{{
users:
  Drake_Blackwood:
    subgroups: []
    permissions: []
    group: Moderator
  NarutoRodriguez:
    subgroups: []
    permissions: []
    group: Vip
  Wyatt803:
    subgroups: []
    permissions: []
    group: Vip
  animeman75:
    subgroups: []
    permissions: []
    group: Admin
  Chessellator:
    subgroups: []
    permissions: []
    group: Admin
  frikaaaaay:
    subgroups: []
    permissions: []
    group: Builder
  eilahhh123:
    subgroups: []
    permissions: []
    group: Builder
  attome16:
    subgroups: []
    permissions: []
    group: Builder
  middaughr:
    subgroups: []
    permissions: []
    group: Cocreator
  esaiah3210:
    subgroups: []
    permissions: []
    group: Builder
  MadamMum:
    subgroups: []
    permissions: []
    group: Builder
  lilnick612:
    subgroups: []
    permissions: []
    group: Admin
  iwalton3:
    subgroups: []
    permissions: []
    group: Creator
  cronoshane:
    subgroups: []
    permissions: []
    group: Admin
  lucamorelli100:
    subgroups: []
    permissions: []
    group: Builder
  jjnnikki:
    subgroups: []
    permissions: []
    group: Builder
  Peanutdude123:
    subgroups: []
    permissions: []
    group: Builder
  willbuntu:
    subgroups: []
    permissions: []
    group: Admin
  morgfootball:
    subgroups: []
    permissions: []
    group: Admin
  JJR118:
    subgroups: []
    permissions: []
    group: Builder
  brickapple:
    subgroups: []
    permissions: []
    group: Builder
  Notex:
    subgroups: []
    permissions: []
    group: Admin
  awalton3:
    subgroups: []
    permissions: []
    group: Admin
  webboysurf3:
    subgroups: []
    permissions: []
    group: Cocreator
  xXus_rangerXx:
    subgroups: []
    permissions: []
    group: Builder
  Huggy_Bear48:
    subgroups: []
    permissions: []
    group: Admin
  BKushlak:
    subgroups: []
    permissions: []
    group: Admin
  MELLOW5000:
    subgroups: []
    permissions: []
    group: Builder
  Hatbag:
    subgroups: []
    permissions: []
    group: Builder
  seth_sh:
    subgroups: []
    permissions: []
    group: Admin
  PlasmaBerry:
    subgroups: []
    permissions: []
    group: Moderator
  ooohaoooo:
    subgroups: []
    permissions: []
    group: Admin
  PhortyTwo:
    subgroups: []
    permissions: []
    group: Builder
  salten03:
    subgroups: []
    permissions: []
    group: Admin
  bumleach:
    subgroups: []
    permissions: []
    group: Builder
  buna101:
    subgroups: []
    permissions: []
    group: Builder
  ya_mums_a_pinata:
    subgroups: []
    permissions: []
    group: Builder
  maxwellsmart712:
    subgroups: []
    permissions: []
    group: Builder
  Marcusmoto:
    subgroups: []
    permissions: []
    group: FakeAdmin
  What125:
    subgroups: []
    permissions: []
    group: Builder
  bazza1411:
    subgroups: []
    permissions: []
    group: Builder
  Samohanka:
    subgroups: []
    permissions: []
    group: Builder
  rwalton3:
    subgroups: []
    permissions: []
    group: Cocreator
  Redstone-Bridge:
    subgroups: []
    permissions: []
    group: Creator
  Jammx3:
    subgroups: []
    permissions: []
    group: Moderator
  BOSpaiin1:
    subgroups: []
    permissions: []
    group: Moderator
  koolcallumrocks:
    subgroups: []
    permissions: []
    group: Admin
    subgroups: []
    permissions: []
    group: Admin
  cfox313:
    subgroups: []
    permissions: []
    group: Builder
  FireandIce9000:
    subgroups: []
    permissions: []
    group: Moderator
  Bacon_Bloc:
    subgroups: []
    permissions: []
    group: Builder
  greendayno1fan:
    subgroups: []
    permissions: []
    group: Builder
  LOLxxGamer:
    subgroups: []
    permissions: []
    group: Vip
  jared_s:
    subgroups: []
    permissions: []
    group: Builder
  jack63903:
    subgroups: []
    permissions: []
    group: Builder
}}}
!Formatting Codes
!!Basic Syntax

Table columns are delimited by pipe characters (|), while table rows are separated with a simple line break:
{{{
|!column heading 1|!column heading 2|!column heading 3|h
|row 1, column 1|row 1, column 2|row 1, column 3|
|row 2, column 1|row 2, column 2|row 2, column 3|
|row 3, column 1|row 3, column 2|row 3, column 3|
|!column footer 1|!column footer 2|!column footer 3|f
|caption|c
}}}
produces
|!column heading 1|!column heading 2|!column heading 3|h
|row 1, column 1|row 1, column 2|row 1, column 3|
|row 2, column 1|row 2, column 2|row 2, column 3|
|row 3, column 1|row 3, column 2|row 3, column 3|
|!column footer 1|!column footer 2|!column footer 3|f
|caption|c
Note that the trailing {{{|h}}} at the end of the first line defines the table header, which is semantically different from the column heading. Usually, however, the table header will contain the column headings.
!!Text Alignment

As the following example shows, spaces before and after a cell's content control the alignment:
{{{
|default alignment|
|left-aligned cell content |
| right-aligned cell content|
| centered cell content |
}}}
produces
|default alignment|
|left-aligned cell content |
| right-aligned cell content|
| centered cell content |

!!Column Spans

A carets (>) in a table cell creates a so-called "colspan", spreading the cell across several columns.

Example:
{{{
|>|>| cell #3 |
}}}
produces
|>|>| cell #3 |

Here the first two cells are omitted so that the third cell spreads across all three columns.
Row Spans

A tilde (~) spreads a table cell across several rows, creating a so-called "rowspan".

Example:
{{{
|cell #1|cell #2|
|~|cell #4|
|~|cell #6|
}}}
produces
|cell #1|cell #2|
|~|cell #4|
|~|cell #6|
Here the first cell spreads across three rows.
!!Simple Lists

TiddlyWiki tables do not easily permit numbered and bulleted lists with formatting. Using <br> for manual line breaks allows the creation of a modest ("fake") list within a cell.

Example:
{{{
|* List item 1<br>* List item 2<br>* list item 3<br><br>* List item with double-spacing|
}}}
produces
|* List item 1<br>* List item 2<br>* list item 3<br><br>* List item with double-spacing|

Bullet points can be used in a table cells only with the help of transclusion (as described in Multi-Line Contents).
!Styling
!!Inline Styles

Custom CSS code can be applied to table cells by adding the CSS ruleset at the beginning of the respective table cell:
{{{
|property:value;cell contents|
}}}

There is also an alternative syntax, which deviates slightly from the CSS standard:
{{{
|property(value);cell contents|
}}}

!!Custom CSS Classes

A table can be assigned a custom CSS class by adding the following code to the beginning or end of the table:

{{{|foo|k}}}

(where "foo" is the name of the custom CSS class)
Alternating Rows

TiddlyWiki automatically assigns the classes oddRow and evenRow to table rows.

These can then be styled via the """StyleSheet""" - for example:
{{{
.viewer tr.oddRow { background-color: #FFF; }
.viewer tr.evenRow { background-color: #FFE; }
}}}
Examples

*the following code makes the second cell's contents appear in bold red: 
{{{
|cell #1|font-weight:bold;color:#f00;cell #2|cell #3|
}}}
produces
|cell #1|font-weight:bold;color:#f00;cell #2|cell #3|
*this code changes the first cell's background color to light blue: 
{{{
|bgcolor(#eef):cell #1|cell #2|
}}}
produces
|bgcolor(#eef):cell #1|cell #2|

!!Centering Tables

{{{
{{centeredTable{
|test1|test2|test3|
|test4|test5|test6|
}}}
produces
{{centeredTable{
|test1|test2|test3|
|test4|test5|test6|
}}}
{{{
{{tableRWrapper{
|cell1|cell2|cell3|
|cell4|cell5|cell6|
}}}
}}}
produces
{{tableRWrapper{
|cell1|cell2|cell3|
|cell4|cell5|cell6|
}}}
!!Borderless Tables
 
{{{
|borderless|k
|!foo|bar|
}}}
produces
|borderless|k
|!foo|bar|

HTML Code

Raw HTML code can also be used by wrapping it in <html></html> tags. This can be useful for setting custom row or cell attributes. 
----
<<gradient vert #FF8614 #DA4A0D>>
{{centeredTable{

                                              [img[http://i.imgur.com/RjUrc.png]]
}}}


This is a simple text to speech program for linux.

To install:
#run install dependencies.sh

To use:
*to convert an entire folder
##run tts.sh
##pick the mode "entire folder"
##select folder with ''only'' text files
##select voice
*to convert a single file
##run tts.sh
##pick the mode "single file"
##select a text file
##select voice
A .wav file of the same name will be created in that folder.

[[>>Download here<<|https://www.dropbox.com/s/6ew5rsdb9bap503/ttspackage.sh]] [[[M]|http://yunpan.cn/QDe6LUtxveMTI]]


    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.


these are my makup and plugin instructions: [[help]]
[[You can download this here|http://iwalton.tiddlyspot.com/download]]