TiddlyWiki  a reusable non-linear personal web notebook
TiddlyWiki  a reusable non-linear personal web notebook

HelloThere
Welcome to TiddlyWiki!

TiddlyWiki is a single html file which has all the characteristics of a wiki - including all of the content, the functionality (including editing, saving, tagging and searching) and the style sheet. Because it's a single file, it's very portable - you can email it, put it on a web server or share it via a USB stick.

But it's not just a wiki! It has very powerful plugin capabilities, so it can also be used to build new tools. You have full control over how it looks and behaves. For example, TiddlyWiki is already being used as:
  • A personal notebook
  • A GTD ("Getting Things Done") productivity tool
  • A collaboration tool
  • For building websites (this site is a TiddlyWiki file!)
  • For rapid prototyping
  • ...and much more!
You can import and export data to and from all sorts of places. Check out some of the Examples of TiddlyWiki in use, and the Features that are available.

You can see the web functionality of TiddlyWiki by clicking on some of the links on this website. Double click some of the text to see 'edit mode'. For the full range of functions, including editing and saving changes, download and install a copy of the basic version and then follow the guidelines in Getting Started. Have fun!

download

Advanced options >>

NewFeatures
Release 2.5.2 is a bugfix release, restoring the upgrade functionality on Mozilla-based browsers.

Release 2.5.1 of TiddlyWiki contained several usability enhancements and bug fixes.
These include:
  • Improved separators and "more/less" extenders for toolbars
  • Added plugin version information to the PluginManager
  • Fixed Tags macro to respect the excludeLists tag
  • Fixed problem with saving of extended tiddler fields
It also starts the process of refactoring useful parts of TiddlyWiki's functionality into generic jQuery plugins that can be reused in other projects:
<!--{{{-->
<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>>
<<options>>
Rename this tiddler to 'ColorPalette' to enable this color scheme

Background: #ffc
Foreground: #000
PrimaryPale: #fc8
PrimaryLight: #f81
PrimaryMid: #b40
PrimaryDark: #410
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #e88
TertiaryLight: #c66
TertiaryMid: #944
TertiaryDark: #633

<<paletteView [[AlternativeColorPalette]]>>
<<tiddler ListTaggedTiddlers with:"ArcGIS">>
/***
|''Name:''|AutoRefreshPlugin|
|''Version:''|1.0.1 (2007-01-20)|
|''Type:''|plugin|
|''Source:''|http://tiddlywiki.abego-software.de/#AutoRefreshPlugin|
|''Author:''|Udo Borkowski (ub [at] abego-software [dot] de)|
|''Documentation:''|[[AutoRefreshPlugin Documentation|http://tiddlywiki.abego-software.de/#%5B%5BAutoRefreshPlugin%20Documentation%5D%5D]]|
|''Licence:''|[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]|
|''~CoreVersion:''|2.1.3|
|''Browser:''|Firefox 1.5.0.9 or better; Internet Explorer 6.0|
A tiddler containing the {{{<<autoRefresh...>>}}} macro is automatically refreshed (re-painted) whenever a tiddler changes.
!Syntax
{{{
<<autoRefresh [observeTiddler: tiddler ...]>>
}}}
|{{{observeTiddler}}}|(optional) when specified the refresh will only happen when one of the tiddlers specified is changed.|
!Source Code
***/
//{{{

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

// autoRefresh Macro =============================================================
//
(function() {


var REFRESHER_NAME = "abego_onEveryChange";

var tiddlersToRefresh = {}; // A set holding the names of tiddlers to be refreshed

var onEveryChangeRefresher = function(e,changeList) {
	
	var tiddlerElem = story.findContainingTiddler(e);
	if (!tiddlerElem) return false;

	var title = tiddlerElem.getAttribute("tiddler");
	if (!title) return false;

	// if "observeTiddler" are specified we only refresh if one of the given 
	// tiddlers has changed.
	var observedTiddlers = e.getAttribute("observedTiddlers");
	if (observedTiddlers) {
		var a = observedTiddlers.readBracketedList();
		if (!changeList || !a.containsAny(changeList))
			return;
	}

	// Refresh the tiddler asynchronously. 
	// This way we can avoid repeated refreshes (e.g. when a tiddler is renamed)
	tiddlersToRefresh[title] = true;
	setTimeout(function() {
		// Refresh all tiddlers in tiddlersToRefresh
		for(var title in tiddlersToRefresh)
			story.refreshTiddler(title,null,true);

		// We have refreshed all pending tiddlers. Clear the set.
		tiddlersToRefresh = {};
	}, 0);

	return true;
}

config.refreshers[REFRESHER_NAME] = onEveryChangeRefresher;


config.macros.autoRefresh = {};

config.macros.autoRefresh.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
    params = paramString.parseParams("observeTiddler",null,true,false,true); // allowEval, cascadeDefaults, names allowed

	var e = createTiddlyElement(place,"span");
	e.setAttribute("refresh",REFRESHER_NAME);
	var observedTiddlers = params[0]["observeTiddler"];
	if (observedTiddlers && observedTiddlers.length) {
		var s = "[["+observedTiddlers.join("]] [[")+"]]";
		e.setAttribute("observedTiddlers",s);
	}
};


})();

//}}}
!Commands
|!F2|Create a new screen |
|!F3, F4 |Switch between screens (forward, backward) |
|!^d |Close a screen |
|!^a A |Rename a screen |
/***
''Name:'' Calendar plugin
''Version:'' <<getversion calendar>> (<<getversiondate calendar "DD MMM YYYY">>)
''Author:'' SteveRumsby

''Configuration:''

|''First day of week:''|<<option txtCalFirstDay>>|(Monday = 0, Sunday = 6)|
|''First day of weekend:''|<<option txtCalStartOfWeekend>>|(Monday = 0, Sunday = 6)|

''Syntax:'' 
|{{{<<calendar>>}}}|Produce a full-year calendar for the current year|
|{{{<<calendar year>>}}}|Produce a full-year calendar for the given year|
|{{{<<calendar year month>>}}}|Produce a one-month calendar for the given month and year|
|{{{<<calendar thismonth>>}}}|Produce a one-month calendar for the current month|
|{{{<<calendar lastmonth>>}}}|Produce a one-month calendar for last month|
|{{{<<calendar nextmonth>>}}}|Produce a one-month calendar for next month|

***/
// //Modify this section to change the text displayed for the month and day names, to a different language for example. You can also change the format of the tiddler names linked to from each date, and the colours used.

// // ''Changes by ELS 2005.10.30:''
// // config.macros.calendar.handler()
// // ^^use "tbody" element for IE compatibility^^
// // ^^IE returns 2005 for current year, FF returns 105... fix year adjustment accordingly^^
// // createCalendarDays()
// // ^^use showDate() function (if defined) to render autostyled date with linked popup^^
// // calendar stylesheet definition
// // ^^use .calendar class-specific selectors, add text centering and margin settings^^

//{{{
config.macros.calendar = {};

config.macros.calendar.monthnames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
config.macros.calendar.daynames = ["M", "T", "W", "T", "F", "S", "S"];

config.macros.calendar.weekendbg = "#c0c0c0";
config.macros.calendar.monthbg = "#e0e0e0";
config.macros.calendar.holidaybg = "#ffc0c0";

//}}}
// //''Code section:''
// (you should not need to alter anything below here)//
//{{{
if(config.options.txtCalFirstDay == undefined)
  config.options.txtCalFirstDay = 0;
if(config.options.txtCalStartOfWeekend == undefined)
  config.options.txtCalStartOfWeekend = 5;

config.macros.calendar.tiddlerformat = "0DD/0MM/YYYY";  // This used to be changeable - for now, it isn't// <<smiley :-(>> 

version.extensions.calendar = { major: 0, minor: 6, revision: 0, date: new Date(2006, 1, 22)};
config.macros.calendar.monthdays = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

config.macros.calendar.holidays = [ ]; // Not sure this is required anymore - use reminders instead
//}}}

// //Is the given date a holiday?
//{{{
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;
}
//}}}

// //The main entry point - the macro handler.
// //Decide what sort of calendar we are creating (month or year, and which month or year)
// // Create the main calendar container and pass that to sub-ordinate functions to create the structure.
// ELS 2005.10.30: added creation and use of "tbody" for IE compatibility and fixup for year >1900//
// ELS 2005.10.30: fix year calculation for IE's getYear() function (which returns '2005' instead of '105')//
//{{{
config.macros.calendar.handler = function(place,macroName,params)
{
   var calendar = createTiddlyElement(place, "table", null, "calendar", null);
   var tbody = createTiddlyElement(calendar, "tbody", null, null, null);
   var today = new Date();
   var year = today.getYear();
   if (year<1900) year+=1900;
   if (params[0] == "thismonth")
  {
      cacheReminders(new Date(year, today.getMonth(), 1, 0, 0), 31);
      createCalendarOneMonth(tbody, year, today.getMonth());
  } 
  else if (params[0] == "lastmonth") {
      var month = today.getMonth()-1; if (month==-1) { month=11; year--; }
      cacheReminders(new Date(year, month, 1, 0, 0), 31);
      createCalendarOneMonth(tbody, year, month);
   }
   else if (params[0] == "nextmonth") {
      var month = today.getMonth()+1; if (month>11) { month=0; year++; }
      cacheReminders(new Date(year, month, 1, 0, 0), 31);
      createCalendarOneMonth(tbody, year, month);
   }
   else {
      if (params[0]) year = params[0];
      if(params[1])
      {
         cacheReminders(new Date(year, params[1]-1, 1, 0, 0), 31);
         createCalendarOneMonth(tbody, year, params[1]-1);
      }
      else
      {
         cacheReminders(new Date(year, 0, 1, 0, 0), 366);
         createCalendarYear(tbody, year);
      }
   }
  window.reminderCacheForCalendar = null;
}
//}}}
//{{{
//This global variable is used to store reminders that have been cached
//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", null, null, null);
  createCalendarMonthHeader(calendar, row, config.macros.calendar.monthnames[mon] + " " + year, true, year, mon);
  row = createTiddlyElement(calendar, "tr", null, null, null);
  createCalendarDayHeader(row, 1);
  createCalendarDayRowsSingle(calendar, year, mon);
}
//}}}

//{{{
function createCalendarMonth(calendar, year, mon)
{
  var row = createTiddlyElement(calendar, "tr", null, null, null);
  createCalendarMonthHeader(calendar, row, config.macros.calendar.monthnames[mon] + " " + year, false, year, mon);
  row = createTiddlyElement(calendar, "tr", null, null, null);
  createCalendarDayHeader(row, 1);
  createCalendarDayRowsSingle(calendar, year, mon);
}
//}}}

//{{{
function createCalendarYear(calendar, year)
{
  var row;
  row = createTiddlyElement(calendar, "tr", null, null, null);
  var back = createTiddlyElement(row, "td", null, null, null);
  var backHandler = function() {
      removeChildren(calendar);
      createCalendarYear(calendar, year-1);
    };
  createTiddlyButton(back, "<", "Previous year", backHandler);
  back.align = "center";

  var yearHeader = createTiddlyElement(row, "td", null, "calendarYear", year);
  yearHeader.align = "center";
  yearHeader.setAttribute("colSpan", 19);

  var fwd = createTiddlyElement(row, "td", null, null, null);
  var fwdHandler = function() {
    removeChildren(calendar);
    createCalendarYear(calendar, year+1);
  };
  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", null, null, null);
  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", null, null, null);
  createCalendarDayHeader(row, 3);
  createCalendarDayRows(cal, year, mon);
}
//}}}

//{{{
function createCalendarMonthHeader(cal, row, name, nav, year, mon)
{
  var month;
  if(nav) {
    var back = createTiddlyElement(row, "td", null, null, null);
    back.align = "center";
    back.style.background = config.macros.calendar.monthbg;

/*
    back.setAttribute("colSpan", 2);

    var backYearHandler = function() {
      var newyear = year-1;
      removeChildren(cal);
      cacheReminders(new Date(newyear, mon , 1, 0, 0), 31);
      createCalendarOneMonth(cal, newyear, mon);
    };
    createTiddlyButton(back, "<<", "Previous year", backYearHandler);
*/
    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);
    };
    createTiddlyButton(back, "<", "Previous month", backMonHandler);


    month = createTiddlyElement(row, "td", null, "calendarMonthname", name)
//    month.setAttribute("colSpan", 3);
    month.setAttribute("colSpan", 5);

    var fwd = createTiddlyElement(row, "td", null, null, null);
    fwd.align = "center";
    fwd.style.background = config.macros.calendar.monthbg; 

//    fwd.setAttribute("colSpan", 2);
    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);
    };
    createTiddlyButton(fwd, ">", "Next month", fwdMonHandler);
/*
    var fwdYear = createTiddlyElement(row, "td", null, null, null);
    var fwdYearHandler = function() {
      var newyear = year+1;
      removeChildren(cal);
      cacheReminders(new Date(newyear, mon , 1, 0, 0), 31);
      createCalendarOneMonth(cal, newyear, mon);
    };
    createTiddlyButton(fwd, ">>", "Next year", fwdYearHandler);
*/
  } else {
    month = createTiddlyElement(row, "td", null, "calendarMonthname", name)
    month.setAttribute("colSpan", 7);
  }
  month.align = "center";
  month.style.background = config.macros.calendar.monthbg;
}
//}}}

//{{{
function createCalendarDayHeader(row, num)
{
  var cell;
  for(var i = 0; i < num; i++) {
    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;
  for(i = 0; i < col; i++) {
    createTiddlyElement(row, "td", null, null, null);
  }
  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", null, null, null);
    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 2005.10.30: use <<date>> macro's showDate() function to create popup
      if (window.showDate) {
        showDate(daycell,celldate,"popup","DD","DD-MMM-YYYY",true, isaWeekend); 
      } else {
        if(isaWeekend) daycell.style.background = config.macros.calendar.weekendbg;
        var title = celldate.formatString(config.macros.calendar.tiddlerformat);
        if(calendarIsHoliday(celldate)) {
          daycell.style.background = config.macros.calendar.holidaybg;
        }
        if(window.findTiddlersWithReminders == null) {
          var link = createTiddlyLink(daycell, title, false);
          link.appendChild(document.createTextNode(day));
        } else {
          var button = createTiddlyButton(daycell, day, title, onClickCalendarDate);
        }
      }
    }
    day++;
  }
}
//}}}

// //We've clicked on a day in a calendar - create a suitable pop-up of options.
// //The pop-up should contain:
// // * a link to create a new entry for that date
// // * a link to create a new reminder for that date
// // * an <hr>
// // * the list of reminders for that date
//{{{
function onClickCalendarDate(e)
{
  var button = this;
  var date = button.getAttribute("title");
  var dat = new Date(date.substr(6,4), date.substr(3,2)-1, date.substr(0, 2));

  date = dat.formatString(config.macros.calendar.tiddlerformat);
  var popup = createTiddlerPopup(this);
  popup.appendChild(document.createTextNode(date));
  var newReminder = function() {
    var t = store.getTiddlers(date);
    displayTiddler(null, date, 2, null, null, false, false);
    if(t) {
      document.getElementById("editorBody" + date).value += "\n<<reminder day:" + dat.getDate() +
                                                                                         " month:" + (dat.getMonth()+1) +
                                                                                         " year:" + (dat.getYear()+1900) + " title: >>";
    } else {
      document.getElementById("editorBody" + date).value = "<<reminder day:" + dat.getDate() +
                                                                                       " month:" + (dat.getMonth()+1) +
                                                                                       " year:" + (dat.getYear()+1900) + " title: >>";
    }
  };
  var link = createTiddlyButton(popup, "New reminder", null, newReminder); 
  popup.appendChild(document.createElement("hr"));

  var t = findTiddlersWithReminders(dat, [0,14], null, 1);
  for(var i = 0; i < t.length; i++) {
    link = createTiddlyLink(popup, t[i].tiddler, false);
    link.appendChild(document.createTextNode(t[i].tiddler));
  }
}
//}}}

//{{{
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", null, null, null);

 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", null, null, null);
 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", null, null, null);

 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", null, null, null);
 createCalendarDays(row, 0, day1, max1, year, mon); day1 += 7;
 }
}
//}}}

// //ELS 2005.10.30: added styles
//{{{
setStylesheet(".calendar, .calendar table, .calendar th, .calendar tr, .calendar td { font-size:10pt; text-align:center; } .calendar, .calendar a { margin:0px !important; padding:0px !important; }", "calendarStyles");
//}}}
/*{{{*/

	span.chunkyButton {
		display: block;
		padding: 0;
		margin: 0;
		border: solid 2px [[ColorPalette::Foreground]];
	}
	span.chunkyButton a.button, span.chunkyButton a:active.button {
		white-space: nowrap;
		font-weight: bold;
		font-size:1.8em;
		color: [[ColorPalette::Background]];
		background-color: [[ColorPalette::PrimaryMid]];
		text-align: center;
		padding: 1em 3em;
		margin: 0;
		border-style: none;
		border-top: solid 1px [[ColorPalette::Background]];
		display: block;
	}
	span.chunkyButton a.button:hover {
		background-color: [[ColorPalette::PrimaryDark]];
		border-style: none;
		color: [[ColorPalette::Background]];
		border-top: solid 1px [[ColorPalette::PrimaryPale]];
	}
	
	.downloadButton table,
	.downloadButton table tr,
	.downloadButton table td  {
		border-style:none;
		vertical-align:bottom;
		padding:0 0.5em 0 0;
		margin:0 0 0.2em 0;
	}


/*}}}*/
Background: #fff
Foreground: #000
PrimaryPale: #8cf
PrimaryLight: #18f
PrimaryMid: #04b
PrimaryDark: #014
SecondaryPale: #F7D278
SecondaryLight: #FCC026
SecondaryMid: #FCAF1C
SecondaryDark: #EF840D
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88

<<paletteView [[ColorPalette]]>>
!What is Commenting?
Commenting is the process of marking content in a program such that it is ignored by the Ruby interpreter. This is typically used so that the programmer can write notes alongside the code describing what that code does such that other humans who look at the code will have a better chance of understanding the code. 

Comments can span multiple lines, occupy a single line, or be tacked onto the end of a line of code. 

!Single Line Ruby Comments 
Single line comments in a Ruby script are defined with the '#' character. For example, to add a single line comment to a simple script: 
{{{
# This is a comment line
print "Welcome to Ruby!"
}}}
Comments lines can be grouped together: 
{{{
# This is a comment line
# it explains that the next line of code displays 
# a welcome message
}}}

!Comments on Lines of Code 
It is common practice to place comments on the same line as the associated code to which the comment applies. For example, if we wanted to place a comment on the same line as our print statement we would do so by placing the comment after a '#' marker: 
{{{
print "Welcome to Ruby!"     # prints the welcome message
}}}

Note that everything on the line after the '#' is ignored by the Ruby interpreter. You cannot, therefore, put more code after the '#' and expect it to executed. Additional code must be placed on the next line. 

!Multi Line or Block Ruby Comments 
Multiple lines of text or code can be defined as comments using the Ruby =begin and =end comment markers. These are known as the comment block markers. 

For example, to provide a comment block containing multiple lines of descriptive text: 
{{{
=begin
This is a comment line.
It explains that the next line of code displays 
a welcome message.
print "Welcome to Ruby Essentials!"
print "Everything you need to know about Ruby"
=end
}}}
//{{{
// Set the default non-existing automatic WikiLinks behaviour to disabled.
config.options.chkDisableNonExistingWikiLinks=true;

// Set the default behaviour to hide editing options over HTTP.
config.options.chkHttpReadOnly = true;
//}}}
[[PageTemplate]]
|>|>|SiteTitle - SiteSubtitle|
|MainMenu|DefaultTiddlers<br><br><br><br>ViewTemplate<br><br>EditTemplate|SideBarOptions|
|~|~|OptionsPanel|
|~|~|AdvancedOptions|
|~|~|<<tiddler Configuration.SideBarTabs>>|

[[StyleSheet]]: StyleSheetColors - StyleSheetLayout - StyleSheetPrint

[[SiteUrl]]
[[SideBarTabs]]
|[[Timeline|TabTimeline]]|[[All|TabAll]]|[[Tags|TabTags]]|>|>|[[More|TabMore]] |
|>|>||[[Missing|TabMoreMissing]]|[[Orphans|TabMoreOrphans]]|[[Shadowed|TabMoreShadowed]]|
Repository completed by: [[PiotrL|http://www.hlplanet.com/phpbb/profile.php?mode=viewprofile&u=3]]
<html>
<style>
.rolodex table {
border: 0px solid;
background-color:#FFFF99;
}

.rolodex tr, .rolodex td {
border: 0px solid;
}
</style>
<span class="rolodex">
 <table>
 <tr>
 <td align="right"><b>Firstname:</b></td>
 <td colspan="3"><input name=firstname type=text style="width:100%" /></td></tr>
 <tr>
 <td align="right"><b>Lastname:</b></td>
 <td colspan="3"><input name=lastname type=text style="width:100%" /></td></tr>
 <tr>
 <td align="right"><b>Email:</b></td>
 <td colspan="3"><input name=email type=text style="width:100%" /></td></tr>
 <tr>
 <td align="right"><b>Phone:</b></td>
 <td colspan="3"><input name=phone type=text 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 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 style="width:100%" >ZIP/Postal Code:</b></td>
 <td><input name=zip type=text size="5" style="width:100%" /></td></tr>
 <tr>
 <td align="right"><b>Country:</b></td>
 <td colspan="3"><input name=country type=text style="width:100%" /></td></tr>
 <tr>
 <td align="right"><b>Webpage:</b></td>
 <td colspan="3"><input name=webpage type=text style="width:100%" /></td></tr>
 <tr>
 <td colspan="4"><sub><b>Notes</b></sub><br>
 <textarea name=notes rows="4" cols="40" style="width:100%" ></textarea></td></tr>
</span>
</html>
@@margin-left:.5em;<<slider chkContents SideBarTabs "contents »" "show
lists of tiddlers contained in this document">>@@
A map topology is a simple topology that you can impose upon simple features on a map during an edit session. A map topology allows you to simultaneously edit simple features that overlap or touch each other in ArcMap.

# Bring up the //Topology// toolbar by choosing ''View -> Toolbars -> Topology''.
# On the //Editor// toolbar, click the Editor menu and click ''Start Editing''. 
# Click the ''Map topology'' button on the //Topology// toolbar: [img[http://webhelp.esri.com/arcgisdesktop/9.2/published_images/button_map_topology.gif]]
# Check the feature classes that will participate in the map topology (all the layers in your current edit session that can participate in the map topology are listed in the dialog box).
# Optionally, set a cluster tolerance for the map topology (the default cluster tolerance is the minimum possible cluster tolerance)
# Click ''OK''.
# Click the ''Topology Edit'' tool  and click the features you want to edit using the map topology: [img[http://webhelp.esri.com/arcgisdesktop/9.2/published_images/button_topology_edit.gif]]
# The map topology is created for the features that are visible in the current display extent.
! Create a new polygon
# Open ArcMap and add the required shapefile.
# Bring up the //Editor// toolbar by choosing ''View -> Toolbars -> Editor''.
# Choose ''Edit -> Start Editing'' (make sure the //Editor// toolbar shows Task: Create new feature and the Target is your shapefile).
# Click the Sketch (pencil) icon on the //Editor// toolbar: [img[http://webhelp.esri.com/arcgisdesktop/9.2/published_images/button_sketch.gif]]
# Left-click to place a point.
# To close the polygon, either press F2, double-click, or right-click and choose ''Close polygon''.
# In the //Editor// toolbar, choose ''Save Edits''.

!Creating an adjacent polygon
When creating polygons of land uses, soils, counties or property ownership, you often need to create polygons next to one another. The polygons should share a border, but you want to avoid digitizing the border twice or having overlaps or spaces between polygons. You can use the ''Auto-Complete Polygon'' task when creating new polygons to help ensure that your data forms a continuous fabric.
# In the //Editor// toolbar, set the task to ''Auto-Complete Polygon''
# Click the //Sketch// tool.
# Construct the new polygon feature. The polygon must cross or intersect the existing polygon edge at least two times for the new polygon to be created.
# Finish the polygon as normal.

!Splitting a polygon by an overlapping line feature 
If you have a line feature that crosses a polygon, you can use the line to split the polygon, for example, if you want to divide an administrative boundary at a road or river.
# Select the polygon.
# Set the task to ''Cut Polygon Features''.
# Click the //Sketch// tool.
# Right-click the line and click ''Replace Sketch''. This lets you see the line's vertices and segments as an edit sketch.
# Finish the sketch (press F2 so you don't inadvertently add more segments to the sketch when you finish it). The polygon is split where the line crosses it.
The general for creating a shapefile process is to:
# Create the new shape file in ArcCatalog (including specifying a coordinate system).
# Create an accompanying table and fields in ArcCatalog.
# Begin the metadata documentation in ArcCatalog.
# Add the new (empty) shape file in ArcMap and start editing the new shape file in ArcMap using the Editor menu functions.

!Creating a shapefile in ArcCatalogue
# Click on the ''Contents'' tab, and navigate to the right file location.
# Create a new shapefile by choosing ''File -> New -> Shapefile''.
# Specify the name and feature type (point, line, or polygon).
# Specify a coordinate/projection system by pressing the ''Edit...'' button.
## Click the ''Select..'' button to browse for a predefined coordinate system. 
## Select ''Geographic Coordinate Systems -> Australia and New Zealand -> Geocentric Datum of Australia 1994.prj''.

!Creating the attribute table
# In ArcCatalogue, right-click on the new shapefile and choose ''Properties''.
# Click on the ''Fields'' tabs and add the required fields (name the field, define the type, etc).

!Start the metadata documentation
# In ArcCatalogue, click the shapefile, then click the Metadata tab.
# To start documentation, click on the ''Edit Metadata'' icon on the //Metadata Tools// menu bar.
Here are some examples that show the usage of tiddler data, as provided by the DataTiddlerPlugin.
<<forEachTiddler
 where
 'tiddler.tags.contains("DataTiddlerExample")'
>>
/***
|''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;

//}}}

Several [[Macros]] including the TodayMacro take a DateFormatString as an optional argument. This string can be a combination of ordinary text, with some special characters that get substituted by parts of the date:
* {{{DDD}}} - day of week in full (eg, "Monday")
* {{{ddd}}} - short day of week (eg, "Mon")
* {{{DD}}} - day of month
* {{{0DD}}} - adds a leading zero
* {{{DDth}}} - adds a suffix
* {{{WW}}} - ISO-8601 week number of year
* {{{0WW}}} - adds a leading zero
* {{{MMM}}} - month in full (eg, "July")
* {{{mmm}}} - short month (eg, "Jul")
* {{{MM}}} - month number
* {{{0MM}}} - adds leading zero
* {{{YYYY}}} - full year
* {{{YY}}} - two digit year
* {{{wYYYY}}} - full year with respect to week number
* {{{wYY}}} two digit year with respect to week number
* {{{hh}}} - hours
* {{{0hh}}} - adds a leading zero
* {{{hh12}}} - hours in 12 hour clock
* {{{0hh12}}} - hours in 12 hour clock with leading zero
* {{{mm}}} - minutes
* {{{0mm}}} - minutes with leading zero
* {{{ss}}} - seconds
* {{{0ss}}} - seconds with leading zero
* {{{am}}} or {{{pm}}} - lower case AM/PM indicator
* {{{AM}}} or {{{PM}}} - upper case AM/PM indicator

/***
|Name|DeleteAllTaggedPlugin|
|Source|http://ido-xp.tiddlyspot.com/#DeleteAllTaggedPlugin|
|Version|1.0|
|Modifications|2007/08/17 Modified by PiotrL for "tags" param|

An adaptation of DeleteDoneTasks (Simon Baird) by Ido Magal
To use this insert {{{<<deleteAllTagged>>}}} into the desired tiddler.

Example usage:
{{{<<deleteAllTagged>>}}}
<<deleteAllTagged>>

{{{<<deleteAllTagged title:"Test with tags" tags:'requiredTag,requiredTag2'>>}}}
<<deleteAllTagged title:"Test with tags" tags:'requiredTag,requiredTag2'>>

{{{<<deleteAllTagged title:'Test with tags' tags:requiredTag>>}}}
<<deleteAllTagged title:'Test with tags' tags:requiredTag include_this:yes>>
***/
//{{{

config.macros.deleteAllTagged = {
	handler: function ( place,macroName,params,wikifier,paramString,tiddler ) {
		var buttonTitle = "Delete Tagged w/ '"+tiddler.title+"'";
		var alsoDeleteThisTiddler = "";
//--->PL
 var parameters = paramString.parseParams("name",null,true);
 var pTags = parameters[0]["tags"]?parameters[0]["tags"][0].split(","):[];

 if (parameters[0]["title"])
   buttonTitle = parameters[0]["title"];
 if (parameters[0]["include_this"])
   alsoDeleteThisTiddler = parameters[0]["include_this"] == "yes"?"delete":"";

var tagsParam = "";
if(pTags.length == 0)
  pTags.push(tiddler.title);
tagsParam = pTags.join("|"); 
  
//<---PL


		createTiddlyButton( place, buttonTitle, "Delete every tiddler tagged with '"+tagsParam+"'", this.deleteAllTagged( tagsParam, alsoDeleteThisTiddler == "delete" ));
	},

	deleteAllTagged: function(tag,deleteMe) {
		return function() {
//--->PL
                        var tags = tag.split("|");
                        var tagMatchCnt = 0; 
//<---PL

			var collected = [];
			store.forEachTiddler( function ( title,tiddler ) {
//--->PL
tagMatchCnt = 0;
for (t=0; t<tags.length; t++)
 if ( tiddler.tags.contains( tags[t] ))
   tagMatchCnt++;

if (tagMatchCnt == tags.length && tagMatchCnt > 0)
//<---PL
				{
					collected.push( title );
				}
			});
			if ( collected.length == 0 )
			{
				alert( "No tiddlers found tagged with '"+tag+"'." );
			}
			else
			{
				if ( confirm( "These tiddlers are tagged with '"+tag+"'\n'"
						+ collected.join( "', '" ) + "'\n\n\n"
						+ "Are you sure you want to delete these?" ))
				{
					for ( var i=0;i<collected.length;i++ )
					{
						store.deleteTiddler( collected[i] );
						story.closeTiddler( collected[i], true );
						displayMessage( "Deleted '"+collected[i]+"'" );
					}
				}
			}
			if (deleteMe)
			{
				if ( confirm( "Also delete this tiddler ('"+tag+"')?" ) )
				{
					store.deleteTiddler( tag );
					story.closeTiddler( tag, true );
					displayMessage( "Deleted '"+tag+"'" );
				}
			}
		}
	}
};

//}}}


/***
Example usage:
{{{<<deleteDone>>}}}
<<deleteDone>>
{{{<<deleteDone daysOld:20 title:'delete old'>>}}}
<<deleteDone daysOld:30 title:'delete old'>>

TODO merge these two

***/
//{{{




config.macros.deleteDone = {
	handler: function (place,macroName,params,wikifier,paramString,tiddler) {
		var namedParams = (paramString.parseParams('daysOld'))[0];
		var daysOld = namedParams['daysOld'] ? namedParams['daysOld'][0] : 30; // default
		var buttonTitle = namedParams['title'] ? namedParams['title'][0] : "Delete Done Actions";
		createTiddlyButton(place,buttonTitle,"Delete done actions older than "+daysOld+" days old",this.deleteDone(daysOld));
	},

	deleteDone: function(daysOld) {
		return function() {
			var collected = [];
			var compareDate = new Date();
			compareDate.setDate(compareDate.getDate() - daysOld);
			store.forEachTiddler(function (title,tiddler) {
				if (tiddler.tags.containsAll(["Action","Done"])
							&& tiddler.modified < compareDate) {
					collected.push(title);
				}
			});
			if (collected.length == 0) {
				alert("No done actions found older than "+daysOld+" days");
			}
			else {
				if (confirm("Done actions older than "+daysOld+" days:\n'"
						+ collected.join("', '") + "'\n\n\n"
						+ "Are you sure you want to delete these actions?")) {
					for (var i=0;i<collected.length;i++) {
						store.removeTiddler(collected[i]);
						displayMessage("Deleted '"+collected[i]+"'");
						story.closeTiddler( collected[i], true );
					}
				}
			}
		}
	}
};

//}}}

/***
|Name|DisableWikiLinksPlugin|
|Source|http://www.TiddlyTools.com/#DisableWikiLinksPlugin|
|Version|1.6.0|
|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|Tiddler.prototype.autoLinkWikiWords, 'wikiLink' formatter|
|Options|##Configuration|
|Description|selectively disable TiddlyWiki's automatic ~WikiWord linking behavior|
This plugin allows you to disable TiddlyWiki's automatic ~WikiWord linking behavior, so that WikiWords embedded in tiddler content will be rendered as regular text, instead of being automatically converted to tiddler links.  To create a tiddler link when automatic linking is disabled, you must enclose the link text within {{{[[...]]}}}.
!!!!!Usage
<<<
You can block automatic WikiWord linking behavior for any specific tiddler by ''tagging it with<<tag excludeWikiWords>>'' (see configuration below) or, check a plugin option to disable automatic WikiWord links to non-existing tiddler titles, while still linking WikiWords that correspond to existing tiddlers titles or shadow tiddler titles.  You can also block specific selected WikiWords from being automatically linked by listing them in [[DisableWikiLinksList]] (see configuration below), separated by whitespace.  This tiddler is optional and, when present, causes the listed words to always be excluded, even if automatic linking of other WikiWords is being permitted.  

Note: WikiWords contained in default ''shadow'' tiddlers will be automatically linked unless you select an additional checkbox option lets you disable these automatic links as well, though this is not recommended, since it can make it more difficult to access some TiddlyWiki standard default content (such as AdvancedOptions or SideBarTabs)
<<<
!!!!!Configuration
<<<
<<option chkDisableWikiLinks>> Disable ALL automatic WikiWord tiddler links
<<option chkAllowLinksFromShadowTiddlers>> ... except for WikiWords //contained in// shadow tiddlers
<<option chkDisableNonExistingWikiLinks>> Disable automatic WikiWord links for non-existing tiddlers
Disable automatic WikiWord links for words listed in: <<option txtDisableWikiLinksList>>
Disable automatic WikiWord links for tiddlers tagged with: <<option txtDisableWikiLinksTag>>
<<<
!!!!!Revisions
<<<
2008.07.22 [1.6.0] hijack tiddler changed() method to filter disabled wiki words from internal links[] array (so they won't appear in the missing tiddlers list)
2007.06.09 [1.5.0] added configurable txtDisableWikiLinksTag (default value: "excludeWikiWords") to allows selective disabling of automatic WikiWord links for any tiddler tagged with that value.
2006.12.31 [1.4.0] in formatter, test for chkDisableNonExistingWikiLinks
2006.12.09 [1.3.0] in formatter, test for excluded wiki words specified in DisableWikiLinksList
2006.12.09 [1.2.2] fix logic in autoLinkWikiWords() (was allowing links TO shadow tiddlers, even when chkDisableWikiLinks is TRUE).  
2006.12.09 [1.2.1] revised logic for handling links in shadow content
2006.12.08 [1.2.0] added hijack of Tiddler.prototype.autoLinkWikiWords so regular (non-bracketed) WikiWords won't be added to the missing list
2006.05.24 [1.1.0] added option to NOT bypass automatic wikiword links when displaying default shadow content (default is to auto-link shadow content)
2006.02.05 [1.0.1] wrapped wikifier hijack in init function to eliminate globals and avoid FireFox 1.5.0.1 crash bug when referencing globals
2005.12.09 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.DisableWikiLinksPlugin= {major: 1, minor: 6, revision: 0, date: new Date(2008,7,22)};

if (config.options.chkDisableNonExistingWikiLinks==undefined) config.options.chkDisableNonExistingWikiLinks= false;
if (config.options.chkDisableWikiLinks==undefined) config.options.chkDisableWikiLinks=false;
if (config.options.txtDisableWikiLinksList==undefined) config.options.txtDisableWikiLinksList="DisableWikiLinksList";
if (config.options.chkAllowLinksFromShadowTiddlers==undefined) config.options.chkAllowLinksFromShadowTiddlers=true;
if (config.options.txtDisableWikiLinksTag==undefined) config.options.txtDisableWikiLinksTag="excludeWikiWords";

// find the formatter for wikiLink and replace handler with 'pass-thru' rendering
initDisableWikiLinksFormatter();
function initDisableWikiLinksFormatter() {
	for (var i=0; i<config.formatters.length && config.formatters[i].name!="wikiLink"; i++);
	config.formatters[i].coreHandler=config.formatters[i].handler;
	config.formatters[i].handler=function(w) {
		// supress any leading "~" (if present)
		var skip=(w.matchText.substr(0,1)==config.textPrimitives.unWikiLink)?1:0;
		var title=w.matchText.substr(skip);
		var exists=store.tiddlerExists(title);
		var inShadow=w.tiddler && store.isShadowTiddler(w.tiddler.title);
		// check for excluded Tiddler
		if (w.tiddler && w.tiddler.isTagged(config.options.txtDisableWikiLinksTag))
			{ w.outputText(w.output,w.matchStart+skip,w.nextMatch); return; }
		// check for specific excluded wiki words
		var t=store.getTiddlerText(config.options.txtDisableWikiLinksList);
		if (t && t.length && t.indexOf(w.matchText)!=-1)
			{ w.outputText(w.output,w.matchStart+skip,w.nextMatch); return; }
		// if not disabling links from shadows (default setting)
		if (config.options.chkAllowLinksFromShadowTiddlers && inShadow)
			return this.coreHandler(w);
		// check for non-existing non-shadow tiddler
		if (config.options.chkDisableNonExistingWikiLinks && !exists)
			{ w.outputText(w.output,w.matchStart+skip,w.nextMatch); return; }
		// if not enabled, just do standard WikiWord link formatting
		if (!config.options.chkDisableWikiLinks)
			return this.coreHandler(w);
		// just return text without linking
		w.outputText(w.output,w.matchStart+skip,w.nextMatch)
	}
}

Tiddler.prototype.coreAutoLinkWikiWords = Tiddler.prototype.autoLinkWikiWords;
Tiddler.prototype.autoLinkWikiWords = function()
{
	// if all automatic links are not disabled, just return results from core function
	if (!config.options.chkDisableWikiLinks)
		return this.coreAutoLinkWikiWords.apply(this,arguments);
	return false;
}

Tiddler.prototype.disableWikiLinks_changed = Tiddler.prototype.changed;
Tiddler.prototype.changed = function()
{
	this.disableWikiLinks_changed.apply(this,arguments);
	// remove excluded wiki words from links array
	var t=store.getTiddlerText(config.options.txtDisableWikiLinksList,"").readBracketedList();
	if (t.length) for (var i=0; i<t.length; i++)
		if (this.links.contains(t[i]))
			this.links.splice(this.links.indexOf(t[i]),1);
};
//}}}
<<tiddler ListTaggedTiddlers with:"Django">>
<<autoRefresh>>
{{sectionTOC{}}}
!Template Loading
Django provides a convenient and powerful API for loading templates from the filesystem, with the goal of removing redundancy both in your template-loading calls and in your templates themselves. To use this template-loading API, first you’ll need to tell the framework where you store your templates. The place to do this is in your settings file — {{{settings.py}}}.
Open {{{settings.py}}} and find the {{{TEMPLATE_DIRS}}} setting. By default, it’s an empty tuple:
{{{
TEMPLATE_DIRS = (
  # Put strings here, like "/home/html/django_templates"
  # or "C:/www/django/templates".
  # Always use forward slashes, even on Windows.
  # Don't forget to use absolute paths, not relative paths.
)
}}}
''NOTE'': Even if your TEMPLATE_DIRS contains only one directory, don’t forget the comma at the end of the directory string!

If you want to be a bit more flexible and decoupled, though, you can take advantage of the fact that Django settings files are just Python code by constructing the contents of {{{TEMPLATE_DIRS}}} dynamically, as in this example:
{{{
import os.path
TEMPLATE_DIRS = (
os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'),
)
}}}
This example uses the “magic” Python variable {{{__file__}}}, which is automatically set to the file name of the Python module in which the code lives. It gets the name of the directory that contains settings.py ({{{os.path.dirname}}}), joins that with templates in a cross-platform way (os.path.join), then ensures that everything uses forward slashes instead of backslashes (in the case of Windows).

!!Shortcuts
Django provides a shortcut that lets you load a template, render it, and return an HttpResponse—all in one line of code. This shortcut is a function called {{{render_to_response()}}}, which lives in the module {{{django.shortcuts}}}. Example:
{{{
from django.shortcuts import render_to_response
import datetime
def current_datetime(request):
  now = datetime.datetime.now()
  return render_to_response('current_datetime.html', {'current_date': now})
}}}

!Subdirectories
Storing templates in subdirectories of your template directory is easy. In your calls to {{{get_template()}}}, just include the subdirectory name and a slash before the template name, like so:
{{{
t = get_template('dateapp/current_datetime.html')
}}}
Because render_to_response() is a small wrapper around get_template(), you can do the same thing with the first argument to render_to_response(), like this:
{{{
return render_to_response('dateapp/current_datetime.html', {'current_date': now})
}}}
''NOTE'': use foward slashes rather than backslashes in Windows systems.

!The {{{include}}} Template Tag
The {{{{% include %}}}} tag allows you to include the contents of another template. This example includes the contents of the template nav.html:
{{{
{% include 'nav.html' %}
}}}
This example includes the contents of the template includes/nav.html:
{{{
{% include 'includes/nav.html' %}
}}}
The following example includes the contents of the template whose name is contained in
the variable template_name:
{{{
{% include template_name %}
}}}

!Template Inheritance
Django’s template-inheritance system is an inside-out version of server-side includes. Instead of defining the HTML snippets that are common, you define the snippets that are different. The first step is to define a base template — a skeleton of your page that child templates will later fill in. Here’s an example base template:
{{{
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
  <title>{% block title %}{% endblock %}</title>
</head>
<body>
  <h1>My helpful timestamp site</h1>
  {% block content %}{% endblock %}
  {% block footer %}
  <hr>
  <p>Thanks for visiting my site.</p>
  {% endblock %}
</body>
</html>
}}}
It’s the job of child templates to override, add to, or leave alone the contents of the blocks. An example child template is as follows:
{{{
{% extends "base.html" %}

{% block title %}The current time{% endblock %}

{% block content %}
<p>It is now {{ current_date }}.</p>
{% endblock %}
}}}
When you load the child template, the template engine sees the {% extends %} tag, noting that this template is a child template. The engine immediately loads the parent template. At that point, the template engine notices the three {% block %} tags in the parent template and replaces those blocks with the contents of the child template. So, the title we’ve defined in {% block title %} will be used, as will the {% block content %}.

You can use as many levels of inheritance as needed. One common way of using inheritance
is the following three-level approach:
# Create a base.html template that holds the main look and feel of your site. This is the stuff that rarely, if ever, changes.
# Create a base_SECTION.html template for each “section” of your site (e.g., base_photos.html and base_forum.html). These templates extend base.html and include section-specific styles/design.
# Create individual templates for each type of page, such as a forum page or a photo gallery. These templates extend the appropriate section template.

!!Notes
* If you use {{{{% extends %}}}} in a template, it must be the first template tag in that template. Otherwise, template inheritance won’t work.
* You may not define multiple {% block %} tags with the same name in the same template.

<<sectionTOC>>
{{sectionTOC{}}}
!Tags
!!{{{if/else}}}
The {{{{% if %}}}} tag evaluates a variable, and if that variable is True (i.e., it exists, is not empty, and is not False), the system will display everything between {{{{% if %}}}} and {{{{% endif %}}}}, as in this example:
{{{
{% if today_is_weekend %}
  <p>Welcome to the weekend!</p>
{% else %}
  <p>Get back to work.</p>
{% endif %}
}}}
Note that there is no {{{{% elif %}}}} tag. Use nested if tags to accomplish the job.

!!{{{for}}}
The {% for %} tag allows you to loop over each item in a sequence. As in Python’s for statement, the syntax is for X in Y, where Y is the sequence to loop over and X is the name of the variable to use for a particular cycle of the loop. Each time through the loop, the template system will render everything between {% for %} and {% endfor %}
{{{
<ul>
{% for athlete in athlete_list %}
  <li>{{ athlete.name }}</li>
{% endfor %}
</ul>
}}}
Add reversed to the tag to loop over the list in reverse:
{{{
{% for athlete in athlete_list reversed %}
  <li>{{ athlete.name }}</li>
{% endfor %}
}}}
Nest {% for %} tags:
{{{
{% for athlete in athlete_list %}
  <h1>{{ athlete.name }}</h1>
  <ul>
  {% for sport in athlete.sports_played %}
    <li>{{ sport }}</li>
  {% endfor %}
  </ul>
{% endfor %}
}}}
A common pattern is to check the size of the list before looping over it, and outputting some special text if the list is empty. Because this pattern is so common, the for tag supports an optional {% empty %} clause that lets you define what to output if the list is empty:
{{{
{% for athlete in athlete_list %}
  <p>{{ athlete.name }}</p>
{% empty %}
  <p>There are no athletes. Only computer programmers.</p>
{% endfor %}
}}}
Within each {{{{% for %} }}}loop, you get access to a template variable called {{{forloop}}}. This variable has a few attributes that give you information about the progress of the loop:
* {{{forloop.counter}}} - is always set to an integer representing the number of times the loop has been entered.
* {{{forloop.counter0}}} - as above, except zero-indexed.
* {{{forloop.revcounter}}} - always set to an integer representing the number of remaining items in the loop. The first time through the loop, forloop.revcounter will be set to the total number of items in the sequence you’re traversing. The last time through the loop, forloop.revcounter will be set to 1.
* {{{forloop.revcounter0}}} - as above, except zero-indexed. The last time through the loop, it will be set to 0.
* {{{forloop.first}}} - is a Boolean value set to True if this is the first time through the loop.
* {{{forloop.last}}} - is a Boolean value set to True if this is the last time through the loop. A common use for this is to put pipe characters between a list of links:
{{{
{% for link in links %}{{ link }}{% if not forloop.last %} | {% endif %}{% endfor %}
}}}
This might output something like this:
{{{Link1 | Link2 | Link3 | Link4}}}
* {{{forloop.parentloop}}} - a reference to the forloop object for the parent loop, in case of nested loops. Eg:
{{{
{% for country in countries %}
  <table>
  {% for city in country.city_list %}
    <tr>
    <td>Country #{{ forloop.parentloop.counter }}</td>
    <td>City #{{ forloop.counter }}</td>
    <td>{{ city }}</td>
    </tr>
  {% endfor %}
  </table>
{% endfor %}
}}}

!!{{{ifequal/ifnotequal}}}
The {% ifequal %} tag compares two values and displays everything between {% ifequal %} and {% endifequal %} if the values are equal. Just like {% if %}, the {% ifequal %} tag supports an optional {% else %}:
{{{
{% ifequal section 'sitenews' %}
  <h1>Site News</h1>
{% else %}
  <h1>No News Here</h1>
{% endifequal %}
}}}
Only template variables, strings, integers, and decimal numbers are allowed as arguments to {% ifequal %}. Any other types of variables, such as Python dictionaries, lists, or Booleans, can’t be used. These are valid examples:
{{{
{% ifequal variable 1 %}
{% ifequal variable 1.23 %}
{% ifequal variable 'foo' %}
{% ifequal variable "foo" %}
}}}
These are invalid examples:
{{{
{% ifequal variable True %}
{% ifequal variable [1, 2, 3] %}
{% ifequal variable {'key': 'value'} %}
}}}

!!Comments
Just as in HTML or Python, the Django template language allows for comments. To designate a comment, use {# #}:
{{{
{# This is a comment #}
}}}
If you want to use multiline comments, use the {% comment %} template tag, like this:
{{{
{% comment %}
This is a
multiline comment.
{% endcomment %}
}}}

!Filters
template filters are simple ways of altering the value of variables before they’re displayed. Filters use a pipe character. The example below displays the value of the {{{{{ name }} }}}variable after being filtered through the {{{lower}}} filter, which converts text to lowercase.:
{{{
{{ name|lower }}
}}}
Filters can be //chained// - that is, they can be used in tandem such that the output of one filter is applied to the next. Here’s an example that converts the first element in a list to uppercase:
{{{
{{ my_list|first|upper }}
}}}
Some filters take arguments. A filter argument comes after a colon and is always in double quotes. Here’s an example:
{{{
{{ bio|truncatewords:"30" }}
}}}
This displays the first 30 words of the bio variable. The following are a few of the most important filters:
* {{{addslashes}}}: Adds a backslash before any backslash, single quote, or double quote. This is useful if the produced text is included in a JavaScript string.
* {{{date}}}: Formats a date or datetime object according to a format string given in the parameter, as in this example:
{{{
{{ pub_date|date:"F j, Y" }}
}}}
* {{{length}}}: Returns the length of the value. For a list, this returns the number of elements. For a string, this returns the number of characters.
<<sectionTOC>>
!Create a new project or app
Open a command prompt in the directory and type:
{{{
django-admin.py startproject projectname
django-admin.py startapp appname
}}}

!Start the Development server
To start running the built-in development server:
{{{
python manage.py runserver
python manage.py runserver 0.0.0.0:8000
}}}
The first command starts the development server on port 8000, listening only for local connections. The second will listen for nonlocal connections on the specified port.

!Admin SQL commands

!Interactive interpreter session
To start an interactive interpreter session, open a command prompt inside a project directory then type: {{{python manage.py shell}}}

!Dumpdata
Example command line script:
{{{
django-admin.py dumpdata APP_NAME --indent=2 > FILENAME.yaml
}}}
Common polygon-editing tasks are described on the [[ArcGIS Help website|http://webhelp.esri.com/arcgisdesktop/9.2/index.cfm?TopicName=Common_polygon_editing_tasks]].

!Reshaping a polygon using a sketch
The Reshape Feature task lets you reshape a line or polygon by constructing a sketch over a selected feature. The feature takes the shape of the sketch from the first place the sketch intersects the feature to the last. If the endpoints are outside the polygon, the feature is cut away. When you reshape a line, the line takes the shape of the sketch you draw. You can snap the sketch to the selected edge or cross it to indicate where to start and stop reshaping. The sketch must cross (or touch the edge) at least two or more times for it to be reshaped.

# In the //Editor// toolbar, click the ''Current Task'' drop-down arrow and click ''Reshape Feature''. 
# Click the Edit tool: [img[http://webhelp.esri.com/arcgisdesktop/9.2/published_images/button_edit.gif]]
# Click the feature you want to reshape.
# Click the tool palette drop-down arrow and click the Sketch tool: [img[http://webhelp.esri.com/arcgisdesktop/9.2/published_images/button_sketch.gif]]
# Create a line according to the way you want the feature reshaped.
# Right-click anywhere on the map and click ''Finish Sketch''.

!Editing shared boundaries
Polygon features often form a continuous fabric. You can select edges and nodes that may be shared by more than one feature, modify them, and have all the features that share the node or edge be updated.
# [[Create a map topology]] of the required features.
# Click the ''Topology Edit'' tool  on the Topology toolbar: [img[http://webhelp.esri.com/arcgisdesktop/9.2/published_images/button_topology_edit.gif]]
# Select the shared edge(s) to modify.
# Choose ''Modify Edge'' from the //Editor// toolbar, then drag vertices around as required.
## ''NOTE'': you can't move/delete endpoints (which may affect more than two polygons). Instead, try inserting a new vertex and moving it. Alternatively, just edit one of the polygons, then snap the other vertexes to it.

!Set the Snapping options
In the //Editor// toolbar, the ''Snapping'' option will open the //Snapping// toolbar. This allows you to choose which layers will snap to which (by Vertex, Edge or End).
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]] previewTiddler'></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>
<div class='editor' macro='preview hide text'></div>
<!--}}}-->
Create a Putty session
''Port'': 10.20.201.201
''SSH -> Tunnels'': port 3128, dynamic


Use SOCKS (localhost:3128) to access the proxy server.
!VirtualBox
Change the network adaptor in use:
{{{
ifup eth1
}}}

!Putty settings
!!Create an SSH tunnel to the Development server:
''Host:'' prod2-fms.wa.gov.au:22
''Username:'' fmsadmin
''Password:'' #9

!SSH to wsgi1-dev:
{{{
ssh ashleyf@wsgi1-dev
}}}
''Password:'' eri...

!Run Django dev server
{{{
./manage.py rundevserver 0.0.0.0:8002
}}}
Use whatever port is available.

!Development server
''URL:''
{{{
https://8002.wsgi1.devp.fms.wa.gov.au/
}}}
''Username:'' fmsadmin
''Password:'' #9

See [[Byobu]].
<<toggleSideBar Sidebar Sidebar show>> Created using [[TiddlyWiki|http://www.tiddlywiki.com]] © Osmosoft 
Here are some examples that show the usage of the write action in the ForEachTiddlerMacro.

//''Select and Sort Examples''//
* InClauseExamples
* WhereClauseExamples
* SortClauseExamples
* ScriptClauseExamples
//''Action Examples''//
* AddToListActionExamples
* WriteActionExamples
//''List Examples''//
Tiddlers having the ''Linux'' tag (note that this depends on the [[ListTaggedTiddlers]] tiddler existing):
<<tiddler ListTaggedTiddlers with: "Linux">>

Of cause you may also combine the examples, e.g. taking the whereClause of one example, the sortClause of a second and the action of a third.
//~~(Part of the [[ForEachTiddlerPlugin]])~~//

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.~~|


''Using JavaScript''

To give you a lot of flexibility the [[ForEachTiddlerMacro]] uses JavaScript in its arguments. Even if you are not that familiar with JavaScript you may find forEachTiddler useful. Just have a look at the various ready-to-use [[ForEachTiddlerExamples]] and adapt them to your needs.

''The Elements of the Macro''

The arguments of the ForEachTiddlerMacro consist of multiple parts, each of them being optional.

<<slider chkFETInClause [[inClause]] "inClause" "inClause">>
<<slider chkFETWhereClause [[whereClause]] "whereClause" "whereClause">>
<<slider chkFETSortClause [[sortClause]] "sortClause" "sortClause">>
<<slider chkFETScriptClause [[scriptClause]] "scriptClause" "scriptClause">>
<<slider chkFETActions [[Action Specification]] "Action Specification" "Action Specification">>

''Using Macros and ">" inside the forEachTiddler Macro''

You may use other macro calls into the expression, especially in the actionParameters. To avoid that the {{{>>}}} of such a macro call is misinterpreted as the end of the {{{<<forEachTiddler...>>}}} macro you must escape the {{{>>}}} of the inner macro with {{{$))}}} E.g. if you want to use {{{<<tiddler ...>>}}} inside the {{{forEachTiddler}}} macro you have to write {{{<<tiddler ...$))}}}.

In addition it is necessary to escape single {{{>}}} with the text {{{$)}}}.

''Using {{{<<tiddler ... with: ...>>}}} to re-use ForEachTiddler definitions''

Sometimes you may want to use a certain ForEachTiddler definition in slight variations. E.g. you may want to list either the tiddlers tagged with "ToDo" and in the other case with "Done". To do so you may use "Tiddler parameters". Here an example:

Replace the variable part of the ForEachTiddler definition with $1 ($2,... $9 are supported). E.g. you may create the tiddler [[ListTaggedTiddlers]] like this
{{{
<<forEachTiddler 
 where 
 'tiddler.tags.contains("$1")'
>>
}}}

Now you can use the [[ListTaggedTiddlers]] for various specific tags, using the {{{<<tiddler ...>>}}} macro:
{{{
<<tiddler ListTaggedTiddlers with: "systemConfig">>
}}}
{{{
<<tiddler ListTaggedTiddlers with: "Plugin">>
}}}


See also [[ForEachTiddlerExamples]].
/***
|''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 */
//}}}

<<gradient horiz #bbbbbb #eeeeee #ffffff>>The new GradientMacro allows simple horizontal and vertical coloured gradients. They are constructed from coloured HTML elements, and don't require any images to work.>>
The GradientMacro is an Extended Macro that processes the text after it up until the next '>>' sequence. It looks like this:
{{{
<<gradient vert #ffffff #ffdddd #ff8888>>gradient fill>>
}}}
The first parameter can be ''vert'' or ''horiz'' to indicate the direction of the gradient. The following parameters are two or more colours (CSS RGB(r,g,b) format is also acceptable). The GradientMacro constructs a smooth linear gradient between each of the colours in turn.

| <<gradient vert #ffffff #ffdddd #ff8888>>No images were harmed in the making of this gradient fill>> | <<gradient vert #ffffff #ddffdd #88ff88>>No images were harmed in the making of this gradient fill>> | <<gradient vert #ffffff #ddddff #8888ff>>No images were harmed in the making of this gradient fill>> |

Inline CSS definitions can be added to gradient fills like this:

<<gradient vert #000000 #660000 #aa2222>>color:#ffffff;font-size:12pt;Darkness>>
{{{
<<gradient vert #000000 #660000 #aa2222>>color:#ffffff;font-size:12pt;Darkness>>
}}}

You can make an abrupt transition in the gradient by using the "snap" prefix, like this:

{{{
<<gradient vert #000000 #999999 snap:#aa2222 #ff444>>color:#ffffff;font-size:24pt;padding:4pt;More darkness>>
}}}
<<gradient vert #000000 #999999 snap:#aa2222 #ff444>>color:#ffffff;font-size:24pt;padding:4pt;More darkness>>
Tiddlers tagged with "Help":
<<tiddler ListTaggedTiddlers with:"Help">>
<<autoRefresh>>

TiddlyWiki developer documentation:
*http://gimcrackd.com/etc/src/codex/

Select palette: SelectPalettePlugin
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/]] and [[here|http://dionysia.org/html/entities/symbols.html]]; some of the more common and useful ones are shown below.

|>|>|>|>|>|>| !HTML Entities |
| &amp;nbsp; | &nbsp; | no-break space | &nbsp;&nbsp; |  |  |  |
| &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; |
To import tiddlers from another ~TiddlyWiki file, or from an external website, follow these instructions.
#If the tiddler is on an external site, first copy the URL of the site which includes the tiddler you'd like to download into your clipboard (eg http://www.tiddlytools.com)
#Open your local ~TiddlyWiki file from your computer.
#Click on the 'Backstage' link that you can see at the very top right hand side of the page.
#In the menu, click on 'Import'. You will then see a window that looks like this - and you can follow the guidance from there. Note that you may need to grant authorisation to your ~TiddlyWiki file to import tiddlers - this is fine and expected.
<<importTiddlers>>
//Note also that the popular [[Firebug|http://www.joehewitt.com/software/firebug/]] extension for Firefox interferes with ImportTiddlers if its "Show XMLHttpRequest" option is switched on//
/***
|Name|ImportTiddlersPlugin|
|Source|http://www.TiddlyTools.com/#ImportTiddlersPlugin|
|Version|3.5.5|
|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|config.macros.importTiddlers.handler|
|Description|interactive controls for import/export with filtering.|

When many people share and edit copies of the same TiddlyWiki document, the ability to quickly collect all these changes back into a single, updated document that can then be redistributed to the entire group is very important.  It can also be very extremely helpful when moving your own tiddlers from document to document (e.g., when upgrading to the latest version of TiddlyWiki, or 'pre-loading' your favorite stylesheets into a new 'empty' TiddlyWiki document.)

This plugin lets you selectively combine tiddlers from any two TiddlyWiki documents.  An interactive control panel lets you pick a document to import from, and then select which tiddlers to import, with prompting for skip, rename, merge or replace actions when importing tiddlers that match existing titles.  Automatically add tags to imported tiddlers so they are easy to find later on.  Generates a detailed report of import 'history' in ImportedTiddlers.
!!!!!Usage
<<<
{{{<<importTiddlers>>}}} or {{{<<importTiddlers core>>}}}
invokes the built-in importTiddlers macro (TW2.1.x+).  If installed in documents using TW2.0.x or earlier, fallback is to use 'link' display (see below)

{{{<<importTiddlers link label tooltip>>}}}
The ''link'' keyword creates an "import tiddlers" link that when clicked to show/hide import control panel.  ''label'' and ''tooltip'' are optional text parameters (enclosed in quotes or {{{[[...]]}}}, and allow you to override the default display text for the link and the mouseover help text, respectively.

{{{<<importTiddlers inline>>}}}
creates import control panel directly in tiddler content

<<importTiddlers inline>>

Press ''[browse]'' to select a TiddlyWiki document file to import, and then press ''[open]''.  Alternatively, you can type in the path/filename or a remote document URL (starting with http://).  When you have entered the desired source location, press ''[load]'' to retrieve the tiddlers from the remote source.  //Note: There may be some delay to permit the browser time to access and load the document before updating the listbox with the titles of all tiddlers that are available to be imported.//

Select one or more titles from the listbox (hold CTRL or SHIFT while clicking to add/remove the highlight from individual list items).  You can press ''[select all]'' to quickly highlight all tiddler titles in the list.  Use the ''[-]'', ''[+]'', or ''[=]'' links to adjust the listbox size so you can view more (or less) tiddler titles at one time.  When you have chosen the tiddlers you want to import and entered any extra tags, press ''[import]'' to begin copying them to the current TiddlyWiki document.

''select: all, new, changes, or differences''

You can click on ''all'', ''new'', ''changes'', or ''differences'' to automatically select a subset of tiddlers from the list. This makes it very quick and easy to find and import just the updated tiddlers you are interested in:
>''"all"'' selects ALL tiddlers from the import source document, even if they have not been changed.
>''"new"'' selects only tiddlers that are found in the import source document, but do not yet exist in the destination document
>''"changes"'' selects only tiddlers that exist in both documents but that are newer in the source document
>''"differences"'' selects all new and existing tiddlers that are different from the destination document (even if destination tiddler is newer)

''Import Tagging:''

Tiddlers that have been imported can be automatically tagged, so they will be easier to find later on, after they have been added to your document.  New tags are entered into the "add tags" input field, and then //added// to the existing tags for each tiddler as it is imported.

''Skip, Rename, Merge, or Replace:''

When importing a tiddler whose title is identical to one that already exists, the import process pauses and the tiddler title is displayed in an input field, along with four push buttons: ''[skip]'', ''[rename]'', ''[merge]'' and ''[replace]''.

To bypass importing this tiddler, press ''[skip]''.  To import the tiddler with a different name (so that both the tiddlers will exist when the import is done), enter a new title in the input field and then press ''[rename]''.   Press ''[merge]'' to combine the content from both tiddlers into a single tiddler.  Press ''[replace]'' to overwrite the existing tiddler with the imported one, discarding the previous tiddler content.

//Note: if both the title ''and'' modification date/////time match, the imported tiddler is assumed to be identical to the existing one, and will be automatically skipped (i.e., not imported) without asking.//

''Import Report History''

When tiddlers are imported, a report is generated into ImportedTiddlers, indicating when the latest import was performed, the number of tiddlers successfully imported, from what location, and by whom. It also includes a list with the title, date and author of each tiddler that was imported.

When the import process is completed, the ImportedTiddlers report is automatically displayed for your review.  If more tiddlers are subsequently imported, a new report is //added// to ImportedTiddlers, above the previous report (i.e., at the top of the tiddler), so that a reverse-chronological history of imports is maintained.

If a cumulative record is not desired, the ImportedTiddlers report may be deleted at any time. A new ImportedTiddlers report will be created the next time tiddlers are imported.

Note: You can prevent the ImportedTiddlers report from being generated for any given import activity by clearing the "create a report" checkbox before beginning the import processing.

<<<
!!!!!Installation
<<<
copy/paste the following tiddlers into your document:
''ImportTiddlersPlugin'' 
''ImportTiddlersPluginPatch2.1.x'' (only for installation in TW2.1.x or earlier)
(both tagged with <<tag systemConfig>>)
>Important Notes:
>* As of 6/27/2007, "patch" functions that provide backward-compatibility with TW2.1.x and earlier have been split into a separate [[ImportTiddlersPluginPatch2.1.x]] tiddler to reduce installation overhead for //this// plugin.  You only need to install this additional plugin tiddler when using ImportTiddlersPlugin in documents using TW2.1.x or earlier.
>* As of 3/21/2007, the interactive {{{<<importTiddlers>>}}} and non-interactive {{{<<loadTiddlers>>}}} macro definitions and related code have been split into separate [[ImportTiddlersPlugin]] and [[LoadTiddlersPlugin]] to permit selective installation of either the interactive and/or non-interactive macro functions
''Quick Installation Tip #1:''
If you are using an unmodified version of TiddlyWiki (core release version <<version>>), you can get a new, empty TiddlyWiki with the Import Tiddlers plugin pre-installed (''[[download from here|TW+ImportExport.html]]''), and then simply import all your content from your old document into this new, empty document.
<<<
!!!!!Revision History
<<<
''2007.06.27 [3.5.5]'' added missing 'fields' params to saveTiddler() calls.  Fixes problem where importing tiddlers would lose the custom fields.  Also, moved functions for backward-compatibility with TW2.1.x to separate [[ImportTiddlersPluginPatch2.1.x]] tiddler, reducing the size of //this// plugin tiddler by a significant amount.
''2007.06.25 [3.5.4]'' added calls to store.suspendNotifications() and store.resumeNotifications().  Eliminates redisplay processing overhead DURING import activities
|please see [[ImportTiddlersPluginHistory]] for additional revision details|
''2005.07.20 [1.0.0]'' Initial Release
<<<
!!!!!Credits
<<<
This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
<<<
!!!!!Code
***/
// // ''MACRO DEFINITION''
//{{{
// Version
version.extensions.importTiddlers = {major: 3, minor: 5, revision: 5, date: new Date(2007,6,27)};

// IE needs explicit global scoping for functions/vars called from browser events
window.onClickImportButton=onClickImportButton;
window.refreshImportList=refreshImportList;

// default cookie/option values
if (!config.options.chkImportReport) config.options.chkImportReport=true;

merge(config.macros.importTiddlers,{
	label: "import tiddlers",
	prompt: "Copy tiddlers from another document",
	openMsg: "Opening %0",
	openErrMsg: "Could not open %0 - error=%1",
	readMsg: "Read %0 bytes from %1",
	foundMsg: "Found %0 tiddlers in %1",
	countMsg: "%0 tiddlers selected for import",
	importedMsg: "Imported %0 of %1 tiddlers from %2",
	loadText: "please load a document...",
	closeText: "close",	// text for close button when remote file is loaded
	doneText: "done",	// text for close button when remote file is not loaded
	src: "",		// path/filename or URL of document to import (retrieved from SiteUrl tiddler)
	proxy: "",		// URL for remote proxy script (retrieved from SiteProxy tiddler)
	useProxy: false,	// use specific proxy script in front of remote URL
	inbound: null,		// hash-indexed array of tiddlers from other document
	newTags: "",		// text of tags added to imported tiddlers
	addTags: true,		// add new tags to imported tiddlers
	listsize: 8,		// # of lines to show in imported tiddler list
	importTags: true,	// include tags from remote source document when importing a tiddler
	keepTags: true,		// retain existing tags when replacing a tiddler
	index: 0,		// current processing index in import list
	sort: ""		// sort order for imported tiddler listbox
});

if (config.macros.importTiddlers.coreHandler==undefined)
	config.macros.importTiddlers.coreHandler=config.macros.importTiddlers.handler; // save built-in handler

config.macros.importTiddlers.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
	if (!params[0] || params[0].toLowerCase()=='core') { // default to built in
		if (config.macros.importTiddlers.coreHandler)
			config.macros.importTiddlers.coreHandler.apply(this,arguments);
		else 
			createTiddlyButton(place,this.label,this.prompt,onClickImportMenu);
	}
	else if (params[0]=='link') { // show link to floating panel
		var label=params[1]?params[1]:this.label;
		var prompt=params[2]?params[2]:this.prompt;
		createTiddlyButton(place,label,prompt,onClickImportMenu);
	}
	else if (params[0]=='inline') {// show panel as INLINE tiddler content
		createImportPanel(place);
		document.getElementById("importPanel").style.position="static";
		document.getElementById("importPanel").style.display="block";
	}
	else if (config.macros.loadTiddlers)
		config.macros.loadTiddlers.handler(place,macroName,params); // any other params: loadtiddlers
}
//}}}

// // ''INTERFACE DEFINITION''
// // Handle link click to create/show/hide control panel
//{{{
function onClickImportMenu(e)
{
	if (!e) var e = window.event;
	var parent=resolveTarget(e).parentNode;
	var panel = document.getElementById("importPanel");
	if (panel==undefined || panel.parentNode!=parent)
		panel=createImportPanel(parent);
	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);
}
//}}}

// // Create control panel: HTML, CSS
//{{{
function createImportPanel(place) {
	var panel=document.getElementById("importPanel");
	if (panel) { panel.parentNode.removeChild(panel); }
	setStylesheet(config.macros.importTiddlers.css,"importTiddlers");
	panel=createTiddlyElement(place,"span","importPanel",null,null)
	panel.innerHTML=config.macros.importTiddlers.html;
	refreshImportList();
	var siteURL=store.getTiddlerText("SiteUrl"); if (!siteURL) siteURL="";
	document.getElementById("importSourceURL").value=siteURL;
	config.macros.importTiddlers.src=siteURL;
	var siteProxy=store.getTiddlerText("SiteProxy"); if (!siteProxy) siteProxy="SiteProxy";
	document.getElementById("importSiteProxy").value=siteProxy;
	config.macros.importTiddlers.proxy=siteProxy;
	return panel;
}
//}}}

// // CSS
//{{{
config.macros.importTiddlers.css = '\
#importPanel {\
	display: none; position:absolute; z-index:11; width:35em; right:105%; top:3em;\
	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;\
}\
#importPanel a, #importPanel td a { color:#009; display:inline; margin:0px; padding:1px; }\
#importPanel table { width:100%; border:0px; padding:0px; margin:0px; font-size:8pt; line-height:110%; background:transparent; }\
#importPanel tr { border:0px;padding:0px;margin:0px; background:transparent; }\
#importPanel td { color:#000; border:0px;padding:0px;margin:0px; background:transparent; }\
#importPanel select { width:98%;margin:0px;font-size:8pt;line-height:110%;}\
#importPanel input  { width:98%;padding:0px;margin:0px;font-size:8pt;line-height:110%}\
#importPanel .box { border:1px solid black; padding:3px; margin-bottom:5px; background:#f8f8f8; -moz-border-radius:5px;}\
#importPanel .topline { border-top:2px solid black; padding-top:3px; margin-bottom:5px; }\
#importPanel .rad { width:auto; }\
#importPanel .chk { width:auto; margin:1px;border:0; }\
#importPanel .btn { width:auto; }\
#importPanel .btn1 { width:98%; }\
#importPanel .btn2 { width:48%; }\
#importPanel .btn3 { width:32%; }\
#importPanel .btn4 { width:24%; }\
#importPanel .btn5 { width:19%; }\
#importPanel .importButton { padding: 0em; margin: 0px; font-size:8pt; }\
#importPanel .importListButton { padding:0em 0.25em 0em 0.25em; color: #000000; display:inline }\
#importCollisionPanel { display:none; margin:0.5em 0em 0em 0em; }\
';
//}}}

// // HTML 
//{{{
config.macros.importTiddlers.html = '\
<!-- source and report -->\
<table><tr><td align=left>\
	import from\
	<input type="radio" class="rad" name="importFrom" id="importFromFile" value="file" CHECKED\
		onClick="document.getElementById(\'importLocalPanel\').style.display=this.checked?\'block\':\'none\';\
			document.getElementById(\'importHTTPPanel\').style.display=!this.checked?\'block\':\'none\'"> local file\
	<input type="radio" class="rad" name="importFrom" id="importFromWeb"  value="http"\
		onClick="document.getElementById(\'importLocalPanel\').style.display=!this.checked?\'block\':\'none\';\
			document.getElementById(\'importHTTPPanel\').style.display=this.checked?\'block\':\'none\'"> web server\
</td><td align=right>\
	<input type=checkbox class="chk" id="chkImportReport" checked\
		onClick="config.options[\'chkImportReport\']=this.checked;"> create a report\
</td></tr></table>\
<!-- import from local file  -->\
<div id="importLocalPanel" style="display:block;margin-bottom:5px;margin-top:5px;padding-top:3px;border-top:1px solid #999">\
local document path/filename:<br>\
<input type="file" id="fileImportSource" size=57 style="width:100%"\
	onKeyUp="config.macros.importTiddlers.src=this.value"\
	onChange="config.macros.importTiddlers.src=this.value;">\
</div><!--panel-->\
\
<!-- import from http server -->\
<div id="importHTTPPanel" style="display:none;margin-bottom:5px;margin-top:5px;padding-top:3px;border-top:1px solid #999">\
<table><tr><td align=left>\
	remote document URL:<br>\
</td><td align=right>\
	<input type="checkbox" class="chk" id="importUseProxy"\
		onClick="config.macros.importTiddlers.useProxy=this.checked;\
			document.getElementById(\'importSiteProxy\').style.display=this.checked?\'block\':\'none\'"> use a proxy script\
</td></tr></table>\
<input type="text" id="importSiteProxy" style="display:none;margin-bottom:1px" onfocus="this.select()" value="SiteProxy"\
	onKeyUp="config.macros.importTiddlers.proxy=this.value"\
	onChange="config.macros.importTiddlers.proxy=this.value;">\
<input type="text" id="importSourceURL" onfocus="this.select()" value="SiteUrl"\
	onKeyUp="config.macros.importTiddlers.src=this.value"\
	onChange="config.macros.importTiddlers.src=this.value;">\
</div><!--panel-->\
\
<table><tr><td align=left>\
	select:\
	<a href="JavaScript:;" id="importSelectAll"\
		onclick="onClickImportButton(this)" title="select all tiddlers">\
		&nbsp;all&nbsp;</a>\
	<a href="JavaScript:;" id="importSelectNew"\
		onclick="onClickImportButton(this)" title="select tiddlers not already in destination document">\
		&nbsp;added&nbsp;</a> \
	<a href="JavaScript:;" id="importSelectChanges"\
		onclick="onClickImportButton(this)" title="select tiddlers that have been updated in source document">\
		&nbsp;changes&nbsp;</a> \
	<a href="JavaScript:;" id="importSelectDifferences"\
		onclick="onClickImportButton(this)" title="select tiddlers that have been added or are different from existing tiddlers">\
		&nbsp;differences&nbsp;</a> \
	<a href="JavaScript:;" id="importToggleFilter"\
		onclick="onClickImportButton(this)" title="show/hide selection filter">\
		&nbsp;filter&nbsp;</a> \
</td><td align=right>\
	<a href="JavaScript:;" id="importListSmaller"\
		onclick="onClickImportButton(this)" title="reduce list size">\
		&nbsp;&#150;&nbsp;</a>\
	<a href="JavaScript:;" id="importListLarger"\
		onclick="onClickImportButton(this)" title="increase list size">\
		&nbsp;+&nbsp;</a>\
	<a href="JavaScript:;" id="importListMaximize"\
		onclick="onClickImportButton(this)" title="maximize/restore list size">\
		&nbsp;=&nbsp;</a>\
</td></tr></table>\
<select id="importList" size=8 multiple\
	onchange="setTimeout(\'refreshImportList(\'+this.selectedIndex+\')\',1)">\
	<!-- NOTE: delay refresh so list is updated AFTER onchange event is handled -->\
</select>\
<input type=checkbox class="chk" id="chkAddTags" checked\
	onClick="config.macros.importTiddlers.addTags=this.checked;">add new tags &nbsp;\
<input type=checkbox class="chk" id="chkImportTags" checked\
	onClick="config.macros.importTiddlers.importTags=this.checked;">import source tags &nbsp;\
<input type=checkbox class="chk" id="chkKeepTags" checked\
	onClick="config.macros.importTiddlers.keepTags=this.checked;">keep existing tags<br>\
<input type=text id="txtNewTags" size=15 onKeyUp="config.macros.importTiddlers.newTags=this.value" autocomplete=off>\
<div align=center>\
	<input type=button id="importLoad" class="importButton" style="width:32%" value="load"\
		title="load listbox with tiddlers from source document"\
		onclick="onClickImportButton(this)">\
	<input type=button id="importStart"	 class="importButton" style="width:32%" value="import"\
		title="add selected source tiddlers to the current document"\
		onclick="onClickImportButton(this)">\
	<input type=button id="importClose"	 class="importButton" style="width:32%" value="close"\
		title="clear listbox or hide control panel"\
		onclick="onClickImportButton(this)">\
</div>\
<div id="importCollisionPanel">\
	tiddler already exists:\
	<input type=text id="importNewTitle" size=15 autocomplete=off">\
	<div align=center>\
	<input type=button id="importSkip"	class="importButton" style="width:23%" value="skip"\
		title="do not import this tiddler"\
		onclick="onClickImportButton(this)">\
	<input type=button id="importRename"  class="importButton" style="width:23%" value="rename"\
		title="rename the incoming tiddler"\
		onclick="onClickImportButton(this)">\
	<input type=button id="importMerge"   class="importButton" style="width:23%" value="merge"\
		title="append the incoming tiddler to the existing tiddler"\
		onclick="onClickImportButton(this)">\
	<input type=button id="importReplace" class="importButton" style="width:23%" value="replace"\
		title="discard the existing tiddler"\
		onclick="onClickImportButton(this)">\
	</div>\
</div>\
';
//}}}

// // Control interactions
//{{{
function onClickImportButton(which)
{
	// DEBUG alert(which.id);
	var theList		  = document.getElementById('importList');
	if (!theList) return;
	var thePanel	= document.getElementById('importPanel');
	var theCollisionPanel   = document.getElementById('importCollisionPanel');
	var theNewTitle   = document.getElementById('importNewTitle');
	var count=0;
	switch (which.id)
		{
		case 'fileImportSource':
		case 'importLoad':		// load import source into hidden frame
			importReport();		// if an import was in progress, generate a report
			config.macros.importTiddlers.inbound=null;	// clear the imported tiddler buffer
			refreshImportList();	// reset/resize the listbox
			if (config.macros.importTiddlers.src=="") break;
			// Load document, read it's DOM and fill the list
			config.macros.importTiddlers.loadRemoteFile(config.macros.importTiddlers.src,
				function(success,params,txt,src,xhr) {
					var src=src.replace(/%20/g," ");
					if (!success) { displayMessage(config.macros.importTiddlers.openErrMsg.format([src,xhr.status])); return; }
					var tiddlers = config.macros.importTiddlers.readTiddlersFromHTML(txt);
					var count=tiddlers?tiddlers.length:0;
					var querypos=src.lastIndexOf("?"); if (querypos!=-1) src=src.substr(0,querypos);
					displayMessage(config.macros.importTiddlers.foundMsg.format([count,src]));
					config.macros.importTiddlers.inbound=tiddlers;
					window.refreshImportList(0);
				});
			break;
		case 'importSelectAll':		// select all tiddler list items (i.e., not headings)
			importReport();		// if an import was in progress, generate a report
			for (var t=0,count=0; t < theList.options.length; t++) {
				if (theList.options[t].value=="") continue;
				theList.options[t].selected=true;
				count++;
			}
			clearMessage(); displayMessage(config.macros.importTiddlers.countMsg.format([count]));
			break;
		case 'importSelectNew':		// select tiddlers not in current document
			importReport();		// if an import was in progress, generate a report
			for (var t=0,count=0; t < theList.options.length; t++) {
				theList.options[t].selected=false;
				if (theList.options[t].value=="") continue;
				theList.options[t].selected=!store.tiddlerExists(theList.options[t].value);
				count+=theList.options[t].selected?1:0;
			}
			clearMessage(); displayMessage(config.macros.importTiddlers.countMsg.format([count]));
			break;
		case 'importSelectChanges':		// select tiddlers that are updated from existing tiddlers
			importReport();		// if an import was in progress, generate a report
			for (var t=0,count=0; t < theList.options.length; t++) {
				theList.options[t].selected=false;
				if (theList.options[t].value==""||!store.tiddlerExists(theList.options[t].value)) continue;
				for (var i=0; i<config.macros.importTiddlers.inbound.length; i++) // find matching inbound tiddler
					{ var inbound=config.macros.importTiddlers.inbound[i]; if (inbound.title==theList.options[t].value) break; }
				theList.options[t].selected=(inbound.modified-store.getTiddler(theList.options[t].value).modified>0); // updated tiddler
				count+=theList.options[t].selected?1:0;
			}
			clearMessage(); displayMessage(config.macros.importTiddlers.countMsg.format([count]));
			break;
		case 'importSelectDifferences':		// select tiddlers that are new or different from existing tiddlers
			importReport();		// if an import was in progress, generate a report
			for (var t=0,count=0; t < theList.options.length; t++) {
				theList.options[t].selected=false;
				if (theList.options[t].value=="") continue;
				if (!store.tiddlerExists(theList.options[t].value)) { theList.options[t].selected=true; count++; continue; }
				for (var i=0; i<config.macros.importTiddlers.inbound.length; i++) // find matching inbound tiddler
					{ var inbound=config.macros.importTiddlers.inbound[i]; if (inbound.title==theList.options[t].value) break; }
				theList.options[t].selected=(inbound.modified-store.getTiddler(theList.options[t].value).modified!=0); // changed tiddler
				count+=theList.options[t].selected?1:0;
			}
			clearMessage(); displayMessage(config.macros.importTiddlers.countMsg.format([count]));
			break;
		case 'importToggleFilter': // show/hide filter
		case 'importFilter': // apply filter
			alert("coming soon!");
			break;
		case 'importStart':		// initiate the import processing
			importReport();		// if an import was in progress, generate a report
			config.macros.importTiddlers.index=0;
			config.macros.importTiddlers.index=importTiddlers(0);
			importStopped();
			break;
		case 'importClose':		// unload imported tiddlers or hide the import control panel
			// if imported tiddlers not loaded, close the import control panel
			if (!config.macros.importTiddlers.inbound) { thePanel.style.display='none'; break; }
			importReport();		// if an import was in progress, generate a report
			config.macros.importTiddlers.inbound=null;	// clear the imported tiddler buffer
			refreshImportList();	// reset/resize the listbox
			break;
		case 'importSkip':	// don't import the tiddler
			var theItem	= theList.options[config.macros.importTiddlers.index];
			for (var j=0;j<config.macros.importTiddlers.inbound.length;j++)
			if (config.macros.importTiddlers.inbound[j].title==theItem.value) break;
			var theImported = config.macros.importTiddlers.inbound[j];
			theImported.status='skipped after asking';			// mark item as skipped
			theCollisionPanel.style.display='none';
			config.macros.importTiddlers.index=importTiddlers(config.macros.importTiddlers.index+1);	// resume with NEXT item
			importStopped();
			break;
		case 'importRename':		// change name of imported tiddler
			var theItem		= theList.options[config.macros.importTiddlers.index];
			for (var j=0;j<config.macros.importTiddlers.inbound.length;j++)
			if (config.macros.importTiddlers.inbound[j].title==theItem.value) break;
			var theImported		= config.macros.importTiddlers.inbound[j];
			theImported.status	= 'renamed from '+theImported.title;	// mark item as renamed
			theImported.set(theNewTitle.value,null,null,null,null);		// change the tiddler title
			theItem.value		= theNewTitle.value;			// change the listbox item text
			theItem.text		= theNewTitle.value;			// change the listbox item text
			theCollisionPanel.style.display='none';
			config.macros.importTiddlers.index=importTiddlers(config.macros.importTiddlers.index);	// resume with THIS item
			importStopped();
			break;
		case 'importMerge':	// join existing and imported tiddler content
			var theItem	= theList.options[config.macros.importTiddlers.index];
			for (var j=0;j<config.macros.importTiddlers.inbound.length;j++)
			if (config.macros.importTiddlers.inbound[j].title==theItem.value) break;
			var theImported	= config.macros.importTiddlers.inbound[j];
			var theExisting	= store.getTiddler(theItem.value);
			var theText	= theExisting.text+'\n----\n^^merged from: ';
			theText		+='[['+config.macros.importTiddlers.src+'#'+theItem.value+'|'+config.macros.importTiddlers.src+'#'+theItem.value+']]^^\n';
			theText		+='^^'+theImported.modified.toLocaleString()+' by '+theImported.modifier+'^^\n'+theImported.text;
			var theDate	= new Date();
			var theTags	= theExisting.getTags()+' '+theImported.getTags();
			theImported.set(null,theText,null,theDate,theTags);
			theImported.status   = 'merged with '+theExisting.title;	// mark item as merged
			theImported.status  += ' - '+theExisting.modified.formatString("MM/DD/YYYY 0hh:0mm:0ss");
			theImported.status  += ' by '+theExisting.modifier;
			theCollisionPanel.style.display='none';
			config.macros.importTiddlers.index=importTiddlers(config.macros.importTiddlers.index);	// resume with this item
			importStopped();
			break;
		case 'importReplace':		// substitute imported tiddler for existing tiddler
			var theItem		  = theList.options[config.macros.importTiddlers.index];
			for (var j=0;j<config.macros.importTiddlers.inbound.length;j++)
			if (config.macros.importTiddlers.inbound[j].title==theItem.value) break;
			var theImported     = config.macros.importTiddlers.inbound[j];
			var theExisting	  = store.getTiddler(theItem.value);
			theImported.status  = 'replaces '+theExisting.title;		// mark item for replace
			theImported.status += ' - '+theExisting.modified.formatString("MM/DD/YYYY 0hh:0mm:0ss");
			theImported.status += ' by '+theExisting.modifier;
			theCollisionPanel.style.display='none';
			config.macros.importTiddlers.index=importTiddlers(config.macros.importTiddlers.index);	// resume with THIS item
			importStopped();
			break;
		case 'importListSmaller':		// decrease current listbox size, minimum=5
			if (theList.options.length==1) break;
			theList.size-=(theList.size>5)?1:0;
			config.macros.importTiddlers.listsize=theList.size;
			break;
		case 'importListLarger':		// increase current listbox size, maximum=number of items in list
			if (theList.options.length==1) break;
			theList.size+=(theList.size<theList.options.length)?1:0;
			config.macros.importTiddlers.listsize=theList.size;
			break;
		case 'importListMaximize':	// toggle listbox size between current and maximum
			if (theList.options.length==1) break;
			theList.size=(theList.size==theList.options.length)?config.macros.importTiddlers.listsize:theList.options.length;
			break;
		}
}
//}}}

// // refresh listbox
//{{{
function refreshImportList(selectedIndex)
{
	var theList  = document.getElementById("importList");
	if (!theList) return;
	// if nothing to show, reset list content and size
	if (!config.macros.importTiddlers.inbound) 
	{
		while (theList.length > 0) { theList.options[0] = null; }
		theList.options[0]=new Option(config.macros.importTiddlers.loadText,"",false,false);
		theList.size=config.macros.importTiddlers.listsize;
		document.getElementById('importLoad').disabled=false;
		document.getElementById('fileImportSource').disabled=false;
		document.getElementById('importFromFile').disabled=false;
		document.getElementById('importFromWeb').disabled=false;
		document.getElementById('importClose').value=config.macros.importTiddlers.closeText;
		return;
	}

	// get the sort order
	if (!selectedIndex)   selectedIndex=0;
	if (selectedIndex==0) config.macros.importTiddlers.sort='title';		// heading
	if (selectedIndex==1) config.macros.importTiddlers.sort='title';
	if (selectedIndex==2) config.macros.importTiddlers.sort='modified';
	if (selectedIndex==3) config.macros.importTiddlers.sort='tags';
	if (selectedIndex>3) {
		// display selected tiddler count
		for (var t=0,count=0; t < theList.options.length; t++) {
			if (!theList.options[t].selected) continue;
			if (theList.options[t].value!="")
				count+=1;
			else { // if heading is selected, deselect it, and then select and count all in section
				theList.options[t].selected=false;
				for ( t++; t<theList.options.length && theList.options[t].value!=""; t++) {
					theList.options[t].selected=true;
					count++;
				}
			}
		}
		clearMessage(); displayMessage(config.macros.importTiddlers.countMsg.format([count]));
		return; // no refresh needed
	}

	// there are inbound tiddlers loaded... disable inapplicable controls...
	document.getElementById('importLoad').disabled=true;
	document.getElementById('fileImportSource').disabled=true;
	document.getElementById('importFromFile').disabled=true;
	document.getElementById('importFromWeb').disabled=true;
	document.getElementById('importClose').value=config.macros.importTiddlers.doneText;

	// get the alphasorted list of tiddlers (optionally, filter out unchanged tiddlers)
	var tiddlers=config.macros.importTiddlers.inbound;
	tiddlers.sort(function (a,b) {if(a['title'] == b['title']) return(0); else return (a['title'] < b['title']) ? -1 : +1; });
	// clear current list contents
	while (theList.length > 0) { theList.options[0] = null; }
	// add heading and control items to list
	var i=0;
	var indent=String.fromCharCode(160)+String.fromCharCode(160);
	theList.options[i++]=new Option(tiddlers.length+' tiddler'+((tiddlers.length!=1)?'s are':' is')+' in the document',"",false,false);
	theList.options[i++]=new Option(((config.macros.importTiddlers.sort=="title"   )?">":indent)+' [by title]',"",false,false);
	theList.options[i++]=new Option(((config.macros.importTiddlers.sort=="modified")?">":indent)+' [by date]',"",false,false);
	theList.options[i++]=new Option(((config.macros.importTiddlers.sort=="tags")?">":indent)+' [by tags]',"",false,false);
	// output the tiddler list
	switch(config.macros.importTiddlers.sort)
		{
		case "title":
			for(var t = 0; t < tiddlers.length; t++)
				theList.options[i++] = new Option(tiddlers[t].title,tiddlers[t].title,false,false);
			break;
		case "modified":
			// sort descending for newest date first
			tiddlers.sort(function (a,b) {if(a['modified'] == b['modified']) return(0); else return (a['modified'] > b['modified']) ? -1 : +1; });
			var lastSection = "";
			for(var t = 0; t < tiddlers.length; t++) {
				var tiddler = tiddlers[t];
				var theSection = tiddler.modified.toLocaleDateString();
				if (theSection != lastSection) {
					theList.options[i++] = new Option(theSection,"",false,false);
					lastSection = theSection;
				}
				theList.options[i++] = new Option(indent+indent+tiddler.title,tiddler.title,false,false);
			}
			break;
		case "tags":
			var theTitles = {}; // all tiddler titles, hash indexed by tag value
			var theTags = new Array();
			for(var t=0; t<tiddlers.length; t++) {
				var title=tiddlers[t].title;
				var tags=tiddlers[t].tags;
				if (!tags || !tags.length) {
					if (theTitles["untagged"]==undefined) { theTags.push("untagged"); theTitles["untagged"]=new Array(); }
					theTitles["untagged"].push(title);
				}
				else for(var s=0; s<tags.length; s++) {
					if (theTitles[tags[s]]==undefined) { theTags.push(tags[s]); theTitles[tags[s]]=new Array(); }
					theTitles[tags[s]].push(title);
				}
			}
			theTags.sort();
			for(var tagindex=0; tagindex<theTags.length; tagindex++) {
				var theTag=theTags[tagindex];
				theList.options[i++]=new Option(theTag,"",false,false);
				for(var t=0; t<theTitles[theTag].length; t++)
					theList.options[i++]=new Option(indent+indent+theTitles[theTag][t],theTitles[theTag][t],false,false);
			}
			break;
		}
	theList.selectedIndex=selectedIndex;		  // select current control item
	if (theList.size<config.macros.importTiddlers.listsize) theList.size=config.macros.importTiddlers.listsize;
	if (theList.size>theList.options.length) theList.size=theList.options.length;
}
//}}}

// // re-entrant processing for handling import with interactive collision prompting
//{{{
function importTiddlers(startIndex)
{
	if (!config.macros.importTiddlers.inbound) return -1;

	var theList = document.getElementById('importList');
	if (!theList) return;
	var t;
	// if starting new import, reset import status flags
	if (startIndex==0)
		for (var t=0;t<config.macros.importTiddlers.inbound.length;t++)
			config.macros.importTiddlers.inbound[t].status="";
	for (var i=startIndex; i<theList.options.length; i++)
		{
		// if list item is not selected or is a heading (i.e., has no value), skip it
		if ((!theList.options[i].selected) || ((t=theList.options[i].value)==""))
			continue;
		for (var j=0;j<config.macros.importTiddlers.inbound.length;j++)
			if (config.macros.importTiddlers.inbound[j].title==t) break;
		var inbound = config.macros.importTiddlers.inbound[j];
		var theExisting = store.getTiddler(inbound.title);
		// avoid redundant import for tiddlers that are listed multiple times (when 'by tags')
		if (inbound.status=="added")
			continue;
		// don't import the "ImportedTiddlers" history from the other document...
		if (inbound.title=='ImportedTiddlers')
			continue;
		// if tiddler exists and import not marked for replace or merge, stop importing
		if (theExisting && (inbound.status.substr(0,7)!="replace") && (inbound.status.substr(0,5)!="merge"))
			return i;
		// assemble tags (remote + existing + added)
		var newTags = "";
		if (config.macros.importTiddlers.importTags)
			newTags+=inbound.getTags()	// import remote tags
		if (config.macros.importTiddlers.keepTags && theExisting)
			newTags+=" "+theExisting.getTags(); // keep existing tags
		if (config.macros.importTiddlers.addTags && config.macros.importTiddlers.newTags.trim().length)
			newTags+=" "+config.macros.importTiddlers.newTags; // add new tags
		inbound.set(null,null,null,null,newTags.trim());
		// set the status to 'added' (if not already set by the 'ask the user' UI)
		inbound.status=(inbound.status=="")?'added':inbound.status;
		// do the import!
		store.suspendNotifications();
		store.saveTiddler(inbound.title, inbound.title, inbound.text, inbound.modifier, inbound.modified, inbound.tags, inbound.fields, true, inbound.created);
                store.fetchTiddler(inbound.title).created = inbound.created; // force creation date to imported value (needed for TW2.1.x and earlier)
		store.resumeNotifications();
		}
	return(-1);	// signals that we really finished the entire list
}
//}}}

//{{{
function importStopped()
{
	var theList     = document.getElementById('importList');
	var theNewTitle = document.getElementById('importNewTitle');
	if (!theList) return;
	if (config.macros.importTiddlers.index==-1)
		importReport();		// import finished... generate the report
	else
		{
		// import collision... show the collision panel and set the title edit field
		document.getElementById('importCollisionPanel').style.display='block';
		theNewTitle.value=theList.options[config.macros.importTiddlers.index].value;
		}
}
//}}}

// // ''REPORT GENERATOR''
//{{{
function importReport(quiet)
{
	if (!config.macros.importTiddlers.inbound) return;
	// DEBUG alert('importReport: start');

	// if import was not completed, the collision panel will still be open... close it now.
	var panel=document.getElementById('importCollisionPanel'); if (panel) panel.style.display='none';

	// get the alphasorted list of tiddlers
	var tiddlers = config.macros.importTiddlers.inbound;
	// gather the statistics
	var count=0;
	for (var t=0; t<tiddlers.length; t++)
		if (tiddlers[t].status && tiddlers[t].status.trim().length && tiddlers[t].status.substr(0,7)!="skipped") count++;

	// generate a report
	if (count && config.options.chkImportReport) {
		// get/create the report tiddler
		var theReport = store.getTiddler('ImportedTiddlers');
		if (!theReport) { theReport= new Tiddler(); theReport.title = 'ImportedTiddlers'; theReport.text  = ""; }
		// format the report content
		var now = new Date();
		var newText = "On "+now.toLocaleString()+", "+config.options.txtUserName
		newText +=" imported "+count+" tiddler"+(count==1?"":"s")+" from\n[["+config.macros.importTiddlers.src+"|"+config.macros.importTiddlers.src+"]]:\n";
		if (config.macros.importTiddlers.addTags && config.macros.importTiddlers.newTags.trim().length)
			newText += "imported tiddlers were tagged with: \""+config.macros.importTiddlers.newTags+"\"\n";
		newText += "<<<\n";
		for (var t=0; t<tiddlers.length; t++) if (tiddlers[t].status) newText += "#[["+tiddlers[t].title+"]] - "+tiddlers[t].status+"\n";
		newText += "<<<\n";
		// update the ImportedTiddlers content and show the tiddler
		theReport.text	 = newText+((theReport.text!="")?'\n----\n':"")+theReport.text;
		theReport.modifier = config.options.txtUserName;
		theReport.modified = new Date();
                store.saveTiddler(theReport.title, theReport.title, theReport.text, theReport.modifier, theReport.modified, theReport.tags, theReport.fields);
		if (!quiet) { story.displayTiddler(null,theReport.title,1,null,null,false); story.refreshTiddler(theReport.title,1,true); }
	}

	// reset status flags
	for (var t=0; t<config.macros.importTiddlers.inbound.length; t++) config.macros.importTiddlers.inbound[t].status="";

	// mark document as dirty and let display update as needed
	if (count) { store.setDirty(true); store.notifyAll(); }

	// always show final message when tiddlers were actually loaded
	if (count) displayMessage(config.macros.importTiddlers.importedMsg.format([count,tiddlers.length,config.macros.importTiddlers.src.replace(/%20/g," ")]));
}
//}}}

// // File and XMLHttpRequest I/O
//{{{
config.macros.importTiddlers.fileExists=function(theFile) {
	var found=false;
	// DEBUG: alert('testing fileExists('+theFile+')...');
	if(window.Components) {
		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(theFile); }
		catch(e) { return false; } // invalid directory
		found = file.exists();
	}
	else { // use ActiveX FSO object for MSIE 
		var fso = new ActiveXObject("Scripting.FileSystemObject");
		found = fso.FileExists(theFile)
	}
	// DEBUG: alert(theFile+" "+(found?"exists":"not found"));
	return found;
}

config.macros.importTiddlers.loadRemoteFile = function(src,callback,quiet) {
	if (src==undefined || !src.length) return null; // filename is required
	if (!quiet) clearMessage();
	if (!quiet) displayMessage(this.openMsg.format([src.replace(/%20/g," ")]));
	if (src.substr(0,5)!="http:" && src.substr(0,5)!="file:") { // if src is relative (i.e., not a URL)
		if (!this.fileExists(src)) { // if file cannot be found, might be relative path.. try fixup
			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);
			src=pathPrefix+src;
			if (pathPrefix.substr(0,5)!="http:") src=getLocalPath(src);
		}
	}
	if (src.substr(0,5)!="http:" && src.substr(0,5)!="file:") { // if not a URL, read from local filesystem
		var txt=loadFile(src);
		if ((txt==null)||(txt==false)) // file didn't load
			{ if (!quiet) displayMessage(config.macros.importTiddlers.openErrMsg.format([src.replace(/%20/g," "),"(filesystem error)"])); }
		else {
			if (!quiet) displayMessage(config.macros.importTiddlers.readMsg.format([txt.length,src.replace(/%20/g," ")]));
			if (callback) callback(true,quiet,convertUTF8ToUnicode(txt),src,null);
		}
	}
	else {
		var xhr=loadRemoteFile(src,callback,quiet);
		if (!quiet && !xhr) displayMessage(config.macros.importTiddlers.openErrMsg.format([src,"(XMLHTTPRequest error)"]));
	}
}

config.macros.importTiddlers.readTiddlersFromHTML=function(html)
{
	var remoteStore=new TiddlyWiki();
	remoteStore.importTiddlyWiki(html);
	return remoteStore.getTiddlers("title");	
}
//}}}	
/***
|Name|InlineJavascriptPlugin|
|Source|http://www.TiddlyTools.com/#InlineJavascriptPlugin|
|Documentation|http://www.TiddlyTools.com/#InlineJavascriptPluginInfo|
|Version|1.9.5|
|Author|Eric Shulman|
|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]]
!!!!!Revisions
<<<
2009.04.11 [1.9.5] pass current tiddler object into wrapper code so it can be referenced from within 'onclick' scripts
2009.02.26 [1.9.4] in $(), handle leading '#' on ID for compatibility with JQuery syntax
|please see [[InlineJavascriptPluginInfo]] for additional revision details|
2005.11.08 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.InlineJavascriptPlugin= {major: 1, minor: 9, revision: 5, date: new Date(2009,4,11)};

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) { // external script library
				var script = document.createElement("script"); script.src = src;
				document.body.appendChild(script); document.body.removeChild(script);
			}
			if (code) { // inline code
				if (show) // display source in tiddler
					wikify("{{{\n"+lookaheadMatch[0]+"\n}}}\n",w.output);
				if (label) { // create 'onclick' command link
					var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",wikifyPlainText(label));
					var fixup=code.replace(/document.write\s*\(/gi,'place.bufferedHTML+=(');
					link.code="function _out(place,tiddler){"+fixup+"\n};_out(this,this.tiddler);"
					link.tiddler=w.tiddler;
					link.onclick=function(){
						this.bufferedHTML="";
						try{ var r=eval(this.code);
							if(this.bufferedHTML.length || (typeof(r)==="string")&&r.length)
								var s=this.parentNode.insertBefore(document.createElement("span"),this.nextSibling);
							if(this.bufferedHTML.length)
								s.innerHTML=this.bufferedHTML;
							if((typeof(r)==="string")&&r.length) {
								wikify(r,s,null,this.tiddler);
								return false;
							} else return r!==undefined?r:false;
						} catch(e){alert(e.description||e.toString());return false;}
					};
					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 script immediately
					var fixup=code.replace(/document.write\s*\(/gi,'place.innerHTML+=(');
					var c="function _out(place,tiddler){"+fixup+"\n};_out(w.output,w.tiddler);";
					try	 { var out=eval(c); }
					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") window.wikifyPlainText=function(text,limit,tiddler) {
	if(limit > 0) text = text.substr(0,limit);
	var wikifier = new Wikifier(text,formatter,null,tiddler);
	return wikifier.wikifyPlain();
}
//}}}

// // GLOBAL FUNCTION: $(...) -- 'shorthand' convenience syntax for document.getElementById()
//{{{
if (typeof($)=='undefined') { function $(id) { return document.getElementById(id.replace(/^#/,'')); } }
//}}}
/***
|Name|InlineJavascriptPluginInfo|
|Source|http://www.TiddlyTools.com/#InlineJavascriptPlugin|
|Documentation|http://www.TiddlyTools.com/#InlineJavascriptPluginInfo|
|Version|1.9.4|
|Author|Eric Shulman|
|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|documentation|
|Requires||
|Overrides||
|Description|Documentation for InlineJavascriptPlugin|
''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.
!!!!!Usage
<<<
This plugin adds wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be recognized as embedded javascript code.
<script show>
	/* javascript code goes here... */
</script>Every time the tiddler content is rendered, the javascript code is automatically evaluated, allowing you to invoke 'side-effect' processing and/or produce dynamically-generated content that is then inserted into the tiddler content, immediately following the script (see below).  By including the optional ''show'' keyword as the final parameter in a {{{<script>}}} marker, the plugin will also include the script source code in the output that it displays in the tiddler.  This is helpful when creating examples for documentation purposes (such as used in this tiddler!)

__''Deferred execution from an 'onClick' link''__
<script label="click here" title="mouseover tooltip text" key="X" show>
	/* javascript code goes here... */
	alert('you clicked on the link!');
</script>
By including a {{{label="..."}}} parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.  You may also include a {{{title="..."}}} parameter to specify the 'tooltip' text that will appear whenever the mouse is moved over the onClick link text, and a {{{key="X"}}} parameter to specify an //access key// (which must be a //single// letter or numeric digit only).

__''Loading scripts from external source files''__
<script src="URL" show>
	/* optional javascript code goes here... */
</script>You can also load javascript directly from an external source URL, by including a src="..." parameter in the initial {{{<script>}}} marker (e.g., {{{<script src="demo.js"></script>}}}).  This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins.  The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.

In addition to loading the javascript from the external file, you can also use this feature to invoke javascript code contained within the {{{<script>...</script>}}} markers.  This code is invoked //after// the external script file has been processed, and can make immediate use of the functions and/or global variables defined by the external script file.
>Note: To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that is rendered as soon as your TiddlyWiki document is opened, such as MainMenu.  For example: put your {{{<script src="..."></script>}}} syntax into a separate 'library' tiddler (e.g., LoadScripts), and then add {{{<<tiddler LoadScripts>>}}} to MainMenu so that the library is loaded before any other tiddlers that rely upon the functions it defines. 
>
>Normally, loading external javascript in this way does not produce any direct output, and should not have any impact on the appearance of your MainMenu.  However, if your LoadScripts tiddler contains notes or other visible content, you can suppress this output by using 'inline CSS' in the MainMenu, like this: {{{@@display:none;<<tiddler LoadScripts>>@@}}}
<<<
!!!!!Creating dynamic tiddler content and accessing the ~TiddlyWiki DOM
<<<
An important difference between TiddlyWiki inline scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document: in a typical web document, you use the {{{document.write()}}} (or {{{document.writeln()}}}) function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.

However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and instead will //completely replace the entire ~TiddlyWiki document in your browser window (which is clearly not a good thing!)//.  In order to allow scripts to use {{{document.write()}}}, the plugin automatically converts and buffers all HTML output so it can be safely inserted into your tiddler content, immediately following the script.

''Note that {{{document.write()}}} can only be used to output "pure HTML" syntax.  To produce //wiki-formatted// output, your script should instead return a text value containing the desired wiki-syntax content'', which will then be automatically rendered immediately following the script.  If returning a text value is not sufficient for your needs, the plugin also provides an automatically-defined variable, 'place', that gives the script code ''direct access to the //containing DOM element//'' into which the tiddler output is being rendered.  You can use this variable to ''perform direct DOM manipulations'' that can, for example:
* generate wiki-formatted output using {{{wikify("...content...",place)}}}
* vary the script's actions based upon the DOM element in which it is embedded
* access 'tiddler-relative' DOM information using {{{story.findContainingTiddler(place)}}}
Note:
''When using an 'onclick' script, the 'place' element actually refers to the onclick //link text// itself, instead of the containing DOM element.''  This permits you to directly reference or modify the link text to reflect any 'stateful' conditions that might set by the script.  To refer to the containing DOM element from within an 'onclick' script, you can use "place.parentNode" instead.
<<<
!!!!!Instant "bookmarklets"
<<<
You can also use an 'onclick' link to define a "bookmarklet": a small piece of javascript that can be ''invoked directly from the browser without having to be defined within the current document.''  This allows you to create 'stand-alone' commands that can be applied to virtually ANY TiddlyWiki document... even remotely-hosted documents that have been written by others!!  To create a bookmarklet, simply define an 'onclick' script and then grab the resulting link text and drag-and-drop it onto your browser's toolbar (or right-click and use the 'bookmark this link' command to add it to the browser's menu).

Notes:
*When writing scripts intended for use as bookmarklets, due to the ~URI-encoding required by the browser, ''you cannot not use ANY double-quotes (") within the bookmarklet script code.''
*All comments embedded in the bookmarklet script must ''use the fully-delimited {{{/* ... */}}} comment syntax,'' rather than the shorter {{{//}}} comment syntax.
*Most importantly, because bookmarklets are invoked directly from the browser interface and are not embedded within the TiddlyWiki document, there is NO containing 'place' DOM element surrounding the script.  As a result, ''you cannot use a bookmarklet to generate dynamic output in your document,''  and using {{{document.write()}}} or returning wiki-syntax text or making reference to the 'place' DOM element will halt the script and report a "Reference Error" when that bookmarklet is invoked.  
Please see [[InstantBookmarklets]] for many examples of 'onclick' scripts that can also be used as bookmarklets.
<<<
!!!!!Special reserved function name
<<<
The plugin 'wraps' all inline javascript code inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler.  To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.
<<<
!!!!!$(...) 'shorthand' function
<<<
As described by Dustin Diaz [[here|http://www.dustindiaz.com/top-ten-javascript/]], the plugin defines a 'shorthand' function that allows you to write:
{{{
$(id)
}}}
in place of the normal standard javascript syntax:
{{{
document.getElementById(id)
}}}
This function is provided merely as a convenience for javascript coders that may be familiar with this abbreviation, in order to allow them to save a few bytes when writing their own inline script code.
<<<
!!!!!Examples
<<<
simple dynamic output:
><script show>
	document.write("The current date/time is: "+(new Date())+"<br>");
	return "link to current user: [["+config.options.txtUserName+"]]\n";
</script>
dynamic output using 'place' to get size information for current tiddler:
><script show>
	if (!window.story) window.story=window;
	var title=story.findContainingTiddler(place).getAttribute("tiddler");
	var size=store.getTiddlerText(title).length;
	return title+" is using "+size+" bytes";
</script>
dynamic output from an 'onclick' script, using {{{document.write()}}} and/or {{{return "..."}}}
><script label="click here" show>
	document.write("<br>The current date/time is: "+(new Date())+"<br>");
	return "link to current user: [["+config.options.txtUserName+"]]\n";
</script>
creating an 'onclick' button/link that accesses the link text AND the containing tiddler:
><script label="click here" title="clicking this link will show an 'alert' box" key="H" show>
	if (!window.story) window.story=window;
	var txt=place.firstChild.data;
	var tid=story.findContainingTiddler(place).getAttribute('tiddler');
	alert('Hello World!\nlinktext='+txt+'\ntiddler='+tid);
</script>
dynamically setting onclick link text based on stateful information:
>{{block{
{{{
<script label="click here">
	/* toggle "txtSomething" value */
	var on=(config.txtSomething=="ON");
	place.innerHTML=on?"enable":"disable";
	config.txtSomething=on?"OFF":"ON";
	return "\nThe current value is: "+config.txtSomething;
</script><script>
	/* initialize onclick link text based on current "txtSomething" value */
	var on=(config.txtSomething=="ON");
	place.lastChild.previousSibling.innerHTML=on?"disable":"enable";
</script>
}}}
<script label="click here">
	/* toggle "txtSomething" value */
	var on=(config.txtSomething=="ON");
	place.innerHTML=on?"enable":"disable";
	config.txtSomething=on?"OFF":"ON";
	return "\nThe current value is: "+config.txtSomething;
</script><script>
	/* initialize onclick link text based on current "txtSomething" value */
	var on=(config.txtSomething=="ON");
	place.lastChild.innerHTML=on?"enable":"disable";
</script>
}}}
loading a script from a source url:
>http://www.TiddlyTools.com/demo.js contains:
>>{{{function inlineJavascriptDemo() { alert('Hello from demo.js!!') } }}}
>>{{{displayMessage('InlineJavascriptPlugin: demo.js has been loaded');}}}
>note: When using this example on your local system, you will need to download the external script file from the above URL and install it into the same directory as your document.
>
><script src="demo.js" show>
	return "inlineJavascriptDemo() function has been defined"
</script>
><script label="click to invoke inlineJavascriptDemo()" key="D" show>
	inlineJavascriptDemo();
</script>
<<<
!!!!!Revisions
<<<
2009.02.26 [1.9.4] in $(), handle leading '#' on ID for compatibility with JQuery syntax
2008.06.11 [1.9.3] added $(...) function as 'shorthand' for document.getElementById()
2008.03.03 [1.9.2] corrected fallback declaration of wikifyPlainText() (fixes Safari "parse error")
2008.02.23 [1.9.1] in onclick function, use string instead of array for 'bufferedHTML' (fixes IE errors)
2008.02.21 [1.9.0] output from 'onclick' scripts (return value or document.write() calls) are now buffered and rendered into into a span following the script.  Also, added default 'return false' handling if no return value provided (prevents HREF from being triggered -- return TRUE to allow HREF to be processed).  Thanks to Xavier Verges for suggestion and preliminary code.
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 tiddler
2007.12.28 [1.8.0] added support for key="X" syntax to specify custom access key definitions
2007.12.15 [1.7.0] autogenerate URI encoded HREF on links for onclick scripts.  Drag links to browser toolbar to create bookmarklets.  IMPORTANT NOTE: place is NOT defined when scripts are used as bookmarklets.  In addition, double-quotes will cause syntax errors.  Thanks to PaulReiber for debugging and brainstorming.
2007.11.26 [1.6.2] when converting "document.write()" function calls in inline code, allow whitespace between "write" and "(" so that "document.write ( foobar )" is properly converted.
2007.11.16 [1.6.1] when rendering "onclick scripts", pass label text through wikifyPlainText() to parse any embedded wiki-syntax to enable use of HTML entities or even TW macros to generate dynamic label text.
2007.02.19 [1.6.0] added support for title="..." to specify mouseover tooltip when using an onclick (label="...") script
2006.10.16 [1.5.2] add newline before closing '}' in 'function out_' wrapper.  Fixes error caused when last line of script is a comment.
2006.06.01 [1.5.1] when calling wikify() on script return value, pass hightlightRegExp and tiddler params so macros that rely on these values can render properly
2006.04.19 [1.5.0] added 'show' parameter to force display of javascript source code in tiddler output
2006.01.05 [1.4.0] added support 'onclick' scripts.  When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked.  'place' value is set to match the clicked button/link element.
2005.12.13 [1.3.1] when catching eval error in IE, e.description contains the error text, instead of e.toString().  Fixed error reporting so IE shows the correct response text.  Based on a suggestion by UdoBorkowski
2005.11.09 [1.3.0] for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content.  Based on a suggestion by BradleyMeck
2005.11.08 [1.2.0] handle loading of javascript from an external URL via src="..." syntax
2005.11.08 [1.1.0] pass 'place' param into scripts to provide direct DOM access 
2005.11.08 [1.0.0] initial release
<<<
/***
| Name:|InstantTimestampPlugin|
| Created by:|SimonBaird|
| Location:|http://simonbaird.com/mptw/#InstantTimestamp|
| Version:|1.0.4 (06-Apr-2006)|
| Requires:|~TW2.x|
!Description
If you enter {ts} in your tiddler content (without the spaces) it will be replaced with a timestamp when you save the tiddler. Full list:
* {ts} or {t} -> timestamp
* {ds} or {d} -> datestamp
* !ts or !t at start of line -> !!timestamp
* !ds or !d at start of line -> !!datestamp
(I added the extra ! since that's how I like it. Remove it from translations below if required)
!Notes
* Change the timeFormat and dateFormat below to suit your preference.
* See also AutoCorrectPlugin
!History
* 06-Apr-06, version 1.0.4
** removed the AutoCorrect stuff and put it in AutoCorrectPlugin
* 05-Apr-06, version 1.0.3
** now have exclusion by tag and tiddler name, probably less important here than in AutoCorrectPlugin
* 05-Apr-06, version 1.0.2
** put matches into array to and eval them to allow generic substitutions
* 05-Apr-06, version 1.0.1
** added ds for datestamp as suggested by DanielBaird
** made case insensitive
** Added translation for !t at start of line
* 05-Apr-06, version 1.0.0
** written after suggestion by Achim Wessling 
!Code
***/
//{{{

version.extensions.InstantTimestamp = { major: 1, minor: 0, revision: 4, date: new Date(2006,4,6),
 source: "http://simonbaird.com/mptw/#InstantTimestamp"
};

config.InstantTimestamp = {

 timeFormat: 'DD/0MM/YY 0hh:0mm',
 dateFormat: 'DD-mmm-YY',

 translations: [
 [/^!ts?$/img, "'!!'+now.formatString(config.InstantTimestamp.timeFormat)"],
 [/^!ds?$/img, "'!!'+now.formatString(config.InstantTimestamp.dateFormat)"],
 [/\{ts?\}/ig, "now.formatString(config.InstantTimestamp.timeFormat)"],
 [/\{ds?\}/ig, "now.formatString(config.InstantTimestamp.dateFormat)"]
 ],
 excludeTags: [
 "noAutoCorrect",
 "CSS",
 "css",
 "systemConfig",
 "zsystemConfig",
 "Plugins",
 "Plugin",
 "plugins",
 "plugin",
 "javascript",
 "code"
 ],
 excludeTiddlers: [
 "StyleSheet",
 "StyleSheetLayout",
 "StyleSheetColors",
 "StyleSheetPrint"
 ]
}; 

if (!Array.prototype.contains)
 Array.prototype.contains = function(item) {
 return (this.find(item) != null);
 };

if (!Array.prototype.containsAny)
 Array.prototype.containsAny = function(items) {
 for (var i=0;i<items.length;i++)
 if (this.contains(items[i]))
 return true;
 return false;
 };

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

 tags = (typeof(tags) == "string") ? tags.readBracketedList() : tags;
 var conf = config.InstantTimestamp;

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

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

 return this.saveTiddler_mptw_instanttimestamp(title,newTitle,newBody,modifier,modified,tags);
}

//}}}
InterfaceOptions are displayed when you click the 'options' button on the right in your TiddlyWiki file. They are saved in a cookie on your browser, making them sticky between visits:
<<<
<<tiddler OptionsPanel>>
<<<
* The user name for edits should be set //before// starting to edit things (ouch. another bug)
* SaveBackups gives the option of whether to generate backup files 
* AutoSave gives the option of automatically saving every time a change is made
* RegExpSearch allows more complex search expressions
* CaseSensitiveSearch does as its name implies
/***
| Name|LessBackupsPlugin|
| Description|Intelligently limit the number of backup files you create|
| Version|3.0 ($Rev: 2320 $)|
| Date|$Date: 2007-06-18 22:37:46 +1000 (Mon, 18 Jun 2007) $|
| Source|http://mptw.tiddlyspot.com/#LessBackupsPlugin|
| Author|Simon Baird|
| Email|simon.baird@gmail.com|
| License|http://mptw.tiddlyspot.com/#TheBSDLicense|
!!!Description
You end up with just backup one per year, per month, per weekday, per hour, minute, and second.  So total number won't exceed about 200 or so. Can be reduced by commenting out the seconds/minutes/hours line from modes array

!!!Notes
Works in IE and Firefox only.  Algorithm by Daniel Baird. IE code by by Saq Imtiaz.
!!!Code
***/
//{{{
window.getSpecialBackupPath = function(backupPath) {

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

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

	var now = new Date();

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

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

		// open the file

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

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

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

//}}}
|!mkdir //name// |Create a directory. |
|!rmdir //name// |Removes a directory (empty only). |
|!rm -r //name// |Removes a directory (recursively deletes contents). |
<<tiddler ListTaggedTiddlers with:"Linux">>
<<autoRefresh>>
<<forEachTiddler 
 where 
 'tiddler.tags.contains("$1")'
>>

{{sectionTOC{}}}
[[Macros]] let you write tiddlers containing more exotic objects than just text. Built-in macros include:
* GradientMacro
* TabMacro
* TaggingMacro
* ToolbarMacro

!NewTiddler
The {{{<<newTiddler>>}}} macro displays a button that can be clicked to create a new tiddler. By default, the new tiddler is opened in edit mode or you can specify a custom template.

The available parameters are:

|!Parameter |!Description |
|label |The text of the button |
|prompt |The tooltip for the button |
|accessKey |The access key to trigger the button (specify a single letter; different browsers require a different modifier key like Alt- or Control-) |
|focus |Which of the edittable fields to default the focus to (eg, "title", "text", "tags") |
|template |The template to use to display the new tiddler (defaults to EditTemplate) |
|text |The default text for the new tiddler |
|title |The default title for the new tiddler |
|tag |A single tag to be applied to the new tiddler (repeat this parameter to specify multiple tags) |

For example: <<newTiddler label:"try this" accessKey:1 focus:tags text:"hello there!" tag:greeting tag:"an example">> (can also be triggered with Alt-1)
{{{
<<newTiddler label:"try this" accessKey:1 focus:tags text:"hello there!" tag:greeting tag:"an example">>
}}}

You can only prime the initial values of fields that map to a text input box in the specified template (for instance, if you specify the standard ViewTemplate as the template you won't be able to prime any fields). For example, this doesn't work as you might expect:
{{{
<<newTiddler template:ViewTemplate text:"To be or not to be">>
}}}
<<newTiddler template:ViewTemplate text:"To be or not to be">>

!NewJournal
The {{{<<newJournal>>}}} macro creates a new tiddler with it's title set to the current date, and the cursor in the body text area ready to type.

This macro is identical to the NewTiddlerMacro except that the "title" parameter is treated as a DateFormatString so that you can specify your own date format.

!Today
The {{{<<today>>}}} macro inserts the current date and time into a tiddler. It's updated each time the tiddler is redisplayed. It can optionally take a [[DateFormatString]] to determine the way that the date is displayed:
{{{
<<today>>
<<today DD-mmm-YY>>
}}}
Results in:
<<today>>
<<today DD-mmm-YY>>

!Search
Use {{{<<search>>}}} to offer a search input field.

!Tag popup
{{{
<<tag features>>
}}}
will result in <<tag features>>

!Slider
{{{
<<slider chkTestSlider OptionsPanel options "Change TiddlyWiki advanced options">>
}}}
Results in this button <<slider chkTestSlider OptionsPanel options "Change TiddlyWiki advanced options">>
The parameters are:
* cookie name to be used to save the state of the slider
* name of the tiddler to include in the slider
* title text of the slider
* tooltip text of the slider

!List Shadow Tiddlers
{{{
<<list shadowed>>
}}}

!Permalink
Changes the browser address bar to a permalink to the current tiddler. It is used with the ToolbarMacro like this:
{{{
<<toolbar permalink>>
}}}
On some browsers, the PermalinkCommand can be unreliable if the tiddler title includes characters that have special meanings in URLs (like "+" and "\") or are outside the basic ANSI character set.

!References
Offers a popup menu displaying the tiddlers that link to the current one. It is used with the ToolbarMacro like this:
{{{
<<toolbar references>>
}}}

!SaveTiddler
Saves any pending edits to the current tiddler, and switches it to the default view. It is used with the ToolbarMacro like this:
{{{
<<toolbar saveTiddler>>
}}}

<<sectionTOC>>
[[Oracle Reference]]
[[Ruby Reference]]
[[Python Reference]]
[[Django Reference]]
[[ArcGIS reference]]
[[Linux reference]]
[[Help]]
!Inline Formatting 
|!Option|!Syntax|!Output|
|bold font|{{{''bold''}}}|''bold''|
|italic type|{{{//italic//}}}|//italic//|
|underlined text|{{{__underlined__}}}|__underlined__|
|strikethrough text|{{{--strikethrough--}}}|--strikethrough--|
|superscript text|{{{^^super^^script}}}|^^super^^script|
|subscript text|{{{~~sub~~script}}}|~~sub~~script|
|highlighted text|{{{@@highlighted@@}}}|@@highlighted@@|
|preformatted text|{{{{{{preformatted}}}}}}|{{{preformatted}}}|

!Block Elements
!!Headings
{{{
!Heading 1
!!Heading 2
!!!Heading 3
!!!!Heading 4
!!!!!Heading 5
}}}
<<<
!Heading 1
!!Heading 2
!!!Heading 3
!!!!Heading 4
!!!!!Heading 5
<<<

!!Lists
{{{
* unordered list, level 1
** unordered list, level 2
*** unordered list, level 3

# ordered list, level 1
## ordered list, level 2
### unordered list, level 3

; definition list, term
: definition list, description
}}}
<<<
* unordered list, level 1
** unordered list, level 2
*** unordered list, level 3

# ordered list, level 1
## ordered list, level 2
### unordered list, level 3

; definition list, term
: definition list, description
<<<

!!Blockquotes
{{{
> blockquote, level 1
>> blockquote, level 2
>>> blockquote, level 3

<<<
blockquote
<<<
}}}
<<<
> blockquote, level 1
>> blockquote, level 2
>>> blockquote, level 3

> blockquote
<<<

!!MonospacedText 
{{{
 {{{
 Monospaced block (e.g. code blocks)
 }}}
}}}
<<<
{{{
Monospaced block(e.g. code)
}}}
<<<
{{{
Inline {{{monospaced text}}} within a sentence.
}}}
<<<
Inline {{{monospaced text}}} within a sentence.
<<<

!!Tables
{{{
|CssClass|k
|!heading column 1|!heading column 2|
|row 1, column 1|row 1, column 2|
|row 2, column 1|row 2, column 2|
|>|COLSPAN|
|ROWSPAN| … |
|~| … |
|CssProperty:value;…| … |
|caption|c
}}}
''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.
<<<
|CssClass|k
|!heading column 1|!heading column 2|
|row 1, column 1|row 1, column 2|
|row 2, column 1|row 2, column 2|
|>|COLSPAN|
|ROWSPAN| … |
|~| … |
|CssProperty:value;…| … |
|caption|c
<<<

!!Images
{{{
[img[caption|FILEPATH\FILENAME.jpg]]
}}}
See also [[TiddlyWiki.com|http://www.tiddlywiki.com/#EmbeddedImages]]

!Plain Text
Sometimes text can inadvertently match TiddlyWiki formatting instructions - particularly program code, or text pasted from elsewhere. In these situations you can either use MonospacedText or you can accomplish the same thing without the monospaced effect like this:
{{{
This is AnotherLink, this is a copyright symbol &copy; and this site is called <<tiddler SiteTitle>>
<nowiki>This is AnotherLink, this is a copyright symbol &copy; and this site is called <<tiddler SiteTitle>></nowiki>
"""This is AnotherLink, this is a copyright symbol &copy; and this site is called <<tiddler SiteTitle>>"""
}}}
Which displays as:
This is AnotherLink, this is a copyright symbol &copy; and this site is called <<tiddler SiteTitle>>
<nowiki>This is AnotherLink, this is a copyright symbol &copy; and this site is called <<tiddler SiteTitle>></nowiki>
"""This is AnotherLink, this is a copyright symbol &copy; and this site is called <<tiddler SiteTitle>>"""

!Hyperlinks
* WikiWords may be transformed to hyperlinks to the respective tiddler. The automatic transformation can be suppressed by preceding the respective WikiWord with a tilde ({{{~}}}): {{{~WikiWord}}}.
** Note that this behaviour has been disabled using the [[DisableWikiLinksPlugin]].
* PrettyLinks 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]]}}}<br>'''N.B.:''' In this case, the target can also be any website (i.e. URL).

!Custom Styling
* Raw HTML can be inserted by enclosing the respective code in HTML tags: {{{<html> … </html>}}}
!Special Markers
* {{{<br>}}} forces a manual line break.
* {{{----}}} creates a horizontal ruler.
* [[HTML entities|HtmlEntities]] - reference for special characters.
* {{{<<macroName>>}}} calls the respective [[macro|Macros]]
* 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"""}}}.

!Plugin Formatting
To make plugins, stylesheets and templates easier to read, you can use special alternative formatting for monospaced blocks.

In JavaScript code:
{{{
//{{{
var id = document.getElementById("mainMenu");
//}}}
}}}
In HTML templates:
{{{
<!--{{{-->
<div id="MainMenu">
</div>
<!--}}}-->
}}}
In CSS stylesheets
{{{
/*{{{*/
div {color: #ff0000;}
/*}}}*/
}}}
It will be displayed as:
//{{{
var id = document.getElementById("mainMenu");
//}}}

<!--{{{-->
<div id="MainMenu">
</div>
<!--}}}-->

/*{{{*/
div {color: #ff0000;}
/*}}}*/
<!--{{{-->
<style type="text/css">
#contentWrapper {display:none;}
#splashScreen {display:block;}
/*{{{*/
body {background:#fff; color:#000;}

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

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

.button {color:#014; border:1px solid [[ColorPalette::Background]];}
.button:hover {color:[[ColorPalette::PrimaryDark]]; background:#fe8; border-color:#db4;}
.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:#8cf;}

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

#s_sidebar {}
#s_sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
#s_sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
#s_sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
#s_sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
#s_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:#ffc [[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:#s_80ff80;}
.wizard .changedServer {background:#s_8080ff;}
.wizard .changedBoth {background:#s_ff8080;}
.wizard .notFound {background:#s_ffff80;}
.wizard .putToServer {background:#s_ff80ff;}
.wizard .gotFromServer {background:#s_80ffff;}

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

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

.popup {background:[[ColorPalette::TertiaryPale]]; color:#666; 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:#f88;}
.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
.lowlight {background:[[ColorPalette::TertiaryLight]];}

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

.imageLink, #s_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]];}

#s_backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
#s_backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#s_backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
#s_backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
#s_backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
#s_backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#s_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]];}
#s_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;}

#s_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;}

#s_mainMenu .tiddlyLinkExisting,
	#s_mainMenu .tiddlyLinkNonExisting,
	#s_sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#s_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;}

#s_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;}

#s_sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#s_sidebarOptions {padding-top:0.3em;}
#s_sidebarOptions a {margin:0 0.2em; padding:0.2em 0.3em; display:block;}
#s_sidebarOptions input {margin:0.4em 0.5em;}
#s_sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#s_sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#s_sidebarOptions .sliderPanel input {margin:0 0 0.3em 0;}
#s_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;}

#s_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;}
#s_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;}

#s_contentWrapper {display:block;}
#s_splashScreen {display:none;}

#s_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 #s_backstage {width:99%;}
* html #s_backstageArea {width:99%;}
#s_backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em;}
#s_backstageToolbar {position:relative;}
#s_backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em;}
#s_backstageButton {display:none; position:absolute; z-index:175; top:0; right:0;}
#s_backstageButton a {padding:0.1em 0.4em; margin:0.1em;}
#s_backstage {position:relative; width:100%; z-index:50;}
#s_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;}
#s_backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}

.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/


#s_splashScreen {display:block;}

/***
This fixes a problem with the tabs slider
***/
/*{{{*/
#s_sidebarTabs .button {
	margin:0em 0.2em;
	padding:0.2em 0.3em;
	display:block;
}
/*}}}*/
/***
This is a sample style definition to demonstrate CustomCssClass formatting
***/
/*{{{*/
.wrappingClass {color: #s_666; background: #s_bbb;}
[[ChunkyButtonStyle]]
/*}}}*/
#s_messageArea {display:none;}

</style>
<!--}}}-->
<!--{{{-->

<div id="splashScreen">

<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
<div class='headerShadow'>
<span class="siteTitle">TiddlyWiki</span>&nbsp;
<span class="siteSubtitle">a reusable non-linear personal web notebook</span>
</div>
<div class='headerForeground'>
<span class="siteTitle">TiddlyWiki</span>&nbsp;
<span class="siteSubtitle">a reusable non-linear personal web notebook</span>
</div>
</div>
<div id='s_mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='s_sidebar'>
<div id='s_sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='s_sidebarTabs' refresh='macro' force='true' macro='slider chkSideBarTabs SideBarTabs "index »" "display lists of tiddlers"'></div>
</div>
<div id='s_displayArea'>
<div id='s_messageArea'></div>
<div id="s_tiddlerDisplay"><div id="splashId_HelloThere" class="tiddler">

<div class="toolbar"><br></div>
<div class="title">HelloThere</div>
<div class="viewer">Welcome to TiddlyWiki!<br><br>TiddlyWiki is a single html file which has all the characteristics of a wiki - including all of the content, the functionality (including editing, saving, tagging and searching) and the style sheet. Because it's a single file, it's very portable - you can email it, put it on a web server or share it via a USB stick.<br><br><strong>But it's not just a wiki!</strong> It has very powerful plugin capabilities, so it can also be used to build new tools. You have full control over how it looks and behaves. For example, TiddlyWiki is already being used as:<br><ul><li>A personal notebook</li><li>A GTD ("Getting Things Done") productivity tool</li><li>A collaboration tool</li><li>For building websites (this site is a TiddlyWiki file!)</li><li>For rapid prototyping</li><li>...and much more!</li></ul>You can import and export data to and from all sorts of places. Check out some of the Examples of TiddlyWiki in use, and the Features that are available.<br><br>You can see the web functionality of TiddlyWiki by clicking on some of the links on this website. Double click some of the text to see 'edit mode'. For the full range of functions, including editing and saving changes, download and install a copy of the basic version and then follow the guidelines in Getting Started. Have fun!<br><br><span tiddler="Download" refresh="content"><div class="downloadButton"><table class="twtable"><tbody><tr class="evenRow"><td><span class="chunkyButton"><a class="button" title="Download TiddlyWiki" href="javascript:;">download</a></span></td></tr></tbody></table><br>Advanced options &gt;&gt;</div></span></div>
<div class="tagClear"></div>

</div>
<div id="splashId_NewFeatures" class="tiddler">

<div class="toolbar"><br></div>
<div class="title">NewFeatures</div>
<div class="viewer">Release 2.5.2 is a bugfix release, restoring the upgrade functionality on Mozilla-based browsers.<br><br>Release 2.5.1 of TiddlyWiki contained several usability enhancements and bug fixes.<br>These include:<br><ul><li> Improved separators and "more/less" extenders for toolbars</li><li> Added plugin version information to the PluginManager</li><li> Fixed <em>Tags</em> macro to respect the <em>excludeLists</em> tag</li><li> Fixed problem with saving of extended tiddler fields</li></ul>It also starts the process of refactoring useful parts of TiddlyWiki's functionality into generic jQuery plugins that can be reused in other projects:<br><ul><li> <a target="_blank" title="External link to http://jquery.tiddlywiki.org/twFile.html" href="http://jquery.tiddlywiki.org/twFile.html" class="externalLink">jQuery.twFile</a> - Access to the local filing system for HTML files loaded from a <a target="_blank" title="External link to file://" href="file://" class="externalLink">file://</a> URL</li><li> <a target="_blank" title="External link to http://jquery.tiddlywiki.org/twStylesheet.html" href="http://jquery.tiddlywiki.org/twStylesheet.html" class="externalLink">jQuery.twStylesheet</a> - Dynamic style-sheet handling</li><li> <a target="_blank" title="External link to http://jquery.tiddlywiki.org/encoding.digests.sha1.html" href="http://jquery.tiddlywiki.org/encoding.digests.sha1.html" class="externalLink">jQuery.encoding.digests.sha1</a> - Cryptographic signing code</li></ul></div>
<div class="tagClear"></div>

</div>
</div>
</div>
</div>
<!--}}}-->

<script type="text/javascript">
document.getElementById("splashScreen").style.display="none";
</script>
!Set up a new repository
Navigate to the working directory.
{{{
hg init
hg add
hg commit
}}}

!Logging and status
{{{
hg log
hg status
hg diff <filename>
}}}

!Reverting
Revert all files to the last commit:
{{{
hg revert --all
}}}

!Remove a file
{{{
hg remove <filename>
hg remove file.txt
}}}

!Reviewing and changing file versions
{{{
hg cat <filename>
hg cat file.txt
}}}
As of a particular revision:
{{{
hg cat -r <revision no> <filename>
hg cat -r 1 file.txt
}}}
Update back to a stated version:
{{{
hg update -r <version no>
}}}
Latest version:
{{{
hg update
}}}
{{{
class passwd(str):
    def __repr__(self):
        return repr('*' * len(self))
}}}
It will not immediately show up in your debugger in plain text or in exception frames as __repr__ is used in those scenarios. Example:
{{{
>>> astr = 'aString'
>>> astr
'aString'
>>> print astr
aString
>>> apwd = passwd('aPassword')
>>> apwd
'*********'
>>> print apwd
aPassword
}}}

Source: http://code.activestate.com/recipes/576905/
<<tagCloud systemTiddlers systemConfig excludeSearch excludeLists includeNew PluginDocm italics:yes>>

Base:
[[MonkeyGTD|http://simonbaird.com/monkeygtd/]]

Packages / projects:
*FormTiddlerProject
*ReminderProject

Best Plugins for TiddlyWiki:
*ImportTiddlersPlugin
*ExportTiddlersPlugin
*[[tagCloud plugin]]
*[[NestedSlidersPlugin]]
*ForEachTiddlerPlugin
*YourSearchPlugin
*SearchOptionsPlugin
*TiddlerWithParamsPlugin
*PartTiddlerPlugin
*IncludePlugin
*DataTiddlerPlugin
*FormTiddlerPlugin
*CheckboxPlugin
*DatePlugin

Best macros:
*[[NewFromTemplateMacro]]
*TagglyTagCloudMacro

All plugins:
<<forEachTiddler
 where
 ' tiddler.tags.contains("Plugin") || tiddler.tags.contains("Macro") || tiddler.tags.contains("systemConfig")'
 sortBy 'tiddler.title'
>>

Plugin examples:
<<forEachTiddler
 where
 ' tiddler.tags.contains("PluginExample")'
 sortBy 'tiddler.title'
>>

Plugin documentation:
<<forEachTiddler
 where
 ' tiddler.tags.contains("PluginDoc")'
 sortBy 'tiddler.title'
>>
Version: <<version>>
!The Magic of Objects
* Polymorphism: You can use the same operations on objects of different classes, and they will work as if “by magic.”
* Encapsulation: You hide unimportant details of how objects work from the outside world.
* Inheritance: You can create specialized classes of objects from general ones.


Some pointers:
* Gather what belongs together. If a function manipulates a global variable, the two of them might be better off in a class, as an attribute and a method.
* Don’t let objects become too intimate. Methods should mainly be concerned with the attributes of their own instance. Let other instances manage their own state.
* Go easy on the inheritance, especially multiple inheritance. Inheritance is useful at times, but can make things unnecessarily complex in some cases. And multiple inheritance can be very difficult to get right and even harder to debug.
* Keep it simple. Keep your methods small. As a rule of thumb, it should be possible to read (and understand) most of your methods in, say, 30 seconds. For the rest, try to keep them shorter than one page or screen.

When determining which classes you need and which methods they should have, you may try something like this:
# Write down a description of your problem (what should the program do?). Underline all the nouns, verbs, and adjectives.
# Go through the nouns, looking for potential classes.
# Go through the verbs, looking for potential methods.
# Go through the adjectives, looking for potential attributes.
# Allocate methods and attributes to your classes.

Now you have a first sketch of an object-oriented model. You may also want to think about what responsibilities and relationships (such as inheritance or cooperation) the classes and objects will have. To refine your model, you can do the following:
# Write down (or dream up) a set of use cases—scenarios of how your program may be used. Try to cover all the functionality.
# Think through every use case step by step, making sure that everything you need is covered by your model. If something is missing, add it. If something isn’t quite right, change it. Continue until you are satisfied.
These [[InterfaceOptions]] are saved in your browser.

Your username for signing your edits:
<<option txtUserName>>
<<option chkSaveBackups>> [[SaveBackups]]
<<option chkAutoSave>> [[AutoSave]]
<<option chkRegExpSearch>> [[RegExpSearch]]
<<option chkCaseSensitiveSearch>> [[CaseSensitiveSearch]]
<<option chkAnimate>> [[EnableAnimations]]

----
Also see [[AdvancedOptions]]
Oracle SQL reference, tricks and tips
<<tiddler ListTaggedTiddlers with:"Oracle">>
{{sectionTOC{}}}
'''SQL''' (pronounced "ess-que-el") stands for '''S'''tructured '''Q'''uery '''L'''anguage. SQL is a data retrieval and manipulation language used to communicate with the [[Oracle database]]. SQL was developed by [[IBM]] in the 1970s for use in [[System R]]. SQL is a de facto standard, as well as an [[ISO]] and [[ANSI]] standard. 

Users looking for GUI tools to help them with SQL may install [[SQL Developer]] or [[TOAD]].

!Queries
Queries are used to [[select]] or extract data from a database.

!!SELECT
Example SELECT statements:
{{{
SELECT * FROM scott.emp;
}}}
{{{
SELECT *
FROM tableName
WHERE col1 = 'value1'
AND col2 = 'value2';
}}}
{{{
SELECT col1, col5
FROM tableName
WHERE col3 = 'value3'
ORDER BY col 5;
}}}

!Data Manipulation Statements
!!INSERT
[[Insert]] a single row into a table:
{{{
INSERT INTO table_name VALUES (col1, col2, ...);
}}}

Insert rows from one table into another:
{{{
INSERT INTO table_name(col1, col2, ...) (SELECT 'value1', 'value2', ... from table_name);
}}}

!!UPDATE
[[Update]] the entire column of that table (all rows):
{{{
UPDATE customer SET state='CA';
}}}

Update the specific record of the table:
{{{
UPDATE customer SET name='Joe' WHERE customer_id=10;
}}}

Updates the column invoice as paid when paid column has more than zero:
{{{
UPDATE movies SET invoice='paid' WHERE paid >= 0;
}}}

===DELETE===
[[Delete]] all rows from a table:
 DELETE FROM tab1;

Conditionally delete rows:
 DELETE FROM tab1 WHERE col1 = '123';

==Data Control Language==

===GRANT===
Grant [[privilege]]s to users or roles:

 GRANT create session TO scott;

===REVOKE===
Revoke privileges from users or roles:

 REVOKE unlimited [[tablespace]] FROM scott;

==Data Definition Statements==

===Tables===

====Create a table====
The syntax to create a [[table]] is:
 CREATE TABLE [table name] (
       columnname datatype, 
       ...
 );

For example:
 CREATE TABLE customers (
   col1 NUMBER,
   col2 VARCHAR2(20)
 );

====Rename a table====
The syntax to rename a table is:
 ALTER TABLE [table name]
       RENAME TO [new table name];

For example:
 ALTER TABLE customers
       RENAME TO customer;

====Add a column====
The syntax to add a [[column]] is:
 ALTER TABLE [table name]
       ADD ( [column name] [datatype], ... );

For example:
 ALTER TABLE employee
       ADD (id int);

====Modify a column====
The syntax to modify a column is:
 ALTER TABLE [table name]
       MODIFY ( [column] [new data type] );

For example:
 ALTER TABLE employee
       MODIFY( sickHours float );

====Drop a column====
The syntax to drop a column is:
 ALTER TABLE [table name]
       DROP COLUMN [column name];

For example:
 ALTER TABLE employee
       DROP COLUMN vacationPay;

===Indexes===

====Create an index====
The syntax for creating an [[index]] is:
 CREATE INDEX index_name
     ON table_name (col1, col2, ...); 

Example of how to add a primary key constraint to a table:
 ALTER TABLE table_name ADD CONSTRAINT constraint_name
       PRIMARY KEY('eno');

====Rename an index====
The syntax for renaming an index is:
 ALTER INDEX index_name
     RENAME TO new_index_name;

For example:
 ALTER INDEX customer_idx
     RENAME TO new_customer_idx;

====Drop an index====
The syntax for dropping an index is:
 DROP INDEX index_name;

For example:
 DROP INDEX customer_idx;

===Sequences===
A [[sequence]] is an object that can generate numeric value in sequence. Sequences are typically used to generate values for primary keys.

====Create a sequence====
The syntax to create a sequence is:
 CREATE SEQUENCE sequence_name
     MINVALUE value
     MAXVALUE value
     START WITH value
     INCREMENT BY value
     CACHE value;

For example:
 CREATE SEQUENCE supplier_seq
     MINVALUE 1
     MAXVALUE 999999999999999999999999999
     START WITH 1
     INCREMENT BY 1
     CACHE 20;

====Generating sequence values====
Use the NEXTVAL function to generate the next sequence value. CURRVAL will return the last generated value. Example:

 SELECT supplier_seq.NEXTVAL FROM dual;

====Alter a sequence====
'''Increment a sequence by a certain amount:'''
 ALTER SEQUENCE <sequence_name> INCREMENT BY <integer>;
 ALTER SEQUENCE seq_inc_by_ten  INCREMENT BY 10;

'''Change the maximum value of a sequence:'''
 ALTER SEQUENCE <sequence_name> MAXVALUE <integer>;
 ALTER SEQUENCE seq_maxval  MAXVALUE  10;

'''Set the sequence to cycle or not cycle:'''
 ALTER SEQUENCE <sequence_name> <CYCLE | NOCYCLE>;
 ALTER SEQUENCE seq_cycle NOCYCLE;

'''Configure the sequence to cache a value:'''
 ALTER SEQUENCE <sequence_name> CACHE <integer> | NOCACHE;
 ALTER SEQUENCE seq_cache NOCACHE;

'''Set whether or not the values are to be returned in order'''
 ALTER SEQUENCE <sequence_name> <ORDER | NOORDER>;
 ALTER SEQUENCE seq_order NOORDER;

===Views===
A [[view]] is a virtual table defined by a SQL statement.

===Materialized Views===
A [[materialized view]] is like a view in that it represents data that is contained in other database tables and views. However, unlike a view, a materialized view contains actual data.

===Materialized View Logs===
DML performed on a table can be stored in a materialized view log. So, next time a materialized view is refreshed, it doesn't have to read the entire table to get the changes performed.

===Synonyms===
A [[synonym]] is a database object used for assign an alias to an object. This is very usefull for Schema Transparency. For example:

 CREATE SYNONYM emp FOR scott.emp;

So, instead of selecting from "scott.emp", you can now select from "emp".

==Also see==
* [[SQL FAQ]] - Frequently asked questions
* [[Hint]] - Query hints
<<sectionTOC>>
<!--{{{-->
<div class='header'>
<table width="100%"  border="0" cellpadding="0">
  <tr>
    <td>
      <div class='siteLogo' refresh='content' tiddler='Logo'></div>
      <div class='siteTitle' refresh='content' tiddler='SiteTitle'></div>
      <div class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></div>
    </td>
    <td><div class='searchBar' macro='search'></div></td>
  </tr>
</table>
</div>
<div id='mainMenu'>
  <span refresh='content' tiddler='MainMenu'></span>
  <span id='noticeBoard' refresh='content' tiddler='NoticeBoard'></span>
</div>
<div id='sidebar'>
  <div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
  <div id='sidebarTabs' refresh='macro' force='true' macro='slider chkSideBarTabs SideBarTabs "index »" "display lists of tiddlers"'></div>
</div>
<div id='displayArea'>
  <div id='messageArea'></div>
  <div id='tiddlerDisplay'></div>
</div>
<div id='siteFooter' refresh='content' tiddler='Footer'></div>
<!--}}}-->
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryMid]]'>
  <div class='siteLogo' refresh='content' tiddler='Logo'></div>
  <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>
  <span class='searchBar' macro='search'></span>
</div>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
  <div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
  <div id='sidebarTabs' refresh='macro' force='true' macro='slider chkSideBarTabs SideBarTabs "index »" "display lists of tiddlers"'></div>
</div>
<div id='displayArea'>
  <div id='messageArea'></div>
  <div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
/***
|''Name''|PaletteViewPlugin|
|''Version''|0.2|
|''Author''|FND|
|''Source''|[[FND's DevPad|http://devpad.tiddlyspot.com/#PaletteViewMacro]]|
|''License''|[[Creative Commons Attribution-ShareAlike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]]|
|''~CoreVersion''|2.1|
|''Type''|macro|
|''Requires''|N/A|
|''Overrides''|N/A|
|''Description''|Displays color palettes.|
!Notes
There is also [[ViewPalettePlugin|http://simon.tiddlyspot.com/#ViewPalettePlugin]], which currently does not work with TiddlyWiki v2.2 though.
!Usage
{{{
<<paletteView [[TiddlerName]]>>
}}}
!!Example
<<paletteView [[ColorPalette]]>>
!Revision History
!!v0.1 (2007-11-18)
* initial release
!!v0.2 (2007-11-20)
* limited processing to slices containing [[actual color values|http://www.w3.org/TR/CSS21/syndata.html#color-units]]
* changed fallback value to the tiddler the macro is called from (instead of using [[ColorPalette]])
!To Do
* selection list for all available palettes (tag-based)
* parameter for custom table class
* customizable column order
* documentation (e.g. using from within [[ViewTemplate]])
!Code
***/
//{{{
config.macros.paletteView = {};

config.macros.paletteView.handler = function(place, macroName, params, wikifier, paramString, tiddler) {
 var title = params[0] || tiddler.title;
 //var palettes = store.getTaggedTiddlers(params[0]); // DEBUG: yet to be implemented
 var colors = store.calcAllSlices(title);
 var labels = [];
 for(var c in colors) {
 if(this.isColor(colors[c])) {
 labels.push(c);
 }
 }
 if(labels.length > 0) {
 var output = "|!Sample|!Value|!Name|h\n";
 for(var i = 0; i < labels.length; i++) {
 output += "|padding:0 4em;background-color:" + colors[labels[i]] + ";&nbsp;|"
 + "{{{" + colors[labels[i]] + "}}}|"
 + "[[" + labels[i] + "|" + title + "]]|\n";
 }
 wikify(output, place);
 }
};

config.macros.paletteView.isColor = function(s) {
 var colors = ["Black", "Green", "Silver", "Lime", "Gray", "Olive", "White", "Yellow",
 "Maroon", "Navy", "Red", "Blue", "Purple", "Teal", "Fuchsia", "Aqua", "Orange"];
 var match = s.match(/^#[0-9A-F]{3}$|^#[0-9A-F]{6}$|^RGB\([\d,\s]{5,}\)$/i);
 if(match) return true;
 if(colors.contains(s)) return true;
 return false;
};
//}}}
/***
|<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>
***/
|Standard Periodic Table (ref. Wikipedia)|c
|| !1 | !2 |!| !3 | !4 | !5 | !6 | !7 | !8 | !9 | !10 | !11 | !12 | !13 | !14 | !15 | !16 | !17 | !18 |
|!1|bgcolor(#a0ffa0): @@color(red):H@@ |>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>||bgcolor(#c0ffff): @@color(red):He@@ |
|!2|bgcolor(#ff6666): Li |bgcolor(#ffdead): Be |>|>|>|>|>|>|>|>|>|>||bgcolor(#cccc99): B |bgcolor(#a0ffa0): C |bgcolor(#a0ffa0): @@color(red):N@@ |bgcolor(#a0ffa0): @@color(red):O@@ |bgcolor(#ffff99): @@color(red):F@@ |bgcolor(#c0ffff): @@color(red):Ne@@ |
|!3|bgcolor(#ff6666): Na |bgcolor(#ffdead): Mg |>|>|>|>|>|>|>|>|>|>||bgcolor(#cccccc): Al |bgcolor(#cccc99): Si |bgcolor(#a0ffa0): P |bgcolor(#a0ffa0): S |bgcolor(#ffff99): @@color(red):Cl@@ |bgcolor(#c0ffff): @@color(red):Ar@@ |
|!4|bgcolor(#ff6666): K |bgcolor(#ffdead): Ca ||bgcolor(#ffc0c0): Sc |bgcolor(#ffc0c0): Ti |bgcolor(#ffc0c0): V |bgcolor(#ffc0c0): Cr |bgcolor(#ffc0c0): Mn |bgcolor(#ffc0c0): Fe |bgcolor(#ffc0c0): Co |bgcolor(#ffc0c0): Ni |bgcolor(#ffc0c0): Cu |bgcolor(#ffc0c0): Zn |bgcolor(#cccccc): Ga |bgcolor(#cccc99): Ge |bgcolor(#cccc99): As |bgcolor(#a0ffa0): Se |bgcolor(#ffff99): @@color(green):Br@@ |bgcolor(#c0ffff): @@color(red):Kr@@ |
|!5|bgcolor(#ff6666): Rb |bgcolor(#ffdead): Sr ||bgcolor(#ffc0c0): Y |bgcolor(#ffc0c0): Zr |bgcolor(#ffc0c0): Nb |bgcolor(#ffc0c0): Mo |bgcolor(#ffc0c0): Tc |bgcolor(#ffc0c0): Ru |bgcolor(#ffc0c0): Rh |bgcolor(#ffc0c0): Pd |bgcolor(#ffc0c0): Ag |bgcolor(#ffc0c0): Cd |bgcolor(#cccccc): In |bgcolor(#cccccc): Sn |bgcolor(#cccc99): Sb |bgcolor(#cccc99): Te |bgcolor(#ffff99): I |bgcolor(#c0ffff): @@color(red):Xe@@ |
|!6|bgcolor(#ff6666): Cs |bgcolor(#ffdead): Ba |bgcolor(#ffbfff):^^*1^^|bgcolor(#ffc0c0): Lu |bgcolor(#ffc0c0): Hf |bgcolor(#ffc0c0): Ta |bgcolor(#ffc0c0): W |bgcolor(#ffc0c0): Re |bgcolor(#ffc0c0): Os |bgcolor(#ffc0c0): Ir |bgcolor(#ffc0c0): Pt |bgcolor(#ffc0c0): Au |bgcolor(#ffc0c0): @@color(green):Hg@@ |bgcolor(#cccccc): Tl |bgcolor(#cccccc): Pb |bgcolor(#cccccc): Bi |bgcolor(#cccc99): Po |bgcolor(#ffff99): At |bgcolor(#c0ffff): @@color(red):Rn@@ |
|!7|bgcolor(#ff6666): Fr |bgcolor(#ffdead): Ra |bgcolor(#ff99cc):^^*2^^|bgcolor(#ffc0c0): Lr |bgcolor(#ffc0c0): Rf |bgcolor(#ffc0c0): Db |bgcolor(#ffc0c0): Sq |bgcolor(#ffc0c0): Bh |bgcolor(#ffc0c0): Hs |bgcolor(#ffc0c0): Mt |bgcolor(#ffc0c0): Ds |bgcolor(#ffc0c0): Rg |bgcolor(#ffc0c0): @@color(green):Uub@@ |bgcolor(#cccccc): Uut |bgcolor(#cccccc): Uuq |bgcolor(#cccccc): Uup |bgcolor(#cccccc): Uuh |bgcolor(#fcfecc): @@color(#cccccc):Uus@@ |bgcolor(#ecfefc): @@color(#cccccc):Uuo@@ |

| !Lanthanides^^*1^^|bgcolor(#ffbfff): La |bgcolor(#ffbfff): Ce |bgcolor(#ffbfff): Pr |bgcolor(#ffbfff): Nd |bgcolor(#ffbfff): Pm |bgcolor(#ffbfff): Sm |bgcolor(#ffbfff): Eu |bgcolor(#ffbfff): Gd |bgcolor(#ffbfff): Tb |bgcolor(#ffbfff): Dy |bgcolor(#ffbfff): Ho |bgcolor(#ffbfff): Er |bgcolor(#ffbfff): Tm |bgcolor(#ffbfff): Yb |
| !Actinides^^*2^^|bgcolor(#ff99cc): Ac |bgcolor(#ff99cc): Th |bgcolor(#ff99cc): Pa |bgcolor(#ff99cc): U |bgcolor(#ff99cc): Np |bgcolor(#ff99cc): Pu |bgcolor(#ff99cc): Am |bgcolor(#ff99cc): Cm |bgcolor(#ff99cc): Bk |bgcolor(#ff99cc): Cf |bgcolor(#ff99cc): Es |bgcolor(#ff99cc): Fm |bgcolor(#ff99cc): Md |bgcolor(#ff99cc): No |

*Chemical Series of the Periodic Table
**@@bgcolor(#ff6666): Alkali metals@@
**@@bgcolor(#ffdead): Alkaline earth metals@@
**@@bgcolor(#ffbfff): Lanthanides@@
**@@bgcolor(#ff99cc): Actinides@@
**@@bgcolor(#ffc0c0): Transition metals@@
**@@bgcolor(#cccccc): Poor metals@@
**@@bgcolor(#cccc99): Metalloids@@
**@@bgcolor(#a0ffa0): Nonmetals@@
**@@bgcolor(#ffff99): Halogens@@
**@@bgcolor(#c0ffff): Noble gases@@

*State at standard temperature and pressure
**those in @@color(red):red@@ are gases
**those in @@color(green):green@@ are liquids
**those in black are solids
/***
|Name|PlayerPlugin|
|Source|http://www.TiddlyTools.com/#PlayerPlugin|
|Version|1.1.4|
|Author|Eric Shulman|
|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||
|Options|##Configuration|
|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>';
//}}}
It is recommended that [[Plugins]]  start with some standard information in TiddlerSlicing format. For example, see the ExamplePlugin:
{{{
|''Name:''|ExamplePlugin|
|''Description:''|To demonstrate how to write TiddlyWiki plugins|
|''Version:''|2.0.2|
|''Date:''|Jul 12, 2006|
|''Source:''|http://www.tiddlywiki.com/#ExamplePlugin|
|''Author:''|JeremyRuston (jeremy (at) osmosoft (dot) com)|
|''License:''|[[BSD open source license]]|
|''~CoreVersion:''|2.1.0|
|''Browser:''|Firefox 1.0.4+; Firefox 1.5; InternetExplorer 6.0|
}}}
At the moment, only ~CoreVersion affects how [[Plugins]] are processed: if the ~CoreVersion is specified for a plugin, TiddlyWiki will only execute the plugin if the core code version matches or exceeds the version specified. For example, if you specify a ~CoreVersion of 2.2, version 2.1.x of TiddlyWiki will refuse to execute the plugin.

To indicate an error, plugins should just {{{throw}}} an exception. The text of the exception will be displayed in the PluginManager.
/***
|Name:|PrettyDatesPlugin|
|Description:|Provides a new date format ('pppp') that displays times such as '2 days ago'|
|Version:|1.0 ($Rev: 3646 $)|
|Date:|$Date: 2008-02-27 02:34:38 +1000 (Wed, 27 Feb 2008) $|
|Source:|http://mptw.tiddlyspot.com/#PrettyDatesPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
!!Notes
* If you want to you can rename this plugin. :) Some suggestions: LastUpdatedPlugin, RelativeDatesPlugin, SmartDatesPlugin, SexyDatesPlugin.
* Inspired by http://ejohn.org/files/pretty.js
***/
//{{{
Date.prototype.prettyDate = function() {
	var diff = (((new Date()).getTime() - this.getTime()) / 1000);
	var day_diff = Math.floor(diff / 86400);

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

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

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

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

//}}}
/***
|Name|PreviewPlugin|
|Source|http://www.TiddlyTools.com/#PreviewPlugin|
|Documentation|http://www.TiddlyTools.com/#PreviewPluginInfo|
|Version|1.8.1|
|Author|Eric Shulman|
|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||
|Options|##Configuration|
|Description|add key-by-key wikified preview to any textarea input field|
Provides key-by-key ''LIVE PREVIEW'' of //formatted// tiddler content as you type input into a textarea (multi-line) edit field.
!!!!!Documentation
>see [[PreviewPluginInfo]]
!!!!!Configuration
<<<
Automatically freeze preview updates when a tiddler takes more than <<option txtPreviewAutoFreeze>> milliseconds to render.
<<<
!!!!!Revisions
<<<
2008.01.08 [*.*.*] plugin size reduction: documentation moved to ...Info tiddler
2007.12.04 [*.*.*] update for TW2.3.0: replaced deprecated core functions, regexps, and macros
2007.11.18 [1.8.1] in config.commands.previewTiddler, changed alt command text to use character-based "psuedo-checkbox" instead of embedded html fragment
2007.09.27 [1.8.0] split TidIDE preview functionality into separate stand-alone plugin (see [[TidIDEPlugin]]).  
|please see [[TidIDEPluginInfo]] for additional revision details|
2006.04.15 [0.5.0] Initial ALPHA release. Converted from inline script.
<<<
!!!!!Code
***/
// // version info
//{{{
version.extensions.PreviewPlugin= {major: 1, minor: 8, revision: 1, date: new Date(2007,11,18)};
//}}}

// //  macro definition
//{{{
if (config.options.txtPreviewAutoFreeze==undefined)
	config.options.txtPreviewAutoFreeze=250; // limit (in milliseconds) for auto-freezing preview display

config.macros.preview = {
	renderMsg: "rendering preview...",
	timeoutMsg: " (> %0ms)",
	freezeMsg: " - preview is frozen.  Press [refresh] to re-display.",
	handler: function(place,macroName,params) {
		var hide=params[0]=="hide"; if (hide) params.shift();
		var field=params[0];
		var height=params[1]; if (!height) height=15;
		var here=this.findContainingForm(place);
		if (!here) here=story.findContainingTiddler(place);
		if (!here) here=place.parentNode;
		if (!here) here=place;
		var elems=here.getElementsByTagName("textarea");
		if (field) for (var e=0; e<elems.length; e++)  // find matching textarea (by fieldname)
			if (elems[e].getAttribute("edit")==field) var ta=elems[e];
		else
			if (elems.length) var ta=elems[elems.length-1]; // default to last rendered text area
		if (!ta) {
			var elems=here.getElementsByTagName("input");
			if (field) for (var e=0; e<elems.length; e++)  // find matching input field (by fieldname)
				if (elems[e].getAttribute("edit")==field) var ta=elems[e];
			else
				if (elems.length) var ta=elems[elems.length-1]; // default to last rendered input field
		}
		if (!ta) return false; // no textarea or input field found... do nothing...
		var id=(new Date().getTime()).toString()+Math.random(); // unique instance ID
		ta.id=id+"_edit";
		ta.setAttribute("previewid",id+"_preview");
		ta.saved_onkeyup=ta.onkeyup;
		ta.onkeyup=function(ev) {
			if (this.saved_onkeyup) this.saved_onkeyup.apply(this,arguments);
			config.macros.preview.render(this.id,this.getAttribute("previewid"));
		}
		var html=this.html.replace(/%previd%/g,id+"_preview")
		html=html.replace(/%srcid%/g,id+"_edit");
		html=html.replace(/%hide%/g,hide?"none":"block");
		html=html.replace(/%limit%/g,config.options.txtPreviewAutoFreeze);
		html=html.replace(/%frozen%/g,hide?"checked":"");
		html=html.replace(/%height%/g,height);
		html=html.replace(/%halfheight%/g,height/2);
		createTiddlyElement(place,"span").innerHTML=html;
		this.render(id+"_edit",id+"_preview");
	},
	findContainingForm: function(e) {
		while (e && e.nodeName.toLowerCase()!="form") e=e.parentNode;
		return e;
	},
	render: function(srcid,previd,force) {
		var value=document.getElementById(srcid).value;
		var panel=document.getElementById(previd);
		var f=this.findContainingForm(panel);
		if (!f || (f.freeze.checked && !force)) return;
		var p=panel.firstChild; var d=f.domview; var h=f.htmlview; if (!p||!d||!h) return;
		p.innerHTML="";
		f.status.value=this.renderMsg;
		var start=new Date();
		wikify(value.replace(/\r/g,''),p);
		var end=new Date();
		this.renderDOM(previd);
		this.renderHTML(previd);
		f.status.value="elapsed: "+(end-start+1)+"ms";
		// automatically suspend preview updates for slow rendering tiddlers
		if (end-start+1>config.options.txtPreviewAutoFreeze) {
			f.freeze.checked=true;
			f.status.value+=this.timeoutMsg.format([config.options.txtPreviewAutoFreeze]);
		}
		if (f.freeze.checked) f.status.value+=this.freezeMsg;
	},
	renderDOM: function(id) {
		var panel=document.getElementById(id);
		var f=this.findContainingForm(panel); if (!f) return;
		var p=panel.firstChild; var d=f.domview; var h=f.htmlview; if (!p||!d||!h) return;
		var height=p.getAttribute("height");
		p.style.height=((f.dom.checked||f.html.checked)?height/2:height)+"em";
		if (f.dom.checked) d.value=this.getNodeTree(p,"|  ");
		if (!d.style||!h.style) return;
		d.style.height=height/2+"em";
		d.style.display=f.dom.checked?"inline":"none";
		d.style.width=f.html.checked?"49.5%":"100%";
		h.style.width=f.dom.checked?"49.5%":"100%";
	},
	renderHTML: function(id) {
		var panel=document.getElementById(id);
		var f=this.findContainingForm(panel); if (!f) return;
		var p=panel.firstChild; var d=f.domview; var h=f.htmlview; if (!p||!d||!h) return;
		var height=p.getAttribute("height");
		p.style.height=((f.dom.checked||f.html.checked)?height/2:height)+"em";
		if (f.html.checked) h.value=this.formatHTML(p.innerHTML);
		if (!h.style||!d.style) return;
		h.style.height=height/2+"em";
		h.style.display=f.html.checked?"inline":"none";
		h.style.width=f.dom.checked?"49.5%":"100%";
		d.style.width=f.html.checked?"49.5%":"100%";
	},
	formatHTML: function(txt) {
		if (config.browser.isIE) return txt; // BYPASS - 4/24/2006 due to IE hang problem.  Will fix later...
		var out="";
		var indent="";
		var level=0;
		for (var i=0;i<txt.length;i++) {
			var c=txt.substr(i,1);
			if (c=="<") {
					if (txt.substr(i+1,1)=="/")  indent=indent.substr(0,indent.length-2);
				out+="\n"+indent;
				if (txt.substr(i+1,1)!="/" && txt.substr(i+1,3)!="br>" && txt.substr(i+1,2)!="p>" && txt.substr(i+1,3)!="hr>")  indent+="  ";
			}
			out+=c;
				if (c=="\n")
				out+=indent;
			if (c==">" && txt.substr(i+1,1)!="<")
				out+="\n"+indent;
		}
		return out;
	},
	getNodeTree: function(theNode,theIndent,showPath,inline,thePrefix,thePath)
	{
		if (!theNode) return "";
		if (!thePrefix) thePrefix="";
		if (!thePath) thePath="";
		var mquote='"'+(inline?"{{{":"");
		var endmquote=(inline?"}}}":"")+'"';
		// generate output for this node
		var out = thePrefix;
		if (showPath && thePath.length)
				out += (inline?"//":"")+thePath.substr(1)+":"+(inline?"//":"")+"\r\n"+thePrefix;
		if (theNode.className=="DOMViewer")
			return out+'[DOMViewer]\r\n'; // avoid self-referential recursion
		out += (inline?"''":"")+theNode.nodeName.toUpperCase()+(inline?"''":"");
		if (theNode.nodeName=="#text")
			out += ' '+mquote+theNode.nodeValue.replace(/\n/g,'\\n')+endmquote;
		if (theNode.className)
			out += ' class='+mquote+theNode.className+endmquote;
		if (theNode.type)
			out += ' type='+mquote+theNode.type+endmquote;
		if (theNode.id)
			out += ' id='+mquote+theNode.id+endmquote;
		if (theNode.name)
			out += " "+theNode.name+(theNode.value?"="+mquote+theNode.value+endmquote:"");
		if (theNode.href)
			out += ' href='+mquote+theNode.href+endmquote;
		if (theNode.src)
			out += ' src='+mquote+theNode.src+endmquote;
		if (theNode.attributes && theNode.getAttribute("tiddlyLink")!=undefined)
			out += ' tiddler='+mquote+theNode.getAttribute("tiddlyLink")+endmquote;
		out += "\r\n";
		// recursively generate output for child nodes
		thePath=thePath+"."+theNode.nodeName.toLowerCase();
		thePrefix=theIndent+thePrefix;
		for (var i=0;i<theNode.childNodes.length;i++)
		{
			var thisChild=theNode.childNodes.item(i);
			var theNum=(inline?"~~":"(")+(i+1)+(inline?"~~":")");
			out += this.getNodeTree(thisChild,theIndent,showPath,inline,thePrefix,thePath+theNum);
		}
		return out;
	},
	html: " <form style='width:100%'><span id='%previd%' editID='%srcid%' style='display:%hide%'><div class='viewer' \
			height='%height%' style='margin:0;margin-top:.5em;height:%height%em;overflow:auto;white-space:normal'> \
			&nbsp; \
			</div> \
		<!-- DOM and HTML viewers --> \
		<textarea name=domview cols=60 rows=12 wrap=off \
			onfocus='this.select()' style='display:none;width:100%;height:%halfheight%em;'></textarea><!-- \
		--><textarea name=htmlview cols=60 rows=12 wrap=off \
			onfocus='this.select()' style='display:none;width:100%;height:%halfheight%em;'></textarea> \
		<!-- status line, preview option checkboxes, run/refresh buttons --> \
		<table width='100%' style='border:0;padding:0;margin:0'><tr style='border:0;padding:0;margin:0'> \
		<td style='border:0;padding:0;margin:0'><!-- \
			--><input type=text name=status style='padding:0;width:100%;' \
				title='ELAPSED: time (in milliseconds) used to render tiddler content in preview display'><!-- \
		--></td><td style='width:1%;border:0;padding:0;margin:0;'><!-- \
			--><input type=text name=limit size='6' maxlength='6' style='padding:0;width:5em;text-align:center' \
				value='%limit%ms' title='TIME LIMIT: maximum rendering time (in milliseconds) before auto-freezing preview' \
				onfocus='this.select()' \
				onchange='var val=this.value.replace(/[^0-9]/g,\"\"); if (!val.length) val=this.defaultValue; \
					this.value=val+\"ms\"; config.options.txtPreviewAutoFreeze=val; saveOptionCookie(\"txtPreviewAutoFreeze\"); \
					this.form.freeze.checked=false; config.macros.preview.render(\"%srcid%\",\"%previd%\",true);'><!-- \
		--></td><td style='width:1%;border:0;padding:0;margin:0;'><!-- \
			--><input type=text name=height size='4' maxlength='4' style='padding:0;width:4em;text-align:center' \
				value='%height%em' title='HEIGHT: size (in \"ems\") of preview area, including controls' \
				onfocus='this.select()' \
				onchange='var val=this.value.replace(/[^0-9]/g,\"\");  if (!val.length) val=this.defaultValue; \
					this.value=val+\"em\"; document.getElementById(\"%previd%\").firstChild.setAttribute(\"height\",val); \
					config.macros.preview.render(\"%srcid%\",\"%previd%\",true)'><!-- \
		--></td><td style='width:1%;border:0;padding:0;margin:0;text-align:right;white-space:nowrap'> \
			<input type=checkbox name=dom style='display:inline;width:auto;margin:1px;' \
				title='show Document Object Model (DOM) information' \
				onclick='config.macros.preview.renderDOM(\"%previd%\");'>DOM \
			<input type=checkbox name=html style='display:inline;width:auto;margin:1px;' \
				title='show rendered HTML' \
				onclick='config.macros.preview.renderHTML(\"%previd%\");'>HTML \
			<input type=checkbox name=freeze style='display:inline;width:auto;margin:1px;' %frozen% \
				title='do not update preview display as changes are made' \
				onclick='var p=document.getElementById(\"%previd%\");  \
					if (this.checked) this.form.status.value+=config.macros.preview.freezeMsg; \
					else config.macros.preview.render(\"%srcid%\",\"%previd%\",true);'>freeze \
			<input type=button style='display:inline;width:auto;' value='refresh' \
				title='update preview display' \
				onclick='config.macros.preview.render(\"%srcid%\",\"%previd%\",true)'> \
		</td></tr></table> \
		</span></form>"
}
//}}}

// // toolbar definition
//{{{
config.commands.previewTiddler = {
	text: 'preview',
	tooltip: 'show key-by-key preview',
	text_alt: '\u221Apreview',
	handler: function(event,src,title) {
		var here=story.findContainingTiddler(src); if (!here) return;
		var elems=here.getElementsByTagName("span");
		for (var e=0; e<elems.length; e++) {
			if (elems[e].getAttribute("editid")) {
				var show=elems[e].style.display=="none";
				src.innerHTML=show?this.text_alt:this.text;
				elems[e].style.display=show?"block":"none";
				config.macros.preview.findContainingForm(elems[e]).freeze.checked=!show;
				if (show) config.macros.preview.render(elems[e].getAttribute("editid"),elems[e].id);
			}
		}
		return false;
	}
};
//}}}
/***
|Name|PreviewPluginInfo|
|Source|http://www.TiddlyTools.com/#PreviewPlugin|
|Documentation|http://www.TiddlyTools.com/#PreviewPluginInfo|
|Version|1.8.1|
|Author|Eric Shulman|
|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|documentation|
|Requires||
|Overrides||
|Description|documentation for PreviewPlugin|
Provides key-by-key ''LIVE PREVIEW'' of //formatted// tiddler content as you type input into a textarea (multi-line) edit field.
!!!!!Usage
<<<
Syntax: (in tiddler content)
{{{
<<preview hide fieldname height>>
}}}
//OR// (in [[EditTemplate]])
{{{
<span macro='preview hide fieldname height'></span>
}}}
where:
* 'hide' (optional)<br>is a keyword that causes the preview display to be initially hidden when created.  This is typically used in an [[EditTemplate]] definition, in conjunction with the 'previewTiddler' toolbar syntax (see below).
* fieldname (optional)<br>specifies the name of the tiddler field that is being previewed.  It corresponds to the fieldname specified in the {{{<span class='editor' macro='edit fieldname height'></span>}}} syntax used to define the textarea edit field, and is used to locate and access the source content that is to be previewed.  When omitted, the previewer will automatically associate itself with the last textarea control that precedes it in the rendered tiddler editor display.
* height (optional)<br>defines the total height of the previewer display (including the status line and controls), using CSS "em" (line height) measurements.  The default height value is "15em" (i.e., approximately 15 lines of standard-sized text)

In addition to the preview macro itself, you can place the following in your [[EditTemplate]] to add a tiddler toolbar command that enables you to toggle the preview display once it has been created:
{{{
<span class='toolbar' macro='toolbar previewTiddler'></span>
}}}
when clicked, this command will show/hide ALL preview controls that are displayed in the current tiddler.  Note that, when desired, you can also embed this toolbar command directly into a tiddler by entering the "{{{<<toolbar previewTiddler>>}}}" syntax in the tiddler source.
<<<
!!!!!Examples
<<<
in [[EditTemplate]]:
{{{
<div class='editor' macro='edit foobar 5'></div>
<div class='editor' macro='preview hide foobar 10'></div>
}}}
OR, embedded in tiddler content:
{{{
<<edit foobar 5>><<preview foobar 10>>
}}}
{{smallform{
<<edit foobar 5>><<preview foobar 10>>}}}
By default, the preview display is automatically rendered each time a key is typed into the tiddler content edit field.  As soon as changes are entered, they will be instantly visible within the preview display.  Unfortunately, the partial tiddler source definitions that occur //during// editing may somtimes cause rendering problems, and some exceptionally complex tiddlers make take an unusually long amount of time to completely render their content.   In such cases, key-by-key display updates are undesirable or impractical.

You can select the ''freeze'' checkbox to suspend automatic key-by-key preview display updates.  The preview display will not be re-rendered again until you press the ''refresh'' button or clear the 'freeze' checkbox.  The preview display will also automatically freeze whenever the //rendering time// exceeds a pre-determined time limit (see configuration section), specified in milliseconds.  Note: the ''actual elapsed time'' used to process and render any given content is reported in the preview "status bar" whenever that content is previewed.

In addition to a 'wikified' preview, the previewer display can show a ''DOM viewer'' and/or an ''HTML viewer'' that are also updated with each keystroke.  These text-based displays can be helpful while attempting to correct or enhance the formatting of tiddler content, especially when complex combinations of wiki-syntax produce unexpected or undesired results.
<<<
{{sectionTOC{}}}
!Defining a Class
Define a new class and its methods like so:
{{{
class Person:
  def setName(self, name):
    self.name = name
  def getName(self):
    return self.name
  def greet(self):
    print "Hello, world! I'm %s." % self.name

>>> foo = Person()
>>> bar = Person()
>>> foo.setName('Luke Skywalker')
>>> bar.setName('Anakin Skywalker')
>>> foo.greet()
Hello, world! I'm Luke Skywalker.
>>> bar.greet()
Hello, world! I'm Anakin Skywalker.
}}}

Another example:
{{{
class Basket:
  def __init__(self, contents=None):
    self.contents = contents or []
  def add(self, element):
    self.contents.append(element)
  def print_me(self):
    result = ""
    for element in self.contents:
      result = result + " " + repr(element)
    print "Contains:" + result

>>> b = Basket(['apple','orange'])
>>> b.add("lemon")
>>> b.print_me()
Contains: 'apple' 'banana' 'lemon'
}}}
Several things are worth noting in this example:
* Methods are called like this: {{{object.method(arg1, arg2)}}}.
* Some arguments can be optional and given a default value. This is done by writing the definition like this: {{{def foobar(age=32): ...}}}
* Here, {{{foobar}}} can be called with one or zero parameters. If it’s called without any parameters, age will have the default value of 32.
* {{{repr}}} converts an object to its string representation. (So if {{{element}}} contains the number 1, then {{{repr(element)}}} is the same as "1", whereas 'element' is a literal string.)

To make a class method or attribute private (inaccessible from the outside), start its name with two underscores:
{{{
class Secretive:
  def __inaccessible(self):
    print "Bet you can't see me..."
  def accessible(self):
    print "The secret message is:"
    self.__inaccessible()
}}}

For more about the {{{__init__}}} constructor method, see [[Python Magic Methods]].

!Defining a Subclass
In the example below, {{{SPAMFilter}}} is a subclass of {{{Filter}}}. As a subclass of {{{Filter}}}, {{{SPAMFilter}}}) automatically inherits a {{{filter}}} method:
{{{
class Filter:
  def __init__(self):
    self.blocked = []
  def filter(self, sequence):
    return [x for x in sequence if x not in self.blocked]

class SPAMFilter(Filter):
  def __init__(self): # Overrides init method from Filter superclass
    self.blocked = ['SPAM']

>>> f = Filter()
>>> f.init()
>>> f.filter([1, 2, 3])
[1, 2, 3]
>>> s = SPAMFilter()
>>> s.init()
>>> s.filter(['SPAM', 'SPAM', 'SPAM', 'SPAM', 'eggs', 'bacon', 'SPAM'])
['eggs', 'bacon']
}}}
The usefulness of the {{{Filter}}} class is that it can be used as a base class (superclass) for other classes such as {{{SPAMFilter}}}), which filters out 'SPAM' from sequences.

!Knowing when to use {{{self}}} and {{{__init__}}}
When defining your class methods, you //must// explicitly list {{{self}}} as the first argument for each method, including {{{__init__}}}. When you call a method of an ancestor class from within your class, you //must// include the {{{self}}} argument. But when you call your class method from outside, you do not specify anything for the {{{self}}} argument; you skip it entirely, and Python automatically adds the instance reference for you.

{{{__init__}}} methods are optional, but when you define one, you must remember to explicitly call the ancestor's {{{__init__}}} method (if it defines one). This is more generally true: whenever a descendant wants to extend the behavior of the ancestor, the descendant method must explicitly call the ancestor method at the proper time, with the proper arguments.
<<sectionTOC>>
|!Expression|!Description |
|x == y |x equals y.|
|x < y |x is less than y.|
|x > y |x is greater than y.|
|x >= y |x is greater than or equal to y.|
|x <= y |x is less than or equal to y.|
|x != y |x is not equal to y.|
|x is y |x and y are the same object.|
|x is not y |x and y are different objects.|
|x in y |x is a member of the container (e.g., sequence) y.|
|x not in |y x is not a member of the container (e.g., sequence) y.|

!String and Sequence Comparisons
Strings are compared according to their order when sorted sorted by
their ordinal values (approximately alphabetically). The ordinal value of a letter can be found with the {{{ord}}} function, whose inverse is {{{chr}}}.
{{{
>>> "alpha" < "beta"
True
>>> 'FnOrD'.lower() == 'Fnord'.lower()
True
}}}
!if, elif, else
Example code:
{{{
num = input('Enter a number: ')
if num > 0:
  print 'The number is positive'
elif num < 0:
  print 'The number is negative'
else:
  print 'The number is zero'
}}}

Conditional statements may evaluate more than one condition:
{{{
number = input('Enter a number between 1 and 10: ')
if number <= 10 and number >= 1:
  print 'Great!'
else:
  print 'Wrong!'
}}}

{{{if}}} statements can also be nested:
{{{
name = raw_input('What is your name? ')
if name.endswith('Gumby'):
  if name.startswith('Mr.'):
    print 'Hello, Mr. Gumby'
  elif name.startswith('Mrs.'):
    print 'Hello, Mrs. Gumby'
  else:
    print 'Hello, Gumby'
else:
  print 'Hello, stranger'
}}}
See also [[Python Comparison Operators]] and [[Python Truthiness]].

!!pass
Use the {{{pass}}} statement to simply do nothing (because you can't have an empty block, but you might not have written the code yet).
!{{{datetime}}}
The {{{datetime}}} module supplies classes for manipulating dates and times in both simple and complex ways. The {{{strftime}}} method returns a string representing the date and time, controlled by an explicit format string. 

Example:
{{{
>>> from datetime import datetime
>>> x = datetime.now()
>>> print x
2009-11-20 12:25:31.222000
>>> print x.__class__
<type 'datetime.datetime'>
>>> x.strftime("%d-%b-%Y")
'20-Nov-2009'
>>> x.strftime("%d-%b-%Y %H:%M:%S")
'20-Nov-2009 12:26:09'
>>> x.strftime("%d-%b-%Y %X")
'20-Nov-2009 12:26:09'
}}}

!!Date formatting
|!Directive |!Meaning |
|%a |Locale’s abbreviated weekday name. |
|%A |Locale’s full weekday name.|
|%b |Locale’s abbreviated month name. |
%B Locale’s full month name.   
%c Locale’s appropriate date and time representation.   
%d Day of the month as a decimal number [01,31].   
%f Microsecond as a decimal number [0,999999], zero-padded on the left (1) 
%H Hour (24-hour clock) as a decimal number [00,23].   
%I Hour (12-hour clock) as a decimal number [01,12].   
%j Day of the year as a decimal number [001,366].   
%m Month as a decimal number [01,12].   
%M Minute as a decimal number [00,59].   
%p Locale’s equivalent of either AM or PM. (2) 
%S Second as a decimal number [00,61]. (3) 
%U Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Sunday are considered to be in week 0. (4) 
%w Weekday as a decimal number [0(Sunday),6].   
%W Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0. (4) 
%x Locale’s appropriate date representation.   
%X Locale’s appropriate time representation.   
%y Year without century as a decimal number [00,99].   
%Y Year with century as a decimal number.   
%z UTC offset in the form +HHMM or -HHMM (empty string if the the object is naive). (5) 
%Z Time zone name (empty string if the object is naive).   
%% A literal '%' character 
{{sectionTOC{}}}
!Dictionaries
Dictionaries are defined like so:
{{{
>>> phonebook = {'Alice': '2341', 'Beth': '9102', 'Cecil': '3258'}
>>> phonebook['Cecil']
3158
}}}

* {{{len(d)}}} returns the number of items (key-value pairs) in d.
* {{{d[k]}}} returns the value associated with the key k.
* {{{d[k] = v}}} associates the value v with the key k.
* {{{del d[k]}}} deletes the item with key k.
* {{{k in d}}} checks whether there is an item in d that has the key k.

!!The {{{keys, values, items}}} functions
{{{
>>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"}
>>> params.keys()
['server', 'uid', 'database', 'pwd']
>>> params.values()
['mpilgrim', 'sa', 'master', 'secret']
>>> params.items()
[('server', 'mpilgrim'), ('uid', 'sa'), ('database', 'master'), ('pwd', 'secret')]
}}}

!String formatting with Dictionaries
{{{
>>> phonebook
{'Beth': '9102', 'Alice': '2341', 'Cecil': '3258'}
>>> "Cecil's phone number is %(Cecil)s." % phonebook
"Cecil's phone number is 3258."
>>> template = '''<html>
<head><title>%(title)s</title></head>
<body>
<h1>%(title)s</h1>
<p>%(text)s</p>
</body>'''
>>> data = {'title': 'My Home Page', 'text': 'Welcome to my home page!'}
>>> print template % data
<html>
<head><title>My Home Page</title></head>
<body>
<h1>My Home Page</h1>
<p>Welcome to my home page!</p>
</body>
}}}

!Dictionary Methods
!!clear
!!copy, deepcopy
!!fromkeys
!!get
!!items, iteritems
The {{{items}}} method returns a list of tuples of the form {{{(key, value)}}}. The list contains all the data in the dictionary.
{{{
>>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"}
>>> params.items()
[('server', 'mpilgrim'), ('uid', 'sa'), ('database', 'master'), ('pwd', 'secret')]
}}}

!!keys, iterkeys
The {{{keys}}} method of a dictionary returns a list of all the keys. The list is not in the order in which the dictionary was defined (elements in a dictionary are unordered), but it is a list.
{{{
>>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"}
>>> params.keys()
['server', 'uid', 'database', 'pwd']
}}}

!!pop, popitem

!!setdefault

!!update

!!values, itervalues
The {{{values}}} method returns a list of all the values. The list is in the same order as the list returned by {{{keys}}}.
{{{
>>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"}
>>> params.values()
['mpilgrim', 'sa', 'master', 'secret']
}}}
<<sectionTOC>>
{{sectionTOC{}}}
!Built-in Exceptions
To view the built-in exceptions, use the following:
{{{
>>> import exceptions
>>> dir(exceptions)
['ArithmeticError', 'AssertionError', 'AttributeError', ...]
}}}

Some built-in Python exceptions:
|!Class Name |!Description |
|Exception |The base class for all exceptions|
|AttributeError |Raised when attribute reference or assignment fails|
|IOError |Raised when trying to open a nonexistent file (among other things)|
|IndexError |Raised when using a nonexistent index on a sequence|
|KeyError |Raised when using a nonexistent key on a mapping|
|NameError |Raised when a name (variable) is not found|
|SyntaxError |Raised when the code is ill-formed|
|TypeError |Raised when a built-in operation or function is applied to an object of the wrong type|
|ValueError |Raised when a built-in operation or function is applied to an object with the correct type, but with an inappropriate value|
|ZeroDivisionError |Raised when the second argument of a division or modulo operation is zero|

!Custom Exceptions
{{{
class MyCustomException(Exception): pass
}}}

!Catching Exceptions
To catch a specific type of exception and perform error-handling, use {{{try...except}}}. More than one type of exception can be specified with different handling for each, and more than one exception can be caught with one block:
{{{
try:
  x = input('Enter the first number: ')
  y = input('Enter the second number: ')
  print x/y
except ZeroDivisionError:
  print "The second number can't be zero!"
except (TypeError, NameError):
  print "That wasn't a number, was it?"
}}}
To simply catch all exceptions, use the following:
{{{
try:
  x = input('Enter the first number: ')
  y = input('Enter the second number: ')
  print x/y
except:
  print 'Something wrong happened...'
}}}
As another example, the following program will keep asking for input as long as exceptions happen. The loop is broken (by the {{{break}}} statement in the {{{else}}} clause) only when no exception is raised:
{{{
while True:
  try:
    x = input('Enter the first number: ')
    y = input('Enter the second number: ')
    value = x/y
    print 'x/y is', value
  except Exception, e:
    print 'Invalid input:', e
    print 'Please try again'
  else:
    break
}}}

!!Catching the Exception Object
The following is a simple program that prints out the exception (if it occurs), but keeps running:
{{{
try:
  x = input('Enter the first number: ')
  y = input('Enter the second number: ')
  print x/y
except (ZeroDivisionError, TypeError), e:
  print e
}}}
>''Note'' In Python 3, the {{{except}}} clause is written {{{except (ZeroDivisionError, TypeError) as e}}}.

!Examples
Let’s say you have a dictionary and you want to print the value stored under a specific key, if it is there. If it isn’t there, you don’t want to do anything. The following is an efficient way of doing this, because (rather than looking up the key twice) it simply tries to print the value and passess if it is missing:
{{{
def describePerson(person):
  print 'Description of', person['name']
  print 'Age:', person['age']
  try:
    print 'Occupation: ' + person['occupation']
  except KeyError: pass
}}}
You may also find try/except useful when checking whether an object has a specific attribute. Let’s say you want to check whether an object has a write attribute, for example. Then you could use code like this:
{{{
try:
  obj.write
except AttributeError:
  print 'The object is not writeable'
else:
  print 'The object is writeable'
}}}
<<sectionTOC>>
{{sectionTOC{}}}
!Reading and Writing
The most important capabilities of files (or streams) are supplying and receiving data. If you have a file-like object named f, you can write data (in the form of a string) with the method {{{f.write}}}, and read data (also as a string) with the method {{{f.read}}}.
Each time you call {{{f.write(string)}}}, the string you supply is written to the file after those you have written previously:
{{{
>>> f = open('somefile.txt', 'w')
>>> f.write('Hello, ')
>>> f.write('World!')
>>> f.close()
}}}
Note that if you can this code again, you will overwrite your previous text.

!File Modes
The mode argument to the open function can have several values:
|!Value |!Description |
|'r' |Read mode|
|'w' |Write mode|
|'a' |Append mode|
|'b' |Binary mode (added to other mode)|
|'+' |Read/write mode (added to other mode)|

The '+' can be added to any of the other modes to indicate that both reading and writing is allowed. So, for example, 'r+' can be used when opening a text file for reading and writing. The 'b' mode changes the way the file is handled. Generally, Python assumes that you are dealing with text files (containing characters). Typically, this is not a problem. But if you are processing some other kind of file (called a binary file) such as a sound clip or an image, you should add a 'b' to your mode: for example, 'rb' to read a binary file.

!Using the Basic File Methods
Assume that {{{hello.txt}}} contains the text 'Hello, world!'.
{{{
>>> f = open('hello.txt')
>>> f.read(7)
'Hello, '
>>> f.close()
>>> f = open('hello.txt')
>>> print f.read()
Hello, world!
>>> f.close()
>>> f = open('hello.txt', 'w')
>>> f.write('this\nis no\nhaiku')
>>> f.close()
}}}
<<sectionTOC>>
{{sectionTOC{}}}
!Built-in Functions
All of Python's built−in functions are grouped into a special module called {{{__builtin__}}}. You can think of Python automatically executing {{{from __builtin__ import *}}} on startup, which imports all the "built−in" functions into the namespace so you can use them directly.

!!type
The {{{type}}} function returns the datatype of any arbitrary object. The possible types are listed in the {{{types}}} module.
{{{
>>> type(1)
<type 'int'>
>>> li = []
>>> type(li)
<type 'list'>
>>> import odbchelper
>>> type(odbchelper)
<type 'module'>
>>> import types
>>> type(odbchelper) == types.ModuleType
True
}}}

!!dir
{{{dir}}} returns a list of the attributes and methods of any object: modules, functions, strings, lists, dictionaries... pretty much anything.
{{{
>>> li = []
>>> dir(li)
['append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
>>> d = {}
>>> dir(d)
['clear', 'copy', 'get', 'has_key', 'items', 'keys', 'setdefault', 'update', 'values']
>>> import odbchelper
>>> dir(odbchelper)
['__builtins__', '__doc__', '__file__', '__name__', 'buildConnectionString']
}}}

!!print, input, raw_input
{{{input}}} assumes that what you enter is a valid Python expression. {{{raw_input}}} treats all input as raw data and puts it into a string.
{{{
>>> print "Hello, world!"
Hello, world!
>>> x = input("x:")
x: 42
>>> print x
42
>>> raw_input("Enter a number: ")
Enter a number: 3
'3'
}}}
* NOTE: In Python 3.0, raw_input() is renamed to input() - they are equivalent.

!!str, repr
{{{str}}} converts a value into a string. Every datatype can be coerced into a string. {{{repr}}} creates a string that is a representation of the value as a legal Python expression.
{{{
>>> x = 31
>>> print "I am " + str(x)
I am 31
>>> str(x)
"31"
>>> horsemen = ['war', 'pestilence', 'famine']
>>> str(horsemen)
"['war', 'pestilence', 'famine']"
>>> str(odbchelper)
"<module 'odbchelper' from 'c:\\docbook\\dip\\py\\odbchelper.py'>"
>>> print repr(10000L)
10000L
}}}

!!range
Python has a built-in function to make ranges:
{{{
>>> range(0, 10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> range(1, 11)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}}}

!!exec, eval
>''Caution'': This is a potential security hole of great dimensions. If you execute a string where parts of the contents have been supplied by a user, you have little or no control over what code you are executing. This is especially dangerous in network applications, such as Common Gateway Interface (CGI) scripts.
The statement for executing a string is {{{exec}}}:
{{{
>>> exec "print 'Hello, world!'"
Hello, world!
}}}
A safer way to use {{{exec}}} is by adding in a dictionary that will function as the namespace for your code string. In the following example, the potentially destructive code does not overwrite the {{{sqrt}}} function:
{{{
>>> from math import sqrt
>>> scope = {}
>>> exec 'sqrt = 1' in scope
>>> sqrt(4)
2.0
>>> scope['sqrt']
1
}}}
Just as {{{exec}}} executes a series of statements, {{{eval}}} evaluates a Python expression (written in a string) and returns the resulting value. {{{exec}}} doesn’t return anything because it is a statement itself. For example, you can use the following to make a Python calculator:
{{{
>>> eval(raw_input("Enter an arithmetic expression: "))
Enter an arithmetic expression: 6 + 18 * 2
42
}}}

!!help
If you use {{{help}}} in the interactive interpreter, you can get information about a function, including its {{{docstring}}}:
{{{
>>> help(square)
Help on function square in module __main__:
square(x)
Calculates the square of the number x.
}}}
See also [[Python Modules]].

!Declaring Functions
Declare a new function with the {{{def}}} (or “function definition”) statement:
{{{
def hello(name):
  return 'Hello, ' + name + '!'

def fibs(number):
  result = [0, 1]
  for i in range(number-2):
    result.append(result[-2] + result[-1])
  return result
}}}
Note that the keyword {{{def}}} starts the function declaration, followed by the function name, followed by the arguments in parentheses. Multiple arguments are separated with commas. Also note that the function doesn't define a return datatype. Python functions do not specify the datatype of their return value; they don't even specify whether or not they return a value. In fact, every Python function returns a value; if the function ever executes a {{{return}}} statement, it will return that value, otherwise it will return {{{None}}}.

!Parameters, Keywords and Defaults
When functions are defined, parameters can be defined to be passed in when the function is called:
{{{
def greet(greeting, name):
  print '%s, %s!' % (greeting, name)
}}}
When calling the function, the parameters may be optionally stated as //keyword parameters// (the order in which they are passed is then unimportant):
{{{
>>> greet(name='Ashley', greeting='Goodbye')
Goodbye, Ashley!
}}}
You can also set default parameters in the function. When a parameter has a default value like this, you don’t need to supply it when you call the function.
{{{
def greet(greeting='Hello', name='world'):
  print '%s, %s!' % (greeting, name)

>>> greet()
Hello, world!
>>> greet(name='Ashley')
Hello, Ashley!
}}}

In addition, you can specify that zero or more parameter can be passed in. Placing a star in front of the parameter puts all the values into the same tuple. Placing two stars in front allows a keyword and value to be provided, and puts these into a dictionary.
{{{
def print_params(title, *params, **params2):
   print title
   print params

>>> print_params('Params:', 1, 2, 3)
Params:
(1, 2, 3)
>>> print_params('Params:')
Params:
()

def print_params_2(title, *params, **params2):
  print title
  print params
  print params2

>>> print_params_4(1, 2, 3, 5, 6, 7, foo=1, bar=2)
1
(2, 3, 5, 6, 7)
{'foo': 1, 'bar': 2}
>>> print_params_4(1, 2)
1
(2,)
{}
}}}

!Documenting Functions
You can document a Python function by giving it a {{{doc}}} string. The following code demonstrates how to add a {{{docstring}}} to a function:
{{{
def square(x):
  'Calculates the square of the number x.'
  return x*x
}}}
The docstring may be accessed like this:
{{{
>>> square.__doc__
'Calculates the square of the number x.'
}}}
A special built-in function called {{{help}}} can be quite useful. If you use it in the interactive interpreter, you can get information about a function, including its {{{docstring}}}:
{{{
>>> help(square)
Help on function square in module __main__:
square(x)
  Calculates the square of the number x.
}}}

!Variable Scope or Namescape
Variables can have global or local scope (inside a function). You can access global variables inside a function easily:
{{{
>>> def combine(parameter): print parameter + external
...
>>> external = 'berry'
>>> combine('Shrub')
Shrubberry
}}}
You can also rebind (change) global variables inside a function:
{{{
>>> x = 1
>>> def change_global():
      global x
      x = x + 1
>>> change_global()
>>> x
2
}}}
<<sectionTOC>>
!wxPython
To download wxPython, sim ply visit the download page, http://wxpython.org/download.php. This page gives you detailed instructions about which version to download, as well as the prerequisites for the various versions.

!A simple GUI
Windows, also known as //frames//, are simply instances of the {{{wx.Frame}}} class. Widgets in the wx framework are created with their parent as the first argument to their constructor.

This is a simple GUI for a text editor:
{{{
import wx

app = wx.App()
win = wx.Frame(None, title = "Simple Text Editor", size=(410, 335))
win.Show()

loadButton = wx.Button(win, label='Open', pos=(225, 5), size=(80, 25))
saveButton = wx.Button(win, label='Save', pos=(315, 5), size=(80, 25))
filename = wx.TextCtrl(win, pos=(5, 5), size=(210, 25))
contents = wx.TextCtrl(win, pos=(5, 35), size=(390, 260), style=wx.TE_MULTILINE | wx.HSCROLL)

app.MainLoop()
}}}
The GUI does not resize its element when the window is resized. One of the easiest ways of doing layout in {{{wx}}} is using sizers, and the easiest one to use is {{{wx.BoxSizer}}}. A sizer manages the size of contents. You simply add widgets to a sizer, together with a few layout parameters, and then give this sizer the job of managing the layout of the parent component. The following code gives the same result as above, but the elements resize themselves with the window.
{{{
import wx

app = wx.App()
win = wx.Frame(None, title = "Simple Text Editor", size=(410, 335))
bkg = wx.Panel(win)

loadButton = wx.Button(bkg, label='Open')
saveButton = wx.Button(bkg, label='Save')
filename = wx.TextCtrl(bkg)
contents = wx.TextCtrl(bkg, style=wx.TE_MULTILINE | wx.HSCROLL)

hbox = wx.BoxSizer()
hbox.Add(filename, proportion=1, flag=wx.EXPAND)
hbox.Add(loadButton, proportion=0, flag=wx.LEFT, border=5)
hbox.Add(saveButton, proportion=0, flag=wx.LEFT, border=5)

vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(hbox, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
vbox.Add(contents, proportion=1, flag=wx.EXPAND | wx.LEFT | wx.BOTTOM | wx.RIGHT, border=5)

bkg.SetSizer(vbox)
win.Show()

app.MainLoop()
}}}
The constructor of the wx.BoxSizer takes an argument determining whether it’s horizontal or vertical (wx.HORIZONTAL or wx.VERTICAL), with horizontal being the default. The Add method takes several arguments. The proportion argument sets the proportions according to which space is allocated when the window is resized. For example, in the horizontal box sizer (the first one), the filename widget gets all of the extra space when resizing. If each of the three had its proportion set to 1, each would get an equal share. You can set the proportion to any number.

The flag argument is similar to the style argument of the constructor. You construct it by using bitwise OR between symbolic constants (integers that have special names). The wx.EXPAND flag makes sure the component will expand into the allotted space. The wx.LEFT, wx.RIGHT, wx.TOP, wx.BOTTOM, and wx.ALL flags determine on which sides the border argument applies, and the border arguments gives the width of the border (spacing).

!Event Handling
The actions performed by the user (such as clicking a button) are called //events//. You need to make your program notice these events somehow, and then react to them. You accomplish this by binding a function to the widget where the event in question might occur. When the event does occur (if ever), that function will then be called. You link the event handler to a given event with a widget method called {{{Bind}}}.

Let’s assume that you have written a function responsible for opening a file, and you’ve called it {{{load}}}. Then you can use that as an event handler for loadButton as follows:
{{{
loadButton.Bind(wx.EVT_BUTTON, load)
}}}

The full code for the text-editing application is below. There are two functions defined ({{{load}}} and {{{save}}}), which are linked to the event handler for each button.
{{{
import wx
def load(event):
  file = open(filename.GetValue())
  contents.SetValue(file.read())
  file.close()

def save(event):
  file = open(filename.GetValue(), 'w')
  file.write(contents.GetValue())
  file.close()

app = wx.App()
win = wx.Frame(None, title = "Simple Text Editor", size=(410, 335))
bkg = wx.Panel(win)

loadButton = wx.Button(bkg, label='Open')
loadButton.Bind(wx.EVT_BUTTON, load)

saveButton = wx.Button(bkg, label='Save')
saveButton.Bind(wx.EVT_BUTTON, save)

filename = wx.TextCtrl(bkg)
contents = wx.TextCtrl(bkg, style=wx.TE_MULTILINE | wx.HSCROLL)

hbox = wx.BoxSizer()
hbox.Add(filename, proportion=1, flag=wx.EXPAND)
hbox.Add(loadButton, proportion=0, flag=wx.LEFT, border=5)
hbox.Add(saveButton, proportion=0, flag=wx.LEFT, border=5)

vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(hbox, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
vbox.Add(contents, proportion=1, flag=wx.EXPAND | wx.LEFT | wx.BOTTOM | wx.RIGHT, border=5)

bkg.SetSizer(vbox)
win.Show()

app.MainLoop()
}}}
!{{{for}}} Loops
!!Iterating over a list
{{{
>>> for f in range(5):
      print f

0
1
2
3
4
}}}
!!Iterating over a dictionary
Use the {{{iteritems}}} or {{{iterkeys}}} methods to return an iterator object from a dictionary:
{{{
>>> phonebook = {'Alice': '2341', 'Beth': '9102', 'Cecil': '3258'}
>>> for k, v in phonebook.iterkeys():
      print k

Alice
Cecil
Beth
}}}

Another example:
{{{
>>> import os
>>> for k, v in os.environ.items():
      print "%s=%s" % (k, v)
USERPROFILE=C:\Documents and Settings\ashleyf
OS=Windows_NT
COMPUTERNAME=BOOR428-014
USERNAME=ashleyf
[...snip...]
}}}
!The {{{__iter__}}} magic method
Python can iterate over sequences and dictionaries in {{{for}}} loops, but can also iterate over other objects that implement the {{{__iter__}}} method. The {{{__iter__}}} method returns an iterator, which is any object with a method called {{{next}}}, which is callable without any arguments. When you call the {{{next}}} method, the iterator should return its “next value.” If the method is called, and the iterator has no more values to return, it should raise a {{{StopIteration}}} exception.
{{{
class Fibs:
  def __init__(self):
    self.a = 0
    self.b = 1
  def next(self):
    self.a, self.b = self.b, self.a+self.b
    return self.a
  def __iter__(self):
    return self

>>> fibs = Fibs()
>>> for f in fibs:
      if f > 1000:
        print f
        break
...
1597
}}}

!{{{iter}}} built-in function
The built-in function {{{iter}}} can be used to get an iterator from an iterable object:
{{{
>>> it = iter([1, 2, 3])
>>> it.next()
1
>>>
}}}

!{{{list}}} constructor
You can explicitly convert an iterator to a list using the list constructor:
{{{
>>> it = iter([1, 2, 3])
>>> x = list(ti)
>>> x
[1, 2, 3]
}}}
{{sectionTOC{}}}
Python lists have 0-based indices. The first item is at index 0, the second is at index 1, and so on. Lists are containers for other objects. Common operations include indexing, slicing, adding, multiplying, and checking for membership.
{{{
>>> edward = ['Edward Gumby', 42]
>>> john = ['John Smith', 50]
>>> database = [edward, john]
>>> database
[['Edward Gumby', 42], ['John Smith', 50]]
>>> edward[0]
'Edward Gumby'
>>> edward[1]
42
}}}

String literals may be indexed directly, without using a variable to refer to them. The effect is exactly the same:
{{{
>>> 'Hello'[1]
'e'
>>> fourth = raw_input('Year: ')[3]
Year: 2005
>>> fourth
'5'
}}}

Lists can be concatenated, as well as multiplied:
{{{
>>> [1, 2, 3] + [4, 5, 6]
[1, 2, 3, 4, 5, 6]
>>> [1, 2, 3] * 3
[1, 2, 3, 1, 2, 3, 1, 2, 3]
}}}

!List Slicing
You can use slicing to access ranges of elements. The //first// index is the number of the first element you want to include. However, the //last// index is the number of the first element after your slice. You can also leave out the index.
{{{
>>> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> numbers[3:6]
[4, 5, 6]
>>> numbers[0:1]
[1]
>>> numbers[-3:]
[8, 9, 10]
>>> numbers[:3]
[1, 2, 3]
>>> numbers[:]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}}}

!Printing a list
Using the {{{join}} method of the string "\n", you can join a list into a single string, with each element of the list on a separate line, and print the result:
{{{
>>> li = ['a', 'b', 'c']
>>> print "\n".join(li)
a
b
c
}}}

!{{{in}}}
To check whether a value can be found in a sequence, you use the {{{in}}} operator:
{{{
>>> permissions = 'rw'
>>> 'w' in permissions
True
>>> 'x' in permissions
False
>>> users = ['ash', 'foo', 'bar']
>>> raw_input('Enter your user name: ') in users
Enter your user name: ash
True
}}}

!{{{len, min, max}}}
The built-in functions {{{len}}}, {{{min}}}, and {{{max}}} can be quite useful:
{{{
>>> a = [1, 2, 3, [1, 2, 3]]
>>> len(a)
4
>>> numbers = [100, 34, 678]
>>> len(numbers)
3
>>> max(numbers)
678
>>> min(numbers)
34
>>> max(2, 3)
3
>>> min(9, 3, 2, 5)
2
}}}

!{{{append}}}
The {{{append}}} method is used to append an object to the end of a list:
{{{
>>> lst = [1, 2, 3]
>>> lst.append(4)
>>> lst
[1, 2, 3, 4]
}}}

!{{{count}}}
The {{{count}}} method counts the occurrences of an element in a list:
{{{
>>> ['to', 'be', 'or', 'not', 'to', 'be'].count('to')
2
>>> x = [[1, 2], 1, 1, [2, 1, [1, 2]]]
>>> x.count(1)
2
>>> x.count([1, 2])
1
}}}

!Extend
The {{{extend}}} method allows you to append several values at once by supplying a sequence of the values you want to append:
{{{
>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> a.extend(b)
>>> a
[1, 2, 3, 4, 5, 6]
}}}

!{{{index}}}
The {{{index}}} method is used for searching lists to find the index of the first occurrence of a value:
{{{
>>> knights = ['We', 'are', 'the', 'knights', 'who', 'say', 'ni']
>>> knights.index('who')
4
}}}

!{{{insert}}}
The {{{insert}}} method is used to insert an object into a list at a defined index:
{{{
>>> numbers = [1, 2, 3, 5, 6, 7]
>>> numbers.insert(3, 'four')
>>> numbers
[1, 2, 3, 'four', 5, 6, 7]
}}}

!{{{pop}}}
The {{{pop}}} method removes an element (by default, the last one) from the list and returns it:
{{{
>>> x = [1, 2, 3]
>>> x.pop()
3
>>> x
[1, 2]
>>> x.pop(0)
1
>>> x
[2]
}}}

!{{{remove}}}
The {{{remove}}} method is used to remove the first occurrence of a value:
{{{
>>> x = ['to', 'be', 'or', 'not', 'to', 'be']
>>> x.remove('be')
>>> x
['to', 'or', 'not', 'to', 'be']
}}}

!{{{reverse}}}
The {{{reverse}}} method reverses the elements in the list.
{{{
>>> x = [1, 2, 3]
>>> x.reverse()
>>> x
[3, 2, 1]
}}}

!{{{sort, sorted}}}
The {{{sort}}} method is used to sort lists in place. Sorting “in place” means changing the original list so its elements are in sorted order, rather than simply returning a sorted copy of the list:
{{{
>>> x = [4, 6, 2, 1, 7, 9]
>>> x.sort()
>>> x
[1, 2, 4, 6, 7, 9]
}}}
Confusion usually occurs when users want a sorted copy of a list while leaving the original alone. An intuitive (but ///wrong///) way of doing this is as follows:
{{{
>>> x = [4, 6, 2, 1, 7, 9]
>>> y = x.sort() # Don't do this!
>>> print y
None
}}}
Because {{{sort}}} modifies x but returns nothing, you end up with a sorted x and a y containing None. To get a sorted copy of a list, use the {{{sorted}}} function:
{{{
>>> x = [4, 6, 2, 1, 7, 9]
>>> y = sorted(x)
>>> x
[4, 6, 2, 1, 7, 9]
>>> y
[1, 2, 4, 6, 7, 9]
}}}

!Mapping Lists
One of the most powerful features of Python is the list comprehension, which provides a compact way of mapping a list into another list by applying a function to each of the elements of the list.
{{{
>>> li = [1, 9, 8, 4]
>>> [elem*2 for elem in li]
[2, 18, 16, 8]
>>> li
[1, 9, 8, 4]
>>> li = [elem*2 for elem in li]
>>> li
[2, 18, 16, 8]
}}}

<<sectionTOC>>
{{sectionTOC{}}}
!while Loops
Print the numbers from 1 to 100:
{{{
x = 1
while x <= 100:
  print x
  x += 1
}}}

!for Loops
Print each of the elements in a list:
{{{
words = ['this', 'is', 'an', 'ex', 'parrot']
for word in words:
  print word
}}}
Using a step number to print every second number from 1 to 100:
{{{
for number in range(1, 101, 2):
  print number
}}}

Because iterating over a range of numbers is a common thing to do, Python has a built-in function to make ranges:
{{{
>>> range(0, 10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> for number in range(1,101):
      print number
...
}}}
The {{{xrange}}} function works just like {{{range}}} in loops, but where range creates the whole sequence at once, xrange creates only one number at a time. In Python 3.0, range is turned into an xrange-style function.

!Iterating over Dictionaries
To loop over the keys of a dictionary, you can use a plain {{{for}}} statement, just as you can with sequences:
{{{
d = {'x': 1, 'y': 2, 'z': 3}
for key, value in d.items():
  print key, 'corresponds to', value
}}}

!!Parallel Iteration using zip
A useful tool for parallel iteration of multiple lists is the built-in function {{{zip}}}, which “zips” together the sequences, returning a list of tuples:
{{{
>>> names = ['anne', 'beth', 'george', 'damon']
>>> ages = [12, 45, 32, 102]
>>> zip(names, ages)
[('anne', 12), ('beth', 45), ('george', 32), ('damon', 102)]
}}}
The {{{zip}}} function works with as many sequences as you want. Note: it stops when the shortest sequence is used up.

!!Numbered Iteration
In some cases, you want to iterate over a sequence of objects and at the same time have access to the index of the current object. A solution is to use the built-in function {{{enumerate}}}:
{{{
for index, string in enumerate(strings):
  if 'xxx' in string:
    strings[index] = '[censored]'
}}}

!Breaking out of loops
Example: find the largest square below 100. Start at 100 and iterate downwards to 0. When you’ve found a square, there’s no need to continue, so you simply {{{break}}} out of the loop:
{{{
from math import sqrt
for n in range(99, 0, -1):
  root = sqrt(n)
  if root == int(root):
    print n
    break
}}}
<<sectionTOC>>
{{sectionTOC{}}}
!{{{__init__}}}
Constructors are a kind of initialising method that is called automatically after an object is created. Creating constructors in Python is really easy; simply change the init method’s name from the plain old init to the magic version, {{{__init__}}}:
{{{
class FooBar:
  def __init__(self, value=42):
    self.somevar = value

>>> e = FooBar()
>>> f = FooBar('Another value')
>>> e.somevar
42
>>> f.somevar
'Another value'
}}}
!!Overriding the Constructor Method (correctly)
Overriding is an important aspect of the inheritance mechanism in general, and may be especially important for constructors. Constructors are there to initialize the state of the newly constructed object, and most subclasses will need to have initialization code of their own, in addition to that of the superclass. Even though the mechanism for overriding is the same for all methods, you will most likely encounter one particular problem more often when dealing with constructors than when overriding ordinary methods: if you override the constructor of a class, you need to call the constructor of the superclass (the class you inherit from) or risk having an object that isn’t properly initialized.
It is trivial to override the superclass constructor, and //also// call the constructor of a superclass (and useful).
{{{
class Bird:
  def __init__(self):
    self.hungry = True
  def eat(self):
    if self.hungry:
      print 'Aaaah...'
      self.hungry = False
    else:
      print 'No, thanks!'

class SongBird(Bird):
  def __init__(self):
    Bird.__init__(self)
    self.sound = 'Squawk!'
  def sing(self):
    print self.sound
}}}
!!Using the {{{super}}} function
If you’re not stuck with an old version of Python, the {{{super}}} function is really the way to go. It works only with new-style classes, but you should be using those anyway. It is called with the current class and instance as its arguments, and any method you call on the returned object will be fetched from the superclass rather than the current class.
{{{
class SongBird(Bird):
  def __init__(self):
    super(SongBird, self).__init__()
    self.sound = 'Squawk!'
  def sing(self):
    print self.sound
}}}

!Properties
Those class attributes that are defined through their accessors are often called //properties//. They can be defined using the {{{property}}} function (this works only on new-style classes).
{{{
__metaclass__ = type

class Rectangle:
  def __init__(self):
    self.width = 0
    self.height = 0
  def setSize(self, size):
    self.width, self.height = size
  def getSize(self):
    return self.width, self.height
  size = property(getSize, setSize)
}}}
In this {{{Rectangle}}} class, a property is created with the property function with the accessor functions as arguments (the //getter// first, then the //setter//), and the name size is then bound to this property. After this, you no longer need to worry about how things are implemented, but can treat {{{width}}}, {{{height}}}, and {{{size}}} the same way:
{{{
>>> r = Rectangle()
>>> r.width = 10
>>> r.height = 5
>>> r.size
(10, 5)
>>> r.size = 150, 100
>>> r.width
150
}}}

See also the {{{__iter__}}} magic method in [[Python Iterators]].

!{{{__main__}}}
If you want your program to be both an importable module and a runnable program, you need to add something like this at the end of it:
{{{
if __name__ == "__main__":
  method_to_run()
}}}

!{{{__str__}}}
This defines how the object wants to look if it is treated like a string. For example, instead of defining a print method:
{{{
def __str__(self):
  result = ""
  for element in self.contents:
  result = result + " " + repr(element)
  return result
}}}

!{{{__class__}}}
This defines the object's class. For example:
{{{
>>> li = []
>>> li.__class__
<type 'list'>
}}}

!{{{__doc__}}}
This is used to access the objects docstring.
{{{
>>> li = []
>>> li.__doc__
"list() -> new list\nlist(sequence) -> new list initialized from sequence's items"
}}}

<<sectionTOC>>
{{sectionTOC{}}}
!Import
To import something from a module, use:
{{{
import module
import module as foobar
from module import function
from module import function1, function2, function3
from module import *
}}}
Example:
{{{
>>> from math import sqrt as foobar
>>> foobar(4)
2.0
}}}

!!Module import search path
You can see where your interpreter is looking for modules, and append a new location, like so:
{{{
>>> import sys
>>> print sys.path
['C:\\Python25\\Lib\\idlelib', 'C:\\WINDOWS\\system32\\python25.zip', ....]
>>> sys.path.append('c:/new_location')
}}}

!!Exploring a module using {{{dir}}}
{{{
>>> import copy
>>> dir(copy)
['Error', 'PyStringMap', '_EmptyClass', ....]
>>> [n for n in dir(copy) if not n.startswith('_')]
['Error', 'PyStringMap', 'copy', 'deepcopy', 'dispatch_table', 'error', 'name', 't']
>>> copy.__all__
['Error', 'copy', 'deepcopy']
}}}
The {{{__all__}}} variable defines the public interface of the module. More specifically, it tells the interpreter what it means to import all the names from this module (e.g. when using {{{from copy import *}}}).

!{{{help}}} function and documentation
To view a list of all available modules:
{{{
>>> help('modules')
}}}

To view information about a specific module function:
{{{
>>> help(copy.copy)
Help on function copy in module copy:
copy(x)
  Shallow copy operation on arbitrary Python objects.
  See the module's __doc__ string for more info.
}}}
The preceeding test was extracted from the {{{copy}}} function's docstring:
{{{
>>> print copy.copy.__doc__
Shallow copy operation on arbitrary Python objects.
See the module's __doc__ string for more info.
}}}
To locate the actual source code for a module, use the {{{__file__}}} property:
{{{
>>> print copy.__file__
C:\Python25\lib\copy.pyc
}}}

!{{{sys}}}
The {{{sys}}} module gives you access to variables and functions that are closely linked to the Python interpreter. Some useful functions and variables:
|!Function/Variable |!Description |
|argv |The command-line arguments, including the script name|
|exit([arg]) |Exits the current program, optionally with a given return value or error message|
|modules |A dictionary mapping module names to loaded modules|
|path |A list of directory names where modules can be found|
|platform |A platform identifier such as sunos5 or win32|
|stdin |Standard input stream—a file-like object|
|stdout |Standard output stream—a file-like object|
|stderr |Standard error stream—a file-like object|

{{{
>>> import sys
>>> print sys.path
['C:\\Python25\\Lib\\idlelib', 'C:\\WINDOWS\\system32\\python25.zip', ....]
>>> sys.path.append('c:/new_location')
>>> print sys.path[0]
C:\Python26\Lib\idlelib
}}}

!{{{os}}}
The {{{os}}} module gives you access to several operating system services. Some useful functions and variables:
|!Function/Variable |!Description |
|environ |Mapping with environment variables|
|system(command) |Executes an operating system command in a subshell|
|sep |Separator used in paths|
|getcwd |Return a string representing the current working directory. |
|pathsep |Separator to separate paths|
|linesep |Line separator ('\n', '\r', or '\r\n')|
|listdir |Returns of list containing the names of the file contents of the specified directory. |
|urandom(n) |Returns n bytes of cryptographically strong random data. |
|path.split |Splits a full pathname and returns a tuple containing the path and filename. |
|path.splitext |Splits a filename and returns a tuple containing the filename and the file extension. |
|path.isfile |Takes a pathname and returns 1 if the path represents a file, and 0 otherwise. |
|path.isdir |Returns 1 if the path represents a directory, and 0 otherwise. |
{{{
>>> from os import getcwd, chdir, listdir
>>> print getcwd()
C:\Python26
>>> chdir('C:\\temp')
>>> print getcwd()
C:\temp
>>> print listdir(getcwd())
['Mapped Drives.doc', 'net.5156', 'PrinterScript.log', ....]
}}}

!{{{webbrowser}}}
Use the following to open a web browser window:
{{{
import webbrowser
webbrowser.open('http://www.python.org')
}}}

!{{{time}}}
The time module contains functions for, among other things, getting the current time, manipulating times and dates, reading dates from strings, and formatting dates as strings. Dates can be represented as either a real number or a tuple containing nine integers. Some important functions:
|!Function |!Description |
|asctime([tuple]) |Converts a time tuple to a string|
|localtime([secs]) |Converts seconds to a date tuple, local time|
|mktime(tuple) |Converts a time tuple to local time|
|sleep(secs) |Sleeps (does nothing) for secs seconds|
|strptime(string[, format]) |Parses a string into a time tuple|
|time() |Current universal time (seconds since the epoch, UTC)|

See also {{{datetime}}} and {{{timeit}}}.

!{{{random}}}
The random module contains functions that return random numbers, which can be useful for programs that generates random output.
|!Function |!Description |
|random() |Returns a random real number n such that 0 n &le; 1|
|getrandbits(n) |Returns n random bits, in the form of a long integer|
|uniform(a, b) |Returns a random real number n such that a n &le; b|
|randrange([start], stop, [step]) |Returns a random number from range(start, stop, step)|
|choice(seq) |Returns a random element from the sequence seq|
|shuffle(seq[, random]) |Shuffles the sequence seq in place|
|sample(seq, n) |Chooses n random, unique elements from the sequence seq|


!{{{math}}}
Contains various standard mathematical functions.
{{{
>>> import math
>>> math.floor(32.9)
32.0
>>> math.sqrt(9)
3.0
>>> math.pi
3.14159265359
>>> math.sin(0)
0.0
}}}

!{{{pprint}}}
Module to pretty-print lists, tuples, & dictionaries recursively.
{{{
>>> import sys, pprint
>>> pprint.pprint(sys.path)
['C:\\Python25\\Lib\\idlelib',
'C:\\WINDOWS\\system32\\python25.zip',
'C:\\Python25',
'C:\\Python25\\DLLs',
'C:\\Python25\\lib',
'C:\\Python25\\lib\\plat-win',
'C:\\Python25\\lib\\lib-tk',
'C:\\Python25\\lib\\site-packages']
}}}
<<sectionTOC>>
<<tiddler ListTaggedTiddlers with:"Python">>
{{sectionTOC{}}}
The {{{re}}} module contains support for regular expressions in Python. For additional info, see:
* http://www.amk.ca/python/howto/regex/
* http://python.org/doc/lib/re-syntax.html

|!Function |!Description |
|compile(pattern[, flags]) |Creates a pattern object from a string with a regular expression|
|search(pattern, string[, flags]) |Searches for pattern in string|
|match(pattern, string[, flags]) |Matches pattern at the beginning of string|
|split(pattern, string[, maxsplit=0]) |Splits a string by occurrences of pattern|
|findall(pattern, string) |Returns a list of all occurrences of pattern in string|
|sub(pat, repl, string[, count=0]) |Substitutes occurrences of pat in string with repl|
|escape(string) |Escapes all special regular expression characters in string|

!The Wildcard
A regular expression can match more than one string, and you create such a pattern by using some special characters. For example, the period character (dot) matches any character (except a newline), so the regular expression '.ython' would match both the string 'python' and the string 'jython'. It would also match strings such as 'qython', '+ython', or ' ython' (in which the first letter is a single space), but not strings such as 'cpython' or 'ython' because the period matches a single letter, and neither two nor zero.
Because it matches “anything” (any single character except a newline), the period is called a wildcard.

|!Symbol |!Matches |
|^ |Matches the beginning of a string. |
|$ |Matches the end of a string. |
|\b |Matches a word boundary. |
|. |Matches any single character. |
|\d |Matches any numeric digit. |
|\D |Matches any non−numeric character. |
|x? |Matches an optional x character (in other words, it matches an x zero or one times). |
|x* |Matches x zero or more times. |
|x+ |Matches x one or more times. |
|x{n,m} |Matches an x character at least n times, but not more than m times. |
|"""(a|b|c)""" |Matches either a or b or c. |
|(x) |In general is a //remembered group//. You can get the value of what matched by using the {{{groups()}}} method of the object returned by {{{re.search}}}. |
|[A-Z] |Matches any character between A and Z (uppercase). |
|[a-z] |Matches any character between a and z (lowercase). |
|[A-Za-z] |Matches any character between a and z (case-insensitive). |

!Escaping Special Characters
To make a special character behave like a normal one, you escape it by placing a backslash in front of it. Thus you would use 'python\\.org' to match 'python.org' and nothing else.
To get a single backslash, which is required here by the re module, you need to write two backslashes in the string — to escape it from the interpreter. If you are tired of doubling up backslashes, use a raw string, such as {{{r'python\.org'}}}.

!Character Sets
Matching any character can be useful, but sometimes you want more control. You can create a character set by enclosing a substring in brackets. Such a character set will match any of the characters it contains. For example, {{{[pj]ython}}} would match both 'python' and 'jython', but nothing else. You can also use ranges, such as {{{[a-z]}}} to match any character from a to z (alphabetically), and you can combine such ranges by putting one after another, such as {{{[a-zA-Z0-9]}}} to match uppercase and lowercase letters and digits. Note that the character set will match only one such character, though.
To invert the character set, put the character ^ first, as in {{{[^abc]}}} to match any character except a, b, or c.

!Alternatives and Subpatterns
Character sets are nice when you let each letter vary independently, but what if you want to match only the strings 'python' and 'perl'? You use the special character for alternatives: the pipe character (|). So, your pattern would be {{{python|perl}}}.
However, sometimes you don’t want to use the choice operator on the entire pattern — just a part of it. To do that, you enclose the part, or subpattern, in parentheses. The previous example could be rewritten as {{{p(ython|erl)}}}.

!Optional and Repeated Subpatterns
By adding a question mark after a subpattern, you make it optional. It may appear in the matched string, but it isn’t strictly required. So, for example, this pattern:

{{{r'(http://)?(www\.)?python\.org'}}}

would match all of the following strings (and nothing else):
"""http://www.python.org"""
"""http://python.org"""
www.python.org
python.org

The question mark means that the subpattern can appear once or not at all. A few other operators allow you to repeat a subpattern more than once:
* (pattern)*: pattern is repeated zero or more times.
* (pattern)+: pattern is repeated one or more times.
* (pattern){m,n}: pattern is repeated from m to n times.

!The Beginning and End of a String
It can sometimes be useful to anchor this substring either at the beginning or the end of the full string. For example, you might want to match 'ht+p' at the beginning of a string, but not anywhere else. Then you use a caret ('^') to mark the beginning. For example, {{{^ht+p}}} would match """'http://python.org' (and 'htttttp://python.org', for that matter""") but not 'www.http.org'. Similarly, the end of a string may be indicated by the dollar sign ($).

!Verbose Regular Expressions
Python allows you to provide inline documentation in regex with something called //verbose regular expressions//. A verbose regular expression is different from a compact regular expression in two ways:
* Whitespace is ignored. Spaces, tabs, and carriage returns are not matched as spaces, tabs, and carriage returns. They're not matched at all. (If you want to match a space in a verbose regular expression, you'll need to escape it by putting a backslash in front of it.)
* Comments are ignored. A comment in a verbose regular expression is just like a comment in Python code: it starts with a # character and goes until the end of the line. In this case it's a comment within a multi−line string instead of within your source code, but it works the same way.
Verbose regex works by adding the {{{VERBOSE}}} flag to an {{{re.search}}} function. An example for parsing valid Roman numerals:
{{{
>>> pattern = """
    ^                # beginning of string
    M{0,4}           # thousands − 0 to 4 M's
    (CM|CD|D?C{0,3}) # hundreds − 900 (CM), 400 (CD), 0−300 (0 to 3 C's),
                     # or 500−800 (D, followed by 0 to 3 C's)
    (XC|XL|L?X{0,3}) # tens − 90 (XC), 40 (XL), 0−30 (0 to 3 X's),
                     # or 50−80 (L, followed by 0 to 3 X's)
    (IX|IV|V?I{0,3}) # ones − 9 (IX), 4 (IV), 0−3 (0 to 3 I's),
                     # or 5−8 (V, followed by 0 to 3 I's)
    $                # end of string
    """
>>> re.search(pattern, 'M', re.VERBOSE)
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, 'MCMLXXXIX', re.VERBOSE)
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, 'MMMMDCCCLXXXVIII', re.VERBOSE)
<_sre.SRE_Match object at 0x008EEB48>
}}}
<<sectionTOC>>
{{sectionTOC{}}}
!Strings
{{{
>>> print "Hello, world!"
Hello, world!
>>> print "Let's go!"
Let's go!
>>> print 'Let\'s go!"
Let's go!
>>> "\"Hello, world!\" she said."
"Hello, world!" she said.
>>> "Hello, " + "world!"
'Hello, world!'
>>> x = "Hello, "
>>> y = "world!"
>>> x + y
'Hello, world!'
>>> path = 'C:\\Program Files\\fnord\\foo\\bar'
>>> print path
C:\Program Files\fnord\foo\bar
}}}

!Long Strings
If you want to write a really long string, one that spans several lines, you can use triple quotes instead of ordinary quotes:
{{{
print '''This is a very long string.
It continues here.
And it's not over yet.
"Hello, world!"
Still here.'''
}}}

!Raw Strings
Raw strings don’t treat the backslash as a special character at all. Every character you put into a raw string stays the way you wrote it:
{{{
>>> print r'C:\nowhere'
C:\nowhere
>>> print r'C:\Program Files\fnord\foo\bar\baz\frozz\bozz'
C:\Program Files\fnord\foo\bar\baz\frozz\bozz
}}}

!String formatting
String formatting uses the string formatting operator, the percent (%) sign. To the left of the %, you place a string (the format string); to the right of it, you place the value you want to format. You can use a single value such as a string or a number, you can use a tuple of values (if you want to format more than one), or you can use a dictionary. The most common case is the tuple:
{{{
>>> format = "Hello, %s. %s enough for ya?"
>>> values = ('world', 'Hot')
>>> print format % values
Hello, world. Hot enough for ya?
}}}
The {{{%s}}} parts of the format string are called //conversion specifiers//. They mark the places
where the values are to be inserted. The {{{s}}} means that the values should be formatted as if they were strings; if they aren’t, they’ll be converted with {{{str}}}. If you are formatting real numbers (floats), you can use the {{{f}}} specifier type and supply the //precision// as a . (dot), followed by the number of decimals you want to keep:
{{{
>>> format = "Pi with three decimals: %.3f"
>>> from math import pi
>>> print format % pi
Pi with three decimals: 3.142
}}}

!String Methods
!!find, rfind, index, rindex, count, startswith, endswith
!!join, split, rsplit, splitlines
!!lower, upper, islower, isupper, capitalize, swapcase, title, istitle
!!replace, translate
The replace method returns a string where all the occurrences of one string have been replaced by another:
{{{
>>> 'This is a test'.replace('is', 'eez')
'Theez eez a test'
}}}

!!strip, lstrip, rstrip
<<sectionTOC>>
!Tools for Testing
Two modules in the Python standard library are available to automate the testing process:
* {{{unittest}}}: A generic testing framework.
* {{{doctest}}}: A simpler module, designed for checking documentation, but excellent for writing unit tests as well.

!{{{doctest}}}
For more information about the doctest module, check out the library reference (http://python.org/doc/lib/module-doctest.html). Let’s say I write a function for squaring a number, and add an example to its docstring:
{{{
def square(x):
  '''
  Squares a number and returns the result.
  >>> square(2)
  4
  '''
  return x*x

def product(x, y):
  return x * y
}}}
Let’s say the square function is defined in the module my_math (that is, a file called my_math.py). Then you could add the following code at the bottom:
{{{
if __name__=='__main__':
  import doctest, my_math
  doctest.testmod(my_math)
}}}
You simply import {{{doctest}}} and the {{{my_math}}} module itself, and then run the {{{testmod}}} (for “test module”) function from {{{doctest}}}. What does this do?
{{{
$ python my_math.py
$
}}}
Nothing seems to have happened, but that’s a good thing. The {{{doctest.testmod}}} function reads all the docstrings of a module and seeks out any text that looks like an example from the interactive interpreter. Then it checks whether the example represents reality. To get some more input, you can just give the -v (for “verbose”) switch to your script:
{{{
$ python my_math.py -v
Running my_math.__doc__
0 of 0 examples failed in my_math.__doc__
Running my_math.square.__doc__
Trying: square(2)
Expecting: 4
ok
Trying: square(3)
Expecting: 9
ok
0 of 2 examples failed in my_math.square.__doc__
1 items had no tests:
  my_math
1 items passed all tests:
  2 tests in my_math.square
2 tests in 2 items.
2 passed and 0 failed.
Test passed.
}}}

!{{{unittest}}}
For complete details of the {{{unittest}}} module, see the Python Library Reference (http://python.org/doc/lib/module-unittest.html). Going back to the previous example:
{{{
import unittest, my_math

class ProductTestCase(unittest.TestCase):
  def testIntegers(self):
    for x in xrange(-10, 10):
      for y in xrange(-10, 10):
        p = my_math.product(x, y)
        self.failUnless(p == x*y, 'Integer multiplication failed')

def testFloats(self):
  for x in xrange(-10, 10):
    for y in xrange(-10, 10):
      x = x/10.0
      y = y/10.0
      p = my_math.product(x, y)
      self.failUnless(p == x*y, 'Float multiplication failed')

if __name__ == '__main__': unittest.main()
}}}
In Python and in the Django template system, these objects evaluate to {{{False}}} in a Boolean context:
* An empty list ([]).
* An empty tuple (()).
* An empty dictionary ({}).
* An empty string ("""''""").
* Zero (0).
* The special object {{{None}}}.
* The object {{{False}}} (obviously).
* Custom objects that define their own Boolean context behavior. (This is advanced Python usage.)

Everything else evaluates to {{{True}}}.
Tuples are sequences, just like lists. The only difference is that //tuples can’t be changed//. The {{{tuple}}} function may be used to convert a sequence to a tuple
{{{
>>> tuple([1, 2, 3])
(1, 2, 3)
>>> tuple('abc')
('a', 'b', 'c')
>>> tuple((1, 2, 3))
(1, 2, 3)
>>> x = 1, 2, 3
>>>x
(1, 2, 3)
>>> x[1]
2
>>> x[0:2]
(1, 2)
}}}
RedCloth is a library that provides a Ruby implementation of the Textile markup language. The Textile markup language is a special way of formatting plain text to be converted into HTML. Textile provides a more human-friendly language that can be converted easily to HTML. RedCloth makes this functionality available in Ruby. RedCloth is available as a RubyGem and can be installed in the usual way (such as with //gem install redcloth//).

To use RedCloth, create an instance of the RedCloth class and pass in the Textile code you want to use:
{{{
require 'redcloth'
text = %q{h1. This is a heading.
This is the first paragraph.
This is the second paragraph.
h1. Another heading
h2. A second level heading
Another paragraph}
document = RedCloth.new(text)
puts document.to_html
}}}

The RedCloth class is a basic extension of the String class, so you can use regular string methods with RedCloth objects, or you can use the to_html method to convert the RedCloth/Textile document to HTML. To learn more about RedCloth and Textile, refer to the official RedCloth Web site at http://redcloth.rubyforge.org
/***
|Macro|redirect (alias)|
|Author|[[Clint Checketts]] and Paul Petterson|
|Version|1.1 Jan 26, 2006|
|Location|http://checkettsweb.com/styles/themes.htm#RedirectMacro|
|Description|This macro tells TW to find all instances of a word and makes it point to a different link. For example, whenever I put the word 'Clint' in a tiddler I want TiddlyWiki to turn it into a link that points to a tiddler titled 'Clint Checketts' Or the word 'TW' could point to a tiddler called 'TiddlyWiki' It even matches clint (which is lowercase) [[Clint]] leet lEEt LEET|
|Usage|{{{<<redirect TW TiddlyWiki>>}}} |
|Example|<<redirect TW "TiddlyWiki">> <<redirect Clint "Clint Checketts">> (Nothing should appear, its just setting it all up)<<redirectExact lEEt Elite>>|

!Redirects
<<redirect AF "Ashley Felton">>AF

!Revisions
1.1- Fixed tiddler refresh so a tiddler declaring a redirect will also render the redirect
1.0- Updated to work with TiddlyWiki 2.0 (thanks to Udo Borkowski)
0.9- Original release October 2005

!Code
***/
//{{{
version.extensions.redirectExact = {major: 1, minor: 2, revision: 0, date: new Date(2005,10,24)};
config.macros.redirectExact = {label: "Pickles Rock!"};
config.macros.redirectExact.handler = function(place,macroName,params,wikifier,paramString,tiddler){
 config.macros.redirect.handler(place,macroName,params,wikifier,paramString,tiddler);
}

version.extensions.redirect = {major: 1, minor: 2, revision: 0, date: new Date(2005,10,24)};
config.macros.redirect = {label: "Pickles Rock!"};

config.macros.redirect.handler = function(place,macroName,params,wikifier,paramString,tiddler){

var redirectExists = false
// Check to see if the wikifier exists
for (var i=0;i<config.formatters.length;i++)
 if (config.formatters[i].name == "redirect"+params[0])
 redirectExists = true;

//If it doesn't exist, add it!
if (!redirectExists){
 for( var i=0; i<config.formatters.length; i++ )
 if ( config.formatters[i].name=='wikiLink') break ;

 if ( i >= config.formatters.length ) {
 var e = "Can't find formatter for wikiLink!" ;
 displayMessage( e ) ;
 throw( e ) ;
 }

var pattern;
 if (macroName == 'redirect'){pattern=params[0].escapeRegExp().replace(/([A-Z])/img, function($1) {return("["+$1.toUpperCase()+$1.toLowerCase()+"]");});
 } else {
 pattern=params[0].escapeRegExp();
 }

 config.formatters.splice( i, 0, {
 name: "redirect"+params[0],
 match: "(?:\\b)(?:\\[\\[)?"+pattern+"(?:\\]\\])?(?:\\b)",
 subst: params[1],
 handler: function(w) {
 var link = createTiddlyLink(w.output,this.subst,false);
 w.outputText(link,w.matchStart,w.nextMatch);
 }
 });
 formatter = new Formatter(config.formatters); //update the tiddler
 if(tiddler) story.refreshTiddler(tiddler.title,null,true); //refresh tiddler so the new rule is applied
} // End if
}
//}}}
/***
|Name:|RenameTagsPlugin|
|Description:|Allows you to easily rename or delete tags across multiple tiddlers|
|Version:|3.0 ($Rev: 5501 $)|
|Date:|$Date: 2008-06-10 23:11:55 +1000 (Tue, 10 Jun 2008) $|
|Source:|http://mptw.tiddlyspot.com/#RenameTagsPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License|http://mptw.tiddlyspot.com/#TheBSDLicense|
Rename a tag and you will be prompted to rename it in all its tagged tiddlers.
***/
//{{{
config.renameTags = {

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

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

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

	storeMethods: {

		saveTiddler_orig_renameTags: TiddlyWiki.prototype.saveTiddler,

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

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

		removeTiddler_orig_renameTags: TiddlyWiki.prototype.removeTiddler,

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

	},

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

config.renameTags.init();

//}}}
/%
|Name|ReplaceDoubleClick|
|Source|http://www.TiddlyTools.com/#ReplaceDoubleClick|
|Version|2.0.0|
|Author|Eric Shulman|
|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|script|
|Requires|InlineJavascriptPlugin|
|Overrides|tiddler background click and doubleclick handlers|
|Description|disable doubleclick-to-edit-tiddler or replace doubleclick with shift/ctrl/alt+singleclick|

Usage:
	in tiddler content:
		<<tiddler ReplaceDoubleClick>> or
		<<tiddler ReplaceDoubleClick with: key trigger>>
	in ViewTemplate:
		<span macro="tiddler ReplaceDoubleClick"></span> or
		<span macro="tiddler ReplaceDoubleClick with: key trigger"></span>
where: 
	'key' (optional) is one of: none (default), ctrl, shift, or alt
	'trigger' (optional) is one of: click, doubleclick (default)

* if no key parameter (or "none") is specified, then the double-click action is **disabled** for that tiddler.
* if a key (other than none) is specified, the doubleclick action for the tiddler will only be invoked
	when the key+trigger combination is used.
* note: double-clicking will also trigger the single-click handler.  As a result, when 'click' option is specified,
	either click OR double-click (plus the specified key) will trigger the action.

Revisions:
2.0.0 renamed from ShiftClickToEdit and merged with DoubleClickDisable and added support specifying alternative key+click combination

%/<script>
	var here=story.findContainingTiddler(place); if (!here) return;
	if (here.ondblclick) {
		here.setAttribute("editKey","none");
		if ("$1"=="shift" || "$1"=="ctrl" || "$1"=="alt")
			here.setAttribute("editKey","$1"+"Key");
		var trigger=("$2"=="click")?"onclick":"ondblclick";
		here.save_dblclick=here.ondblclick;
		here.ondblclick=null;
		if (here.getAttribute("editKey")!="none")
			here[trigger]=function(e) {
				var ev=e?e:window.event;
				if (ev[this.getAttribute("editKey")])
					this.save_dblclick.apply(this,arguments);
			}
	}
</script>
An //Array// is a collection of objects. To define an array:
{{{
x = [1, 2, 3, 4]
y = ['a', 'b', 'c', 'd']
}}}

Define an array using a string (splits on whitespace):
{{{
x = %w{a b c d}
y = %w{this is a new array}
z = "This is another new array".scan(/\w+)
}}}

Reference objects in the array;
{{{
puts x[2]
=> 3
}}}

Change an array object value:
{{{
x[2] = "Fish"
puts x[2]
=> Fish
}}}

Push new data into an array:
{{{
x << 5
=> [1, 2, "Fish", 4, 5]
}}}

Delete items from an array (named or by index):
{{{
a = [ "a", "b", "c", "d", "e" ]
a.delete("b")
a.delete_at(0)
=> ["c", "d", "e"]
}}}

Array methods:
{{{
puts x.length
=> 5

puts x.first
=> 1

puts x.last
=> 5

puts x.join
=> 12Fish45

puts x.join(',')
=> 1,2,Fish,4,5

puts x.reverse.inspect
=> [5, 4, "Fish", 2, 1]

puts x.include?("Fish")
=> True

puts x.sort
}}}
A //class// is a blueprint for //objects//. An //object// is an //instance// of a //class//. The following example shows how to create a //Square// class with two methods, plus some objects of the //Square// class:
{{{
class Square
def initialize(side_length)
  @side_length = side_length
end
def area
  @side_length * @side_length
end
end

a = Square.new(10)
b = Square.new(5)
puts a.area
=> 100
puts b.area
=> 25
}}}
|!Comparison |!Meaning |
|x > y |Greater than.|
|x < y |Less than.|
|x == y |Equal to.|
|x >= y |Greater than or equal to.|
|x <= y |Less than or equal to.|
|x <=> y |Comparison. Returns 0 if x and y are equal, 1 if x is higher, -1 if y is higher.|
|x != y |Not equal to.|
|&& |Logical AND, eg (2 == 2) && (1 == 1) => TRUE|
|"""||""" |"""Logical OR, eg (2 == 5) || (1 == 1) => TRUE"""|
!Text and CSV Databases
The //CSV// class provided by the csv library manages the manipulation of data in text files. In the following example, each line is passed into the block one by one. The //inspect// method demonstrates that each entry is now represented in array form.
{{{
require 'csv'
CSV.open('text.txt', 'r') do |person|
puts person.inspect
end
}}}

You can also use CSV alongside the File class:
{{{
require 'csv'
people = CSV.parse(File.read('text.txt'))
puts people[0][0]
puts people[1][0]
puts people[2][0]
}}}

An even more succinct way of loading the data from a ~CSV-formatted file into an array is with CSV.read:
{{{
puts CSV.read('text.txt').inspect
}}}

The //find// and //find_all// methods provided by the //Enumerable// module to //Array// make it easy for you to perform searches upon the data available in the array. For example, you’d use this code if you wanted to pick out the first person in the data called Laura, and all the people aged between 20 and 40:
{{{
require 'csv'
people = CSV.read('text.txt')
laura = people.find { |person| person[0] =~ /Laura/ }
puts laura.inspect

young_people = people.find_all do |p|
  p[3].to_i.between?(20, 40)
end
puts young_people.inspect
}}}

!!Saving data back to the CSV file

{{{
require 'csv'
people = CSV.read('text.txt')
laura = people.find { |person| person[0] =~ /Laura/ }
laura[0] = "Lauren Smith"

CSV.open('text.txt', 'w') do |csv|
  people.each do |person|
    csv << person
  end
end
}}}
Current time can be manipulated by adding or subtracting the number of seconds:
{{{
puts Time.now
puts Time.now - 10
puts Time.now + 86400
puts Time.now.year
puts Time.now.month
puts Time.now.day
}}}
Extending the Fixnum class to include minutes, hours and days:
{{{
class Fixnum
def seconds
self
end
def minutes
self * 60
end
def hours
self * 60 * 60
end
def days
self * 60 * 60 * 24
end
end
puts Time.now
puts Time.now + 10.minutes
puts Time.now + 16.hours
puts Time.now - 7.days
}}}

!Time Object Methods Used to Access Date/Time Attributes
|!Method |!What the Method Returns |
|hour |A number representing the hour in 24-hour format (21 for 9 p.m., for example). |
|min |The number of minutes past the hour.|
|sec |The number of seconds past the minute.|
|usec |The number of microseconds past the second (there are 1,000,000 microseconds per second).|
|day |The number of the day in the month.|
|mday |Synonym for the day method, considered to be “month” day.|
|wday |The number of the day in terms of the week (Sunday is 0, Saturday is 6).|
|yday |The number of the day in terms of the year.|
|month |The number of the month of the date (11 for November, for example).|
|year |The year associated with the date.|
|zone |Returns the name of the time zone associated with the time.|
|utc? |Returns true or false depending on if the time/date is in the UTC/GMT time zone or not.|
|gmt? |Synonym for the //utc?// method for those who prefer to use the term GMT. |
!Detecting the Operating System
Use the following code to detect the OS:
{{{
if RUBY_PLATFORM =~ /win32/
  puts "We're in Windows!"
elsif RUBY_PLATFORM =~ /linux/
  puts "We're in Linux!"
elsif RUBY_PLATFORM =~ /darwin/
  puts "We're in Mac OS X!"
elsif RUBY_PLATFORM =~ /freebsd/
  puts "We're in FreeBSD!"
else
  puts "We're running under an unknown operating system."
end
}}}

!Display Environmental Variables
To display environmental varaibles of you current environment, use the following code:
{{{
ENV.each {|e| puts e.join(': ') }
}}}

You can use these variables to decide where to store temporary files. For examples:
{{{
tmp_dir = '/tmp'
if ENV['OS'] =~ /Windows_NT/
  puts "This program is running under Windows NT/2000/XP!"
  tmp_dir = ENV['TMP']
elsif ENV['PATH'] =~ /\/usr/
  puts "This program has access to a UNIX-style file system!"
else
  puts "I cannot figure out what environment I'm running in!"
exit
end

.. do something here ..]
}}}
!Exceptions
Ruby has about 30 main predefined exception classes that deal with different types of errors, such as NoMemoryError, RuntimeError, SecurityError, ZeroDivisionError, and NoMethodError. One of the standard exception classes is ArgumentError, which is used when the arguments provided to a method are fatally flawed. You can use this class as an exception if bad data is supplied to a method of your own:
{{{
class Person
  def initialize(name)
    raise ArgumentError, "No name present" if name.empty?
  end
end

ashley = Person.new('')
=> ArgumentError: No name present
}}}

You can create your own type of exception as follows:
{{{
class BadDataException < RuntimeError
end

class Person
  def initialize(name)
    raise BadDataException, "No name present" if name.empty?
  end
end
}}}

!Handling Exceptions
In Ruby, the //rescue// clause is used, along with //begin// and //end//, to define blocks of code to handle exceptions. For example:
{{{
begin
  puts 10 / 0
rescue
  puts "You caused an error!"
end
}}}

You can handle different types of exceptions using the following syntax:
{{{
begin
  ... code here ...
rescue ZeroDivisionError
  ... code to rescue the zero division exception here ...
rescue YourOwnException
  ... code to rescue a different type of exception here ...
rescue
  ... code that rescues all other types of exception here ...
end
}}}

Ruby can also receive exceptions and use them as objects. For example:
{{{
begin
  puts 10 / 0
rescue => err
  puts err.class
end

=> ZeroDivisionError
}}}
{{sectionTOC{}}}
!Reading from files
To open and read from a file, you can use the //File.open// method. This method can accept a code black and once that block is finished, the file is closed automatically. Several examples of this method:
{{{
File.open("text.txt").each { |line| puts line }
File.open("text.txt").each(',') { |line| puts line }
File.open("text.txt").each_byte { |byte| puts byte }
puts File.open("text.txt").readlines
}}}

You can also use //File.new// which returns a //File// object referring to the file. To close the file, you have to use its //close// method. For example"
{{{
f = File.new("text.txt", "r")
puts f.gets
f.close
}}}

Both the code block and file handle techniques have their uses. Using a code block is a clean way to open a single file quickly and perform operations in a single location. However, assigning the //File// object with //File.new// makes the file reference available throughout the entire current scope without needing to contain file manipulation code within a single block.

!!Reading data into a variable
The following examples open a file, read the contents into a string and an array, then close it again:
{{{
data = File.read("text.txt")
data_array = File.readlines("text.txt")
}}}

!Writing to files
The following code creates a new file (or overwrites an existing file) called //test.txt// and puts a single line of text within it:
{{{
File.open("test.txt", "w") do |f|
  f.puts "This is a test"
end
}}}

Different file modes are follows:
|!File Mode |!Properties of the I/O Stream |
|r |Read-only. The file pointer is placed at the start of the file. |
|r+ |Both reading and writing are allowed. The file pointer is placed at the start of the file. |
|w |Write-only. A new file is created (or an old one overwritten as if new). |
|w+ |Both reading and writing are allowed, but File.new creates a new file from scratch (or overwrites an old one as if new). |
|a |Write (in append mode). The file pointer is placed at the end of the file and writes will make the file longer. |
|a+ |Both reading and writing are allowed (in append mode). The file pointer is placed at the end of the file and writes will make the file longer. |
|b |Binary file mode (only required on Windows). You can use it in conjunction with any of the other modes listed. |

!Deleting and renaming files
Deleting files:
{{{
File.delete("file1.txt")
File.delete("file2.txt", "file3.txt", "file4.txt")
}}}

Renaming files:
{{{
File.rename("file1.txt", "file2.txt")
}}}

!File operations
Checking if files are identical:
{{{
puts "They're identical!" if File.identical?"file1.txt", "file2.txt")
}}}

Creating directory paths (Windows and Linux)"
{{{
File.join('full', 'path', 'here', 'filename.txt')
}}}

Expand the full path of a file:
{{{
File.expand_path("text.txt")
}}}

To establish when a file was last modified:
{{{
puts File.mtime("text.txt")
}}}

Checking if a file exists:
{{{
puts "It exists!" if File.exist?("file1.txt")
}}}

Return the size of a file (in bytes):
{{{
puts File.size("text.txt")
}}}

!Directories
To output the current directory:
{{{
puts Dir.pwd
}}}

To change the current directory (NOTE - in Windows this still uses forward-slashes, not back-slashes):
{{{
Dir.chdir("c:/myfolder")
}}}

To output a list of files and directories within a specified directory:
{{{
puts Dir.entries(Dir::pwd)
puts Dir.entries("c:/ruby").join(' ')
Dir["c:/ruby/*"]
}}}
In the last example above, each entry is returned in an array as an absolute filename.

To create a new directory, use the following. You cannot create directories under directories that don’t yet exist themselves. If you want to create an entire structure of directories you must create them one by one from the top down.
{{{
Dir.mkdir("mynewdirectory")
Dir.mkdir("c:\test")
}}}

To delete a directory (both methods work the same):
{{{
Dir.delete("mynewdirectory")
Dir.rmdir("test")
}}}

!!Temporary directories and files
The method //Dir.tmpdir// provides the path to the temporary directory on the current system, although the method is not available by default. To make //Dir.tmpdir// available it’s necessary to use //require 'tmpdir'//. The following code creates a temporary file, writes data to it, and deletes it.
{{{
require 'tmpdir'
tempfilename = File.join(Dir.tmpdir, "myapp.dat")
tempfile = File.new(tempfilename, "w")
tempfile.puts "This is only temporary"
tempfile.close
File.delete(tempfilename)
}}}

Ruby’s standard library also includes a library called //Tempfile// that can create temporary files for you:
{{{
require 'tempfile'
f = Tempfile.new('myapp')
f.puts "Hello"
puts f.path
f.close
}}}
<<sectionTOC>>
!If and Unless
{{{
age = 10
if age < 18
  puts "You're too young to use this system"
  puts "So we're going to exit your program now"
  exit
end
}}}
//If not// is also allowed, as are [[Ruby Comparison Operators]]:
{{{
age = 10
if not age = 10||20
  puts "You are not ten or twenty"
else
  puts "You are either ten or twenty"
end
}}}
{{{
age = 10
unless age >= 18
  puts "You're too young to use this system"
  puts "So we're going to exit your program now"
  exit
end
}}}

!Ternary Operator ?:
This is a shortended If/Then function. The structure of the ternary operator is as follows:
//<condition> ? <result if condition is true> : <result if condition is false>//
{{{
age = 10
type = age < 18 ? "child" : "adult"
puts "You are a " + type
}}}

!Elsif and Case
Elsif structure:
{{{
fruit = "orange"
if fruit == "orange"
color = "orange"
elsif fruit == "apple"
color = "green"
elsif fruit == "banana"
color = "yellow"
else
color = "unknown"
end
}}}
Case structure 1:
{{{
fruit = "orange"
case fruit
when "orange"
color = "orange"
when "apple"
color = "green"
when "banana"
color = "yellow"
else
color = "unknown"
end
}}}
Case structure 2:
{{{
fruit = "orange"
color = case fruit
when "orange"
"orange"
when "apple"
"green"
when "banana"
"yellow"
else
"unknown"
end
}}}

!While and Until
{{{
x = 1
while x < 100
puts x
x = x * 2
end

x = 1
until x > 99
puts x
x = x * 2
end
}}}
Single-line alternative:
{{{
i = 1
i = i * 2 until i > 1000
puts i
}}}
A //Hash// is a collection of objects that are referenced with a key.
{{{
dictionary = { 'cat' => 'feline animal', 'dog' => 'canine animal' }
}}}

Methods:
{{{
puts dictionary['cat']
=> feline animal

puts dictionary.size
=> 2

dictionary['cat'] = "fluffy animal"
puts dictionary['cat']
=> fluffy animal

dictionary['frog'] = "amphibian animal"

puts dictionary.keys.inspect
=> ["cat","dog","frog"]

puts dictionary.values.inspect
=> ["feline animal","canine animal","amphibian animal"]
}}}

Deleting hash elements:
{{{
dictionary.delete("cat")
dictionary.keys.inspect
=> ["dog","frog"]
}}}

Deleting hash elements conditionally:
{{{x = { "a" => 100, "b" => 20 }
x.delete_if { |key, value| value < 25 }
puts x.inspect
=> {"a"=>100}
}}}

!Hashes within hashes
It’s possible to have hashes (or, indeed, any sort of object) within hashes, and even arrays within hashes, within hashes!

{{{
people = {
  'fred' => {
    'name' => 'Fred Elliott',
    'age' => 63,
    'gender' => 'male',
    'favorite painters' => ['Monet', 'Constable', 'Da Vinci']
    },
  'janet' => {
    'name' => 'Janet S Porter',
    'age' => 55,
    'gender' => 'female'
    }
  }

puts people['fred']['age']
=> 63
puts people['janet']['gender']
=> female
puts people['janet'].inspect
=> ["name"=>"Janet S Porter", "gender"=>"female", "age"=>55]
puts people['fred']['favorite painters'].length
=> 3
puts people['fred']['favorite painters'].join(", ")
=> Monet, Constable, Da Vinci
}}}
Loop structures:
{{{
5.times do 
 puts "Test" 
 end
}}}
A shorter alternative:
{{{
5.times { puts "Test" }
}}}
The code between { and } or do and end is a code block, essentially an anonymous, nameless method or function. Other Loop methods include:
{{{
1.upto(5) { ...code to loop here... }
1.upto(5) { |number| puts number }
10.downto(5) { ...code to loop here... }
0.step(50, 5) { ...code to loop here... }
}}}
Looping with an array:
{{{
x = [1, 2, 3]
x.each { |y| puts y }
}}}
!Nested Classes
In Ruby, it’s possible to place classes within other classes. These are called nested classes. Nested classes are useful when a class depends on other classes, but those classes aren’t necessarily useful anywhere else. They can also be useful when you want to separate classes into groups of classes rather than keep them all distinct. Here’s an example:
{{{
class Drawing
  def Drawing.give_me_a_circle
    Circle.new
  end

  class Line
  end

  class Circle
    def what_am_i
      "This is a circle"
    end
  end
end

a = Drawing.give_me_a_circle
puts a.what_am_i
=> This is a circle
a = Drawing::Circle.new
puts a.what_am_i
=> This is a circle
a = Circle.new
puts a.what_am_i
=> NameError: uninitialized constant Circle
}}}
In the above example, from within //Drawing// you can access the //Line// and //Circle// classes directly, but from outside the //Drawing// class, you can only access //Line// and //Circle// as //Drawing::Line// and //Drawing::Circle//.

//a = Drawing.give_me_a_circle// calls the //give_me_a_circle// class method, which returns a new instance of //Drawing::Circle//. Next, //a = Drawing::Circle.new// gets a new instance of //Drawing::Circle// directly, whereas it doesn’t succeed because //Circle// doesn’t exist. That’s because as a nested class under //Drawing//, it’s known as //Drawing::Circle// instead.
Ruby reference pages:
<<tiddler ListTaggedTiddlers with:"Ruby">>
!Characters
|!Character |!Meaning |
|^ |Anchor for the beginning of a line|
|$ |Anchor for the end of a line|
|\A |Anchor for the start of a string|
|\Z |Anchor for the end of a string|
|. |Any character|
|\ |Escape character (eg match fullstop using \. |
|\w |Any letter, digit, or underscore|
|\W |Anything that \w doesn’t match|
|\d |Any digit|
|\D |Anything that \d doesn’t match (non-digits)|
|\s |Whitespace (spaces, tabs, newlines, and so on)|
|\S |Non-whitespace (any visible character)|

!Modifiers
|!Modifier |!Description |
|* |Match zero or more occurrences of the preceding character, and match as many as possible.|
|+ |Match one or more occurrences of the preceding character, and match as many as possible.|
|*? |Match zero or more occurrences of the preceding character, and match as few as possible.|
|+? |Match one or more occurrences of the preceding character, and match as few as possible.|
|? |Match either one or none of the preceding character.|
|[xyz] |Match any of the characters inside the brackets.|
|{x} |Match x occurrences of the preceding character.|
|{x,y} |Match at least x occurrences and at most y occurrences.|
|!Expression |!Output |
|"Test" + "Test" |~TestTest|
|"Test".capitalize |Test|
|"Test".downcase |test|
|"Test".chop |Tes|
|"Test".hash |-98625764|
|"Test".next |Tesu|
|"Test".reverse |tseT|
|"Test".sum |416|
|"Test".swapcase |tEST|
|"Test".upcase |TEST|
|"Test".upcase.reverse |TSET|
|"Test".upcase.reverse.next |TSEU|
|"Test".length |4|

Define a multi-line string (note that the deliminator can be anything):
{{{
x = %q{This is a test
of the multi
line capabilities}

x = <<END_MY_STRING_PLEASE
This is the string
And a second line
END_MY_STRING_PLEASE
}}}

Print ASCII Character values:
{{{
puts ?x
=> 120

puts 120.chr
=> x
}}}

Interpolate and/or substitute strings, operations or logic into a string output:
{{{
x = 10
y = 20
z = "Ashley"

puts "#{x} + #{y} = #{x + y}"
=> 10 + 20 = 30

puts "My name is #{z}."
=>My name is Ashley.
}}}

Scanning strings for matching defined characters (using Regex);
{{{
puts "this is a test".scan(/\w/).join
=> thisisatest
puts "this is a test".scan(/\w+/).join('-')
=> this-is-a-test
puts "this is a test".scan(/\w+/).length
=> 4
}}}

Global substitution within a text string:
{{{
puts "this is a test".gsub(/t/, 'X')
=> Xhis is a XesX
}}}
Symbols are used as a consistent name within code.
{{{
current_situation = :good
puts "Everything is fine" if current_situation == :good
puts "PANIC!" if current_situation == :bad
}}}
Using symbols when creating a hash:
{{{
person1 = { :name => "Fred", :age => 20, :gender => :male }
person2 = { :name => "Laura", :age => 23, :gender => :female }
puts person1[:name]
=> Fred
puts person2[:gender]
=> female
}}}
!Testing using Exceptions
You can raise defined exceptions produce errors if the code does note produce the expected results. For example, the following code adds a method to the //String// class to capitalise the first letter of each separate word. Three exceptions are defined, each containing a different input and the expected output:
{{{
class String
  def titleize
    self.gsub(/(\A|\s)\w/) { |letter| letter.upcase }
  end
end

raise "Fail 1" unless "this is a test".titleize == "This Is A Test"
raise "Fail 2" unless "another test 1234".titleize == "Another Test 1234"
raise "Fail 3" unless "We're still testing titleize.".titleize == "We're Still Testing Titleize."
}}}

!Unit Testing
Ruby comes with a library, //Test::Unit//, that makes testing easy and organizes test cases into a clean structure. One of the benefits of //Test::Unit// is that it gives you a standardized framework for writing and performing tests. Rather than writing assertions in an inconsistent number of ways, //Test::Unit// gives you a core set of assertions to use. 

The following script contains some assertions that should be true if our code works propoerly. Run the script to see the tests in action.
{{{
class String
  def titleize
    self.gsub(/(\A|\s)\w/) { |letter| letter.upcase }
  end
end

require 'test/unit'

class TestTitleize < Test::Unit::TestCase
  def test_basic
    assert_equal("This Is A Test", "this is a test".titleize, "Test 1 failed")
    assert_equal("Another Test 1234", "another test 1234".titleize, "Test 2 failed")
    assert_equal("We're Testing", "We're testing".titleize, "Test 3 failed")
    assert_equal("Let's make a test fail", "foo".titleize, "Test 4 failed")
  end
end
}}}

!Benchmarking
Ruby’s standard library includes a module called //Benchmark//, which provides several methods that measure the speed it takes to complete the code you provide. For example:
{{{
require 'benchmark'
puts Benchmark.measure { 10000.times { print "." } }
}}}

Because //measure// accepts code blocks, you can make it as elaborate as you wish. For example, the following script benchmarks two different ways to count to a million:
{{{
require 'benchmark'
iterations = 1000000
b = Benchmark.measure do
  for i in 1..iterations do
    x = i
  end
end
c = Benchmark.measure do
  iterations.times do |i|
    x = i
  end
end
puts b
puts c
}}}

Alternatively (this method provides a better report output):
{{{
require 'benchmark'
iterations = 1000000

Benchmark.bm do |bm|
  bm.report("for:") do
    for i in 1..iterations do
      x = i
    end
  end
  bm.report("times:") do
    iterations.times do |i|
      x = i
    end
  end
end
}}}

!Profiling
Where benchmarking is the process of measuring the total time it takes to achieve something and comparing those results between different versions of code, profiling tells you //what// code is taking //what amount of time//. For example, you might have a single line in your code that’s causing the program to run slowly, so by profiling your code you can immediately see where you should focus your optimization efforts.

Ruby comes with a code profiler built in, and all you have to do to have your code profiled automatically is to add //require "profile"// to the start of your code, or run it with //"""ruby --r profile"""// before your source file. Example script:
{{{
require 'profile'
class Calculator
  def self.count_to_large_number
    x = 0
    100000.times { x += 1 }
  end
  def self.count_to_small_number
    x = 0
    1000.times { x += 1 }
  end
end

Calculator.count_to_large_number
Calculator.count_to_small_number
}}}
|!Variable Name |!Valid Or Invalid? |
|x |Valid|
|y2 |Valid|
|_x |Valid|
|7x |Invalid (starts with a digit)|
|this_is_a_test |Valid|
|this is a test |Invalid (not a single word)|
|this'is@a'test! |Invalid (contains invalid characters: ', @, and !)|
|this-is-a-test |Invalid (looks like subtraction)|
!Local Variables
Local variables can only be used in the same place that they are defined (the local area of code).
{{{
x = 20
name = "John"

def basic_method
  puts x
  name = "Ashley"
end
}}}
In the above example, the variables within the defined method are different local variables to those set outside the method.

!Global Variables
Global variables are available from everywhere within an application, including within classes and objects.
{{{
$x = 10
$name = "Ashley"
}}}

!Object Variables
These variables have scope within, and are associated to, the current object.
{{{
class Square
  def initialize(side_length)
    @side_length = side_length
  end
  def area
    @side_length * @side_length
  end
end

a = Square.new(10)
b = Square.new(5)
puts a.area
=> 100
puts b.area
=> 25
}}}
In the above example, //@side_length//, as an object variable, is accessible from any other method inside that object of the Square class. 

!Class Variables
The scope of a //class variable// is within the current class, as opposed to within specific objects of that class.
{{{
class Square
  def initialize
    if defined?(@@number_of_squares)
      @@number_of_squares += 1
    else
      @@number_of_squares = 1
    end
  end
end
}}}
In the above example, //"""@@number_of_squares"""// is a class variable. It is already defined each time you create a new object of class Square (except for the first time, but that’s why you check to see if it’s defined, and if not, give it an initial value of 1).
To install the //tidy// Gem from the internet, from a command prompt type:
{{{
gem install tidy
}}

To install //tidy// from a local file, use:
{{{
gem install tidy-1.1.2.gem
}}}

To uninstall the //tidy// Gem, use:
{{{
gem uninstall tidy
}}}

To check what Gems are installed on your system, use:
{{{
gem list
}}}

To update all Gems from the internet, use:
{{{
gem update
}}}
[[Oracle SQL]] FAQ:

==What is SQL and where does it come from?==
Structured Query Language (SQL) is a language that provides an interface to relational database systems. The proper pronunciation of SQL is "ess cue ell," and not "sequel" as is commonly heard. 

SQL was developed by IBM in the 1970s for use in System R, and is a de facto standard, as well as an ISO and ANSI standard. 

In common usage SQL also encompasses DML (Data Manipulation Language), for INSERTs, UPDATEs, DELETEs and DDL (Data Definition Language), used for creating and modifying tables and other database structures.

The development of SQL is governed by standards. A major revision to the SQL standard was completed in 1992, called SQL2. SQL3 support object extensions and are (partially?) implemented in Oracle8 and 9i.

Example SQL statements:
 CREATE TABLE table1 (column1 NUMBER, column2 VARCHAR2(30));

 INSERT INTO table1 VALUES (1, 'XYZ');

 SELECT * FROM table1 WHERE column2 = 'XYZ';

==What are the difference between DDL, DML and DCL commands?==
'''DDL''' - Data Definition Language: statements used to define the database structure or schema. Some examples:
* CREATE - to create objects in the database
* ALTER - alters the structure of the database
* DROP - delete objects from the database
* TRUNCATE - remove all records from a table, including all spaces allocated for the records are removed
* COMMENT - add comments to the data dictionary
* RENAME - rename an object

'''DML''' - Data Manipulation Language: statements used for managing data within schema objects. Some examples:
* SELECT - retrieve data from the a database
* INSERT - insert data into a table
* UPDATE - updates existing data within a table
* DELETE - deletes all records from a table, the space for the records remain
* MERGE - UPSERT operation (insert or update)
* CALL - call a PL/SQL or Java subprogram
* EXPLAIN PLAN - explain access path to data
* LOCK TABLE - control concurrency

'''DCL''' - Data Control Language. Some examples:
* GRANT - gives user's access privileges to database
* REVOKE - withdraw access privileges given with the GRANT command

'''TCL''' - Transaction Control: statements used to manage the changes made by DML statements. It allows statements to be grouped together into logical transactions.
* COMMIT - save work done
* SAVEPOINT -  identify a point in a transaction to which you can later roll back
* ROLLBACK - restore database to original since the last COMMIT
* SET TRANSACTION - Change transaction options like isolation level and what rollback segment to use

DML are not auto-commit. i.e. you can roll-back the operations, but DDL are auto-commit

==Difference between TRUNCATE, DELETE and DROP commands?==
The [[DELETE]] command is used to remove '''some or all''' rows from a table. A WHERE clause can be used to only remove some rows. If no WHERE condition is specified, all rows will be removed. After performing a DELETE operation you need to [[COMMIT]] or [[ROLLBACK]] the transaction to make the change permanent or to undo it. Note that this operation will cause all DELETE triggers on the table to fire.

 SQL> SELECT COUNT(*) FROM emp;
   COUNT(*)
 ----------
         14
 
 SQL> DELETE FROM emp WHERE job = 'CLERK';
 4 rows deleted.
 
 SQL> COMMIT;
 Commit complete.
 
 SQL> SELECT COUNT(*) FROM emp;
   COUNT(*)
 ----------
         10

[[TRUNCATE]] removes '''all rows''' from a table. The operation cannot be rolled back and no triggers will be fired. As such, TRUNCATE is faster and doesn't use as much undo space as a DELETE.

 SQL> TRUNCATE TABLE emp;
 Table truncated.
 
 SQL> SELECT COUNT(*) FROM emp;
 
   COUNT(*)
 ----------
          0

The [[DROP]] command removes a table from the database. All the tables' rows, indexes and privileges will also be removed. No DML triggers will be fired. The operation cannot be rolled back. 

 SQL> DROP TABLE emp;
 Table dropped.
 
 SQL> SELECT * FROM emp;
 SELECT * FROM emp
               *
 ERROR at line 1:
 ORA-00942: table or view does not exist

DROP and TRUNCATE are DDL commands, whereas DELETE is a DML command. Therefore DELETE operations can be rolled back (undone), while DROP and TRUNCATE operations cannot be rolled back.

From [[Oracle 10g]] a table can be "undropped". Example:
 SQL> FLASHBACK TABLE emp TO BEFORE DROP;
 Flashback complete.

PS: DELETE will not free up used space within a table. This means that repeated DELETE commands will severely fragment the table and queries will have to navigate this "free space" in order to retrieve rows.

==How does one escape special characters when writing SQL queries?==
'''Escape quotes'''

Use two quotes for every one displayed. Examples:

 <nowiki>SQL> SELECT 'Frank''s Oracle site' AS text FROM DUAL;
 TEXT
 --------------------
 Franks's Oracle site

 SQL> SELECT 'A ''quoted'' word.' AS text FROM DUAL;
 TEXT
 ----------------
 A 'quoted' word.

 SQL> SELECT 'A ''''double quoted'''' word.' AS text FROM DUAL;
 TEXT
 -------------------------
 A ''double quoted'' word.</nowiki>

'''Escape wildcard characters'''

The LIKE keyword allows for string searches. The '_' wild card character is used to match exactly one character, while '%' is used to match zero or more occurrences of any characters. These characters can be escaped in SQL. Examples:

 SELECT name FROM emp 
  WHERE id LIKE '%/_%' ESCAPE '/';

 SELECT name FROM emp 
  WHERE id LIKE '%\%%' ESCAPE '\';

'''Escape ampersand (&) characters in SQL*Plus'''

When using SQL*Plus, the DEFINE setting can be changed to allow &'s (ampersands) to be used in text:

 SET DEFINE ~
 SELECT 'Lorel & Hardy' FROM dual;

'''Other methods:'''

Define an escape character:
 SET ESCAPE '\'
 SELECT '\&abc' FROM dual;

Don't scan for substitution variables:
 SET SCAN OFF
 SELECT '&ABC' x FROM dual;

Use the 10g Quoting mechanism:

 Syntax
  q'[QUOTE_CHAR]Text[QUOTE_CHAR]'
  Make sure that the QUOTE_CHAR doesnt exist in the text.

 SELECT q'{This is Orafaq's 'quoted' text field}' FROM DUAL;

==Can one select a random collection of rows from a table?==
The following methods can be used to select a random collection of rows from a table:

'''The SAMPLE Clause'''

From Oracle 8i, the easiest way to randomly select rows from a table is to use the SAMPLE clause with a SELECT statement. Examples: 
 SELECT * FROM emp SAMPLE(10);

In the above example, Oracle is instructed to randomly return 10% of the rows in the table. 
 SELECT * FROM emp SAMPLE(5) BLOCKS;

This example will sample 5% of all formatted database blocks instead of rows. 

This clause only works for single table queries on local tables. If you include the SAMPLE clause within a multi-table or remote query, you will get a parse error or "ORA-30561: SAMPLE option not allowed in statement with multiple table references". One way around this is to create an inline view on the driving table of the query with the SAMPLE clause. Example: 
 SELECT t1.dept, t2.emp
   FROM (SELECT * FROM dept SAMPLE(5)) t1,
        emp t2
  WHERE t1.dep_id = t2.dep_id;

If you examine the execution plan of a "Sample Table Scan", you should see a step like this:
 TABLE ACCESS (SAMPLE) OF 'EMP' (TABLE)

'''ORDER BY dbms_random.value()'''

This method orders the data by a random column number. Example:
 SQL> SELECT * FROM (SELECT ename
   2                   FROM emp
   3                  ORDER BY dbms_random.value())
   4   WHERE rownum <= 3;
 ENAME
 ----------
 WARD
 MILLER
 TURNER

'''The ORA_HASH() function'''

The following example retrieves a subset of the data in the emp  table by specifying 3 buckets (0 to 2) and then returning the data from bucket 1:

 SELECT * FROM emp WHERE ORA_HASH(empno, 2) = 1;

==How does one eliminate duplicates rows from a table?==
Choose one of the following queries to identify or remove duplicate rows from a table leaving only unique records in the table:

'''Method 1:'''

Delete all rowids that is BIGGER than the SMALLEST rowid value (for a given key):
 SQL> DELETE FROM table_name A WHERE ROWID > (
   2    SELECT min(rowid) FROM table_name B
   3    WHERE A.key_values = B.key_values);

'''Method 2:'''

This method is usually faster. However, remember to recreate all indexes, constraints, triggers, etc. on the table when done.
 SQL> create table table_name2 as select distinct * from table_name1;
 SQL> drop table table_name1;
 SQL> rename table_name2 to table_name1;

'''Method 3:'''

(contributed by [mailto:dgurnick@otb.com Dennis Gurnick])
 SQL> delete from my_table t1
 SQL> where  exists (select 'x' from my_table t2
 SQL>                 where t2.key_value1 = t1.key_value1
 SQL>                   and t2.key_value2 = t1.key_value2
 SQL>                   and t2.rowid      > t1.rowid);

Note: One can eliminate N^2 unnecessary operations by creating an index on the joined fields in the inner loop (no need to loop through the entire table on each pass by a record). This will speed-up the deletion process.


Note 2: If you are comparing NULL columns, use the NVL function. Remember that NULL is not equal to NULL. This should not be a problem as all key columns should be NOT NULL by definition.

==How does one get the time difference between two date columns?==
Oracle allows two date values to be subtracted from each other returning a numeric value indicating the number of days between the two dates (may be a fraction). This example will show how to relate it back to a time value.

Let's investigate some solutions. Test data:

 SQL> CREATE TABLE dates (date1 DATE, date2 DATE);
 Table created.
 SQL>
 SQL> INSERT INTO dates VALUES (SYSDATE, SYSDATE-1);
 1 row created.
 SQL> INSERT INTO dates VALUES (SYSDATE, SYSDATE-1/24);
 1 row created.
 SQL> INSERT INTO dates VALUES (SYSDATE, SYSDATE-1/60/24);
 1 row created.
 SQL> SELECT (date1 - date2) FROM dates;
 DATE1-DATE2
 -----------
           1
  .041666667
  .000694444

'''Solution 1'''

 SQL> SELECT floor(((date1-date2)*24*60*60)/3600)
   2         || ' HOURS ' ||
   3         floor((((date1-date2)*24*60*60) -
   4         floor(((date1-date2)*24*60*60)/3600)*3600)/60)
   5         || ' MINUTES ' ||
   6         round((((date1-date2)*24*60*60) -
   7         floor(((date1-date2)*24*60*60)/3600)*3600 -
   8         (floor((((date1-date2)*24*60*60) -
   9         floor(((date1-date2)*24*60*60)/3600)*3600)/60)*60) ))
  10         || ' SECS ' time_difference
  11    FROM dates;
 TIME_DIFFERENCE
 --------------------------------------------------------------------------------
 24 HOURS 0 MINUTES 0 SECS
 1 HOURS 0 MINUTES 0 SECS
 0 HOURS 1 MINUTES 0 SECS

'''Solution 2'''

If you don't want to go through the floor and ceiling maths, try this method  (contributed by [mailto:esw@wile.org Erik Wile]):

 SQL> SELECT to_number( to_char(to_date('1','J') +
   2         (date1 - date2), 'J') - 1)  days,
   3         to_char(to_date('00:00:00','HH24:MI:SS') +
   4         (date1 - date2), 'HH24:MI:SS') time
   5   FROM dates;
       DAYS TIME
 ---------- --------
          1 00:00:00
          0 01:00:00
          0 00:01:00

'''Solution 3'''

Here is a simpler method (contributed by [mailto:aniruddha_jathar@hotmail.com Aniruddha Jathar]):

 SQL> SELECT trunc(date1-date2) days,
   2         trunc(sysdate) + (date1 - date2), 'HH24 "Hours" MI "Minutes" SS "Seconds"') time
   3   FROM dates;
       DAYS TIME
 ---------- ------------------------------
          1 00 Hours 00 Minutes 00 Seconds
          0 01 Hours 00 Minutes 00 Seconds
          0 00 Hours 01 Minutes 00 Seconds

==How does one add a day/hour/minute/second to a date value?==
The SYSDATE pseudo-column shows the current system date and time. Adding 1 to SYSDATE will advance the date by 1 day. Use fractions to add hours, minutes or seconds to the date. Look at these examples:

 SQL> select sysdate, sysdate+1/24, sysdate +1/1440, sysdate + 1/86400 from dual;
 SYSDATE              SYSDATE+1/24         SYSDATE+1/1440       SYSDATE+1/86400
 -------------------- -------------------- -------------------- --------------------
 03-Jul-2002 08:32:12 03-Jul-2002 09:32:12 03-Jul-2002 08:33:12 03-Jul-2002 08:32:13

The following format is frequently used with Oracle Replication:

 select sysdate NOW, sysdate+30/(24*60*60) NOW_PLUS_30_SECS from dual;
 NOW                  NOW_PLUS_30_SECS
 -------------------- --------------------
 03-JUL-2005 16:47:23 03-JUL-2005 16:47:53

'''Here are a couple of examples:'''

<table border=1 STYLE="Font-size: 8pt;">
<tr><th>Description</th><th>Date Expression</th></tr>
<tr><td>Now</td><td>SYSDATE</td></tr>
<tr><td>Tomorow/ next day</td><td>SYSDATE + 1</td></tr>
<tr><td>Seven days from now</td><td>SYSDATE + 7</td></tr>
<tr><td>One hour from now</td><td>SYSDATE + 1/24</td></tr>
<tr><td>Three hours from now</td><td>SYSDATE + 3/24</td></tr>
<tr><td>An half hour from now</td><td>SYSDATE + 1/48</td></tr>
<tr><td>10 minutes from now</td><td>SYSDATE + 10/1440</td></tr>
<tr><td>30 seconds from now</td><td>SYSDATE + 30/86400</td></tr>
<tr><td>Tomorrow at 12 midnight</td><td>TRUNC(SYSDATE + 1)</td></tr>
<tr><td>Tomorrow at 8 AM</td><td>TRUNC(SYSDATE + 1) + 8/24</td></tr>
<tr><td>Next Monday at 12:00 noon</td><td>NEXT_DAY(TRUNC(SYSDATE), 'MONDAY') + 12/24</td></tr>
<tr><td>First day of the month at 12 midnight</td><td>TRUNC(LAST_DAY(SYSDATE ) + 1)</td></tr>
<tr><td>The next Monday, Wednesday or Friday at 9 a.m</td><td>TRUNC(LEAST(NEXT_DAY(sysdate,''MONDAY' ' ),NEXT_DAY(sysdate,''WEDNESDAY''), NEXT_DAY(sysdate,''FRIDAY'' ))) + (9/24)</td></tr></table>

==How does one code a matrix/crosstab/pivot report in SQL?==
Newbies frequently ask how one can display "rows as columns" or "columns as rows". Look at these example crosstab queries (also sometimes called transposed, matrix or pivot queries):

 SELECT  *
   FROM  (SELECT job,
                 sum(decode(deptno,10,sal)) DEPT10,
                 sum(decode(deptno,20,sal)) DEPT20,
                 sum(decode(deptno,30,sal)) DEPT30,
                 sum(decode(deptno,40,sal)) DEPT40
            FROM scott.emp
        GROUP BY job)
 ORDER BY 1;
 JOB           DEPT10     DEPT20     DEPT30     DEPT40
 --------- ---------- ---------- ---------- ----------
 ANALYST                    6000
 CLERK           1300       1900        950
 MANAGER         2450       2975       2850
 PRESIDENT       5000
 SALESMAN                              5600

Here is the same query with some fancy headers and totals:

 SQL> ttitle "Crosstab Report"
 SQL> break on report;
 SQL> compute sum of dept10 dept20 dept30 dept40 total on report;
 SQL>
 SQL> SELECT     *
   2    FROM     (SELECT job,
   3                  sum(decode(deptno,10,sal)) DEPT10,
   4                  sum(decode(deptno,20,sal)) DEPT20,
   5                  sum(decode(deptno,30,sal)) DEPT30,
   6                  sum(decode(deptno,40,sal)) DEPT40,
   7                  sum(sal)                   TOTAL
   8             FROM emp
   9            GROUP BY job)
  10  ORDER BY 1;
 
 Mon Aug 23                                                             page    1
                                 Crosstab Report
 
 JOB           DEPT10     DEPT20     DEPT30     DEPT40      TOTAL
 --------- ---------- ---------- ---------- ---------- ----------
 ANALYST                    6000                             6000
 CLERK           1300       1900        950                  4150
 MANAGER         2450       2975       2850                  8275
 PRESIDENT       5000                                        5000
 SALESMAN                              5600                  5600
           ---------- ---------- ---------- ---------- ----------
 sum             8750      10875       9400                 29025

Here's another variation on the theme:

 SQL> SELECT DECODE(MOD(v.row#,3)
   2                 ,1, 'Number: '  ||deptno
   3                 ,2, 'Name: '    ||dname
   4                 ,0, 'Location: '||loc
   5                 ) AS "DATA"
   6    FROM dept,
   7         (SELECT rownum AS row# FROM user_objects WHERE rownum < 4) v
   8   WHERE deptno = 30
   9  /
 DATA
 --------------------------------------- ---------
 Number: 30
 Name: SALES
 Location: CHICAGO

==Can one retrieve only rows X to Y from a table?==
[mailto:skhaleel@bodhtree.com Shaik Khaleel] provided this solution to the problem:

----

 SELECT * FROM (
    SELECT ename, rownum rn 
             FROM emp WHERE rownum < 101
 ) WHERE  RN between 91 and 100 ;

Note: the 101 is just one greater than the maximum row of the required rows (means x= 90, y=100, so the inner values is  y+1).

[mailto:rpachalla@hotmail.com Ravi Pachalla] provided this solution:

 SELECT rownum, f1 FROM t1
 GROUP BY rownum, f1 HAVING rownum BETWEEN 2 AND 4;

Another solution is to use the MINUS operation. For example, to display rows 5 to 7, construct a query like this:

 SELECT *
 FROM   tableX
 WHERE  rowid in (
    SELECT rowid FROM tableX
     WHERE rownum <= 7
   MINUS
    SELECT rowid FROM tableX
    WHERE rownum < 5);

[mailto:youssefkh3@hotmail.com Youssef Youssef] provided this solution:
"this one was faster for me and allowed for sorting before filtering by rownum. The inner query (table A) can be a series of tables joined together with any operation before the filtering by rownum is applied."

 SELECT * 
   FROM (SELECT a.*, rownum RN 
 	   FROM (SELECT * 
                   FROM t1 ORDER BY key_column) a
          WHERE rownum <=7)
  WHERE rn >=5;

Please note, there is no explicit row order in a relational database. However, this query is quite fun and may even help in the odd situation.

[mailto:konkimallap@yahoo.com Praveena Konkimalla] provided this solution:

The generic solution to get full information of rows between x and y

----

SELECT * FROM emp WHERE empno in (SELECT empno  FROM emp
GROUP BY rownum,empno HAVING rownum BETWEEN &x AND &y);

----

==Can one retrieve only the Nth row from a table?==
[mailto:rupak6@yahoo.com Rupak Mohan] provided this solution to select the Nth row from a table:

 SELECT * FROM t1 a
 WHERE  n = (SELECT COUNT(rowid)
               FROM t1 b
              WHERE a.rowid >= b.rowid);

[mailto:skhaleel@bodhtree.com Shaik Khaleel] provided this solution:

 SELECT * FROM (
    SELECT ENAME,ROWNUM RN FROM EMP WHERE ROWNUM < 101 )
  WHERE  RN = 100;

Note: In this first query we select one more than the required row number, then we select the required one. Its far better than using a MINUS operation.

[mailto:prasadravi@hotmail.com Ravi Pachalla] provided these solutions:

 SELECT f1 FROM t1
 WHERE  rowid = (
         SELECT rowid FROM t1
         WHERE  rownum <= 10
         MINUS
         SELECT rowid FROM t1
         WHERE  rownum < 10);

 SELECT rownum,empno FROM scott.emp a
  GROUP BY rownum,empno HAVING rownum = 4;

Alternatively...

 SELECT * FROM emp WHERE rownum=1 AND rowid NOT IN
   (SELECT rowid FROM emp WHERE rownum < 10);

Please note, there is no explicit row order in a relational database. However, this query is quite fun and may even help in the odd situation.

==How can one dump/ examine the exact content of a database column?==
Table data can be extracted from the database as octal, decimal or hex values:

 SELECT DUMP(col1, 10)
 FROM tab1
 WHERE cond1 = val1;
 DUMP(COL1)
 -------------------------------------
 Typ=96 Len=4: 65,66,67,32

For this example, type=96 is indicating a CHAR column. The last byte in the column is 32, which is the ASCII code for a space. This tells us that this column is blank-padded.

==How does one add a column to the middle of a table?==
Oracle only allows columns to be added to the end of an existing table. Example:

 SQL> CREATE TABLE tab1 ( col1 NUMBER );
 Table created.
 
 SQL> ALTER TABLE tab1 ADD (col2 DATE);
 Table altered.
 
 SQL> DESC tab1
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 COL1                                               NUMBER
 COL2                                               DATE

Nevertheless, some databases also allow columns to be added to an existing table after a particular column (i.e. in the middle of the table). For example, in MySQL the following syntax is valid:

 ALTER TABLE tablename ADD columnname AFTER columnname;

Oracle does not support this syntax. However, it doesn't mean that it cannot be done.

'''Workarounds:'''

1. Create a new table and copy the data across.
 SQL> RENAME tab1 TO tab1_old;
 Table renamed.
 
 SQL> CREATE TABLE tab1 AS SELECT 0 AS col1, col1 AS col2 FROM tab1_old;
 Table created.

2. Use the DBMS_REDEFINITION package to change the structure on-line while users are working.

==How does one code a hierarchical tree-structured query?==
The SCOTT/TIGER database schema contains a table EMP with a self-referencing relation (EMPNO and MGR columns). This table is perfect for testing and demonstrating tree-structured queries as the MGR column contains the employee number of the "current" employee's boss.

The LEVEL pseudo-column is an indication of how deep in the tree one is. Oracle can handle queries with a depth of up to 255 levels. Look at this example:

 SQL> SELECT     level, empno, ename, mgr
   2    FROM     emp
   3  CONNECT BY PRIOR empno = mgr
   4    START WITH mgr IS NULL
   5  /
      LEVEL      EMPNO ENAME             MGR
 ---------- ---------- ---------- ----------
          1       7839 KING
          2       7566 JONES            7839
          3       7788 SCOTT            7566
 ...

One can produce an indented report by using the level number to substring or lpad() a series of spaces, and concatenate that to the string. Look at this example:

 SQL> SELECT     LPAD(' ', LEVEL * 2) || ename
   2    FROM     emp
   3  CONNECT BY PRIOR empno = mgr
   4    START WITH mgr IS NULL;
 LPAD('',LEVEL*2)||ENAME
 ------------------------------------------------------
   KING
     JONES
       SCOTT
 ...

Use the "start with" clause to specify the start of the tree. More than one record can match the starting condition. One disadvantage of having a "connect by prior" clause is that you cannot perform a join to other tables. The "connect by prior" clause is rarely implemented in the other database offerings. Trying to do this programmatically is difficult as one has to do the top level query first, then, for each of the records open a cursor to look for child nodes.

One way of working around this is to use PL/SQL, open the driving cursor with the "connect by prior" statement, and the select matching records from other tables on a row-by-row basis, inserting the results into a temporary table for later retrieval.

NOTE: Tree-structured queries are definitely non-relational (enough to kill Codd and make him roll in his grave). Also, this feature is not often found in other database offerings.

==How does one count/sum data values in a column?==
'''Count/sum FIX values:'''

Use this simple query to count the number of data values in a column:

 select my_table_column, count(*)
 from   my_table
 group  by my_table_column;

A more sophisticated example...

 select dept, sum(  decode(sex,'M',1,0)) MALE,
              sum(  decode(sex,'F',1,0)) FEMALE,
              count(decode(sex,'M',1,'F',1)) TOTAL
   from my_emp_table
  group by dept;

'''Count/sum RANGES of data values in a column:'''

A value x will be between values y and z if GREATEST(x, y) = LEAST(x, z). Look at this example:

 select f2,
        sum(decode(greatest(f1,59), least(f1,100), 1, 0)) "Range 60-100",
        sum(decode(greatest(f1,30), least(f1, 59), 1, 0)) "Range 30-59",
        sum(decode(greatest(f1, 0), least(f1, 29), 1, 0)) "Range 00-29"
 from   my_table
 group  by f2;

For equal size ranges it might be easier to calculate it with DECODE(TRUNC(value/range), 0, rate_0, 1, rate_1, ...).  Eg.

 select ename "Name", sal "Salary",
        decode( trunc(f2/1000, 0), 0, 0.0,
                                   1, 0.1,
                                   2, 0.2,
                                   3, 0.31) "Tax rate"
 from   my_table;

==How does one drop/ rename a columns in a table?==
'''Drop a column'''

From [[Oracle 8i]] one can DROP a column from a table. Look at this [http://www.orafaq.com/scripts/sql/dropcol.txt sample script], demonstrating the ''ALTER TABLE table_name DROP COLUMN column_name;'' command.

'''Workarounds for older releases:'''

 SQL> update t1 set column_to_drop = NULL;
 SQL> rename t1 to t1_base;
 SQL> create view t1 as select >specific columns> from t1_base;

 SQL> create table t2 as select >specific columns> from t1;
 SQL> drop table t1;
 SQL> rename t2 to t1;

'''Rename a column'''

From [[Oracle 9i]] one can RENAME a column from a table. Look at this example:
 ALTER TABLE tablename RENAME COLUMN oldcolumn TO newcolumn;

'''Workarounds for older releases:'''

Use a view with correct column names:

 rename t1 to t1_base;
 create view t1 >column list with new name> as select * from t1_base;

Recreate the table with correct column names:

 create table t2 >column list with new name> as select * from t1;
 drop table t1;
 rename t2 to t1;

Add a column with a new name and drop an old column:

 alter table t1 add ( newcolame datatype );  
 update t1 set newcolname=oldcolname;
 alter table t1 drop column oldcolname;

==How does one implement IF-THEN-ELSE logic in a SELECT statement?==
One can use the [[CASE]] statement or functions like [[DECODE]], [[NVL]], [[NVL2]], [[NULLIF]], [[COALESCE]], etc.

Here is the syntax for the CASE-statement:

 CASE exp WHEN comparison_exp1 THEN return_exp1
         [WHEN comparison_exp2 THEN return_exp2
          WHEN comparison_exp3 THEN return_exp3
           ELSE else_exp
         ]
 END

And for DECODE:
 DECODE( col | exprn, srch1, rslt1 [, srch2, rslt2,...,] [,default] )

==How does one prevent Oracle from using an Index?==
In certain cases, one may want to disable the use of a specific, or all indexes for a given query. Here are some examples:

'''Adding an expression to the indexed column'''

 SQL>select count(*) from t where empno+0=1000;
   COUNT(*)
 ----------
          1
 
 Execution Plan
 --------------------------------------------- ----- --------
    0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=3)
    1    0   SORT (AGGREGATE)
    2    1     TABLE ACCESS (FULL) OF 'T' (Cost=2 Card=1 Bytes=3)

'''Specifying the FULL hint to force full table scan'''

 SQL>select /*+ FULL(t) */ * from t where empno=1000;
      EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO GRADE
 ---------- ---------- --------- ---------- --------- ---------- ---------- ---------- ----------
       1000 Victor     DBA             7839 20-MAY-03      11000          0         10 JUNIOR
 
 Execution Plan
 --------------------------------------------- ----- --------
    0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=41)
    1    0   TABLE ACCESS (FULL) OF 'T' (Cost=2 Card=1 Bytes=41)

'''Specifying NO_INDEX hint'''

 SQL>select /*+ NO_INDEX(T) */ count(*) from t where empno=1000;
   COUNT(*)
 ----------
          1
 
 Execution Plan
 --------------------------------------------- ----- --------
    0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=3)
    1    0   SORT (AGGREGATE)
    2    1     TABLE ACCESS (FULL) OF 'T' (Cost=2 Card=1 Bytes=3)

'''Using a function over the indexed column'''

 SQL>select count(*) from t where to_number(empno)=1000;
   COUNT(*)
 ----------
          1
 
 Execution Plan
 --------------------------------------------- ----- --------
    0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=3)
    1    0   SORT (AGGREGATE)
    2    1     TABLE ACCESS (FULL) OF 'T' (Cost=2 Card=1 Bytes=3)

==How does one select EVERY Nth row from a table?==
One can easily select all even, odd, or Nth rows from a table using SQL queries like this:

'''Method 1:''' Using a [[subquery]]

 SELECT *
 FROM   emp
 WHERE  (ROWID,0) IN (SELECT ROWID, MOD(ROWNUM,4)
                      FROM   emp);

'''Method 2:''' Use dynamic views (available from Oracle7.2):

 SELECT *
 FROM   ( SELECT rownum rn, empno, ename
          FROM emp
        ) temp
 WHERE  MOD(temp.ROWNUM,4) = 0;

'''Method 3:''' Using GROUP BY and HAVING - provided by [mailto:rpachalla@hotmail.com Ravi Pachalla]

 SELECT rownum, f1 FROM t1
 GROUP BY rownum, f1 HAVING MOD(rownum,n) = 0 OR rownum = 2-n;

Please note, there is no explicit row order in a relational database. However, these queries are quite fun and may even help in the odd situation.

==How does one select the LAST N rows from a table?==
From Oracle 9i onwards, the RANK() and DENSE_RANK() functions can be used to determine the '''LAST N''' or '''BOTTOM N''' rows. Examples:

'''Get the bottom 10 employees based on their salary'''

 SELECT ename, sal 
   FROM ( SELECT ename, sal, RANK() OVER (ORDER BY sal DESC) sal_rank
            FROM emp ) 
  WHERE sal_rank <= 10;

'''Select the employees getting the lowest 10 salaries'''

 SELECT ename, sal 
   FROM ( SELECT ename, sal, DENSE_RANK() OVER (ORDER BY sal) sal_dense_rank
            FROM emp ) 
  WHERE sal_dense_rank <= 10;

For [[Oracle 8i]] and above, one can get the '''bottom N''' rows using an inner-query with an ORDER BY clause:

 SELECT *
   FROM (SELECT * FROM my_table ORDER BY col_name_1)
  WHERE ROWNUM < 10;

Use this workaround for older (8.0 and prior) releases:

 SELECT *
   FROM my_table a
  WHERE 10 >= (SELECT COUNT(DISTINCT maxcol)
                 FROM my_table b
                WHERE b.maxcol <= a.maxcol)
  ORDER BY maxcol;

==How does one select the TOP N rows from a table?==
From Oracle 9i onwards, the RANK() and DENSE_RANK() functions can be used to determine the TOP N rows. Examples:

'''Get the top 10 employees based on their salary'''

 SELECT ename, sal 
   FROM ( SELECT ename, sal, RANK() OVER (ORDER BY sal DESC) sal_rank
            FROM emp ) 
  WHERE sal_rank <= 10;

'''Select the employees making the top 10 salaries'''

 SELECT ename, sal 
   FROM ( SELECT ename, sal, DENSE_RANK() OVER (ORDER BY sal DESC) sal_dense_rank
            FROM emp ) 
  WHERE sal_dense_rank <= 10;

For Oracle 8i and above, one can get the Top N rows using an inner-query with an ORDER BY clause:

 SELECT *
   FROM (SELECT * FROM my_table ORDER BY col_name_1 DESC)
  WHERE ROWNUM < 10;

Use this workaround for older (8.0 and prior) releases:

 SELECT *
   FROM my_table a
  WHERE 10 >= (SELECT COUNT(DISTINCT maxcol)
                 FROM my_table b
                WHERE b.maxcol >= a.maxcol)
  ORDER BY maxcol DESC;

==How to generate a text graphs (histograms) using SQL?==
 SELECT d.dname AS "Department",
              LPAD('+', COUNT(*), '+') as "Graph"
   FROM emp e, dept d
  WHERE e.deptno = d.deptno
  GROUP BY d.dname;

Sample output:

 Department     Graph
 -------------- --------------------------------------------------
 ACCOUNTING     +++
 RESEARCH       +++++
 SALES          ++++++

In the above example, the value returned by COUNT(*) is used to control the number of "*" characters to return for each department. We simply pass COUNT(*) as an argument to the string function LPAD (or RPAD) to return the desired number of *'s.

==Map/ concatenate several rows to a column==
This FAQ will demonstrate how row values can be concatenated into a single column value (similar to MySQL's [i]GROUP_CONCAT[/i] function).

Start by creating this function:

 SQL> CREATE OR REPLACE FUNCTION rowconcat(q in VARCHAR2) RETURN VARCHAR2 IS
   2    ret  VARCHAR2(4000);
   3    hold VARCHAR2(4000);
   4    cur  sys_refcursor;
   5  BEGIN
   6    OPEN cur FOR q;
   7    LOOP
   8      FETCH cur INTO hold;
   9      EXIT WHEN cur%NOTFOUND;
  10      IF ret IS NULL THEN
  11        ret := hold;
  12      ELSE
  13        ret := ret || ',' || hold;
  14      END IF;
  15    END LOOP;
  16    RETURN ret;
  17  END;
  18  /
 Function created.

This function returns a string result with the concatenated non-NULL values from a SQL statement. It returns NULL if there are no non-NULL values.

Here is an example of how to map several rows to a single concatenated column:

 SQL> SELECT rowconcat('SELECT dname FROM dept') AS departments
   2    FROM dual;
 DEPARTMENTS
 --------------------------------------------------------------------------------
 ACCOUNTING,RESEARCH,SALES,OPERATIONS

This example is more interresting, it concatenates a column across several rows based on an aggregation:

 SQL> col employees format a50
 SQL> SELECT deptno,
   2         rowconcat('SELECT ename FROM emp a WHERE deptno='||deptno) AS Employees
   3    FROM emp
   4   GROUP BY deptno
   5  /
     DEPTNO EMPLOYEES
 ---------- --------------------------------------------------
         30 ALLEN,WARD,MARTIN,BLAKE,TURNER,JAMES
         20 SMITH,JONES,SCOTT,ADAMS,FORD
         10 CLARK,KING,MILLER

==What is the difference between VARCHAR, VARCHAR2 and CHAR data types?==
Both CHAR and VARCHAR2 types are used to store character string values, however, they behave very differently. The VARCHAR type should not be used:

'''CHAR'''

CHAR should be used for storing fix length character strings. String values will be space/blank padded before stored on disk. If this type is used to store variable length strings, it will waste a lot of disk space.

 SQL> CREATE TABLE char_test (col1 CHAR(10));
 Table created.
 
 SQL> INSERT INTO char_test VALUES ('qwerty');
 1 row created.
 
 SQL> SELECT col1, length(col1), dump(col1) "ASCII Dump" FROM char_test;
 COL1       LENGTH(COL1) ASCII Dump
 ---------- ------------ ------------------------------------------------------------
 qwerty               10 Typ=96 Len=10: 113,119,101,114,116,121,32,32,32,32

Note: ASCII character 32 is a blank space.

'''VARCHAR'''

Currently VARCHAR behaves exactly the same as VARCHAR2. However, this type should not be used as it is reserved for future usage.

 SQL> CREATE TABLE varchar_test (col1 VARCHAR2(10));
 Table created.
 
 SQL> INSERT INTO varchar_test VALUES ('qwerty');
 1 row created.
 
 SQL> SELECT col1, length(col1), dump(col1) "ASCII Dump" FROM varchar_test;
 COL1       LENGTH(COL1) ASCII Dump
 ---------- ------------ ------------------------------------------------------------
 qwerty                6 Typ=1 Len=6: 113,119,101,114,116,121

'''VARCHAR2'''

VARCHAR2 is used to store variable length character strings. The string value's length will be stored on disk with the value itself.

 SQL> CREATE TABLE varchar2_test (col1 VARCHAR2(10));
 Table created.
 
 SQL> INSERT INTO varchar2_test VALUES ('qwerty');
 1 row created.
 
 SQL> SELECT col1, length(col1), dump(col1) "ASCII Dump" FROM varchar2_test;
 COL1       LENGTH(COL1) ASCII Dump
 ---------- ------------ ------------------------------------------------------------
 qwerty                6 Typ=1 Len=6: 113,119,101,114,116,121
/***
|Name|SectionLinksPlugin|
|Source|http://www.TiddlyTools.com/#SectionLinksPlugin|
|Documentation|http://www.TiddlyTools.com/#SectionLinksPlugin|
|Version|1.2.2|
|Author|Eric Shulman|
|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|allow tiddler sections in TiddlyLinks to be used as anchor points|
This plugin enhances the processing of section references they can be used in links to auto-scroll to the indicated heading within a tiddler (i.e., similar to the 'anchor' behavior provided in HTML by {{{<a name="foo">}}} and {{{<a href="#foo">...</a>}}})
!!!Usage
<<<
!!!!!{{{<<tiddler>>}}} macro 
>The {{{<<tiddler SomeTiddler##SomeSection>>}}} syntax has been extended so that if the tiddler title is omitted or the 'here' keyword is used (e.g., {{{<<tiddler ##SomeSection>>}}} or {{{<<tiddler here##SomeSection>>}}}), then the current containing tiddler is implied by default.
!!!!!~TiddlyLink syntax
>the standard link syntax has been extended so that a section name can included in a tiddler link (e.g., {{{[[SomeTiddler##SomeSection]]}}}).  When clicked, the tiddler is displayed and the specified section heading will be automatically scrolled into view. If the tiddler title is omitted or the 'here' keyword is used (e.g., {{{[[##SomeSection]]}}} or {{{[[here##SomeSection]]>>}}}), then the current containing tiddler is implied by default.
!!!!!"""<<sectionTOC>>""" macro
>This macro generates a 'Table of Contents'-style numbered-bullet list with links to all sections within the current tiddler.  Simply place the following macro at the //end of the tiddler content// (i.e., following all section headings):
{{{
<<sectionTOC>> or <<sectionTOC className>>
}}}
>Note: The macro must occur at the end of the tiddler in order to locate the rendered section headings that precede it. In addition, to position the macro's //output// within the tiddler, you must create a special 'target element' that uses a specified classname (default='sectionTOC'), like this:
{{{
{{sectionTOC{}}}
}}}
>When the {{{<<sectionTOC>>}}} macro is rendered, it will find the matching 'sectionTOC'-classed element and writes it's output there.  You can also add the macro and/or target elements directly to the [[ViewTemplate]] definition, so that every tiddler can automatically display the table of contents:
{{{
<span class='sectionTOC'></span> <!-- target element -->
...
<span macro='sectionTOC'></span> <!-- must be at end of tiddler -->
}}}
<<<
!!!Revisions
<<<
2009.07.06 [1.2.2] fixed displayTiddler() hijack
2009.07.03 [1.2.1] in {{{<<sectionTOC>>}}}, suppress output if target is not found
2009.06.02 [1.2.0] added support for 'here' keyword in {{{[[here##section]]}}} links and {{{<<tiddler here##section>>}}} macro
2009.04.09 [1.1.1] in sectionTOC macro, make target visible when TOC is rendered.
2009.01.18 [1.1.0] added {{{<<sectionTOC>>}}} macro to generate numbered-bullet links to sections of current tiddler
2009.01.06 [1.0.0] converted to stand-alone plugin
2008.10.14 [0.0.0] initial release (as [[CoreTweaks]] #784 - http://trac.tiddlywiki.org/ticket/784)
<<<
!!!Code
***/
//{{{
version.extensions.SectionLinksPlugin= {major: 1, minor: 2, revision: 2, date: new Date(2009,7,6)};

Story.prototype.scrollToSection = function(title,section) {
	if (!title||!section) return; var t=this.getTiddler(title); if (!t) return null;
	var elems=t.getElementsByTagName('*');
	for (var i=0; i<elems.length; i++) { var e=elems[i];
		if (!['H1','H2','H3','H4','H5'].contains(e.nodeName)) continue;
		if (getPlainText(e).indexOf(section)!=-1) {
			// if section heading is collapsed, click to expand it - see [[FoldHeadingsPlugin]]
			if (hasClass(e,'foldable') && e.nextSibling.style.display=='none') e.onclick();
			// scroll *after* tiddler animation
			var delay=config.options.chkAnimate?config.animDuration+100:0;
			setTimeout('window.scrollTo('+findPosX(e)+','+findPosY(e)+')',delay);
			return e;
		}
	}
}
//}}}
/***
!!!!core hijacks
***/
/***
!!!!!createTiddlyLink
***/
//{{{
// [[tiddlername##section]] and [[##section]]
window.createTiddlyLink_sectionanchor=window.createTiddlyLink;
window.createTiddlyLink=function(place,title) {
	var t=story.findContainingTiddler(place); var tid=t?t.getAttribute('tiddler'):'';
	var parts=title.split(config.textPrimitives.sectionSeparator);
	if (!parts[0].length || parts[0].toLowerCase()=='here') parts[0]=tid;  // default=current tiddler
	if (parts[1]) arguments[1]=parts[0]; // trim section from tiddler title
	var btn=createTiddlyLink_sectionanchor.apply(this,arguments);
	btn.setAttribute('tiddlyLink',parts[0]);
	if (parts[1]) btn.setAttribute('section',parts[1]); // save section
	return btn;
}
//}}}
/***
!!!!!onClickTiddlerLink
***/
//{{{
window.onClickTiddlerLink_sectionanchor=window.onClickTiddlerLink;
window.onClickTiddlerLink=function(ev) {
	var e=ev||window.event;	var target=resolveTarget(e); var title=null;
	while (target!=null && title==null) {
		title=target.getAttribute('tiddlyLink');
		section=target.getAttribute('section');
		target=target.parentNode;
	} 
	var t=story.findContainingTiddler(target); var tid=t?t.getAttribute('tiddler'):'';
	if (title!=tid||!section) onClickTiddlerLink_sectionanchor.apply(this,arguments); // avoid excess scrolling
	story.scrollToSection(title,section);
	return false;
}
//}}}
/***
!!!!!displayTiddler
***/
//{{{
Story.prototype.displayTiddler_sectionanchor=Story.prototype.displayTiddler;
Story.prototype.displayTiddler = function(srcElement,tiddler)
{
	var title=(tiddler instanceof Tiddler)?tiddler.title:tiddler;
	var parts=title.split(config.textPrimitives.sectionSeparator);
	if (!parts[0].length || parts[0].toLowerCase()=='here') {
		var t=story.findContainingTiddler(place);
		parts[0]=t?t.getAttribute('tiddler'):'';
	}
	arguments[1]=parts[0];  // default=current tiddler
	this.displayTiddler_sectionanchor.apply(this,arguments);
	story.scrollToSection(parts[0],parts[1]);
}
//}}}
/***
!!!!!isExternalLink
***/
//{{{
config.formatterHelpers.isExternalLink_sectionanchor=config.formatterHelpers.isExternalLink;
config.formatterHelpers.isExternalLink=function(link) {
	if (link.indexOf(config.textPrimitives.sectionSeparator)!=-1) return false;
	return config.formatterHelpers.isExternalLink_sectionanchor.apply(this,arguments);
}
//}}}
/***
!!!!!tiddler.handler
***/
//{{{
config.macros.tiddler.handler_section=config.macros.tiddler.handler;
config.macros.tiddler.handler=function(place,macroName,params,wikifier,paramString,tiddler)
{
	if (!params[0]) return;
	var sep=config.textPrimitives.sectionSeparator;
	var parts=params[0].split(sep); var tid=parts[0]; var sec=parts[1];
	if ((tid.toLowerCase()=='here'||!tid.length) && sec) { // fixup for 'here##section' and '##section'
		var here=story.findContainingTiddler(place)
		var tid=here?here.getAttribute('tiddler'):tiddler?tiddler.title:'';
		arguments[2][0]=tid+sep+sec;
		arguments[4]=paramString.replace(new RegExp('(here)?'+sep+sec),tid+sep+sec);
	}
	config.macros.tiddler.handler_section.apply(this,arguments);
}
//}}}
/***
!!!!sectionTOC macro
***/
//{{{
config.macros.sectionTOC = {
	targetClass: 'sectionTOC',
	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		var out=[];
		var targetClass=params[0]||this.targetClass;
		var t=story.findContainingTiddler(place); if (!t) return;
		var elems=t.getElementsByTagName('*');
		var level=5; // topmost heading level
		for (var i=0; i<elems.length; i++) {
			var link='[['+getPlainText(elems[i])+'|##'+getPlainText(elems[i])+']]';
			switch(elems[i].nodeName) {
				case 'H1': out.push('#'+link);		level=1; break;
				case 'H2': out.push('##'+link);		level=level<2?level:2; break;
				case 'H3': out.push('###'+link);	level=level<3?level:3; break;
				case 'H4': out.push('####'+link);	level=level<4?level:4; break;
				case 'H5': out.push('#####'+link);	level=level<5?level:5; break;
				default: if (hasClass(elems[i],targetClass)) var target=elems[i];
			}
		}
		// trim excess bullet levels
		if (level>1) for (var i=0; i<out.length; i++) out[i]=out[i].substr(level-1);
		// show numbered list
		if (out.length && target) {
			if (target.style.display=='none') target.style.display='block';
			wikify(out.join('\n'),target);
		}
	}
}
//}}}
/***
!!!Invoke macro
{{{
<<sectionTOC>>
}}}
***/
// //<<sectionTOC>>
!Select By Location in ArcGIS
Basically, in ArcGIS / ArcMap lingo, you’re selecting a feature (our block boundary polygons) that intercept or contain elements from another feature (our point data layer).
# Go to the //Select// menu, and choose ''Select by Location''.
# In the dialogue, first select the data layer that contains the polygons you’d like to select (select features FROM the following layer).
# Chose ''Intersect'' in the drop-down, followed by your points layer in the //Features from this layer// box.
<<closeAll>><<permaview>><<newTiddler>><<newJournal "DD MMM YYYY" "journal">><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel "options »" "Change TiddlyWiki advanced options">>
/***
|''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 personal wiki notebook
~AshWiki
http://calmweb.calm.wa.gov.au/drb/rsd/LandUsePlanning/index.htm
TiddlyWiki defines a small number of SpecialTags that are used to indicate that tiddlers should be treated differently in some way:
* ''excludeSearch'': excludes a tiddler from search results.
* ''excludeLists'': excludes a tiddler from the lists in the sidebar tabs.
* ''systemConfig'': marks tiddlers that contain JavaScript that should be executed once TiddlyWiki has loaded.
* ''excludeMissing'': excludes a tiddler from the processing that generates the MissingTiddlers list. Use it when you have a tiddler that contains links to missing tiddlers and you don't want those missing links to appear in the MissingTiddlers list.
/*{{{*/
#contentWrapper {font-size: 11px; line-height: 1.6em; color: #666; font-family: Lucida Grande, Tahoma, Arial, Helvetica, sans-serif;}
  /* Lucida Grande for the Macs, Tahoma for the PCs */
/*border:1px dashed #000; */

.header {background: #FFF; clear: both; border-bottom: 4px solid #948979;}
/*.headerShadow {border:1px dotted #000; position:relative; color:#000000; padding:4.5em 0 1em 1em; left:-1px; top:-1px;}*/
/*.headerForeground {border:1px dotted #000; position:absolute; padding:4.5em 0 1em 1em; left:0px; top:0px;}*/

.searchBar {text-align:right; font-size: 1em;}
.searchBar input {background-color: #FFF; color: #999999; border: 1px solid #CCC;}

.siteLogo {}

.siteTitle {font-size: 3em; color: #CC6633; padding-top:0.5em; padding-bottom:0.5em;}
.siteTitle a {color:#CC6633; border-bottom:1px dotted #cc6633;}

.siteSubtitle {font-size: 1.0em; display: block; margin: .5em 3em; color: #999999;}

#mainMenu {position:relative; float:left; margin-bottom:1em; display:inline; text-align:left; padding: 2em 0.5em 0.5em 0em; width:13em; font-size:1em;}

#sidebar {position:relative; float:right; margin-bottom:1em; padding-top:2em; display:inline;}

#displayArea { margin: 0em 17em 0em 15em; }

.tagClear { clear:none; }

#siteFooter {background:#575352; color:#BFB6B3; clear: both; padding: 0.5em 1em; }
#siteFooter a {color: #BFB6B3; border-bottom: 1px dotted #BFB6B3;}
#siteFooter a:hover {color: #FFFFFF; background-color:#575352;}

a,#sidebarOptions .sliderPanel a {
	color:#CC6714;
	text-decoration: none;
	}

a:hover,#sidebarOptions .sliderPanel a:hover {
	color:#CC6714;
	background-color: #F5F5F5; 
	}

.viewer .button, .editorFooter .button {
	color: #666;
	border: 1px solid #CC6714;
	}

.viewer .button:hover, 

.editorFooter .button:hover {
	color: #fff;
	background: #CC6714;
	border-color: #CC6714;
	}

.viewer .button:active, .viewer .highlight, .editorFooter .button:active, .editorFooter .highlight {
	color:#fff; 
	background:#575352;
	border-color:#575352;
	}

#mainMenu a {
	display: block;
	padding: 5px;
	border-bottom: 1px solid #CCC;
	}

#mainMenu a:link, #navlist a:visited {
	color:#CC6714;
	text-decoration: none;
	}
	
#mainMenu a:hover {
	background: #000000 url(arrow.gif) 96% 50% no-repeat;
	background-color: #F5F5F5;
	color:#CC6714;
	}
	
#mainMenu a:hover, #mainMenu a:active, #mainMenu .highlight, #mainMenu .marked {
	background: #000000 url(arrow.gif) 96% 50% no-repeat;
	background-color: #F5F5F5;
	color:#CC6714;
	}

#mainMenu span { position:relative; }

#mainMenu br { display:none; }

#sidebarOptions a {
	color:#999;
	text-decoration: none;
	}

#sidebarOptions	a:hover {
	color:#4F4B45;
	background-color: #F5F5F5;border:1px solid #fff;
	}

#sidebarOptions { line-height:1.4em; }

.tiddler {
	padding-bottom: 40px;
	border-bottom: 1px solid #DDDDDD; 
	}

.title {color:#CC6633;}

.subtitle, .subtitle a {
	color:#999999; 
	font-size:0.8em; 
	margin:0.2em;
	}

.shadow .title { color:#948979; }

.selected .toolbar a { color:#999999; }

.selected .toolbar a:hover {
	color:#4F4B45; 
	background:transparent;
	border:1px solid #fff;
	}

.toolbar .button:hover, .toolbar .highlight, .toolbar .marked, .toolbar a.button:active {
	color:#4F4B45; 
	background:transparent;
	border:1px solid #fff;
	}

.listLink, #sidebarTabs .tabContents { line-height:1.5em; }

.listTitle { color:#888; }

#sidebarTabs .tabContents { background:#fff; }

#sidebarTabs .tabContents .tiddlyLink, #sidebarTabs .tabContents .button { color:#999; }

#sidebarTabs .tabContents .tiddlyLink:hover,#sidebarTabs .tabContents .button:hover { color:#4F4B45;background:#fff }

#sidebarTabs .tabContents .button:hover, #sidebarTabs .tabContents .highlight, #sidebarTabs .tabContents .marked, #sidebarTabs .tabContents a.button:active { 
	color:#4F4B45;
	background:#fff 
	}

.tabSelected { color:#fff; background:#948979; }

.tabUnselected { background: #ccc; }

.tabSelected, .tabSelected:hover {
	color: #fff;
	background: #948979;
	border: solid 1px #948979;
	padding-bottom:1px;
	}

.tabUnselected {
	color: #999;
	background: #eee;
	border: solid 1px #ccc;
	padding-bottom:1px;
	}

#sidebarTabs .tabUnselected { 
	border-bottom:none;
	padding-bottom:3px;
	}

#sidebarTabs .tabSelected { padding-bottom:3px; }

#sidebarTabs .tabUnselected:hover { 
	border-bottom:none;
	padding-bottom:3px;
	color:#4F4B45
	}

#sidebarOptions .sliderPanel {
	background:#fff; 
	border:none;
	font-size: .9em;
	}

#sidebarOptions .sliderPanel a { font-weight:normal; }

#sidebarOptions .sliderPanel input {border:1px solid #999;}

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

.viewer table { border: 2px solid [[ColorPalette::TertiaryDark]]; }

.viewer th, thead td {
	background: #948979;
	border: 1px solid #948979;
	color: #fff;
	}

.viewer pre {
	border: 1px solid #948979;
	background: #f5f5f5;
	}

.viewer code {
	color: #2F2A29;
	}

.viewer hr {
	border-top: dashed 1px #948979;
	}

.editor input, .editor textarea {font-family: 'Consolas' monospace; border: 1px solid #948979;}

.popup {
	background: #948979;
	border: 1px solid #948979;
	}

.popup li.disabled {
	color: #000;
	}

.popup li a, .popup li a:visited {
	color: #eee;
	border: none;
	}

.popup li a:hover {
	background: #575352;
	color: #fff;
	border: none;
	}

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

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

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

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

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

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

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

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

h1, h2, h3, h4, h5 { 
	color: #666; 
	background: transparent; 
	padding-bottom: 2px; 
	font-family: Arial, Helvetica, sans-serif;
	}

h1 {font-size: 18px;}

h2 {font-size: 14px;}

h3 {font-size: 11px;}

#messageArea {
	border: 4px solid #948979;
	background: #f5f5f5;
	color: #999;
	font-size:90%;
	}

#messageArea a:hover { background:#f5f5f5; }

#messageArea .button {
	color: #666;
	border: 1px solid #CC6714;
	}

#messageArea .button:hover {
	color: #fff;
	background: #948979;
	border-color: #948979;
	}


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

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

#sidebarOptions .button:active, #sidebarOptions .highlight { background:#F5F5F5; }

*html #contentFooter { padding:0.25em 1em 0.5em 1em;}

#noticeBoard {
	font-size: 0.9em; 
	color:#999; 
	position:relative;
	display:block;
	background:#fff; 
	clear: both;
	margin-right:0.5em; 
	margin-top:60px; 
	padding:5px; 
	border-bottom: 1px dotted #CCC; 
	border-top: 1px dotted #CCC;
	}

#mainMenu #noticeBoard a,#mainMenu #noticeBoard .tiddlyLink {
	display:inline;
	border:none;
	padding:5px 2px;
	color:#DF9153
	}

#noticeBoard a:hover {border:none;}	

#noticeBoard br {display:inline;}

#mainMenu #noticeBoard .button {
	color: #666;
	border: 1px solid #DF9153;
	padding:2px;
	}

#mainMenu #noticeBoard .button:hover {
	color: #fff;
	background: #DF9153;
	border-color: #DF9153;
	}




/*}}}*/
/***
This fixes a problem with the tabs slider.
***/
/*{{{*/
#sidebarTabs .button {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
/*}}}*/
/***
This is a sample style definition to demonstrate CustomCssClass formatting.
***/
/*{{{*/
.wrappingClass {color: #666; background: #bbb;}
[[ChunkyButtonStyle]]
/*}}}*/
/*{{{*/
.siteLogo {border:1px dotted #000; float:right;}
.searchBar {float:right; border:1px dotted #000; font-size: 1em;}
.searchBar input {background-color: #FFF; color: #999999; border: 1px solid #CCC;}
/*}}}*/
This is an example of a tab:

<<tabs txtFavourite
One "First tab" HelloThere
Two "Second tab" ThankYou
>>
The TaggingMacro produces a list of links to tiddlers that carry the specified tag. If no tag is specified, it looks for tiddlers tagged with the name of the current tiddler. It looks like this:
{{{
<<tagging>>
<<tagging TiddlerTitle>>
<<tagging sep:[[, ]]>>
}}}
In HTML, the list is formatted like so:
{{{
<ul>
<li class="listTitle">List title label</li>
<li><a class="tiddlyLink ..." href="javascript:;" onclick="..."
	   refresh="link" tiddlyLink="ExampleOne">ExampleOne</a></li>
</ul>
}}}
The optional {{{sep}}} parameter specifies a string of characters to be inserted as a separator between each {{{<li>}}} element. In conjunction with the CSS {{{ul li {display: none;} }}} this allows the tagging list to be formatted as a nicely formatted inline list.
!Django installation version
Open an interactive interpreter, and type:
{{{
>>> import django
>>> django.VERSION
(1, 1, 0, 'final', 1)
}}}

!Test database settings
To test the db settings in your {{{settings.py}}} file, start an interactive shell session (see above) and run:
{{{
>>> from django.db import connection
>>> cursor = connection.cursor()
}}}
If there are no error messages, the settings are configured correctly.
TiddlerSlicing allows you to use special notation to pull out a chunk of specially marked text from within a tiddler. Each slice has a name and a value which can be specified anywhere within a tiddler in any of these formats:
{{{
    theName:  textSlice
    |theName:| textSlice |
    |theName| textSlice |
}}}
The name may contain any of the characters "a-ZA-Z_0-9", and may also be decorated with {{{''}}} or {{{//}}} markers for ''bold'' and //italic// formatting that are ignored. For example:
{{{
    |''theName:''| textSlice |
}}}
Slices can be then be referenced by qualifying the parent tiddler name with the symbols "::" and the name of the slice. For example:
{{{
ColorPalette::PrimaryLight
}}}
>TiddlerSlicing does not work everywhere; at this point, it is mainly intended to support the [[ColorPalette]] and similar usages.

!Retrieval 
There are two basic way to retrieve tiddler slices: 

* Using the {{{[[Tiddler::Slice]]}}} notation (only works in tiddlers evaluated by TiddlyWiki, e.g. PageTemplate) 
* Using the tiddler macro: {{{<<tiddler [[tiddlerName::sliceName]]>>}}}

!Examples
{{{
|''theName:''| textSlice |
}}}
Slices can then be referenced by qualifying the parent tiddler name with the symbols :: and the name of the slice: 
{{{
<<tiddler [[theTiddler::theName]]>>
}}}
[[ColorPalette]] example:
{{{
<<tiddler [[ColorPalette::PrimaryPale]]>>
}}}

Finally, here's an example of some more complex slice formatting:
{{{
version: 1.2.3.4
Author: Joe Brown
''Credits:'' ASmith BBrown CCalony
//~SeeAlso:// The rise and fall of the M-perium
|!Name|!Value|
|Name:|TextSlice Tester|
|URL:|http:\\sample.com\TestSliced |
|''Type:''| Plugin |
|//Source//| http:\\sample.com\TestSliced\src\text.js |
}}}
The slices defined in that example are:
|version|1.2.3.4|
|Author|Joe Brown|
|Credits|ASmith BBrown CCalony|
|~SeeAlso|The rise and fall of the M-perium|
|Name|TextSlice Tester|
|URL|http:\\sample.com\TestSliced|
|Type|Plugin|
|Source|http:\\sample.com\TestSliced\src\text.js|
/***
!Metadata:
|''Name:''|TiddlyCalendarPlugin|
|''Description:''|Tiddlers Calendar and Date picker|
|''Version:''|1.0.0|
|''Date:''|Nov 21, 2007|
|''Source:''|http://sourceforge.net/project/showfiles.php?group_id=150646|
|''Author:''|BramChen (bram.chen (at) gmail (dot) com)|
|''License:''|[[Creative Commons Attribution-ShareAlike 3.0 License]]|
|''~CoreVersion:''|2.2.3|
|''Browser:''|Firefox 1.5+; InternetExplorer 6.0|
|''Optional''|DatePlugin|
!Usage:
{{{<<tCalendar [year [month [count]]]>>}}}
{{{<<tCalendar [last [n] | next [n] {year(s) | month(s)}]>>}}}
eg,
{{{<<tCalendar>>}}},
{{{<<tCalendar 2007 10 3>>}}},
{{{<<tCalendar thisyear>>}}},
{{{<<tCalendar last year>>}}},
{{{<<tCalendar next 4 months>>}}}
{{{<<tCalendar lastModified>>}}}
!Revision History:
|''Version''|''Date''|''Note''|
|1.0.1|Dec 20, 2007|Added parameter 'lastModified' control for showing the month calendar including the last modified tiddler|
|1.0.0|Nov 21, 2007|Initial release|
!Code section:
***/
//{{{
version.extensions.tCalendar = {major: 1, minor: 0, revision: 0, date: new Date("Nov 21, 200")};

//# Calendar object
function Calendar()
{
	this.styles = Calendar.styles;

	this.callback = {
		fn: null,
		fnEnable: false,
		option: null,
		params: {date:null, title:null, celldate:null, cellClass:null, dateFmt:null}
	};
	return this;
};

Calendar.locale = 'en';
Calendar[Calendar.locale] = {
	dates: {
		days: ["Su", "M", "Tu", "W", "Th", "F", "Sa"],
		yearFmt: "YYYY",
		monthFmt: "mmm YYYY",
		dateFmt: "MMM DD, YYYY",
		longHolidayFmt: "0DD/0MM/YYYY",
		shortHolidayFmt: "0DD/0MM",
		startOfWeek: 0, /* 0,1*/
		weekends: [true, false, false, false, false, false, true], /* Default: 0 (Sun) and 6 (Sa) are true*/
		holidays: [] /* using short (or long) holidayFmt*/
	}
};

Calendar.styles = '/*{{{*/'
		+ '\ntable.calendarWrapper {border-collapse:collapse; border:2px solid #c6dbff;}'
		+ '\n.calendarWrapper td {border-collapse:collapse; border:1px solid #c6dbff; text-align:center;margin:0; padding:0 0.05em;}'
/*		+ '\ntable.calendar {border-collapse:separate; border:1px solid #c6dbff;}'*/
		+ '\ntable.calendar {border-collapse:collapse; border:0;}'
		+ '\n.calendar tbody, .calendar th, .calendar td, .calendar tr {border:0; text-align:center; font-size:1em; padding:0 0.1em;}'
		+ '\n.calendar th {color:#000; background-color:#c6dbff;}'
		+ '\n#sidebarOptions .calendar td {font-size:0.96em; margin:0; padding:0;}'
		+ '\n#sidebarTabs .calendar td, #mainMenu .calendar td  {padding:0 0.25em;}'
		+ '.calendar .naviBar select {border:0;}'
		+ '\n.calendar .today a {padding:0; border:1px solid blue;}'
		+ '\n.calendar .weekend {background-color:#deeeff;}'
		+ '\n.calendar .hasChanged {font-family:bold; background-color:#fe8; color:darkblue;}'
		+ '\n.calendar .holiday {font-weight:bold; font-size:1.06em; color:red;}'
		+ '\n.datePopup {background:#efffff;}'
		+ '\n.datePopup .isCreated {color:#df6300;}'
		+ '\n.datePopup .isExcluded {filter:alpha(opacity=60); -moz-opacity:0.6; opacity:0.6;}'
/*		+ '.viewer .calendarOptios {display:block;}'*/
		+ '\n/*}}}*/';

Calendar.prototype.getLocale = function()
{
	var locale = config.options.txtLocale ? config.options.txtLocale : Calendar.locale;
	return Calendar[locale] ? locale : 'en';
};

Calendar.prototype.show = function(place, year, month, count)
{
	this.locale=this.getLocale();
	var y, m = new Date().getMonth()+1, c = 1;
	c = isNaN(count) ? (isNaN(year) && isNaN(month) ? c : (!isNaN(year) && isNaN(month) ? 12 : c)) : parseInt(count);
	m = isNaN(month) ? (isNaN(year) ? m : (isNaN(month) ? 1 : parseInt(month))) : parseInt(month);
	y = isNaN(year) ? new Date().getFullYear() : parseInt(year);

	this.dateFmt = (this.callback.params.dateFmt) ? this.callback.params.dateFmt : Calendar[this.locale].dates.dateFmt;

	for (var i=0; i<c; i++){
		var firstDate = new Date(y,m-1+i,1);
		if ((m+i)%12 == 1 || i==0){
			var wrapper = createTiddlyElement(place,'table',null,'calendarWrapper');
			var tbody = createTiddlyElement(wrapper,'tbody');
			if (c > 1 && m==1 && c%12==0){
				this.naviBar(wrapper,tbody,firstDate,true);
			}
		}
		if (i%3 == 0)
			var tr = createTiddlyElement(tbody,'tr',null,'monthRow');
		var td = createTiddlyElement(tr,'td');
		td.vAlign = "top";

		this.selectMonth(td, firstDate);
	}
	if (c>3)
		Calendar.dummyDateCell(tr,(3-c%3)%3,2);
};

Calendar.prototype.selectMonth = function(place, firstDate)
{
	var year = firstDate.getFullYear();

	var calElm = createTiddlyElement(place,'table',null,'calendar');
	var monElm = createTiddlyElement(calElm,'tbody');

	this.naviBar(place,monElm,firstDate);

	if (store.isDirty() || !Calendar.tiddlers)
		Calendar.hashTiddlers(firstDate);

	this.showMonth(monElm, firstDate);
};

Calendar.prototype.naviBar = function(place,monElm,firstDate,isYearView)
{
	var cal = this;
	var monthHeader = createTiddlyElement(createTiddlyElement(monElm,'tr'),'td',null, 'naviBar', null, {colSpan:7});

	var _selectMonthHandler = function(s,date,isYearView){
		if (isYearView){
			cal.show(s.parentNode,date.getFullYear(),1,12);
			removeNode(s);
		} else {
			cal.selectMonth(s, date,cal.dateFmt);
			removeNode(s.firstChild);
		}
	};

	var onchange = function(ev){
		var e = ev ? ev : window.event;
		var date=null;
		for (var i=0, options=this.options; i<this.options.length; i++){
			if (options[i].selected)
				date = new Date(options[i].value);
		}
		_selectMonthHandler.call(this,place,date,isYearView);
		return false;
	};

	var year = firstDate.getFullYear();
	var n = 3;
	var y = isYearView ? year - n : year;
	var m = isYearView ? 0 : new Date(firstDate).getMonth()- n;
	var c = null;
	var options = [];
	var fmt = isYearView ? Calendar[this.locale].dates.yearFmt : Calendar[this.locale].dates.monthFmt;
	for (var i=0; i<n*2+1; i++){
		c= isYearView ? new Date(y+i,1,1) : new Date(y,m+i,1); 
		options.push({caption: c.formatString(fmt), name: c});
	}

	var sel=createTiddlyDropDown(monthHeader,onchange,options,n);
	sel.selectedIndex = n;
};

Calendar.prototype.showMonth = function(monElm, firstDate)
{
	var year = new Date(firstDate).getFullYear();
	var month = new Date(firstDate).getMonth()+1; 
	var lastDate = new Date(year,month,0).getDate();
	var nextFirstDay = new Date(year,month,1).getDay();

	var offset = (7 + firstDate.getDay() - Calendar[this.locale].dates.startOfWeek)%7;
	var	dayHearder = createTiddlyElement(monElm,'tr');
	for (var i=0, ii=0, text=null; i<7; i++){
		ii = (Calendar[this.locale].dates.startOfWeek + i)%7;
		text = Calendar[this.locale].dates.days[ii];
//#		createTiddlyElement(theParent,theElement,theID,theClass,theText,attribs)
		createTiddlyElement(dayHearder,'th',null,null,text); 
	}

	var d=1, dayRow=null, celldate=null, isWeekend = false;
	while (d<=lastDate){
		dayRow = createTiddlyElement(monElm,'tr');

		if (offset > 0)
			Calendar.dummyDateCell(dayRow,offset,6);

		for (var i=offset; i<7 && d <= lastDate; i++, d++){
			celldate = new Date(year, month-1, d);
			isWeekend = Calendar[this.locale].dates.weekends[(i + Calendar[this.locale].dates.startOfWeek)%7];
			this.showDate(dayRow, d, celldate, isWeekend);
		}

		offset=0;
	}
	var n = 7 - (7 + nextFirstDay - Calendar[this.locale].dates.startOfWeek)%7;
	if (n < 7)
		Calendar.dummyDateCell(dayRow,n,6);
};

Calendar.prototype.showDate = function(dayRow, date, celldate, isWeekend)
{
	var now = new Date();
	var dateFmt = this.dateFmt;
	var today = now.formatString(dateFmt);
	var cellClass = 'dateCell';
	var title = celldate.formatString(dateFmt);
	var day = celldate.getDay();
	var isToday = today == title;
	var isHoliday = this.isHoliday(celldate);

	if (isToday)
		cellClass += ' today';
	if (isWeekend)
		cellClass += ' weekend';
	if (isHoliday) 
		cellClass += ' holiday';

	var dateCell=createTiddlyElement(dayRow,'td',null,cellClass);

	var ymd = celldate.convertToLocalYYYYMMDDHHMM().substr(0,8);
	var callback = this.callback;
	var option = callback.option;
	if (!option){
		if (Calendar.tiddlers[ymd]){
			cellClass += ' hasChanged';
			option = 'popup';
		} else {
			option = 'displayTiddler';
		}
	}

	var params = callback.params;
	merge (params,{date:date, title:title, celldate:celldate, cellClass:cellClass, dateFmt:dateFmt});
	if (callback.fn instanceof Function && callback.fnEnable){
		callback.fn(dateCell, params);
	} else
		Calendar.optionHandler(dateCell, option, params, celldate);
};

Calendar.prototype.isHoliday = function(date) 
{
	return Calendar[this.locale].dates.holidays.containsAny([
		date.formatString(Calendar[this.locale].dates.longHolidayFmt),
		date.formatString(Calendar[this.locale].dates.shortHolidayFmt)]);
};

Calendar.dummyDateCell = function (srcElm,n,max)
{
	for (var i=0; i< n && i<max; i++)
		createTiddlyElement(srcElm,'td');
};

Calendar.hashTiddlers = function(date)
{
	if (date)
		var ymd = date.convertToLocalYYYYMMDDHHMM().substr(0,8);
	var isChanged = false;
	var tiddlers = {};

	store.forEachTiddler(function(title, tiddler){
		var modified = tiddler.modified.convertToLocalYYYYMMDDHHMM().substr(0,8);
		var created = tiddler.created.convertToLocalYYYYMMDDHHMM().substr(0,8);

//		if (modified.substr(0,6) == ymd.substr(0,6)){
//			var dd = modified.substr(6,2);
			var extraCellClass = '';
			extraCellClass += (tiddler.modified == tiddler.created) ? ' isCreated' : '';
			extraCellClass += tiddler.isTagged("excludeLists") ? ' isExcluded' : '';
			if (!tiddlers[modified])
				tiddlers[modified]=[];
			tiddlers[modified].push({title:tiddler.title, modified:tiddler.modified, ymd:modified, extraCellClass:extraCellClass});
//		}
	});

	this.tiddlers = tiddlers;
};

Calendar.optionHandler = function(srcElm,option,params,celldate)
{
	var fn = Calendar.optionType[option];
	var action = function(ev) {
		var e = ev ? ev : window.event;
			var fn = Calendar.optionType[option];
		if (fn instanceof Function)
			fn.call(this,e,srcElm,params);
		return false;
	};
//#	createTiddlyButton(parent,text,tooltip,action,className,id,accessKey,attribs);
	createTiddlyButton(srcElm, params.date, params.title, action, params.cellClass, null,null,params);
};

Calendar.optionType = {
	displayTiddler: function(e) {
		story.displayTiddler(null,this.title)
	},
	popup: function(e,daetCell) {
		var celldate = new Date(this.getAttribute('celldate'));
		Calendar.onClickDatePopup(e,daetCell,this.title,celldate);
	},
	pickDate: function(e) {
		Calendar.pickDate.call(this,e);
	}
};

Calendar.onClickDatePopup = function (ev,detaCell,title,celldate)
{
	var e = ev ? ev : window.event;
	if (store.isDirty())
		Calendar.hashTiddlers(celldate);

	var ymd = celldate.convertToLocalYYYYMMDDHHMM().substr(0,8);
	var tiddlers = Calendar.tiddlers[ymd];
	var popup = Popup.create(detaCell,null,'datePopup popup');
	createTiddlyElement(popup,'br');
	this.optionHandler(popup, "displayTiddler", {title:title, date:title});

	if (tiddlers){
		createTiddlyElement(popup,'hr');
		for (var i=0; i<tiddlers.length; i++)
			this.optionHandler(createTiddlyElement(popup,"li"), "displayTiddler",{date:tiddlers[i].title, title:tiddlers[i].title, cellClass:tiddlers[i].extraCellClass});
	}
	Popup.show();
	e.cancelBubble = true;
	if(e.stopPropagation) e.stopPropagation();
	return false;
};

Calendar.pickDate = function(ev)
{
	var e = ev ? ev : window.event;
	var inputId = this.getAttribute('inputId');
	if (inputId){
		var input = document.getElementById(inputId);
		if (input)
			input.value = this.title;
	}
	return false;
};
//}}}
/***
!Initialize Calendar
***/
//{{{
config.shadowTiddlers.CalendarStyle = Calendar.styles;
config.notifyTiddlers.pushUnique({name: 'CalendarStyle', notify: refreshStyles});
var calendar = new Calendar(); /* Create global Calendar object */
var datepicker = new Calendar(); /*Create global Date picker */
//}}}
/***
!Macros
***/
//{{{

config.macros.tCalendar = {
	init: function(){
		var fnEnable = config.options.chkCalendarCallback == undefined ? false : config.options.chkCalendarCallback;
		calendar.callback = {
			fn: this.showDate,
			fnEnable: (window.showDate instanceof Function && fnEnable),
			option: null,
			params: {date: null, dateFmt:null, celldate:null}
		};
	}
};

config.macros.tCalendar.handler = function(place,macroName,params)
{
	this.init();
	var mode = params[2] ? params[2] : (params[1] ? params[1] : params[0]);
	var modeType = params[0];
	var modeCount = isNaN(params[1]) ? 1 : parseInt(params[1]);

	var now = new Date();
	var y = now.getFullYear();
	var m = now.getMonth()+1;
	var c = isNaN(params[1]) ? 1 : parseInt(params[1]);
	switch (mode) {
		case 'month':
		case 'months':
			m = modeType == 'last' ? m - c : m + 1;
			break;
		case 'thisyear':
			m = 1;
			c = 12;
			break;
		case 'year':
		case 'years':
			y = modeType == 'last' ? y - c : y + 1;
			m = 1;
			c = 12 * c;
			break;
		case 'lastModified':
			var lastModified = this.getlastModified();
			y = lastModified.getFullYear();
			m = lastModified.getMonth()+1;
			c = 1;
			break;
		default:
			y = params[0];
			m = params[1];
			c = params[2];
	}
	calendar.show(place,y,m,c);
};

config.macros.tCalendar.showDate = function(dateCell,params)
{
	var isWeekend = (params.cellClass.indexOf('weekend') != -1);
//# For co-working with showDate() of DatePlugin
//#	showDate(place,date,mode,format,linkformat,autostyle,weekend)
	window.showDate(dateCell, params.celldate,'popup','DD',params.dateFmt,true,isWeekend);
};

config.macros.tCalendar.getlastModified = function()
{
	var tiddlers = store.reverseLookup("tags","excludeLists",false,'modified');
	return tiddlers[tiddlers.length-1].modified;
};

config.macros.datePicker = {
	onClick: function(ev) {
		var e = ev ? ev : window.event;
		var inputId = this.getAttribute("inputId");
		var dateFmt = this.getAttribute("dateFmt");
		dateFmt = dateFmt == 'null' ? null : dateFmt; /* For Opera */
		datepicker.callback = {
			fn: null,
			fnEnable: false,
			option: 'pickDate',
			params: {inputId:inputId, dateFmt:dateFmt}
		};

		var popup = Popup.create(this);
		datepicker.show(popup);
		Popup.show();

		e.cancelBubble = true;
		if(e.stopPropagation) e.stopPropagation();
		return false;
	}
};

config.macros.datePicker.handler = function(place,macroName,params)
{
	if (!params) return;
	var id = params[0];
	var dateFmt = params[1] ? params[1] : null;
	var pickParams = {inputId:id, dateFmt:dateFmt};

	var inputElm = createTiddlyElement(place,'input',id);
	var btn = createTiddlyButton(place, '?', 'Date Picker', this.onClick,'datepicker',null,null,pickParams);
};
//}}}
/***

|Name|ToggleSideBarMacro|
|Created by|SaqImtiaz|
|Location|http://tw.lewcid.org/#ToggleSideBarMacro|
|Version|1.0|
|Requires|~TW2.x|
!Description:
Provides a button for toggling visibility of the SideBar. You can choose whether the SideBar should initially be hidden or displayed.

!Demo
<<toggleSideBar "Toggle Sidebar">>

!Usage:
{{{<<toggleSideBar>>}}} <<toggleSideBar>>
additional options:
{{{<<toggleSideBar label tooltip show/hide>>}}} where:
label = custom label for the button,
tooltip = custom tooltip for the button,
show/hide = use one or the other, determines whether the sidebar is shown at first or not.
(default is to show the sidebar)

You can add it to your tiddler toolbar, your MainMenu, or where you like really.
If you are using a horizontal MainMenu and want the button to be right aligned, put the following in your StyleSheet:
{{{ .HideSideBarButton {float:right;} }}}

!History
*23-07-06: version 1.0: completely rewritten, now works with custom stylesheets too, and easier to customize start behaviour. 
*20-07-06: version 0.11
*27-04-06: version 0.1: working.

!Code
***/
//{{{
config.macros.toggleSideBar={};

config.macros.toggleSideBar.settings={
         styleHide :  "#sidebar { display: none;}\n"+"#contentWrapper #displayArea { margin-right: 1em;}\n"+"",
         styleShow : " ",
         arrow1: "«",
         arrow2: "»"
};

config.macros.toggleSideBar.handler=function (place,macroName,params,wikifier,paramString,tiddler)
{
          var tooltip= params[1]||'toggle sidebar';
          var mode = (params[2] && params[2]=="hide")? "hide":"show";
          var arrow = (mode == "hide")? this.settings.arrow1:this.settings.arrow2;
          var label= (params[0]&&params[0]!='.')?params[0]+" "+arrow:arrow;
          var theBtn = createTiddlyButton(place,label,tooltip,this.onToggleSideBar,"button HideSideBarButton");
          if (mode == "hide")
             { 
             (document.getElementById("sidebar")).setAttribute("toggle","hide");
              setStylesheet(this.settings.styleHide,"ToggleSideBarStyles");
             }
};

config.macros.toggleSideBar.onToggleSideBar = function(){
          var sidebar = document.getElementById("sidebar");
          var settings = config.macros.toggleSideBar.settings;
          if (sidebar.getAttribute("toggle")=='hide')
             {
              setStylesheet(settings.styleShow,"ToggleSideBarStyles");
              sidebar.setAttribute("toggle","show");
              this.firstChild.data= (this.firstChild.data).replace(settings.arrow1,settings.arrow2);
              }
          else
              {    
               setStylesheet(settings.styleHide,"ToggleSideBarStyles");
               sidebar.setAttribute("toggle","hide");
               this.firstChild.data= (this.firstChild.data).replace(settings.arrow2,settings.arrow1);
              }

     return false;
}

setStylesheet(".HideSideBarButton .button {font-weight:bold; padding: 0 5px;}\n","ToggleSideBarButtonStyles");

//}}}
The ToolbarMacro is used in the TiddlerTemplateMechanism to define the toolbar that appears when the mouse is hovered over a tiddler. It looks like this:
{{{
<<toolbar closeTiddler -editTiddler +jump>>
}}}
The arguments to the ToolbarMacro is a list of command names, as discussed in the CommandMechanism. The available commands are tagged <<tag commands>>.

You can precede a command name with a "+" to specify a default command that is automatically chosen when a tiddler is double-clicked, or the ctrl-Enter key combination pressed. Similarly, precede it with "-" to specify a command to be chosen when the Escape key is pressed.
<<showtoc>>
!Ruby Constants 
A Ruby constant is used to store a value for the duration of a Ruby program's execution. Constants are declared by beginning the variable name with a capital letter (a common convention for declaring constants is to use uppercase letters for the entire name). For example: 

{{{
MYCONSTANT = "hello"
=> "hello"
}}}

Unlike other programming languages, Ruby actually allows the value assigned to a constant to be changed after it has been created. The Ruby interpreter will, however, issue a warning - even though it allows the change: 

{{{
MYCONSTANT = "hello2"
(irb):34: warning: already initialized constant Myconstant
=> "hello2"
}}}

!Ruby and Variable Dynamic Typing 
Many languages such as Java and C use what is known as strong or static variable typing. This means that when you declare a variable in your application code you must define the variable type. For example if the variable is required to store an integer value, you must declare the variable as an integer type. With such languages, when a varaible has been declared as a particular type, the type cannot be changed. 

Ruby, on the other hand, is a dynamically typed language. This has a couple of key advantages. Firstly it means that you do not need to declare a type when creating a variable. Instead, the Ruby interpreter looks at the type of value you are assigning to the variable and dynamically works out the variable type. Another advantage of this is that once a variable has been declared, you can dynamically change the variable type later in your code. 

!Declaring a Variable
Variables are declared and assigned values by placing the variable name and the value either side of the assignment operator (=). For example, to assign a value of 10 to a variable we will designate as "y" we would write the following: 

{{{
y = 10
}}}

We have now created a variable called y and assigned it the value of 10. 

In common with some other scripting languages, Ruby supports something called parallel assignment. This is useful if you need to assign values to a number of variables. One way to do this would be as follows: 

{{{
a = 10
b = 20
c = 30
d = 40
}}}

The same result can be achieved more quickly, however, using parallel assignment: 

{{{
a, b, c, d = 10, 20, 30, 40
}}}

!Identifying a Ruby Variable Type 
Once a Ruby variable has been declared it can often be helpful to find out the variable type. This can be achieved using the kind_of? method of the Object class. For example, to find out if our variable is an Integer we can use the kind_of? method: 

{{{
y.kind_of? Integer
=> true
}}}

We can also ask the variable exactly what class of variable it is using the class method: 


{{{
y.class
=> Fixnum
}}}

This tells us that the variable is a fixed number class. 

Similarly, we could perform the same task on a string variable we will call s: 

{{{
s = "hello"
s.class
=> String
}}}

Here we see that the variable is of type String. 

!Changing Variable Type 
One of simplest ways to change the type of a variable is to simply assign a new value to it. Ruby will dynamically change the type for that variable to match the type of the new value assigned. For example, we can create a variable containing an integer and verify the type: 

{{{
x = 10
=> 10
x.class
=> Fixnum
}}}

Suppose, we now want to store a string in a variable named 'x'. All we need to do is make the assignment, and Ruby will change the variable type for us: 

{{{
x = "hello"
=> "hello"
x.class
=> String
}}}

As we can see, the x variable is now a string. 

!Converting Variable Values 
It is important to note that the above approach is a somewhat destructive way to change a variable type. Often something a little more subtle is needed. For example, we might want to read the value from a variable but convert the extracted value to a different type. The Ruby variable classes have methods that can be called to convert their value to a different type. For example, the Fixnum class has a method named to_f that can be used to retrieve the integer stored in a variable as a floating point value: 

{{{
y = 20
=> 20
y.to_f
=> 20.0
}}}

Similarly, you can convert a Ruby integer to a string using the to_s() method. The to_s() method takes as an argument the number base to which you want the conversion made. If no number base is specified, decimal is assumed: 

{{{
54321.to_s
=> "54321"
}}}

Alternatively, we could convert to binary by specifying a number base of 2: 

{{{
54321.to_s(2)
=> "1101010000110001"
}}}

Or even to hexadecimal or octal: 

{{{
54321.to_s(16)
=> "d431"
54321.to_s(8)
=> "152061"
}}}

In fact, we can use any number base between 1 and 36 when using the to_s method.
See also [[VIM Tutorial]].
!Cursor movement
|!h |move left|
|!j |move down|
|!k |move up|
|!l |move right|
|!w |jump by start of words (punctuation considered words)|
|!W |jump by words (spaces separate words)|
|!e |jump to end of words (punctuation considered words)|
|!E |jump to end of words (no punctuation)|
|!b |jump backward by words (punctuation considered words)|
|!B |jump backward by words (no punctuation)|
|!0 (zero) |start of line|
|!^ |first non-blank character of line|
|!$ |end of line |

!Insert mode
|!i |start insert mode at cursor|
|!I |insert at the beginning of the line|
|!a |append after the cursor|
|!A |append at the end of the line |
|!o |open (append) blank line below current line (no need to press return)|
|!O |open blank line above current line|
|!Esc |exit insert mode |

!Using Tabs
Help using tabs:
{{{
:help tab-page-intro
}}}
Open a new tab:
{{{
:tabnew
:tabedit <filename>
:tabe file.txt
}}}
Show a list of tabs:
{{{
:tabs
}}}
This shows a ">" for the current window, and a "+" for modifed buffers.

Move between tabs:
{{{
:tabnext
:tabn
:tabprevious
:tabp
:tabfirst
:tabfir
:tablast
:tabl
}}}
In normal mode:
{{{
g t
g T
}}}
Rearrange tabs (numbering is zero-based):
{{{
:tabmove 0
:tabm 1
}}}
Close tabs:
{{{
:q
:tabclose
:tabc
:tabonly
}}}

!Access VimTutor
{{{
vimtutor
}}}
All images sourced at http://www.viemu.com

!Lesson 1 - Basic editing
[img[Basic editing|files\vi-vim-tutorial-1.gif]]

!Lesson 2 - Operators and repetition
[img[Vim basic editing|files\vi-vim-tutorial-2.gif]]

!Lesson 3 - Yank (copy) and paste
[img[Yank and paste|files\vi-vim-tutorial-3.gif]]

!Lesson 4 - Searching
[img[Searching|files\vi-vim-tutorial-4.gif]]

!Lesson 5 - Marks and macros
[img[Marks and macros|files\vi-vim-tutorial-5.gif]]

!Lesson 6 - Motions
[img[Motions|files\vi-vim-tutorial-6.gif]]

!Lesson 7 - Various commands
[img[Various commands|files\vi-vim-tutorial-7.gif]]
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer'>
 <span macro="tiddler ReplaceDoubleClick"></span>
 <div macro='view text wikified'></div>
</div>
<div class='tagClear'></div>
<!--}}}-->
/***
|''Name:''|XHTML10Plugin|
|''Version:''|1.0.1 (2006-09-16)|
|''Source:''|http://tiddlywiki.abego-software.de/#XHTML10Plugin|
|''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-2006 [[abego Software|http://www.abego-software.de]]|
|''~CoreVersion:''|2.1.0|
|''Browser:''|Firefox 1.5.0.2 or better; Internet Explorer 6.0|

Make your ~TiddlyWiki XHTML 1.0 compliant format.

Once the plugin is installed the existing tiddlers of that TiddlyWiki are automatically converted to the new (XHTML 1.0 compliant) format on the first save. After that all changes are stored in the XHTML format.

!Source Code
***/
//{{{
// Ensure the Plugin is only installed once.
//
if (!version.extensions.XHTML10Plugin) {

if (version.major < 2 || (version.major == 2 && version.minor < 1)) {
	(function() {
		var s = "Use TiddlyWiki 2.1 or better to run the XHTML10Plugin.";
		alert(s);
		throw s;
	})();
}

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

// Ensure the global abego namespace is set up.
if (!window.abego) window.abego = {};


//--------------------------------
// XHTML10Saver (inherits from SaverBase)

abego.XHTML10Saver = function() {};

abego.XHTML10Saver.prototype = new SaverBase();

abego.XHTML10Saver.prototype.externalizeTiddler = function(store, tiddler) {
	try {
		var s = '';
		store.forEachField(tiddler, 
			function(tiddler, fieldName, value) {
				// don't store stuff from the temp namespace
				if (!fieldName.match(/^temp\./)) {
					if (value)
						value = value.htmlEncode();
					s += ['<pre title="',fieldName,'">',value,'</pre>'].join("");
				}
			});
		return ['<div title="',tiddler.title.htmlEncode(),'">',s,'</div>'].join("");

	} catch (e) {
		showException(e, config.messages.tiddlerSaveError.format([tiddler.title]));
		return '';
	}
};

abego.XHTML10Saver.prototype.externalize = function(store) {
	return ["<div class='twXHTML10'>\n",SaverBase.prototype.externalize.apply(this, arguments),"\n</div>"].join("");
};


//--------------------------------
// Overwrite TiddlyWiki.prototype.getSaver to use the XHTML10 format on save

TiddlyWiki.prototype.getSaver = function() {
	if (!this.saver) 
		this.saver = new abego.XHTML10Saver();
	return this.saver;
};

//======================================
// Install the Loader into the HTML page

(function() {
	// The loader code will be inserted into the PostHead markup block,
	// so it can be executed before tiddlers are loaded. We cannot just put this
	// code into a normal plugin since this "load" code is required to load
	// tiddlers. I.e. this code must be executed before any tiddlers/plugins
	// can be loaded.

	var getXHTML10LoaderBlock = function() {
		// The loader code in a big JavaScript string.
		// You may get a non-stringified version of the XHTML10Loader source code at
		// http://tiddlywiki.abego-software.de/archive/XHTML10Plugin/XHTML10Loader.1.0.1.js

		XHTML10LoaderCode = 
			"if (!window.abego) window.abego = {};\nif (!abego.XHTML10Loader) {\n\t//-"+
			"-------------------------------\n\t// abego.XHTML10Loader (inherits from"+
			" LoaderBase)\n\t\n\tabego.XHTML10Loader = function() {};\n\tabego.XHTML10Loa"+
			"der.prototype = new LoaderBase();\n\t\n\tabego.XHTML10Loader.prototype.lin"+
			"go = {\n\t\tunnamedValue: \"Unnamed value\",\n\t\tredefining: \"Redefining valu"+
			"e of %0\",\n\t\tnoXHTML10Format: \"Storage not in XHTML 1.0 format\"\n\t}\n\t\n\ta"+
			"bego.XHTML10Loader.prototype.getTitle = function(store, e) {\n\t\tvar tit"+
			"le = null;\n\t\tif(e.getAttribute)\n\t\t\ttitle = e.getAttribute('title');\n\t\t"+
			"if(!title && e.id) {\t\n\t\t\tvar lenPrefix = store.idPrefix.length;\n\t\t\tif "+
			"(e.id.substr(0,lenPrefix) == store.idPrefix)\n\t\t\t\ttitle = e.id.substr(l"+
			"enPrefix);\n\t\t}\n\t\treturn title;\n\t};\n\t\n\tabego.XHTML10Loader.prototype.in"+
			"ternalizeTiddler = function(store, tiddler, title, data) {\n\t\tvar field"+
			"s = {};\n\t\tvar elems = data.childNodes;\n\t\tfor(var i = 0; i < elems.leng"+
			"th; i++) {\n\t\t\tvar e = elems[i];\n\t\t\tvar name = e.getAttribute('title');"+
			"\n\t\t\tif (!name) \n\t\t\t\tthrow this.lingo.unnamedValue;\n\t\t\tif (fields[name]"+
			" !== undefined) \n\t\t\t\tthrow this.lingo.redefining.format([name]);\n\t\t\tfi"+
			"elds[name] = getNodeText(e.firstChild); \n\t\t}\n\t\n\t\t// Extract (and remov"+
			"e) the standard fields from the extended fields\n\t\tvar text = fields.te"+
			"xt;\n\t\tvar modifier = fields.modifier;\n\t\tvar modified = Date.convertFro"+
			"mYYYYMMDDHHMM(fields.modified);\n\t\tvar c = fields.created;\n\t\tvar create"+
			"d = c ? Date.convertFromYYYYMMDDHHMM(c) : modified;\n\t\tvar tags = field"+
			"s.tags;\n\t\tdelete fields.modifier;\n\t\tdelete fields.modified;\n\t\tdelete f"+
			"ields.created;\n\t\tdelete fields.tags;\n\t\tdelete fields.text;\n\t\tdelete fi"+
			"elds.title;\n\t\n\t\ttiddler.assign(title,text,modifier,modified,tags,creat"+
			"ed,fields);\n\t\t\n\t\treturn tiddler;\n\t};\n\t\n\tvar findRootNode = function(no"+
			"des) {\n\t\tif (nodes) {\n\t\t\t// skip leading text nodes\n\t\t\tfor (var i = 0;"+
			" i < nodes.length; i++)\n\t\t\t\tif (nodes[i].nodeType != 3)\n\t\t\t\t\tbreak;\n\t\t"+
			"\t\t\t\n\t\t\tif (i < nodes.length && nodes[i].className == 'twXHTML10')\n\t\t\t\t"+
			"return nodes[i];\n\t\t}\n\t};\n\t\n\tabego.XHTML10Loader.prototype.loadTiddlers"+
			" = function(store,nodes) {\n\t\t// in the twXHMTL10 format all tiddler el"+
			"ements are contained in one enclosing DIV\n\t\t// that contains the forma"+
			"t information\n\t\tvar root = findRootNode(nodes)\n\t\tif (!root) \n\t\t\tthrow "+
			"this.lingo.noXHTML10Format;\n\t\treturn LoaderBase.prototype.loadTiddlers"+
			".apply(this, [store, root.childNodes]);\n\t};\n\t\n\t\n\t//-------------------"+
			"-------------\n\t// Hijack the loadFromDiv\n\t(function() {\n\t\tvar origTidd"+
			"lyWikiLoadFromDiv = TiddlyWiki.prototype.loadFromDiv;\n\t\tTiddlyWiki.pro"+
			"totype.loadFromDiv = function(srcID,idPrefix) {\n\t\t\t// use the XHTML 1."+
			"0 loader when the storearea is in 'twXHTML10' format,\n\t\t\t// otherwise "+
			"use the default loader\n\t\t\tvar e = document.getElementById(srcID);\n\t\t\ti"+
			"f (e && findRootNode(e.childNodes))\n\t\t\t\tthis.loader = new abego.XHTML1"+
			"0Loader();\n\t\t\treturn origTiddlyWikiLoadFromDiv.apply(this, arguments);"+
			"\n\t\t};\n\t})();\n}\n\n";
		return '<'+'script type="text/javascript">\n//<![CDATA[\n'+XHTML10LoaderCode+'\n//]]>\n</script'+'>\n';
	};

	var insertLoaderBlock = function() {
		if (!store)
			throw "XHTML10LoaderInstaller must run as a plugin";
			
		var START = "<!--XHMTL10Loader-START-->";
		var END = "<!--XHMTL10Loader-END-->";
		
		var postHeadText = store.getTiddlerText("MarkupPostHead");
		if (postHeadText.getChunk(START, END)) 
			return; // already installed

		postHeadText += "\n"+START+getXHTML10LoaderBlock()+END+"\n";
		var tiddler = store.getTiddler("MarkupPostHead");
		var tags = tiddler ? tiddler.tags : [];
		store.saveTiddler("MarkupPostHead","MarkupPostHead",postHeadText,config.options.txtUserName,new Date(),tags);
		alert("XHTML10Loader installed.\nPlease save and reload your TiddlyWiki to complete the installation. After that your TiddlyWiki will be stored in an XHTML 1.0 compliant format.");
	};
	
	insertLoaderBlock();														
})();

} // of single install

//}}}
/***
!Metadata:
|''Name:''|XMLReader2Plugin|
|''Description:''||
|''Version:''|2.2.2|
|''Date:''|Jul 30, 2008|
|''Source:''|http://sourceforge.net/project/showfiles.php?group_id=150646|
|''Author:''|BramChen (bram.chen (at) gmail (dot) com)|
|''License:''|[[Creative Commons Attribution-ShareAlike 2.5 License]]|
|''~CoreVersion:''|2.2.0|
|''Browser:''|Firefox 1.5+; InternetExplorer 6.0|
|''Required:''|[[NestedSlidersPlugin|http://www.tiddlytools.com/#NestedSlidersPlugin]] is required for using the parameter "asHtml".|
!Syntax:
{{{<<rssfeed withDesc|noDesc|asHtml rssfeed.xml|http://www.example.com/rssfeed.rdf>>}}}
!Revision History:
|''Version''|''Date''|''Note''|
|2.2.2|Jul 30, 2008|Fix bug for link format of title of tiddlers|
|2.2.1|May 29, 2008|Improve isCrossSite()|
|2.2.0|May 19, 2007|Atom feeds suppported|
|2.1.1|May 15, 2007|Fixed cache bug|
|2.1.0|May 10, 2007|Fixed bugs:<br>1.missing parameter 'responseText' of processResponse<br>2.Caches failed|
|2.0.0|Mar 08, 2007|Required TW 2.2.0+|
|1.5.0|Mar 04, 2007|Codes reworked, more easier reused|
|1.2.0|Jul 20, 2006|Runs compatibly with TW 2.1.0 (rev #403+)|
|1.1.0|Jul 10, 2006)|change xmlhttp.send(null)/send() to xmlhttp.send("") for more compatibility for some browsers|
|1.0.0|Mar 11, 2006|Initial release|
|~|~|This macro is reworked from RssNewsMacro, but it can be easy to extended to support different structure of xml document from rss feeds|
|~|~|You could uninstall the RssNewsMacro, but still use the original syntax,<br>{{{<<rssfeed  withDesc|noDesc|asHtml "rssfeed.xml"|"http://www.example.com/rssfeed.rdf">>}}}|

!Code section:
***/
//{{{
version.extensions.xmlreader = {major: 2, minor: 2, revision: 2,
	date: new Date("Jul 30, 2008"),
	name: "XMLReader2",
	type: "Macro",
	author: "BramChen",
	source: "http://sourceforge.net/project/showfiles.php?group_id=150646"
};

config.messages.XmlReader = {
	fromCache: "^^(//from cache//)^^",
	errorInDataRetriveing: "Problem retrieving XML data: %0",
	invalidXML: "Invalid XML retrieved from: %0",
	urlNotAccessible: "Access to %0 is not allowed,\nPlease check the setting of your browser:\n1.For Gecko based, you should set the 'signed.applets.codebase_principal_support' to be true, in about:config.\n2.For IE, you should add this web site to your trust list."
};

function XmlReader(place,withDesc,xmlURL) {
	this.xmlhttp = null;
	this.place = place;
	this.xmlURL = xmlURL;
	this.withDesc = withDesc;
	this.itemStructure = {title:'Title',link:'Link',pubDate:'PubDate',description:'Desc'};
	this.atomStructure = {title:'Title',id:'Link',updated:'Updated',summary:'Desc'};
//	this.rsTemplate = function(){var t='';for (var i in itemStructure){t+='_'+itemStructure[i]}};
	this.rsTemplate = '_pubDate\n**[[_title|_link]]_description';
	this.items = {Elm: "%0Elm", Text: "_%0"};
	this.keyItem = "item";
	this.dateFormat = "DDD, DD MMM YYYY";
	this.groupBy = null;
	return this;
};

XmlReader.prototype.asyncGet = function(xmlURL,callback){
	if(window.Components && window.netscape && window.netscape.security && this.isCrossSite(xmlURL)){
		try {netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");}
		catch (ex) {displayMessage(ex.description?ex.description:ex.toString());}
	}
//	return doHttp("GET",xmlURL,null,'text/xml',null,null,callback);
	var r = httpReq("GET",xmlURL,callback,null,null,null,'text/xml');
	return r = r || null;
};

XmlReader.prototype.genLists = function(xml){
	var itemStructure;
	if (xml.lastChild.nodeName == 'feed'){
		this.keyItem = 'entry';
		itemStructure = this.atomStructure;
	}
	else {
		itemStructure = this.itemStructure;
	}
	var itemList = xml.getElementsByTagName(this.keyItem);
	var items = this.items;
	var rsLists='', rssItem; this.groupBy='';
	for (var i=0; i<itemList.length; i++){
		var itemElms=[],itemTexts=[];
		var rsTemplate=this.rsTemplate;
		for (var j in itemStructure){
			var itemElm = items.Elm.format([j]);
			var itemText = items.Text.format([j]);
			itemElms[itemElm] = itemList[i].getElementsByTagName(j).item(0);
			if(itemElms[itemElm]){
				var theTitle = itemStructure[j];
				var theText = (itemElms[itemElm].firstChild)?itemElms[itemElm].firstChild.nodeValue:'';
				rsTemplate=this.convertTemplate(rsTemplate,j,theText);
			}
			else {
				rsTemplate = rsTemplate.replace('_'+j, '');
			}
		}
		rsLists += rsTemplate;
	}
	return rsLists;
};
	
XmlReader.prototype.convertTemplate = function(rsTemplate,j,theText){
	switch (j){
		case 'title':
			rsTemplate = rsTemplate.replace(/_title/,theText.replace(/\[|\]/g,''));
			break;
		case 'id':
			j = 'link';
		case 'link' || 'id':
			rsTemplate = rsTemplate.replace('_'+j, encodeURI(theText));
			break;
		case 'updated':
			j = 'pubDate';
		case 'pubDate':
			theText = this.dateFormatString(this.dateFormat, theText);
			if (this.groupBy == theText){
				rsTemplate = rsTemplate.replace('_'+j, '');
			}
			else{
				rsTemplate = rsTemplate.replace('_'+j, '\n* '+theText);
				this.groupBy = theText;
			}
			break;
		case 'summary':
			j = 'description';
		case 'description':
			var regexpDesc = new RegExp("withDesc|asHtml","g");
			if (regexpDesc.exec(this.withDesc)  && theText){
				var _description = theText.replace(/\n/g,' ');
					_description =_description.replace(/<br \/>/ig,'\n');
				if (version.extensions.nestedSliders){
					_description = ((this.withDesc == "asHtml")?"<html>"+_description+"</html>":_description);
					rsTemplate = rsTemplate.replace('_'+j,'+++[...]'+_description+'\n===\n');
				}
				else {
					rsTemplate = rsTemplate.replace('_'+j,_description+'\n');
				}
			}
			else {
				rsTemplate = rsTemplate.replace('_'+j,'');
			}
			break;
	}
	return (rsTemplate);
};

XmlReader.prototype.dateFormatString = function(template, theDate){
	theDate = theDate.replace(/-/g,'/').replace(/T.*UT|T.*Z/,'');
	var dateString = new Date(theDate);
	template = template.replace(/hh|mm|ss/g,'');
	return dateString.formatString(template);
};

XmlReader.prototype.isCrossSite = function (url){
	var result = false;
	var curLoc = document.location;
	if (url.indexOf(":") != -1 && curLoc.protocol.indexOf("http") != -1) {
		var m = /(\w+):\/\/([^/:]+)(:\d*)?([^# ].*$)/.exec(url);
		result = (curLoc.protocol == m[1] && curLoc.host == m[2] && curLoc.port == m[3]);
	}
	return (!result);
};

//}}}
/***
!Macro rssfeed
***/
//{{{
config.macros.rssfeed = {
	cache: {},
	dateFormat: "YYYY-0MM-0DD"
};

config.macros.rssfeed.handler = function(place,macroName,params){
	var withDesc = params[0];
	var xmlURL = params[1];
	var rss = new XmlReader(place,withDesc,xmlURL);
	rss.dateFormat = this.dateFormat;

	var processResponse = function(status,params,responseText,xmlURL,x){
		if (window.netscape){
			if (rss.isCrossSite(xmlURL)){
				try {netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");}
				catch (e) { displayMessage(e.description?e.description:e.toString()); }
			}
		}
		if (x && x.responseXML){
			xmlURL = xmlURL.replace(/[\?|\&]nocache.*/,'').replace(/[^0-9a-zA-Z]/mg,"_");
			config.macros.rssfeed.cache[xmlURL] = x;
			wikify(rss.genLists(x.responseXML),place);
		}
		else {
			wikify("<html>"+ x.responseText +"</html>", place);
			displayMessage(config.messages.XmlReader.invalidXML.format([xmlURL]));
		}
	};
	if (this.cache[xmlURL]) {
		wikify(config.messages.XmlReader.fromCache,place);
		var status = false;
		var x=this.cache[xmlURL];
		processResponse(status,null,x.responseText,xmlURL,x);
	}
	else {
		rss.xmlhttp = rss.asyncGet(xmlURL, processResponse);
	}
};
//}}}
!Sample TagCloud:
<<tagCloud about>>

!About TagCloud
This plugin was created by [[Clint Checketts]] list the current tags in alphabetical order and displays the more popular tags larger.

!How to use TagCloud
After intalling the [[tagCloud plugin]], in any tiddler call the tagCloud macro:
{{{
<<tagCloud>>
}}}
Any tags listed after the macro name will be excluded from the tag listing:
{{{
<<tagCloud systemTiddlers systemConfig>>
}}}
The previous code listing will excluded the systemTiddlers and systemConfig tags from the listing.
/***
|''Name:''|tagCloud plugin|
|''Version:''|1.0.2 (2007-08-21)|
|''Source:''|http://checkettsweb.com/styles/themes.htm|
|''Author:''|Clint Checketts|
|''Contributors:''|Jonny Leroy, Eric Shulman, Piotr Likus|
|''Licence:''|[[?/BSD open source license|
|''~CoreVersion:''|2.1.0|
|''Browser:''|Firefox 1.0.4+; Firefox 1.5; ~InternetExplorer 6.0|
!Parameters
* reqOrTags - tags that are required to be assigned to tiddlers that are source for tag cloud (''any'')
* reqAndTags - tags that are required to be assigned to tiddlers that are source for tag cloud (''all'' are required)
* exTags - tags that are excluded
* (unnamed param) - tags that are excluded (mask with '*' is allowed, e.g. '*Project')
* reqTids - list of names for tiddlers that are source for tag cloud (''any'')
* italics - if equal to "yes" tags will be italic if no tiddler is found with a title equal to tag name

!Usage
{{{<<tagCloud>>}}}
<<tagCloud>>
or (require Plugin or PluginDoc tags, exclude PluginExamples tags and any PluginEx* tags):

{{{<<tagCloud reqOrTags:'Plugin,PluginDoc' exTags:'PluginExamples' PluginEx*>>
}}}
<<tagCloud reqOrTags:'Plugin,PluginDoc' exTags:'PluginExamples' PluginEx*>>

{{{<<tagCloud reqTids:'*Plugin' italics:"yes">>
}}}
<<tagCloud reqTids:'*Plugin' italics:"yes">>

!Source Code
***/
//{{{
version.extensions.tagCloud = {major: 1, minor: 0 , revision: 1, date: new Date(2005,8,16)};
//Created by Clint Checketts, contributions by Jonny Leroy and Eric Shulman
//Modified by PiotrL, 2007/07/29 - filter for excluded tags

config.macros.tagCloud = {
 noTags: "No tag cloud created because there are no tags.",
 tooltip: "%1 tiddlers tagged with '%0'"
};

config.macros.tagCloud.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
 
var tagCloudWrapper = createTiddlyElement(place,"div",null,"tagCloud",null);

var parameters = paramString.parseParams("name",null,true);
// excluded
var exTags = parameters[0]["exTags"]?parameters[0]["exTags"][0].split(","):[];
// required
var reqOrTags = parameters[0]["reqOrTags"]?parameters[0]["reqOrTags"][0].split(","):[];
var reqAndTags = parameters[0]["reqAndTags"]?parameters[0]["reqAndTags"][0].split(","):[];
var reqTids = parameters[0]["reqTids"]?parameters[0]["reqTids"][0].split(","):[];
var useItalics = parameters[0]["italics"]?parameters[0]["italics"] == "yes":false;

//testFunc('italics: '+parameters[0]["italics"]);

var tags = store.getTags();
var foundCnt = 0;

for (t=0; t<tags.length; t++) {
  for (p=0;p<params.length; p++) 
  {
    if (matchesMask(tags[t][0], params[p])) 
      tags[t][0] = "";
  }
  for (p=0;p<exTags.length; p++) 
  {
    if (matchesMask(tags[t][0], exTags[p])) 
      tags[t][0] = "";
  }
  foundCnt = 0;
  for (p=0;p<reqOrTags.length; p++) 
  {
    if (tagAssignedWith(tags[t][0], reqOrTags[p])) 
      foundCnt++;
  }
  if (!foundCnt && reqOrTags.length)
    tags[t][0] = "";  

  foundCnt = 0;
  for (p=0;p<reqAndTags.length; p++) 
  {
    if (tagAssignedWith(tags[t][0], reqAndTags[p])) 
      foundCnt++;
  }
  if (foundCnt != reqAndTags)
    tags[t][0] = "";  

  foundCnt = 0;
  for (p=0;p<reqTids.length; p++) 
  {
    if (tagAssignedToMask(tags[t][0], reqTids[p])) 
      foundCnt++;
  }
  if (!foundCnt && reqTids.length)
    tags[t][0] = "";  
}

 if(tags.length == 0) 
   createTiddlyElement(tagCloudWrapper,"span",null,null,this.noTags);
 //Findout the maximum number of tags
 var mostTags = 0;
 for (t=0; t<tags.length; t++) if (tags[t][0].length > 0){
  if (tags[t][1] > mostTags) mostTags = tags[t][1];
 }
 //divide the mostTags into 4 segments for the 4 different tagCloud sizes
 var tagSegment = mostTags / 4;

  for (t=0; t<tags.length; t++) if (tags[t][0].length > 0){
 var tagCloudElement = createTiddlyElement(tagCloudWrapper,"span",null,null,null);
 tagCloudWrapper.appendChild(document.createTextNode(" "));
 var tagClassName = "tagCloud"+(Math.round(tags[t][1]/tagSegment)+1);
 if (useItalics && !store.tiddlerExists(tags[t][0]))
   tagClassName += "It";  
 var theTag = createTiddlyButton(tagCloudElement,tags[t][0],this.tooltip.format(tags[t]),onClickTag,"tagCloudtag "+tagClassName);
  theTag.setAttribute("tag",tags[t][0]);
 }

  function testFunc(txt) {
    alert(txt);
  }

  function matchesMask(txt, mask) {
    var idx, prefix, suffix, res;

    res = false;

    if (mask.indexOf('*') >= 0) 
    {
      sToken = mask;
      idx = sToken.indexOf('*');
      if (idx > 0)   
        prefix = sToken.substring(0, idx - 1);
      else
        prefix = '';  
      suffix = sToken.substring(idx+1, sToken.length);

      if ((txt.indexOf(prefix) == 0) || (prefix == ''))
        if ((txt.indexOf(suffix) == txt.length - suffix.length) || (suffix == ''))
          res = true;
    }
    else if (txt == mask) 
        res = true;

    return(res);
  }

  function tagAssignedWith(tag1, tag2) {
    //var tiddlerNames = store.reverseLookup("tags",tag1,false,"title");
    var res;
    var collected = [];
    var foundTids = 0;

    store.forEachTiddler( function ( title,tiddler ) {
      if (tiddler.isTagged(tag1) && tiddler.isTagged(tag2)) {
        collected.push(title );
        foundTids++;
      } 
    }
    ); 
    
    if (foundTids>0)
      res = true;
    else
      res = false;
    return res;
  }

  function tagAssignedToMask(tag, tidMask) {
    var res;
    var collected = [];
    var foundTids = 0;

    store.forEachTiddler( function ( title,tiddler ) {
      if (tiddler.isTagged(tag) && matchesMask(title, tidMask)) {
        collected.push(title);
        foundTids++;
      } 
    }
    ); 
    
    if (foundTids>0)
      res = true;
    else
      res = false;
    return res;
  }
};

setStylesheet(
".tagCloud span{height: 1.8em;margin: 3px;}"+
".tagCloud1{font-size: 1.2em;}"+
".tagCloud2{font-size: 1.4em;}"+
".tagCloud3{font-size: 1.6em;}"+
".tagCloud4{font-size: 1.8em;}"+
".tagCloud5{font-size: 1.8em;font-weight: bold;}"+
".tagCloudIt span{height: 1.8em;margin: 3px;font-style: italic;}"+
".tagCloud1It{font-size: 1.2em;font-style: italic;}"+
".tagCloud2It{font-size: 1.4em;font-style: italic;}"+
".tagCloud3It{font-size: 1.6em;font-style: italic;}"+
".tagCloud4It{font-size: 1.8em;font-style: italic;}"+
".tagCloud5It{font-size: 1.8em;font-weight: bold;font-style: italic;}"+
"",
"tagCloudsStyles");
//}}}
OK, here is another bone headed mistake I made that I will post about in hopes of helping other poor souls who run across the problem. This morning I decided to play around a bit with the RedCloth library that comes with Ruby 1.8 on Mac OS X 10.5 Leopard. So I wrote a simple program in Ruby and tried to run it. Every time I did I got:
{{{
uninitialized constant RedCloth (NameError)
}}}

And here is my program if you are interested: 
{{{
#!/usr/bin/ruby
require 'rubygems'
require 'RedCloth'
doc = RedCloth.new "h2. test header

just some text

another para" 
puts doc.to_html
}}}

Pretty simple. (RedCloth, by the way, is a processor for Textile. Kind of a simple yet powerful way to mark up text like you would in a wiki.) Well I know the RedCloth Gem is in my path and is installed. I know my code should work, but it doesn't. What is the problem?

Turns out I unfortunately named my test file "redcloth.rb". Turns out the Gem file is named "~RedCloth.rb" and is in the path and can be executed from the command line. If you don't know, OS X is case insensitive like Windows XP, so those names are equivalent. Thusly, Ruby faithfully tried to load my local script file, which doesn't define any of the needed objects. Therefore, the error. Naming the file "rctest.rb" fixes the problem.

So I hope this little note helps someone else out in the future. So if you get a: "uninitialized constant XXXXXX (~NameError)", make sure your file name isn't the same as a the Gem file.