Background: #fff
Foreground: #000
PrimaryPale: #8cf
PrimaryLight: #18f
PrimaryMid: #04b
PrimaryDark: #014
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
/*{{{*/
body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

hr {height:1px;}

a {text-decoration:none;}

dt {font-weight:bold;}

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

.txtOptionInput {width:11em;}

#contentWrapper .chkOptionInput {border:0;}

.externalLink {text-decoration:underline;}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/
/***
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which use a logographic writing system and need larger font sizes.
***/

/*{{{*/
body {font-size:0.8em;}

#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}

.subtitle {font-size:0.8em;}

.viewer table.listView {font-size:0.95em;}

.htmlarea .toolbarHA table {border:1px solid ButtonFace; margin:0em 0em;}
/*}}}*/
/*{{{*/
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none ! important;}
#displayArea {margin: 1em 1em 0em 1em;}
/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
noscript {display:none;}
}
/*}}}*/
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler > fields syncing permalink references jump'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='edit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
<!--}}}-->
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* MainMenu: The menu (usually on the left)
* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: <<option txtUserName>>
These InterfaceOptions for customising TiddlyWiki are saved in your browser

Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)

<<option txtUserName>>
<<option chkSaveBackups>> SaveBackups
<<option chkAutoSave>> AutoSave
<<option chkRegExpSearch>> RegExpSearch
<<option chkCaseSensitiveSearch>> CaseSensitiveSearch
<<option chkAnimate>> EnableAnimations

----
Also see AdvancedOptions
! Purchases
* Doggie Stuff
* S-Video cable
* Black & Decker firestorm set [[B&D Website| http://www.blackanddecker.com/ProductGuide/CategoryOverview.aspx?cPath=1496.1502#1496.1502.1532]]
** [[2-for-1 @ Lowes| http://forums.slickdeals.net/showthread.php?sduid=128895&t=529957]]
My name is Tom Nichols.  I am a software developer and live with my wife in Rochester, NY.  

Other points of interest:
* [[My Resume | http://docs.google.com/Doc?id=dfcz5rn4_4cscn2j]]
* [[LinkedIn Profile | http://www.linkedin.com/pub/0/6aa/47b]]
* [[Facebook profile | http://rit.facebook.com/profile.php?id=24411634]]
* [[Flickr photos | http://flickr.com/photos/tom_and_stacie/]]
<<newTiddler>>
<<newJournal 'DD MMM YYYY'>>
<<saveChanges>>
<<upload http://medidarcade.tiddlyspot.com/store.cgi index.html backup>>
[[Import]]
<<slider chkSliderOptionsPanel OptionsPanel 'options ยป' 'Change TiddlyWiki advanced options'>>
^^TW v.<<version>>^^
Hot on the heels of Microsoft's [[ announcement of Silverlight | http://www.dailytech.com/article.aspx?newsid=6924 ]], Adobe fires back by announcing they will release Flex under an open source license.  I think this is big news, largely because web technologies that utilize traditional HTML/CSS/JavaScript will be less relevant as rich media become more common.  I'm not talking about AJAX; I'm talking about "AJAX 2.0," if there was such a thing.  

Up to now, Flash has been number one for delivering [[multimedia | http://www.youtube.com]] and [[http://finance.google.com/finance?q=JANSX| rich UIs]]).  MS is challenging that.  With Flex becoming open source, it won't fall behind, and will provide a slightly less proprietary solution to Microsoft's offerings.  [[HTML 5 | http://annevankesteren.nl/2005/04/html5]] and SVG are still a long way off from providing any standard 'rich application' environment.  Flex, being already proven, sounds like the next best thing.  

[[ Adobe Flex goes Open Source | http://labs.adobe.com/wiki/index.php/Flex:Open_Source ]]
<<option chkGenerateAnRssFeed>> GenerateAnRssFeed
<<option chkOpenInNewWindow>> OpenLinksInNewWindow
<<option chkSaveEmptyTemplate>> SaveEmptyTemplate
<<option chkToggleLinks>> Clicking on links to tiddlers that are already open causes them to close
^^(override with Control or other modifier key)^^
<<option chkHttpReadOnly>> HideEditingFeatures when viewed over HTTP
<<option chkForceMinorUpdate>> Treat edits as MinorChanges by preserving date and time
^^(override with Shift key when clicking 'done' or by pressing Ctrl-Shift-Enter^^
<<option chkConfirmDelete>> ConfirmBeforeDeleting
Maximum number of lines in a tiddler edit box: <<option txtMaxEditRows>>
Folder name for backup files: <<option txtBackupFolder>>

[[UploadOptions]]
[[AirCombat | http://aircombatusa.com/]] is a program where basically you get to fly a high-performance airplane in simulated dogfights, with no previous flight experience.  Oh yeah.  

This is what I did on my summer vacation.

[img[AirCombat | http://farm2.static.flickr.com/1153/553713876_5c9723f90d.jpg]]

* [[Video | http://video.google.com/videoplay?docid=-1777825159473053046&hl=en]] (sorry it's kinda long...)
* [[Photos | http://www.flickr.com/photos/tom_and_stacie/sets/72157600391528312/]]
Stuff other people wrote that I like... Isn't that what all blogs are??

!! [[Raible's thoughts on Grails | http://raibledesigns.com/rd/entry/introduction_to_grails_with_scott]]
Here is an interesting post by someone who really seems to understand the Java web space.  

!! [[ A word on performance... | http://www.codinghorror.com/blog/archives/000957.html ]]
I admit I am guilty of this.  Micro-benchmarks suck.

!! [[ Java Urban performance legends | http://www.ibm.com/developerworks/java/library/j-jtp04223.html]]
This article is a Java "classic", if there is such a thing.

!! [[ How to Do it Right | http://highscalability.com/youtube-architecture]]
Overview of YouTube's HW and SW architecture.

!! [[Groovy 1.0 Released | http://www.osnews.com/comment.php?news_id=16862]]

This is absolutely fantastic.  Here's hoping more Java developers 'see the light' of dynamic languages.  

!! [[ 10 Eclipse Navigation Shortcuts | http://rayfd.wordpress.com/2007/05/20/10-eclipse-navigation-shortcuts-every-java-programmer-should-know/]]

!! [[ More New I/O APIs for Java | http://www.artima.com/lejava/articles/more_new_io.html]]
We should see it in Java 7 -- about time!  File change listeners are long overdue.

!! [[ Annotation-Driven Dependency Injection in Spring 2.1 | http://www.theserverside.com/news/thread.tss?thread_id=45421]]

!! [[ Interview with Google CEO Eric Schmidt | http://blog.wired.com/business/2007/04/my_other_interv.html]]
''Excellent'' insight into leadership, thought and culture of a leading technology company.

!! [[Singletons, sans Synchronization | http://www.oreillynet.com/onjava/blog/2007/01/singletons_and_lazy_loading.html]]
My first thought was, "no wait, that won't work..  Wait...  Yeah I guess it does!"  Clever! 
''Update:''  See [[ this article | http://www-128.ibm.com/developerworks/java/library/j-dcl.html]] describing why the traditional synchronized/ null check //doesn't work!//

!! [[Free eBook - Getting Started with Grails | http://www.infoq.com/minibooks/grails]]
Dynamic, fast, and painless web application development for Java.

!! [[Book Excerpt: Groovy in Action | http://www.theserverside.com/tt/articles/article.tss?l=GroovyinActionPart1]]
Read this to get an idea of what Groovy is capable of.

!! [[Groovy Gains Big Sky Sponsorship and AboutGroovy Portal | http://www.infoq.com/news/2006/12/groovy-sponsorship]]
Great news for Groovy and Grails.  Groovy should be also be releasing v1.0 in the next week or two.  [[Link | http://www.eweek.com/article2/0,1895,2074908,00.asp]] to the original eWeek article.

!! [[GWT is fully Open Sourced | http://googlewebtoolkit.blogspot.com/2006/12/gwt-13-release-candidate-is-100-open_12.html]]
GWT has always looked pretty cool but I've been reluctant to use it too much since portions were still proprietary.  No more!  GWT's Java/JavaScript translation engine is nifty and I'd like to see what comes of it now that it's fully open.  

!! [[Java 6 SE Released | http://www.oreillynet.com/onjava/blog/2006/12/java_se_6_is_now_officially_fi.html]]
Java 6 SE is now final.  See [[this page | Java6SEFeatures]] for articles discussing new features of Java 6.

!! [[Myers-Briggs Personality Test for Programmers | http://www.eggheadcafe.com/tutorials/aspnet/9acd1f3b-7d04-429f-ab59-08031feb6401/are-you-a-programmer-mye.aspx]]
Unsurprisingly, I am INTJ.  Trying to be more "E".

!! [[Groovy reaches RC-1; Nearing 1.0 | http://www.infoq.com/news/2006/12/groovy-rc1]]
News about the RC-1 release, and information about Groovy vs. JRuby, DynLangs in general, etc.

!! [[Why OOP Alone in Java is Not Enough | http://debasishg.blogspot.com/2006/10/why-oop-alone-in-java-is-not-enough.html]]
Another good article describing the shortcomings of Java, and why it is about time we graduate to something that actually meets the needs of our problem domain.  Note that Groovy fills this space just as well as Ruby does (and keeps your legacy code intact!)

!! [[Dealing with InterruptedException | http://www-128.ibm.com/developerworks/java/library/j-jtp05236.html]]
Ever wondered what the point of InterruptedException is?  Don't swallow it!  It's actually useful for implementing cancellable tasks.  

!! [[Annotations for Software Defect Detection | http://www.infoq.com/news/jsr-305-annotations]]
I never thought of using annotations that way.  It's kind of like a transparent, enhanced assert() that doesn't actually affect the code unless you're looking for errors.  Clever.

!! [[Side-by-Side Comparison of Spring and EJB 3.0 | http://www.theserverside.com/news/thread.tss?thread_id=42010]]
No opinion this time (since I don't have much experience with either) but interesting nonetheless.  Be sure to read the comments after reading the linked article.
From [[WebToolKit| http://www.webtoolkit.info/ajax-file-upload.html]] - this is a great way to get AJAX (Gmail-style) file uploads.  And WTK was nice enough to package it up in a handy, well-written JS library.  

I tweaked the code slightly to take advantage of Prototype functions, and it uses a single {{{iframe}}} instead of creating a new one each time.

+++!!![The Code]
{{{
/**
* AJAX IFRAME METHOD (AIM)
* http://www.webtoolkit.info/ajax-file-upload.html
*
* Modified to use Prototype functions by Tom Nichols
* http://mediarcade.tiddlyspot.com/
**/

AIM = {

  frame : function( c ) {
    var iframeID = 'aim_upload_iframe_target';
    //var n = 'f' + Math.floor( Math.random() * 99999 );
    var n = iframeID;
    if( ! $(n) ) {
      var d = $( document.createElement( 'div' ) );
      d.update( '<iframe style="display:none" src="about:blank" id="'
        +n+'" name="'+n+'" onload="AIM.loaded(\''+n+'\')"></iframe>' );
      document.body.appendChild( d );
    }

    var i = $( n );
    if ( c && typeof( c.onComplete ) == 'function' )
      i.onComplete = c.onComplete;

    return n; //iframe ID
  },

  form : function( f, name ) {
    f.setAttribute( 'target', name );
    f.setAttribute( 'enctype', 'multipart/form-data' );
  },

  submit : function( f, c ) {
    AIM.form( $(f), AIM.frame(c) );
    return ( c && typeof(c.onStart) == 'function' ) ? 
      c.onStart() : true;
  },

  loaded : function( id ) {
    var i = $( id );
	
    if ( i.contentDocument ) var d = i.contentDocument;
    else if ( i.contentWindow ) var d = i.contentWindow.document;
    else var d = window.frames[id].document;
	
    if ( d.location.href == "about:blank" ) return;

    if ( typeof(i.onComplete) == 'function' ) 
      i.onComplete( d.body.innerHTML );
  }
}
}}}
===
Here is my full [[AHK | http://www.autohotkey.com/]] script, hopefully well enough commented to understand what's going on...

{{{
; My Autohotkey file! - By Tom Nichols

SendMode InputThenPlay

; Key remappings (useful for laptops):
#Down::		Send {PgDn}
#Up::		Send {PgUp}
#Left::		Send {Home}
#Right::	Send {End}
>!^Up:: 	Send {CTRLDOWN}{Home}{CTRLUP}
>!^Down::	Send {CTRLDOWN}{End}{CTRLUP}
#q::		Send {ALTDOWN}{F4}{ALTUP}
; Vim style:
^BS::		Send {Del}
^j::		Send {PgDn}
^k::		Send {PgUp}

; Quick Apps:
<^+S::		Run C:\WINDOWS\system32\ssmyst.scr /S 		;left ctrl-shift-S
;<#F::		Run %SystemRoot%\explorer.exe /E`, A:\dev
#T::		Run C:\cygwin\rxvt.bat
!#T::		Run C:\cygwin\cygwin.bat
#C::		Run %comspec%
<^+X::		Run "C:\apps\DropMyRights\DropMyRights.exe" "C:\Program Files\Mozilla Firefox\firefox.exe"
#v::		Run "C:\progra~1\vim\gvim", B:\

; Window controls
!M::		ToggleWinMin() ;alt-M
#M::		ToggleWinMax() ;win-M

; Clipboard controls:
CapsLock:: 	PushClip()
+CapsLock::	PushClip(true)
#CapsLock::	PopClip()
!CapsLock::	bufPtr:=1 ;reset the buffer stack
CapsLock & `::	InspectStack()

^!CapsLock::  ; to turn off capslock when it accidentally gets triggered
	suspend on
	send {CapsLock}
	suspend off
	return

; Copy to named buffers
CapsLock & 1:: ClipBuffer(1)
CapsLock & 2:: ClipBuffer(2)
CapsLock & 3:: ClipBuffer(3)
CapsLock & 4:: ClipBuffer(4)
#1:: PasteBuffer(1)
#2:: PasteBuffer(2)
#3:: PasteBuffer(3)
#4:: PasteBuffer(4)


; "Home" media key pulls up FireFox
SC112:: WinActivateBottom ahk_class MozillaUIWindowClass
; Media_Play_Pause:: MsgBox blah
; F12:: WinActivate ahk_class _GD_Sidebar
; !Up :: send {Volume_Up}
; !Down :: send {Volume_Down}


; ----------------- Complex remappings --------------------------------

; give the terminal window focus
F12::
	Process, Exist, rxvt.exe
	WinActivate ahk_pid %ErrorLevel%
	return

; run a command prompt, starting in the currently highlited path
#!C::
	bak:= clipboard
	suspend on
	Send ^c
	suspend off
	Run %comspec%, %clipboard%
	clipboard:= bak
	return

; capture a highlighted text and "Run..." it
$>#r::
	val:= ""
	SmartClip( val )
	if( val != "" )
		Run %val%
	else 
		Send #r
	return

; make ^x and ^c PASTE if nothing was highlighted.
;^x::
;	val:= ""
;	SmartClip( val, true, true )
;	if( val == "" )
;		Send ^v
;	else clipboard:=val
;	return

;<^c::
;	val:= ""
;	SmartClip( val, false, true )
;	;msgBox % strlen( val )
;	if( val == "" )
;		Send ^v
;	else clipboard:=val
;	return

; ------------------   Window mgmt Functions -----------------------------

ToggleWinMax() { ;maximizes or unmaximizes a window
	WinGet state, MinMax, A
	if state=1 ;window is maximized
		WinRestore A
	else WinMaximize A
}

; minimizes or unminimizes a window -- doesn't quite work right
; (minimized windows usually aren't in focus)
ToggleWinMin() {
	WinGet state, MinMax, A
	if state=-1 ;window is minimized
		WinRestore A
	else WinMinimize A
}


; --------------------   Handy copy and paste routines   ----------------------
bufPtr:=0
; Cut or copy onto a stack, preserving the original clipboard contents
; Default operation is to copy
PushClip(cut=false) {
	global
	bufPtr++		
	ClipBuffer(bufPtr, cut)
}

; Paste the topmost item on the clipboard stack
PopClip() {
	global
	PasteBuffer(bufPtr)
	if( bufPtr > 1 )
		bufPtr--
}

ClipBuffer( num, cut=false ) {
	global ;  NEED THIS else clip%num% is LOCAL
	SmartClip( clip%num%, cut )
}

PasteBuffer( num ) {
	local bak
	bak:=clipboard
	clipboard:=clip%num%
	Suspend On
	Send ^v
	Suspend Off
	clipboard:=bak
}

; Display the clipboard stack contents in a message box
InspectStack() {
	local str
	str:= "_____ Clipboard _______`n" . clipboard . "`n`n_____ Buffer :  (" . bufPtr . ") ______`n"
	Loop % bufPtr {
		str:= str . A_Index . ": __________`n" . clip%A_Index% . "`n"
	}
	MsgBox %str%
}
	
; Cut/copy only if there was something highlighted.
; Return that val and put the original contents back on the clipboard.
SmartClip( ByRef val, cut=false, raw=false ) {
	bak:= clipboardAll
	clipboard:= ""
	Suspend On
	if( cut )
		Send ^x
	else Send ^c
	ClipWait 0.1
	Suspend Off
	length:= StrLen( clipboardAll ) + StrLen( clipboard )
;	if( length == 0 ) 
;		MsgBox %length% : %clipboard%
	if( length > 0 && raw ) 
		val:= clipboardAll
	else if( length > 0 )
		val:= clipboard
	clipboard:=bak
}


; From: http://www.biancolo.com/content_show.cfm?content_id=26596 ----------

!Insert:: clipboard=         ; clear clipboard

Insert::                     ; add to clipboard w/ a newline in between
	bak = %clipboard%
	Send, ^c
	clipboard = %bak%`r`n%clipboard%
	return

^Insert::           ; surround highlighted text with what was clipped, replacing '@@'
	bak = %clipboard%
	Send, ^c
	StringReplace, clipboard, bak, @@, %clipboard%
	Send, ^v
	clipboard = %bak%
	return
; ------------------------------------------------------
}}}
/***
|Name|AutoOpenTiddlersPlugin|
|Created by|SaqImtiaz|
|Location|http://tw.lewcid.org/#AutoOpenTiddlersPlugin|
|Version|0.21|
|Requires|~TW2.x|
!!!Description:
Open a user defined number of recent tiddlers automatically when the TW loads.
You can also specify a tag and only load tiddlers that have that tag.
To change the number of tiddlers automatically opened, or define a tag to use, ed the config.autoOpenTiddlers part of the code below.

!!!To Do
*add an option to exclude tiddlers with a particular tag

!!!Code
***/
//{{{
//edit this section to change the default settings
config.autoOpenTiddlers = 
{
          count: 5,   //number of tiddlers opened.
          tag: undefined //change if you want to open tiddlers with a specific tag, eg: 'DefaultTiddlers'
}


config.autoOpenTiddlers.handler = function()
{
          if (this.tag == undefined)
             var newTiddlers = store.getTiddlers("modified");
          else
             var newTiddlers = store.getTaggedTiddlers(this.tag,"modified");
          var newTiddlers = newTiddlers.reverse();
           var max = Math.min(this.count,newTiddlers.length-1);
          for (var i=max; i>=0; i--)
               { story.displayTiddler(null,newTiddlers[i].title);} 
}

window.old_lewcid_autoOpenTiddlers_restart = restart;
restart = function ()
{
        window.old_lewcid_autoOpenTiddlers_restart();
        config.autoOpenTiddlers.handler();
}
//}}}
/***
''Name:'' Calendar plugin
''Version:'' 0.5
''Author:'' SteveRumsby

''Syntax:'' 
{{{<<calendar>>}}} or {{{<<calendar year>>}}} or {{{<<calendar year month>>}}} or {{{<<calendar thismonth>>}}}

''Description:'' 
The first form produces an full-year calendar for the current year. The second produces a full-year calendar for the given year. The third produces a single month calendar for the given month and year. The fourth form produces a single month calendar for the current month.
Weekends and holidays are highlighted (see below for how to specify holdays).

''Configuration:''
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.firstday = 6; 
config.macros.calendar.firstweekend = 5;

config.macros.calendar.weekendbg = "#eeeebb";
config.macros.calendar.monthbg = "#770000";
config.macros.calendar.holidaybg = "#ffc0c0";
//}}}
/***
!Code section:
***/
// (you should not need to alter anything below here)//
//{{{
config.macros.calendar.tiddlerformat = "0DD/0MM/YYYY";  // This used to be changeable - for now, it isn't// <<smiley :-(>> 

version.extensions.calendar = { major: 0, minor: 5, revision: 0, date: new Date(2006, 0, 11)};
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")
      createCalendarOneMonth(tbody, year, today.getMonth());
   else if (params[0] == "lastmonth") {
      var month = today.getMonth()-1; if (month==-1) { month=11; year--; }
      createCalendarOneMonth(tbody, year, month);
   }
   else if (params[0] == "nextmonth") {
      var month = today.getMonth()+1; if (month>11) { month=0; year++; }
      createCalendarOneMonth(tbody, year, month);
   }
   else {
      if (params[0]) year = params[0];
      if(params[1])
         createCalendarOneMonth(tbody, year, params[1]-1);
      else
         createCalendarYear(tbody, year);
   }
}
//}}}

//{{{
function createCalendarOneMonth(calendar, year, mon)
{
  var row = createTiddlyElement(calendar, "tr", null, "calenderMonthTitle", null);
  createCalendarMonthHeader(calendar, row, config.macros.calendar.monthnames[mon] + " " + year, true, year, mon);
  row = createTiddlyElement(calendar, "tr", null, "calendarDaysOfWeek", 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, "<", "Back", 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, ">", "Fwd", 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);
    var backHandler = function() {
      var newyear = year;
      var newmon = mon-1;
      if(newmon == -1) { newmon = 11; newyear = newyear-1;}
      removeChildren(cal);
      createCalendarOneMonth(cal, newyear, newmon);
    };
    createTiddlyButton(back, "<", "Back", backHandler);
    back.align = "center";
    back.style.background = config.macros.calendar.monthbg; 
    month = createTiddlyElement(row, "td", null, "calendarMonthname", name)
    month.setAttribute("colSpan", 5);
    var fwd = createTiddlyElement(row, "td", null, null, null);
    var fwdHandler = function() {
      var newyear = year;
      var newmon = mon+1;
      if(newmon == 12) { newmon = 0; newyear = newyear+1;}
      removeChildren(cal);
      createCalendarOneMonth(cal, newyear, newmon);
    };
    createTiddlyButton(fwd, ">", "Fwd", fwdHandler);
    fwd.align = "center";
    fwd.style.background = config.macros.calendar.monthbg; 
  } 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.macros.calendar.firstday;
      if(d > 6) d = d - 7;
      cell = createTiddlyElement(row, "td", null, null, config.macros.calendar.daynames[d]);

      if(d == config.macros.calendar.firstweekend || d == config.macros.calendar.firstweekend+1)
        cell.className = "calendarWeekend";
    }
  }
}
//}}}

//{{{
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.macros.calendar.firstday;
    if(d > 6) d = d - 7;
    var daycell = createTiddlyElement(row, "td", null, null, null);
    var isaWeekend = ((d == config.macros.calendar.firstweekend || d == (config.macros.calendar.firstweekend+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, null, null);
  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.macros.calendar.firstday;
 if(first1 < 0) first1 = first1 + 7;
 var day1 = -first1 + 1;
 var first2 = (new Date(year, mon+1, 1)).getDay() -1 - config.macros.calendar.firstday;
 if(first2 < 0) first2 = first2 + 7;
 var day2 = -first2 + 1;
 var first3 = (new Date(year, mon+2, 1)).getDay() -1 - config.macros.calendar.firstday;
 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.macros.calendar.firstday;
 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 { margin:0px !important; }", "calendarStyles");
//}}}
Cobertura integration with Ant is super cool.  Here is a sample Ant script that spells it out a little better than the Cobertura documentation seemed to.  

Also note that I used Maven's Ant integration to handle Cobertura's classpath dependencies

!! The Ant targets:
{{{
<!-- Cobertura code coverage -->
<target name="initCoverage" if="use.coverage" depends="init, compileCore">
  <mvn:pom id="pom.cobertura" file="${pom.file.cobertura}" />
  <mvn:dependencies pathid="cp.cobertura" pomrefid="pom.cobertura" />

  <taskdef classpathref="cp.cobertura" resource="tasks.properties" />
  
  <delete file="${coverage.data}" />
  <delete dir="${build.coverage}" />
  <cobertura-instrument datafile="${coverage.data}" todir="${build.coverage}">
    <includeClasses regex=".*" />
    <instrumentationClasspath>
      <pathelement location="${build.core}" />
    </instrumentationClasspath>
  </cobertura-instrument>
</target>

<target name="runTests" unless="test.skip" depends="initCoverage, compileTests">
  <mkdir .../>
  <junit fork="true" forkmode="once" tempdir="${env.TEMP}" 
    failureproperty="tests.failed" maxmemory="512M">
    <classpath id="test.cp">
      <pathelement path="${build.coverage}" />
      <pathelement path="${build.test}" />
      <pathelement path="${build.core}" />
      <path refid="cp.test" />
      <path refid="cp.all" />
      <pathelement path="${test.resources}" />
    </classpath>
    <sysproperty key="net.sourceforge.cobertura.datafile" 
      value="${coverage.data}"/>
    
    <batchtest ...>
    <formatter type="xml" />
  </junit>
  
  <junitreport ...>
</target>

<target name="runCoverage" if="use.coverage" depends="initCoverage, runTests">
  <cobertura-report datafile="${coverage.data}" 
    destdir="${coverage.results}" srcdir="${src.core}" />
</target>
}}}
//Not that anything here is revolutionary, but I hate seeing somebody have such a hard time doing something that should be easy.//

!! [[Windows Paths in Java]]
More of a tip than a trick.  In summary:  Don't use the double backslash.  Click the title to read more.


!! Create incremental file names with padded zeros.
You need to generate sequential output files that will list in the correct order (so '10.txt' doesn't come after '1.txt').  You could contsruct the file name by hand, and calculate the zero-padding (what a pain!)  

Or, use Java 1.5's [[String.formatter | http://java.sun.com/j2se/1.5.0/docs/api/java/util/Formatter.html#syntax]]:
{{{
int fileCount = 0;
String fileName = String.format( "load-%05d.xml", ++fileCount ); // "load-00001.xml"
}}}


!! VarArgs are your friend
What do you do when you have a method signature that takes a list, but you only have one or two values that need to be passed in?   Or when you quickly need a read-only list with a few values?  

Create a new ArrayList?  No!  [[Arrays.asList(...)| http://java.sun.com/j2se/1.5.0/docs/api/java/util/Arrays.html#asList(T...)]] to the rescue.
{{{
myDAO.saveAll( Arrays.asList( billy, bobby, joe ) );

List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
}}}


!! Java Dates
Dates are a regular pain in the arse.  Here are some helpful tips:

Fastest way to create a @@java.sql.Date@@ for "now".  Thanks to John Feustel for pointing this out...
{{{
java.sql.Date now = new java.sql.Date( System.currentTimeMillis() );
}}}

Remember, if you're using '*@@java.util.Date@@*', you can simply call @@new Date()@@.  

But @@java.'*util*'.Date@@ doesn't have a "parse" function!  Here ya go:
{{{
Date dt = new java.text.SimpleDateFormat("dd-MMM-yyyy").parse("15-APR-1978");
// or...
dt = java.text.DateFormat.getInstance().parse("1/2/03 3:30 PM");
}}}
/***
|Name|CollapseTiddlersPlugin|
|Source|http://gensoft.revhost.net/Collapse.html|
|Version|2007.30.03|
|Author|Bradley Meck (modified by ELS)|
|License|unknown|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Description|show/hide content of a tiddler while leaving tiddler title visible|

|ELS 6/5/2007: add "return false" at the end of each command handler to prevent IE 'page transition' problem. |
|ELS 3/30/2007: add a shadow definition for CollapsedTemplate.  Tweak ViewTemplate shadow so "fold/unfold" and "focus" toolbar items automatically appear when using default templates.  Remove error check for "CollapsedTemplate" existence, since shadow version will now always work as a fallback. |
|ELS 2/24/2006: added fallback to "CollapsedTemplate" if "WebCollapsedTemplate" is not found |
|ELS 2/6/2006: added check for 'readOnly' flag to use alternative "WebCollapsedTemplate" |

***/

//{{{

config.shadowTiddlers.CollapsedTemplate=
	"<!--{{{-->\
	<div class='toolbar' macro='toolbar expandTiddler collapseOthers closeTiddler closeOthers +editTiddler permalink references jump'></div>\
	<div class='title' macro='view title'></div>\
	<!--}}}-->";

// automatically tweak shadow ViewTemplate to add "collapseTiddler collapseOthers" commands at 'front' of toolbar (before 'closeTiddler')
config.shadowTiddlers.ViewTemplate=config.shadowTiddlers.ViewTemplate.replace(/closeTiddler/,"collapseTiddler collapseOthers closeTiddler");

config.commands.collapseTiddler = {
text: "fold",
tooltip: "Collapse this tiddler",
handler: function(event,src,title)
{
var e = story.findContainingTiddler(src);
if(e.getAttribute("template") != config.tiddlerTemplates[DEFAULT_EDIT_TEMPLATE]){
var t = (readOnly&&store.tiddlerExists("WebCollapsedTemplate"))?"WebCollapsedTemplate":"CollapsedTemplate";
if(e.getAttribute("template") != t ){
e.setAttribute("oldTemplate",e.getAttribute("template"));
story.displayTiddler(null,title,t);
}
}
return false;
}
}

config.commands.expandTiddler = {
text: "unfold",
tooltip: "Expand this tiddler",
handler: function(event,src,title)
{
var e = story.findContainingTiddler(src);
story.displayTiddler(null,title,e.getAttribute("oldTemplate"));
return false;
}
}

config.macros.collapseAll = {
handler: function(place,macroName,params,wikifier,paramString,tiddler){
createTiddlyButton(place,"Collapse All","",function(){
story.forEachTiddler(function(title,tiddler){
if(tiddler.getAttribute("template") != config.tiddlerTemplates[DEFAULT_EDIT_TEMPLATE])
var t = (readOnly&&store.tiddlerExists("WebCollapsedTemplate"))?"WebCollapsedTemplate":"CollapsedTemplate";
story.displayTiddler(null,title,t);
})})
}
}

config.macros.expandAll = {
handler: function(place,macroName,params,wikifier,paramString,tiddler){
createTiddlyButton(place,"Expand All","",function(){
story.forEachTiddler(function(title,tiddler){
var t = (readOnly&&store.tiddlerExists("WebCollapsedTemplate"))?"WebCollapsedTemplate":"CollapsedTemplate";
if(tiddler.getAttribute("template") == t) story.displayTiddler(null,title,tiddler.getAttribute("oldTemplate"));
})})
}
}

config.commands.collapseOthers = {
text: "focus",
tooltip: "Expand this tiddler and collapse all others",
handler: function(event,src,title)
{
var e = story.findContainingTiddler(src);
story.forEachTiddler(function(title,tiddler){
if(tiddler.getAttribute("template") != config.tiddlerTemplates[DEFAULT_EDIT_TEMPLATE]){
var t = (readOnly&&store.tiddlerExists("WebCollapsedTemplate"))?"WebCollapsedTemplate":"CollapsedTemplate";
if (e==tiddler) t=e.getAttribute("oldTemplate");
//////////
// ELS 2006.02.22 - removed this line.  if t==null, then the *current* view template, not the default "ViewTemplate", will be used.
// if (!t||!t.length) t=!readOnly?"ViewTemplate":"WebViewTemplate";
//////////
story.displayTiddler(null,title,t);
}
})
return false;
}
} 
//}}}
<div class='title titleCollapsed' macro='view title'></div>
<div class='toolbar toolbarCollapsed' macro='toolbar +editTiddler permalink references jump expandTiddler collapseOthers closeOthers +closeTiddler'></div>

Cygwin is an incredibly powerful tool, ''infinitely'' more useful than the windows command shell (CMD).  Some of Cygwin's defaults make it a little awkward to use.  This page serves as my reference for the many Cygwin customizations I've done.  Cygwin is a great tool that I have open ''constantly''.  

! Profile and Environment

!! .profile
{{{
# Ignore boring entries in command history.  
# Prefix any commands with whitespace that you _don't_ want to be in the history:
export HISTIGNORE="&:[ \t]*:[bf]g:exit:logout"

# this is needed by some programs 
export http_proxy="http://webproxy:80"
export ftp_proxy="http://webproxy:80"

# quickly CD to ~/* from any directory:
export CDPATH=".:~"

export IGNOREEOF=1  # ^D won't exit shell.

# This allows C-v to paste (set from .inputrc)
stty lnext ^q stop undef start undef
}}}


!! .bashrc
This should also be sourced from your .profile or .bash_profile so it is active on login.  

{{{
alias l="ls -F"                 #quick ls
alias la='ls -AF'               #quick list all
alias ll='ls -lhF'              #quick list long
alias bd='cd -'                 #go back to the last directory
alias up='cd ..'                #go to parent dir
alias pd='pushd'                #CD and save the dir we were in
alias ld='popd'                 #go back to the last dir that was 'pd'd
alias md='mkdir -p'             #quick make directory tree
alias ff='find . -iname'        #find fast from here
alias wad='tar -czvf'           #quickly tar/gz: "wad files.tgz dir"
alias unwad='tar -xzvf'         #quickly deflate: "unwad files.tgz"
alias usage='du -hcs'           #disk usage
alias wgetall='wget -rk -np'    #quickly download a web page and child pages

# Do simple directory spelling corrections
shopt -s cdspell
shopt -s no_empty_cmd_completion

# "grep in find"
# usage: gf "some text" "*.xml"
function gf {
	grep "$1" `find . -iwholename "$2"` | less
}
}}}


!! .inputrc

The .inputrc file allows you to set key bindings to common commands in the console.  See "man readline" for full options.

{{{
#Macros:
# Go to the last directory of the last arg of the previous cmd (Alt-d). 
"\M-d": "cd !$\n"

#repeat the last command with sudo in front:
"\M-s": "sudo !!"  

# Insert-Option Macro
# Load the previous command and position the cursor for typing an option
"\M-x": "\C-p\C-a\M-f "

# Cycle through possible auto-completions
"\M-a": menu-complete

# Clear the entire line in one keystroke
"\M-z": "\C-e\C-u"

# forward delete does not always work in some shells... this should fix it.
"\e[3~": delete-char
#"\C-?": backward-delete-char

"\C-v": paste-from-clipboard

# Incremental history searching with Up and Down  
"\e[A": history-search-backward 
"\e[B": history-search-forward 
# Old behavior still available with Ctrl+P and Ctrl+N
# If that prevents Left and Right from working, fix them like this:
"\e[C": forward-char 
"\e[D": backward-char 

#Home and end keys (rxvt and konsole only)
"\e[7~":beginning-of-line
"\e[8~":end-of-line

# Move forward and backwards 1 word at a time with Ctrl + Left & Right
# Note that this can screw things up on some terminals
"\eOc": forward-word
"\eOd": backward-word
#"\e[5C": forward-word
#"\e[5D": backward-word
#"\e\e[C": forward-word
#"\e\e[D": backward-word


#Options:
set completion-ignore-case On
set visible-stats On
set show-all-if-ambiguous On
set bell-style none

#Existing useful key commands:
#C-P	Previous command	(Ctrl-P)
#C-R	Search command history
#C-A	Beginning of line
#C-E	End of line
#C-U	Clear to beginning of line
#A-B	Back one word		(Alt-B)
#A-F	Foward one word
#A-C	Capitalize letter under cursor & jump to next word
#A-.	Insert last arg of last command
#
# !! is the previous command line
# !:0 is the previous command name
# !-2, !-3, are earlier commands
# !^, !:2, !:3, !$ are the arguments
# !* is all the arguments
}}}

! Symlinks & Drive Paths

I hate typing [@cd /cygdrive/c/whatever@] so the first thing I do is change where Cygwin mounts the hard drives:
{{{
mount -s -c /  # this will cause drives to be mounted as /c/, /d/, etc.
}}}

Symlinks help make it easier to work with the long Windows folder names:
{{{
ln -s /c/docume~1/user/My\ Documents docs   # docs -> %USERPROFILE%
ln -s /c/Program\ Files apps  # apps -> C:\Program Files
ln -s /dev/scd0 /dev/cdrom  # more convenient location for CD-ROM drive
}}}
Run the above commands ''once'' from your home directory (~)

I prefer to set a @@HOME@@ environment variable in Windows, pointing to @@USERPROFILE@@ (aka @@C:\documents and settings\u000000@@).  Cygwin will detect this from Windows and use it instead of the default (@@C:\cygwin\home\u0000000@@) but that's up to you.

! Rxvt

I use rxvt simply because it looks better than a CMD window.  It's an optional package that can be installed from the Cygwin installer. 

[[Attach:rxvt.bat.txt|Here]] is the startup script (to use instead of cygwin.bat).  

I use [[http://www.angusj.com/resourcehacker/|ResHack]] or [[http://www.wilsonc.demon.co.uk/d10resourceeditor.htm|Resource Editor]] to modify the rxvt.exe's icon to match the cygwin icon.  


! Applications

Here's a list of Cygwin tools that I use often:
* curl
* ssh & scp
* find - [[Main/FindIsYourFriend]]
* grep
* svn
* vim

! Further reading:
* [[http://www.ukuug.org/events/linux2003/papers/bash_tips/| Power Shell Usage]]
* [[http://lifehacker.com/software/command-line/ctrl%252Br-to-search-and-other-terminal-history-tricks-278888.php| Ctrl-R to search and other terminal tricks]]
This is primarily for Vim on Windows, but I've used an almost identical script on OS X.  Some of the remaps might override some default (G)Vim functionality, but I've never used the defaults :)

+++!![ .gvimrc ...]
{{{
set nocompatible
source $VIMRUNTIME/vimrc_example.vim
source $VIMRUNTIME/mswin.vim
source ~/.vimrc
behave mswin

"___________KEY  MAPPINGS_______________
" close an unsaved buffer (only in normal mode):
map <C-S-B> :bd! "Hit return to close buffer.
" This will close a saved buffer:
"map <C-b> :bd<CR>
" This does the same from insert mode.  
map! <C-b> <ESC>:bd<CR><insert>
" go to the next buffer:
map <C-j> :bnext<CR>
map! <C-j> <ESC><C-j><insert>
" go to the previous buffer:
map <C-k> :bprev<CR>
map! <C-k> <ESC><C-k><insert>
" same buffer nav mappings w/ up/down arrow keys (redundant):
map <C-down> :bn<CR>
map! <C-down> <ESC><C-down><insert>
map <C-up> :bp<CR>
map! <C-up> <ESC><C-up><insert>
" go to the last buffer (alternate):
map <C-TAB> :b #<CR>
map! <C-TAB> <ESC><C-TAB><INSERT>
" navigate tabs (Vim7) works w/ C-PgUP and C-PgDN
" open a new tab:
map <C-T> :tabe<CR>
map! <C-T> <ESC><C-T><CR><INSERT>
" This will convert text to lowercase
"map <C-F> u
" typical windows find keystroke:
"map <C-F> :promptfind<CR>
"map! <C-F> <ESC><C-F><INSERT>
" most IDEs use Ctrl-Space for autocomplete/intellisense.  
" insert mode only
map! <C-SPACE> <C-N>
map! <C-S-SPACE> <C-P>
" open a new buffer for editing 
map <F2> :enew<CR>
map! <F2> <ESC><F2><INSERT>
" find next: (repeat last find- spans multiple editing sessions)
map <F3> /<CR>
map! <F3> <ESC>l<F3><INSERT>
" un-highlight last searched for text:
map /// :let @/ = ""<CR>
map! <C-F3> <ESC><C-F3><INSERT>
" indent/ unindent
map <F5> <
map <F6> >
" This allows it to work from insert mode.  Yip-ee.
imap <F5> <ESC><F5>
imap <F6> <ESC><F6>
" toggle folding gutter
map <F7> :call MyToggleFolding()<CR>
map! <F7> <ESC><F7><INSERT>
" toggle line wrapping:
map <F8> :call ToggleWrap()<CR>
map! <F8> <ESC><F8><INSERT>
" comment/ uncomment
map <F9> :call EnhancedCommentify('', 'first')<CR>


"___________CUSTOM MENU OPTIONS___________________
" Add a seperator and a command to the File menu to open _vimrc (this file).
an 10.332	 &File.-SEP0-				<Nop>
an 10.333	 &File.Open\ &\.vimrc		:e $HOME/.vimrc<CR>
an 10.334	 &File.Open\ Vim\ &Tips		:e $HOME/vimfiles/vimtips.txt<CR>


"_______________MISC STUFF_______________________
" do syntax hilighting on certain file types that Vim doensn't already know:
au! BufRead,BufNewFile *.x68		setf asm68k
au! BufRead,BufNewFile *.pom		setf xml

" setting this var will make .prg files use FoxPro highlighting
let filetype_prg = "foxpro"
}}}
===

+++!![ .vimrc ...]
{{{
" this sets the tabs to 4 spaces
set ts=4  sw=4
" set word wrap to wrap at the end of a word instead of character.
set lbr
" set auto/smart/copy current indent scheme.
set ai si ci
" turn off backup so that "filename.ext~" files aren't written all over.
set nobackup writebackup
set fileformat=unix
" this will allow switching buffers w/o saving in between.
set hidden
" when doing a /something/ search, ignore case unless upper/mixed case is used
set ignorecase smartcase
" use my colors in vimfiles/colors
colorscheme desert
" folding: (don't usually want this enabled by default).
set foldmethod=indent
set nofoldenable
set foldlevel=99 "when folds are enabled they are left open.
" set foldcolumn=2
" scroll options:  keep 1 line visible above & below cursor when scrolling
set so=1
" sidescroll options:
set siso=3
" put this char at the beginning of a wrapped line:
set sbr=*
" options for EnhancedCommentify (use :h EnhComm-Options)
let g:EnhCommentifyMultiPartBlocks = 'yes'
let g:EnhCommentifyAlignRight = 'yes'
" jumps to search word as you type (annoying but excellent)
set incsearch
" tab complete now ignores these
set wildignore+=*.o,*.obj,*.bak,*.exe,*.class,*~
" Maki GUI File Open use current directory
set browsedir=buffer
" change to directory of current file automatically
" autocmd BufEnter * lcd %:p:h
set acd
}}}
===
It's interesting to see the sudden influx of 'Java is dead' propaganda, much of it on the heels of [[Ruby on Rails | http://www.rubyonrails.org/]] evangelism.  I am personally a believer of the Java ''platform'' moreso than the Java language ''per se''.   Java the language needs to become much more dynamic than it is now.  Take a look at the C# 3.0 [[feature list | http://en.wikipedia.org/wiki/C_Sharp#C.23_3.0_new_language_features]] (heck, check out the [[2.0 feature list | http://en.wikipedia.org/wiki/C_Sharp#C.23_2.0_new_language_features]]).  Read the Bruce Tate interview above.  

Luckily some people at Sun are seeing the light, and Mustang is adding some [[great new features | http://java.sun.com/developer/technicalArticles/J2SE/Desktop/mustang/beta2.html]] like a scripting API.  This, along with other initiatives like the [[standardization | http://jcp.org/en/jsr/detail?id=241]] of [[Groovy | http://groovy.codehaus.org]], and projects like [[Grails | http://grails.codehaus.org]] and JRuby will keep Java, at least the platform, alive and kicking.  

For your entertainment, here's a collection of 'Java is dead' articles.  Take it as you will. 
* http://www.infoq.com/news/Java-EE-Demise-Report
* http://www.infoq.com/news/Martin-Fowler-Enterprise-Rails 
* http://www.infoq.com/news/Is-Ruby-Ready-for-the-Enterprise 
* http://www.infoq.com/news/Java-EE-5-light-enough 
* http://www.oreillynet.com/windows/blog/2006/07/the_beginning_of_the_end_for_j.html 

Also, thank God that Sun is finally open sourcing Java.  Even if it takes a while, this will be another saving grace in the long run.
[[Home]]
[[Sun's documentation | http://java.sun.com/docs/books/tutorial/deployment/applet/mixedbrowser.html]] isn't entirely correct when it comes to deploying applets.  [[This page | http://ww2.cs.fsu.edu/~steele/XHTML/appletObject.html]] outlines an XHTML-compliant way of using the <object> tag that works in most environments.

!! Putting it all together...
Now, since you'll (hopefully) be packaging your Applet in a JAR rather than spitting .class files in your web content, I've put together a template here:
{{{
  <object classid="java:${className}.class" width="200" height="300"
    type="application/x-java-applet" archive="${appletName}">
    
    <param name="cache_archive" value="${appletName}" />
    <!-- Increment this version number to force a client update of the applet: -->
    <param name="cache_version" value="0.0.0.2" />
    <param name="archive" value="${appletName}" />
    <param name="codebase" value="${appletPath}" />
  <!--<![endif]-->
    <!-- MSIE (Microsoft Internet Explorer) will use inner object --> 
    <object classid="clsid:CAFEEFAC-0015-0000-0000-ABCDEFFEDCBA" 
      width="200" height="300"
      codebase="http://java.sun.com/update/1.5.0/jinstall-1_5_0-windows-i586.cab#Version=1,5,0,0" > 
      
      <param name="code" value="${className}" />
      <param name="archive" value="${appletName}" />
      <param name="codebase" value="${appletPath}" />     

      <strong>This browser does not have a Java Plug-in. <br />
        <a href="http://java.sun.com/products/plugin/downloads/index.html">
          Get the latest Java Plug-in here.</a>
      </strong>
    </object> 
  <!--[if !IE]> -->
  </object>
  <!--<![endif]-->
}}}
So (unless I'm blind) Prototype is conspicuously missing any convenience methods for cookie handling.  [[Dojo | http://dojotoolkit.org/book/dojo-book-0-9/part-3-programmatic-dijit-and-dojo/other-miscellaneous-function/cookies]], however, does have such support.  

Not wanting to jump to Dojo just for cookie handling (I'm sure I'll have some reason eventually, but for now I'm quite happy with Prototype) I made a quick change to cookie.js, and it works quite happily without the core Dojo js files.

First, *remove* the three lines at the top of the script that look like this:
{{{
if(!dojo._hasResource["dojo.cookie"]){ // ...
dojo._hasResource["dojo.cookie"] = true;
dojo.provide("dojo.cookie");

// dojo.cookie definition here...

// also remove the bottom closing paren:
}
}}}

Then, just add the dojo namespace declaration to the top of the file like so:
{{{
if( typeof dojo == undefined ) dojo = {};

// dojo.cookie definition same as before
}}}

That's it!
<!--{{{-->
<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
<div class='title' macro='view title'></div>
<div class='editor'>
<span class='titleEdit' macro='edit title'></span>
<span macro='tagChooser'></span>
<span class='tagEdit' macro='edit tags'></span>
</div>
<div class='editor' macro='edit text'></div>
<!--}}}-->
The following is some opinionated content that I have nowhere better to publish. 
[[Here |Articles]] are other interesting articles, sans opinionated commentary.

<<list tagged "opinion">>

! [[Case Study: ChangingThePresent.org | http://www.infoq.com/articles/changing-the-present-case-stud]]

Although I'd much rather use a Java-based solution, it's very insightful to see development done right.  Focus on the techniques that are mentioned rather than the technology:
* Weekly iterative releases
* Test coverage
* Improved productivity means you have more time for testing and optimization
Obviously this is a star team, but there's no reason why the same principles can't be applied to most other projects.


! [[Terracotta Clustering | http://www.terracotta.org/]] is now [[Open | http://www.eweek.com/print_article2/0,1217,a=195577,00.asp]] [[Source | http://terracottatech.com/press_12_04_06_oss.shtml]] 

Lightweight application stacks (e.g. Spring+ Hibernate+ Tomcat) have become an increasingly popular platform choice for their speed and simplicity versus a full-blown J2EE server container.  

One of the lost features in moving from a EJB to POJO model, as described in [[this post | http://pojomojo.blogspot.com/2006/11/clustering-ejbs-vs-jms-vs-pojos.html]] is the need for maintaining state across a distributed system.  Terracotta distributes the application at the JVM level, making it totally transparent and, conceivably, allow one to use a clustered system without the drawbacks of JNDI and the full EJB stack.

I can't speak to its usefulness in practice, but Terracotta seems to be a unique and very powerful piece of software nonetheless.  I think it will be a welcome contribution to the open source Java world.

! Great News!
[[Sun relicenses Java under GPL! | http://www.theserverside.com/news/thread.tss?thread_id=43046]]  Regardless of the 'practical' benefits of it being open-sourced, this should help Java's perception of being truly 'free' and developer-friendly, especially in other open-source arenas.  The JRE and JDK can be included in Linux distributions which will make it more accessible on those platforms (in my experience Java has not been easy to install on Linux).
More commentary [[here | http://www.oreillynet.com/onjava/blog/2006/11/java_gpled_1.html]] and [[here | http://blogs.zdnet.com/BTL/index.php?p=3937]].

! WebSphere
I came across [[this article | http://www.theserverside.com/news/thread.tss?thread_id=39933#206317]] while looking for info on EJB3 support.  What caught my attention was the //rash// of comments left [[below | http://www.theserverside.com/news/thread.tss?thread_id=39933]].  For an article that is not at all opinionated, there sure are a lot of opinionated comments.  What's worse is that there is not '*a single rebuttal*' in all of the comments...  There seems to be quite a negative consensus regarding WebSphere.

! JRuby, or Dynamic Languages for the JVM
When I found out [[Sun | http://www.oreillynet.com/onjava/blog/2006/09/sun_hires_jruby_developers.html]] [[hired | http://www.infoq.com/news/jruby-groovy-next]] the [[JRuby guys | http://www.oreillynet.com/onjava/blog/2006/09/charles_nutter_responds_our_fu.html]], I was perplexed to say the least.  Why support a language that Sun has no control over?  Why not support a language like [[Groovy | http://groovy.codehaus.org]] which is meant to run in the Java environment and fits better?  I left a slew of comments on the above linked pages, so I'm not going to repeat what I said here.  

But it brought to mind an interesting point when thinking about the [[Java| http://blogs.sun.com/jag/entry/the_black_hole_theory_of]] [[Closures | http://blogs.sun.com/gbracha/entry/achieving_closure]] [[proposal | http://blogs.sun.com/ahe/entry/full_disclosure]].  A lot of (Java) people commented that it will clutter up the Java syntax, make Java confusing, etc.  Now that I think of it, it would actually be better if Sun //does// limit the new features added to Java, and instead focus on supporting those features through dynamic languages like Groovy and JRuby.  

Even if you wanted to keep programming in Java (syntax) it wouldn't be a big deal.  Considering that you can switch between the two languages at the class level, it should be easy enough to write most of your code in Java (syntax) and simply use Groovy/JRuby for the portions that need the more powerful language constructs.  

!Java++
Interesting post re: [[Closures in Java | http://blogs.sun.com/roller/page/ahe?entry=full_disclosure]].  This would be a great boon to the language, provided they keep the syntax terse.  I've ''tried'' to emulate closures (actually, a simple event system, like an intelligent, strongly-typed Observer pattern) using anonymous inner classes, and it doesn't work.  

Either way, I'm actually more happy with [[Groovy | http://groovy.codehaus.org]] and I really hope that gets first- class support for the Java platform.  

!JavaDoc++
If you haven't already, check out [[JavaRef | http://www.javaref.com/]].  It's JavaDoc for just about any OSS Java library, all in one place, and a million times more user-friendly than the old multi-frame JavaDoc that we're all used to.  You can create an account, but that's only so it remembers what libraries you want to search.  

JavaRef is extremely user-firendly, thanks to auto-complete searching and some other well-used AJAX to reduce page loading.  The coolest thing, in my opinion, is the source viewer.  So, I can actually see the source code for Spring's [[MultiActionController | http://www.javaref.com/cls?c=MultiActionController&p=org.springframework.web.servlet.mvc.multiaction&a=93]] without having to download the source and trudge through it.  

And if they don't have an API on the site, you can [[ask for it | http://bitsofabyte.com/blog/pivot/entry.php?id=5]] and they will usually add it within a week.  Bueno!

!Ruby vs. Java...  Fight!
* Very enlightening [[Interview with Bruce Tate | http://java.sys-con.com/read/251986.htm]] re: Ruby and Java...  Especially considering he's a well known author of many Java books.  I hope James Gosling reads Bruce's list of features he would add to Java.
* Here's [[another article | http://www.infoq.com/articles/From-Java-to-Ruby--Strategies]] by Bruce Tate on using Ruby in Java shops.  His replies to the first comment thread are compelling as well.
<<exportTiddlers inline>>
/***
''Export Tiddlers Plugin for TiddlyWiki version 2.0''
^^author: Eric Shulman - ELS Design Studios
source: http://www.TiddlyTools.com/#ExportTiddlersPlugin
license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^

When many people edit copies of the same TiddlyWiki document, the ability to easily copy and share these changes so they can then be redistributed to the entire group is very important. This ability is also very useful 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.)

ExportTiddlersPlugin let you ''select and extract tiddlers from your ~TiddlyWiki documents and save them to a local file'' or a remote server (requires installation of compatible server-side scripting, still under development...). An interactive control panel lets you specify a destination, and then select which tiddlers to export. A convenient 'selection filter' helps you pick desired tiddlers by specifying a combination of modification dates, tags, or tiddler text to be matched or excluded. ''Tiddler data can be output as ~TiddlyWiki "storeArea ~DIVs" that can be imported into another ~TiddlyWiki or as ~RSS-compatible XML that can be published for RSS syndication.''

!!!!!Inline interface (live)
<<<
<<exportTiddlers inline>>
<<<
!!!!!Usage
<<<
Optional "special tiddlers" used by this plugin:
* SiteUrl^^
URL for official server-published version of document being viewed
default: //none//^^
* SiteHost^^
host name/address for remote server (e.g., "www.server.com" or "192.168.1.27")
default: //none//^^
* SitePost^^
remote path/filename for submitting changes (e.g., "/cgi-bin/submit.cgi")
default: //none//^^
* SiteParams^^
arguments (if any) for server-side receiving script
default: //none//^^
* SiteID^^
username or other authorization identifier for login-controlled access to remote server
default: current TiddlyWiki username (e.g., "YourName")^^
* SiteDate^^
stored date/time stamp for most recent published version of document
default: current document.modified value (i.e., the 'file date')^^
<<<
!!!!!Example
<<<
<<exportTiddlers>>
<<<
!!!!!Installation
<<<
Import (or copy/paste) the following tiddlers into your document:
''ExportTiddlersPlugin'' (tagged with <<tag systemConfig>>)

create/edit ''SideBarOptions'': (sidebar menu items) 
^^Add "< < exportTiddlers > >" macro^^
<<<
!!!!!Revision History
<<<
''2006.02.12 [2.1.2]''^^
added var to unintended global 'tags' in matchTags(). Avoids FF1501 bug when filtering by tags. (based on report by TedPavlic)
''2006.02.04 [2.1.1]''^^
added var to variables that were unintentionally global. Avoids FireFox 1.5.0.1 crash bug when referencing global variables
''2006.02.02 [2.1.0]''^^
Added support for output of complete TiddlyWiki documents. Let's you use ExportTiddlers to generate 'starter' documents from selected tiddlers.^^
''2006.01.21 [2.0.1]''^^
Defer initial panel creation and only register a notification function when panel first is created
in saveChanges 'hijack', create panel as needed. Note: if window.event is not available to identify the click location, the export panel is positioned relative to the 'tiddlerDisplay' element of the TW document.
^^
''2005.12.27 [2.0.0]''^^
Update for TW2.0
Defer initial panel creation and only register a notification function when panel first is created
^^
''2005.12.24 [0.9.5]''^^
Minor adjustments to CSS to force correct link colors regardless of TW stylesheet selection
^^
''2005.12.16 [0.9.4]''^^
Dynamically create/remove exportPanel as needed to ensure only one instance of interface elements exists, even if there are multiple instances of macro embedding.
^^
''2005.11.15 [0.9.2]''^^
added non-Ajax post function to bypass javascript security restrictions on cross-domain I/O. Moved AJAX functions to separate tiddler (no longer needed here). Generalized HTTP server to support UnaWiki servers
^^
+++[previous releases...]
''2005.11.08 [0.9.1]''^^
moved HTML, CSS and control initialization into exportInit() function and call from macro handler instead of at load time. This allows exportPanel to be placed within the same containing element as the "export tiddlers" button, so that relative positioning can be achieved.
^^
''2005.10.28 [0.9.0]''^^
added 'select opened tiddlers' feature
Based on a suggestion by Geoff Slocock
^^
''2005.10.24 [0.8.3]''^^
Corrected hijack of 'save changes' when using http:
^^
''2005.10.18 [0.8.2]''^^
added AJAX functions
^^
''2005.10.18 [0.8.1]''^^
Corrected timezone handling when filtering for date ranges.
Improved error checking/reporting for invalid filter values and filters that don't match any tiddlers.
Exporting localfile-to-localfile is working for IE and FF
Exporting server-to-localfile works in IE (after ActiveX warnings), but has security issues in FF
Cross-domain exporting (localfile/server-to-server) is under development
Cookies to remember filter settings - coming soon
More style tweaks, minor text changes and some assorted layout cleanup.
^^
''2005.10.17 [0.8.0]''^^
First pre-release.
^^
''2005.10.16 [0.7.0]''^^
filter by tags
^^
''2005.10.15 [0.6.0]''^^
filter by title/text
^^
''2005.10.14 [0.5.0]''^^
export to local file (DIV or XML)
^^
''2005.10.14 [0.4.0]''^^
filter by start/end date
^^
''2005.10.13 [0.3.0]''^^
panel interaction
^^
''2005.10.11 [0.2.0]''^^
panel layout
^^
''2005.10.10 [0.1.0]''^^
code framework
^^
''2005.10.09 [0.0.0]''^^
development started
^^
===
<<<
!!!!!Credits
<<<
This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
<<<
!!!!!Code
***/
// // +++[version]
//{{{
version.extensions.exportTiddlers = {major: 2, minor: 1, revision: 2, date: new Date(2006,2,12)};
//}}}
// //===

// // +++[macro handler]
//{{{
config.macros.exportTiddlers = {
 label: "export tiddlers",
 prompt: "Copy selected tiddlers to an export document",
 datetimefmt: "0MM/0DD/YYYY 0hh:0mm:0ss" // for "filter date/time" edit fields
};

config.macros.exportTiddlers.handler = function(place,macroName,params) {
 if (params[0]!="inline")
 { createTiddlyButton(place,this.label,this.prompt,onClickExportMenu); return; }
 var panel=createExportPanel(place);
 panel.style.position="static";
 panel.style.display="block";
}

function createExportPanel(place) {
 var panel=document.getElementById("exportPanel");
 if (panel) { panel.parentNode.removeChild(panel); }
 setStylesheet(config.macros.exportTiddlers.css,"exportTiddlers");
 panel=createTiddlyElement(place,"span","exportPanel",null,null)
 panel.innerHTML=config.macros.exportTiddlers.html;
 exportShowPanel(document.location.protocol);
 exportInitFilter();
 refreshExportList(0);
 store.addNotification(null,refreshExportList); // refresh listbox after every tiddler change
 return panel;
}

function onClickExportMenu(e)
{
 if (!e) var e = window.event;
 var parent=resolveTarget(e).parentNode;
 var panel = document.getElementById("exportPanel");
 if (panel==undefined || panel.parentNode!=parent)
 panel=createExportPanel(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);
}
//}}}
// //===

// // +++[Hijack saveChanges] diverts 'notFileUrlError' to display export control panel instead
//{{{
window.coreSaveChanges=window.saveChanges;
window.saveChanges = function()
{
 if (document.location.protocol=="file:") { coreSaveChanges(); return; }
 var e = window.event;
 var parent=e?resolveTarget(e).parentNode:document.body;
 var panel = document.getElementById("exportPanel");
 if (panel==undefined || panel.parentNode!=parent) panel=createExportPanel(parent);
 exportShowPanel(document.location.protocol);
 if (parent==document.body) { panel.style.left="30%"; panel.style.top="30%"; }
 panel.style.display = "block" ;
}
//}}}
// //===

// // +++[IE needs explicit scoping] for functions called by browser events
//{{{
window.onClickExportMenu=onClickExportMenu;
window.onClickExportButton=onClickExportButton;
window.exportShowPanel=exportShowPanel;
window.exportShowFilterFields=exportShowFilterFields;
window.refreshExportList=refreshExportList;
//}}}
// //===

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

// // +++[HTML] for export control panel interface
//{{{
config.macros.exportTiddlers.html = '\
<!-- output target and format -->\
<table cellpadding="0" cellspacing="0"><tr><td width=50%>\
 export to\
 <select size=1 id="exportTo" onchange="exportShowPanel(this.value);">\
 <option value="file:" SELECTED>this computer</option>\
 <option value="http:">web server (http)</option>\
 <option value="https:">secure web server (https)</option>\
 <option value="ftp:">file server (ftp)</option>\
 </select>\
</td><td width=50%>\
 output format\
 <select id="exportFormat" size=1>\
 <option value="DIV">TiddlyWiki export file</option>\
 <option value="TW">TiddlyWiki document</option>\
 <option value="XML">RSS feed (XML)</option>\
 </select>\
</td></tr></table>\
\
<!-- export to local file -->\
<div id="exportLocalPanel" style="margin-bottom:5px;margin-top:5px;">\
local path/filename<br>\
<input type="file" id="exportFilename" size=56 style="width:100%"><br>\
</div><!--panel-->\
\
<!-- export to http server -->\
<div id="exportHTTPPanel" style="display:none;margin-bottom:5px;margin-top:5px;">\
document URL<br>\
<input type="text" id="exportHTTPSiteURL" onfocus="this.select()"><br>\
server script / parameters<br>\
<input type="text" id="exportHTTPServerURL" onfocus="this.select()"><br>\
</div><!--panel-->\
\
<!-- export to ftp server -->\
<div id="exportFTPPanel" style="display:none;margin-bottom:5px;margin-top:5px;">\
<table cellpadding="0" cellspacing="0" width="33%"><tr valign="top"><td>\
 host server<br>\
 <input type="text" id="exportFTPHost" onfocus="this.select()"><br>\
</td><td width="33%">\
 username<br>\
 <input type="text" id="exportFTPID" onfocus="this.select()"><br>\
</td><td width="33%">\
 password<br>\
 <input type="password" id="exportFTPPW" onfocus="this.select()"><br>\
</td></tr></table>\
FTP path/filename<br>\
<input type="text" id="exportFTPFilename" onfocus="this.select()"><br>\
</div><!--panel-->\
\
<!-- list of tiddlers -->\
<table><tr align="left"><td>\
 select:\
 <a href="JavaScript:;" id="exportSelectAll"\
 onclick="onClickExportButton(this)" title="select all tiddlers">\
 &nbsp;all&nbsp;</a>\
 <a href="JavaScript:;" id="exportSelectChanges"\
 onclick="onClickExportButton(this)" title="select tiddlers changed since last save">\
 &nbsp;changes&nbsp;</a> \
 <a href="JavaScript:;" id="exportSelectOpened"\
 onclick="onClickExportButton(this)" title="select tiddlers currently being displayed">\
 &nbsp;opened&nbsp;</a> \
 <a href="JavaScript:;" id="exportToggleFilter"\
 onclick="onClickExportButton(this)" title="show/hide selection filter">\
 &nbsp;filter&nbsp;</a> \
</td><td align="right">\
 <a href="JavaScript:;" id="exportListSmaller"\
 onclick="onClickExportButton(this)" title="reduce list size">\
 &nbsp;&#150;&nbsp;</a>\
 <a href="JavaScript:;" id="exportListLarger"\
 onclick="onClickExportButton(this)" title="increase list size">\
 &nbsp;+&nbsp;</a>\
</td></tr></table>\
<select id="exportList" multiple size="10" style="margin-bottom:5px;"\
 onchange="refreshExportList(this.selectedIndex)">\
</select><br>\
\
<!-- selection filter -->\
<div id="exportFilterPanel" style="display:none">\
<table><tr align="left"><td>\
 selection filter\
</td><td align="right">\
 <a href="JavaScript:;" id="exportHideFilter"\
 onclick="onClickExportButton(this)" title="hide selection filter">hide</a>\
</td></tr></table>\
<div class="box">\
<input type="checkbox" class="chk" id="exportFilterStart" value="1"\
 onclick="exportShowFilterFields(this)"> starting date/time<br>\
<table cellpadding="0" cellspacing="0"><tr valign="center"><td width="50%">\
 <select size=1 id="exportFilterStartBy" onchange="exportShowFilterFields(this);">\
 <option value="0">today</option>\
 <option value="1">yesterday</option>\
 <option value="7">a week ago</option>\
 <option value="30">a month ago</option>\
 <option value="site">SiteDate</option>\
 <option value="file">file date</option>\
 <option value="other">other (mm/dd/yyyy hh:mm)</option>\
 </select>\
</td><td width="50%">\
 <input type="text" id="exportStartDate" onfocus="this.select()"\
 onchange="document.getElementById(\'exportFilterStartBy\').value=\'other\';">\
</td></tr></table>\
<input type="checkbox" class="chk" id="exportFilterEnd" value="1"\
 onclick="exportShowFilterFields(this)"> ending date/time<br>\
<table cellpadding="0" cellspacing="0"><tr valign="center"><td width="50%">\
 <select size=1 id="exportFilterEndBy" onchange="exportShowFilterFields(this);">\
 <option value="0">today</option>\
 <option value="1">yesterday</option>\
 <option value="7">a week ago</option>\
 <option value="30">a month ago</option>\
 <option value="site">SiteDate</option>\
 <option value="file">file date</option>\
 <option value="other">other (mm/dd/yyyy hh:mm)</option>\
 </select>\
</td><td width="50%">\
 <input type="text" id="exportEndDate" onfocus="this.select()"\
 onchange="document.getElementById(\'exportFilterEndBy\').value=\'other\';">\
</td></tr></table>\
<input type="checkbox" class="chk" id=exportFilterTags value="1"\
 onclick="exportShowFilterFields(this)"> match tags<br>\
<input type="text" id="exportTags" onfocus="this.select()">\
<input type="checkbox" class="chk" id=exportFilterText value="1"\
 onclick="exportShowFilterFields(this)"> match titles/tiddler text<br>\
<input type="text" id="exportText" onfocus="this.select()">\
</div> <!--box-->\
</div> <!--panel-->\
\
<!-- action buttons -->\
<div style="text-align:center">\
<input type=button class="btn3" onclick="onClickExportButton(this)"\
 id="exportFilter" value="apply filter">\
<input type=button class="btn3" onclick="onClickExportButton(this)"\
 id="exportStart" value="export tiddlers">\
<input type=button class="btn3" onclick="onClickExportButton(this)"\
 id="exportClose" value="close">\
</div><!--center-->\
';
//}}}
// //===

// // +++[initialize interface]>
// // +++[exportShowPanel(which)]
//{{{
function exportShowPanel(which) {
 var index=0; var panel='exportLocalPanel';
 switch (which) {
 case 'file:':
 case undefined:
 index=0; panel='exportLocalPanel'; break;
 case 'http:':
 index=1; panel='exportHTTPPanel'; break;
 case 'https:':
 index=2; panel='exportHTTPPanel'; break;
 case 'ftp:':
 index=3; panel='exportFTPPanel'; break;
 default:
 alert("Sorry, export to "+which+" is not yet available");
 break;
 }
 exportInitPanel(which);
 document.getElementById('exportTo').selectedIndex=index;
 document.getElementById('exportLocalPanel').style.display='none';
 document.getElementById('exportHTTPPanel').style.display='none';
 document.getElementById('exportFTPPanel').style.display='none';
 document.getElementById(panel).style.display='block';
}
//}}}
// //===

// // +++[exportInitPanel(which)]
//{{{
function exportInitPanel(which) {
 switch (which) {
 case "file:": // LOCAL EXPORT PANEL: file/path:
 // ** no init - security issues in IE **
 break;
 case "http:": // WEB EXPORT PANEL
 case "https:": // SECURE WEB EXPORT PANEL
 // url
 var siteURL=store.getTiddlerText("SiteUrl");
 if (store.tiddlerExists("unawiki_download")) {
 var theURL=store.getTiddlerText("unawiki_download");
 theURL=theURL.replace(/\[\[download\|/,'').replace(/\]\]/,'');
 var title=(store.tiddlerExists("unawiki_host"))?"unawiki_host":"SiteHost";
 var theHost=store.getTiddlerText(title);
 if (!theHost || !theHost.length) theHost=document.location.host;
 if (!theHost || !theHost.length) theHost=title;
 siteURL=which+"//"+theHost+theURL
 }
 if (!siteURL) siteURL="SiteUrl";
 document.getElementById("exportHTTPSiteURL").value=siteURL;;
 // server script/params
 var title=(store.tiddlerExists("unawiki_host"))?"unawiki_host":"SiteHost";
 var theHost=store.getTiddlerText(title);
 if (!theHost || !theHost.length) theHost=document.location.host;
 if (!theHost || !theHost.length) theHost=title;
 // get POST
 var title=(store.tiddlerExists("unawiki_post"))?"unawiki_post":"SitePost";
 var thePost=store.getTiddlerText(title);
 if (!thePost || !thePost.length) thePost="/"+title;
 // get PARAMS
 var title=(store.tiddlerExists("unawiki_params"))?"unawiki_params":"SiteParams";
 var theParams=store.getTiddlerText(title);
 if (!theParams|| !theParams.length) theParams=title;
 var serverURL = which+"//"+theHost+thePost+"?"+theParams;
 document.getElementById("exportHTTPServerURL").value=serverURL;
 break;
 case "ftp:": // FTP EXPORT PANEL
 // host
 var siteHost=store.getTiddlerText("SiteHost");
 if (!siteHost || !siteHost.length) siteHost=document.location.host;
 if (!siteHost || !siteHost.length) siteHost="SiteHost";
 document.getElementById("exportFTPHost").value=siteHost;
 // username
 var siteID=store.getTiddlerText("SiteID");
 if (!siteID || !siteID.length) siteID=config.options.txtUserName;
 document.getElementById("exportFTPID").value=siteID;
 // password
 document.getElementById("exportFTPPW").value="";
 // file/path
 document.getElementById("exportFTPFilename").value="";
 break;
 }
}
//}}}
// //===

// // +++[exportInitFilter()]
//{{{
function exportInitFilter() {
 // TBD: persistent settings via local cookies
 // start date
 document.getElementById("exportFilterStart").checked=false;
 document.getElementById("exportStartDate").value="";
 // end date
 document.getElementById("exportFilterEnd").checked=false;
 document.getElementById("exportEndDate").value="";
 // tags
 document.getElementById("exportFilterTags").checked=false;
 document.getElementById("exportTags").value="not excludeExport";
 // text
 document.getElementById("exportFilterText").checked=false;
 document.getElementById("exportText").value="";
 // show/hide filter input fields
 exportShowFilterFields();
}
//}}}
// //===

// // +++[exportShowFilterFields(which)]
//{{{
function exportShowFilterFields(which) {
 var show;

 show=document.getElementById('exportFilterStart').checked;
 document.getElementById('exportFilterStartBy').style.display=show?"block":"none";
 document.getElementById('exportStartDate').style.display=show?"block":"none";
 var val=document.getElementById('exportFilterStartBy').value;
 document.getElementById('exportStartDate').value
 =getFilterDate(val,'exportStartDate').formatString(config.macros.exportTiddlers.datetimefmt);
 if (which && (which.id=='exportFilterStartBy') && (val=='other'))
 document.getElementById('exportStartDate').focus();

 show=document.getElementById('exportFilterEnd').checked;
 document.getElementById('exportFilterEndBy').style.display=show?"block":"none";
 document.getElementById('exportEndDate').style.display=show?"block":"none";
 var val=document.getElementById('exportFilterEndBy').value;
 document.getElementById('exportEndDate').value
 =getFilterDate(val,'exportEndDate').formatString(config.macros.exportTiddlers.datetimefmt);
 if (which && (which.id=='exportFilterEndBy') && (val=='other'))
 document.getElementById('exportEndDate').focus();

 show=document.getElementById('exportFilterTags').checked;
 document.getElementById('exportTags').style.display=show?"block":"none";

 show=document.getElementById('exportFilterText').checked;
 document.getElementById('exportText').style.display=show?"block":"none";
}
//}}}
// //===
// //===

// // +++[onClickExportButton(which): control interactions]
//{{{
function onClickExportButton(which)
{
 // DEBUG alert(which.id);
 var theList=document.getElementById('exportList'); if (!theList) return;
 var count = 0;
 var total = store.getTiddlers('title').length;
 switch (which.id)
 {
 case 'exportFilter':
 count=filterExportList();
 var panel=document.getElementById('exportFilterPanel');
 if (count==-1) { panel.style.display='block'; break; }
 theList.options[0].text=formatExportListHeader(count,total);
 document.getElementById("exportStart").disabled=(count==0);
 clearMessage(); displayMessage("filtered "+theList.options[0].text);
 if (count==0) { alert("No tiddlers were selected"); panel.style.display='block'; }
 break;
 case 'exportStart':
 exportTiddlers();
 break;
 case 'exportHideFilter':
 case 'exportToggleFilter':
 var panel=document.getElementById('exportFilterPanel')
 panel.style.display=(panel.style.display=='block')?'none':'block';
 break;
 case 'exportSelectChanges':
 var lastmod=new Date(document.lastModified);
 for (var t = 0; t < theList.options.length; t++) {
 if (theList.options[t].value=="") continue;
 var tiddler=store.getTiddler(theList.options[t].value); if (!tiddler) continue;
 theList.options[t].selected=(tiddler.modified>lastmod);
 count += (tiddler.modified>lastmod)?1:0;
 }
 theList.options[0].text=formatExportListHeader(count,total);
 document.getElementById("exportStart").disabled=(count==0);
 clearMessage(); displayMessage(theList.options[0].text);
 if (count==0) alert("There are no unsaved changes");
 break;
 case 'exportSelectAll':
 for (var t = 0; t < theList.options.length; t++) {
 if (theList.options[t].value=="") continue;
 theList.options[t].selected=true;
 count += 1;
 }
 theList.options[0].text=formatExportListHeader(count,count);
 document.getElementById("exportStart").disabled=(count==0);
 clearMessage(); displayMessage(theList.options[0].text);
 break;
 case 'exportSelectOpened':
 for (var t = 0; t < theList.options.length; t++) theList.options[t].selected=false;
 var tiddlerDisplay = document.getElementById("tiddlerDisplay");
 for (var t=0;t<tiddlerDisplay.childNodes.length;t++) {
 var tiddler=tiddlerDisplay.childNodes[t].id.substr(7);
 for (var i = 0; i < theList.options.length; i++) {
 if (theList.options[i].value!=tiddler) continue;
 theList.options[i].selected=true; count++; break;
 }
 }
 theList.options[0].text=formatExportListHeader(count,total);
 document.getElementById("exportStart").disabled=(count==0);
 clearMessage(); displayMessage(theList.options[0].text);
 if (count==0) alert("There are no tiddlers currently opened");
 break;
 case 'exportListSmaller': // decrease current listbox size
 var min=5;
 theList.size-=(theList.size>min)?1:0;
 break;
 case 'exportListLarger': // increase current listbox size
 var max=(theList.options.length>25)?theList.options.length:25;
 theList.size+=(theList.size<max)?1:0;
 break;
 case 'exportClose':
 document.getElementById('exportPanel').style.display='none';
 break;
 }
}
//}}}
// //===

// // +++[list display]
//{{{
function formatExportListHeader(count,total)
{
 var txt=total+' tiddler'+((total!=1)?'s':'')+" - ";
 txt += (count==0)?"none":(count==total)?"all":count;
 txt += " selected for export";
 return txt;
}

function refreshExportList(selectedIndex)
{
 var theList = document.getElementById("exportList");
 var sort;
 if (!theList) return;
 // get the sort order
 if (!selectedIndex) selectedIndex=0;
 if (selectedIndex==0) sort='modified';
 if (selectedIndex==1) sort='title';
 if (selectedIndex==2) sort='modified';
 if (selectedIndex==3) sort='modifier';

 // get the alphasorted list of tiddlers
 var tiddlers = store.getTiddlers('title');
 // unselect headings and count number of tiddlers actually selected
 var count=0;
 for (var i=0; i<theList.options.length; i++) {
 if (theList.options[i].value=="") theList.options[i].selected=false;
 count+=theList.options[i].selected?1:0;
 }
 // disable "export" button if no tiddlers selected
 document.getElementById("exportStart").disabled=(count==0);
 // update listbox heading to show selection count
 if (theList.options.length)
 theList.options[0].text=formatExportListHeader(count,tiddlers.length);

 // if a [command] item, reload list... otherwise, no further refresh needed
 if (selectedIndex>3) return;

 // 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(formatExportListHeader(0,tiddlers.length), "",false,false);
 theList.options[i++]=
 new Option(((sort=="title" )?">":indent)+' [by title]', "",false,false);
 theList.options[i++]=
 new Option(((sort=="modified")?">":indent)+' [by date]', "",false,false);
 theList.options[i++]=
 new Option(((sort=="modifier")?">":indent)+' [by author]', "",false,false);
 // output the tiddler list
 switch(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 "modifier":
 case "modified":
 var tiddlers = store.getTiddlers(sort);
 // sort descending for newest date first
 tiddlers.sort(function (a,b) {if(a[sort] == b[sort]) return(0); else return (a[sort] > b[sort]) ? -1 : +1; });
 var lastSection = "";
 for(var t = 0; t < tiddlers.length; t++)
 {
 var tiddler = tiddlers[t];
 var theSection = "";
 if (sort=="modified") theSection=tiddler.modified.toLocaleDateString();
 if (sort=="modifier") theSection=tiddler.modifier;
 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;
 }
 theList.selectedIndex=selectedIndex; // select current control item
}
//}}}
// //===

// // +++[list filtering]
//{{{
function getFilterDate(val,id)
{
 var result=0;
 switch (val) {
 case 'site':
 var timestamp=store.getTiddlerText("SiteDate");
 if (!timestamp) timestamp=document.lastModified;
 result=new Date(timestamp);
 break;
 case 'file':
 result=new Date(document.lastModified);
 break;
 case 'other':
 result=new Date(document.getElementById(id).value);
 break;
 default: // today=0, yesterday=1, one week=7, two weeks=14, a month=31
 var now=new Date(); var tz=now.getTimezoneOffset()*60000; now-=tz;
 var oneday=86400000;
 if (id=='exportStartDate')
 result=new Date((Math.floor(now/oneday)-val)*oneday+tz);
 else
 result=new Date((Math.floor(now/oneday)-val+1)*oneday+tz-1);
 break;
 }
 // DEBUG alert('getFilterDate('+val+','+id+')=='+result+"\nnow="+now);
 return result;
}

function filterExportList()
{
 var theList = document.getElementById("exportList"); if (!theList) return -1;

 var filterStart=document.getElementById("exportFilterStart").checked;
 var val=document.getElementById("exportFilterStartBy").value;
 var startDate=getFilterDate(val,'exportStartDate');

 var filterEnd=document.getElementById("exportFilterEnd").checked;
 var val=document.getElementById("exportFilterEndBy").value;
 var endDate=getFilterDate(val,'exportEndDate');

 var filterTags=document.getElementById("exportFilterTags").checked;
 var tags=document.getElementById("exportTags").value;

 var filterText=document.getElementById("exportFilterText").checked;
 var text=document.getElementById("exportText").value;

 if (!(filterStart||filterEnd||filterTags||filterText)) {
 alert("Please set the selection filter");
 document.getElementById('exportFilterPanel').style.display="block";
 return -1;
 }
 if (filterStart&&filterEnd&&(startDate>endDate)) {
 var msg="starting date/time:\n"
 msg+=startDate.toLocaleString()+"\n";
 msg+="is later than ending date/time:\n"
 msg+=endDate.toLocaleString()
 alert(msg);
 return -1;
 }

 // scan list and select tiddlers that match all applicable criteria
 var total=0;
 var count=0;
 for (var i=0; i<theList.options.length; i++) {
 // get item, skip non-tiddler list items (section headings)
 var opt=theList.options[i]; if (opt.value=="") continue;
 // get tiddler, skip missing tiddlers (this should NOT happen)
 var tiddler=store.getTiddler(opt.value); if (!tiddler) continue; 
 var sel=true;
 if ( (filterStart && tiddler.modified<startDate)
 || (filterEnd && tiddler.modified>endDate)
 || (filterTags && !matchTags(tiddler,tags))
 || (filterText && (tiddler.text.indexOf(text)==-1) && (tiddler.title.indexOf(text)==-1)))
 sel=false;
 opt.selected=sel;
 count+=sel?1:0;
 total++;
 }
 return count;
}
//}}}

//{{{
function matchTags(tiddler,cond)
{
 if (!cond||!cond.trim().length) return false;

 // build a regex of all tags as a big-old regex that 
 // OR's the tags together (tag1|tag2|tag3...) in length order
 var tgs = store.getTags();
 if ( tgs.length == 0 ) return results ;
 var tags = tgs.sort( function(a,b){return (a[0].length<b[0].length)-(a[0].length>b[0].length);});
 var exp = "(" + tags.join("|") + ")" ;
 exp = exp.replace( /(,[\d]+)/g, "" ) ;
 var regex = new RegExp( exp, "ig" );

 // build a string such that an expression that looks like this: tag1 AND tag2 OR NOT tag3
 // turns into : /tag1/.test(...) && /tag2/.test(...) || ! /tag2/.test(...)
 cond = cond.replace( regex, "/$1\\|/.test(tiddlerTags)" );
 cond = cond.replace( /\sand\s/ig, " && " ) ;
 cond = cond.replace( /\sor\s/ig, " || " ) ;
 cond = cond.replace( /\s?not\s/ig, " ! " ) ;

 // if a boolean uses a tag that doesn't exist - it will get left alone 
 // (we only turn existing tags into actual tests).
 // replace anything that wasn't found as a tag, AND, OR, or NOT with the string "false"
 // if the tag doesn't exist then /tag/.test(...) will always return false.
 cond = cond.replace( /(\s|^)+[^\/\|&!][^\s]*/g, "false" ) ;

 // make a string of the tags in the tiddler and eval the 'cond' string against that string 
 // if it's TRUE then the tiddler qualifies!
 var tiddlerTags = (tiddler.tags?tiddler.tags.join("|"):"")+"|" ;
 try { if ( eval( cond ) ) return true; }
 catch( e ) { displayMessage("Error in tag filter '" + e + "'" ); }
 return false;
}
//}}}
// //===

// // +++[output data formatting]>
// // +++[exportHeader(format)]
//{{{
function exportHeader(format)
{
 switch (format) {
 case "TW": return exportTWHeader();
 case "DIV": return exportDIVHeader();
 case "XML": return exportXMLHeader();
 }
}
//}}}
// //===

// // +++[exportFooter(format)]
//{{{
function exportFooter(format)
{
 switch (format) {
 case "TW": return exportDIVFooter();
 case "DIV": return exportDIVFooter();
 case "XML": return exportXMLFooter();
 }
}
//}}}
// //===

// // +++[exportTWHeader()]
//{{{
function exportTWHeader()
{
 // Get the URL of the document
 var originalPath = document.location.toString();
 // Check we were loaded from a file URL
 if(originalPath.substr(0,5) != "file:")
 { alert(config.messages.notFileUrlError); return; }
 // 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"),"\\");
 // Load the original file
 var original = loadFile(localPath);
 if(original == null)
 { alert(config.messages.cantSaveError); return; }
 // Locate the storeArea div's
 var posOpeningDiv = original.indexOf(startSaveArea);
 var posClosingDiv = original.lastIndexOf(endSaveArea);
 if((posOpeningDiv == -1) || (posClosingDiv == -1))
 { alert(config.messages.invalidFileError.format([localPath])); return; }
 return original.substr(0,posOpeningDiv+startSaveArea.length)
}
//}}}
// //===

// // +++[exportDIVHeader()]
//{{{
function exportDIVHeader()
{
 var out=[];
 var now = new Date();
 var u = store.getTiddlerText("SiteUrl",null);
 var title = wikifyPlain("SiteTitle").htmlEncode();
 var subtitle = wikifyPlain("SiteSubtitle").htmlEncode();
 var user = config.options.txtUserName.htmlEncode();
 var twver = version.major+"."+version.minor+"."+version.revision;
 var pver = version.extensions.exportTiddlers.major+"."
 +version.extensions.exportTiddlers.minor+"."+version.extensions.exportTiddlers.revision;
 out.push("<html><body>");
 out.push("<style type=\"text/css\">");
 out.push("#storeArea {display:block;margin:1em;}");
 out.push("#storeArea div");
 out.push("{padding:0.5em;margin:1em;border:2px solid black;height:10em;overflow:auto;}");
 out.push("#javascriptWarning");
 out.push("{width:100%;text-align:left;background-color:#eeeeee;padding:1em;}");
 out.push("</style>");
 out.push("<div id=\"javascriptWarning\">");
 out.push("TiddlyWiki export file<br>");
 out.push("Source: <b>"+document.location+"</b><br>");
 out.push("Title: <b>"+title+"</b><br>");
 out.push("Subtitle: <b>"+subtitle+"</b><br>");
 out.push("Created: <b>"+now.toLocaleString()+"</b> by <b>"+user+"</b><br>");
 out.push("TiddlyWiki "+twver+" / "+"ExportTiddlersPlugin "+pver+"<br>");
 out.push("</div>");
 out.push("<div id=\"storeArea\">");
 return out;
}
//}}}
// //===

// // +++[exportDIVFooter()]
//{{{
function exportDIVFooter()
{
 var out=[];
 out.push("</div></body></html>");
 return out;
}
//}}}
// //===

// // +++[exportXMLHeader()]
//{{{
function exportXMLHeader()
{
 var out=[];
 var now = new Date();
 var u = store.getTiddlerText("SiteUrl",null);
 var title = wikifyPlain("SiteTitle").htmlEncode();
 var subtitle = wikifyPlain("SiteSubtitle").htmlEncode();
 var user = config.options.txtUserName.htmlEncode();
 var twver = version.major+"."+version.minor+"."+version.revision;
 var pver = version.extensions.exportTiddlers.major+"."
 +version.extensions.exportTiddlers.minor+"."+version.extensions.exportTiddlers.revision;
 out.push("<" + "?xml version=\"1.0\"?" + ">");
 out.push("<rss version=\"2.0\">");
 out.push("<channel>");
 out.push("<title>" + title + "</title>");
 if(u) out.push("<link>" + u.htmlEncode() + "</link>");
 out.push("<description>" + subtitle + "</description>");
 out.push("<language>en-us</language>");
 out.push("<copyright>Copyright " + now.getFullYear() + " " + user + "</copyright>");
 out.push("<pubDate>" + now.toGMTString() + "</pubDate>");
 out.push("<lastBuildDate>" + now.toGMTString() + "</lastBuildDate>");
 out.push("<docs>http://blogs.law.harvard.edu/tech/rss</docs>");
 out.push("<generator>TiddlyWiki "+twver+" plus ExportTiddlersPlugin "+pver+"</generator>");
 return out;
}
//}}}
// //===

// // +++[exportXMLFooter()]
//{{{
function exportXMLFooter()
{
 var out=[];
 out.push("</channel></rss>");
 return out;
}
//}}}
// //===

// // +++[exportData()]
//{{{
function exportData(theList,theFormat)
{
 // scan export listbox and collect DIVs or XML for selected tiddler content
 var out=[];
 for (var i=0; i<theList.options.length; i++) {
 // get item, skip non-selected items and section headings
 var opt=theList.options[i]; if (!opt.selected||(opt.value=="")) continue;
 // get tiddler, skip missing tiddlers (this should NOT happen)
 var thisTiddler=store.getTiddler(opt.value); if (!thisTiddler) continue; 
 if (theFormat=="TW") out.push(thisTiddler.saveToDiv());
 if (theFormat=="DIV") out.push(thisTiddler.title+"\n"+thisTiddler.saveToDiv());
 if (theFormat=="XML") out.push(thisTiddler.saveToRss());
 }
 return out;
}
//}}}
// //===
// //===

// // +++[exportTiddlers(): output selected data to local or server]
//{{{
function exportTiddlers()
{
 var theList = document.getElementById("exportList"); if (!theList) return;

 // get the export settings
 var theProtocol = document.getElementById("exportTo").value;
 var theFormat = document.getElementById("exportFormat").value;

 // assemble output: header + tiddlers + footer
 var theData=exportData(theList,theFormat);
 var count=theData.length;
 var out=[]; var txt=out.concat(exportHeader(theFormat),theData,exportFooter(theFormat)).join("\n");
 var msg="";
 switch (theProtocol) {
 case "file:":
 var theTarget = document.getElementById("exportFilename").value.trim();
 if (!theTarget.length) msg = "A local path/filename is required\n";
 if (!msg && saveFile(theTarget,txt))
 msg=count+" tiddler"+((count!=1)?"s":"")+" exported to local file";
 else if (!msg)
 msg+="An error occurred while saving to "+theTarget;
 break;
 case "http:":
 case "https:":
 var theTarget = document.getElementById("exportHTTPServerURL").value.trim();
 if (!theTarget.length) msg = "A server URL is required\n";
 if (!msg && exportPost(theTarget+encodeURIComponent(txt)))
 msg=count+" tiddler"+((count!=1)?"s":"")+" exported to "+theProtocol+" server";
 else if (!msg)
 msg+="An error occurred while saving to "+theTarget;
 break;
 case "ftp:":
 default:
 msg="Sorry, export to "+theLocation+" is not yet available";
 break;
 }
 clearMessage(); displayMessage(msg,theTarget);
}
//}}}
// //===

// // +++[exportPost(url): cross-domain post] uses hidden iframe to submit url and capture responses
//{{{
function exportPost(url)
{
 var f=document.getElementById("exportFrame"); if (f) document.body.removeChild(f);
 f=document.createElement("iframe"); f.id="exportFrame";
 f.style.width="0px"; f.style.height="0px"; f.style.border="0px";
 document.body.appendChild(f);
 var d=f.document;
 if (f.contentDocument) d=f.contentDocument; // For NS6
 else if (f.contentWindow) d=f.contentWindow.document; // For IE5.5 and IE6
 d.location.replace(url);
 return true;
}
//}}}
// //===
[[internal link(not existing)]]
[[internal link(exists)|StyleSheet]]
[[external link|http://www.tiddlywiki.com]]
[[a pretty link that doesn't exist|blahBlueBlah]]

<<newTiddler>>

!Header 1
!!Header 2
!!!Header 3
!!!!Header 4
!!!!!Header 5

''Bold''
--Strike--
__Underline__
//Italic//
2^^3^^=8
a~~ij~~ = -a~~ji~~
@@highlight@@
@@color(green):green colored@@
@@bgcolor(#ff0000):color(#ffffff):red colored@@

[img[Selectutorial|http://css.maxdesign.com.au/selectutorial/images/header.gif][http://css.maxdesign.com.au/selectutorial/]]

{{{
 Preformated text
}}}

I think this is {{{rendered as code}}}.

To make quoted bits of text stand out, you can use BlockQuotes within your [[tiddler]]s, like this:

JeremyRuston said:
<<<
A TiddlyWiki is like a blog because it's divided up into neat little chunks, but it encourages you to read it by hyperlinking rather than sequentially: if you like, a non-linear blog analogue that binds the individual microcontent items into a cohesive whole.
<<<

Like BulletPoints and NumberedBulletPoints, you can have three different levels of BlockQuotes. Just [[edit]] this tiddler to see how it's done.

>level 1
>level 1
>>level 2
>>level 2
>>>level 3
>>>level 3
>>level 2
>level 1

Creating BulletPoints is simple.
* Just add an asterisk
* at the beginning of a line.
** If you want to create sub-bullets
** start the line with two asterisks
*** And if you want yet another level
*** use three asterisks
* Edit this tiddler to see how it's done
* You can also do NumberedBulletPoints

It's easy to create NumberedBulletPoints.
# Use a single '#' at the start of each line
# and the tiddler will automatically
# start numbering your list.
## If you want a sub-list
## within any bullets
## add two '#'s at the start of the lines.
# When you go back to a single '#'
# the main numbered list will start up
# where it left off.
It's just as simple to do normal BulletPoints.

Edit this tiddler to see how to insert images.
[img[Fractal vegetable|fractalveg.jpg]]
(This curious vegetable is called 'Romanesque broccoli' and is one of [[my photos|http://www.flickr.com/photos/jermy/]])

You can divide a tiddler into
----
sections by typing four dashes on a line by themselves

*sample:
|!th1111111111|!th2222222222|
|>| colspan |
| rowspan |left|
|~| right|
|bgcolor(#a0ffa0):colored| center |
|caption|c

*sample (changed caption and table headers):
|caption|c
|th1111111111|th2222222222|h
|>| colspan |
| rowspan |left|
|~| right|
|bgcolor(#a0ffa0):colored| center |

|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
Miscellaneous free games that are super fun...

* [[Tremulous|http://digg.com/linux_unix/Tremulous_The_best_free_software_game_ever]]
* Gate 88
* Wing Commander Privateer
/***
''GotoPlugin for TiddlyWiki version 2.x''
^^author: Eric Shulman - ELS Design Studios
source: http://www.TiddlyTools.com/#GotoPlugin
license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^

''View a tiddler by typing its title and pressing //enter//.''  Input just enough to uniquely match a single tiddler title and ''press //enter// to auto-complete the title for you!!''  If multiple titles match your input, a list is displayed.  You can scroll-and-click (or use arrows+enter) to select/view a tiddler, or press //escape// to close the listbox to resume typing.  When the listbox is ''//not//'' being displayed, press //escape// to clear the current text input and start over.

Note: ''At any time, you can move the focus directly to the text input field by using the ~ALT-G keyboard shortcut.''
!!!!!Example
<<<
syntax: {{{<<goto quiet inputstyle liststyle>>}}}
All parameters are optional.
* ''quiet'' prevents //automatic// display of the list as each character is typed.  To view the list when ''quiet'', use //down// or //enter//.
* ''inputstyle'' and ''liststyle'' are CSS declarations that modify the default input and listbox styles.  Note: styles containing spaces must be surrounded by ({{{"..."}}} or {{{'...'}}}) or ({{{[[...]]}}}).
{{{<<goto>>}}}
<<goto>>
{{{<<goto quiet>>}}}
<<goto quiet>>
{{{<<goto width:20em width:20em>>}}}
<<goto width:20em width:20em>>
<<<
!!!!!Configuration
<<<
The following ~TiddlyWiki search options (see AdvancedOptions) are applied when matching tiddler titles:
><<option chkRegExpSearch>> use regular expressions (text patterns)
><<option chkCaseSensitiveSearch>> use case sensitive matching
You can also create a tiddler tagged with <<tag systemConfig>> to control listing of tiddlers/shadows/tags, as well as the maximum height of the listbox.  //The default values are shown below://
//{{{
config.macros.goto.includeTiddlers=true;
config.macros.goto.includeShadows=true;
config.macros.goto.includeTags=true;
config.macros.goto.listMaxSize=10;
//}}}
<<<
!!!!!Installation
<<<
import (or copy/paste) the following tiddlers into your document:
''GotoPlugin'' (tagged with <<tag systemConfig>>)
<<<
!!!!!Revisions
<<<
''2006.05.10 [1.1.2]'' when filling listbox, set selection to 'heading' item... auto-select first tiddler title when down/enter moves focus into listbox
''2006.05.08 [1.1.1]'' added accesskey ("G") to input field html (also set when field gets focus).  Also, inputKeyHandler() skips non-printing/non-editing keys. 
''2006.05.08 [1.1.0]'' added heading to listbox for better feedback (also avoids problems with 1-line droplist)
''2006.05.07 [1.0.0]'' list matches against tiddlers/shadows/tags.  input field auto-completion... 1st enter=complete matching input (or show list)... 2nd enter=view tiddler.  optional "quiet" param controls when listbox appears.
''2006.05.06 [0.5.0]'' added handling for enter (13), escape(27), and down(40) keys.   Change 'ondblclick' to 'onclick' for list handler to view tiddlers (suggested by Florian Cauvin - prevents unintended trigger of tiddler editor).  shadow titles inserted into list instead of appended to the end.
''2006.05.05 [0.0.0]'' started
<<<
!!!!!Credits
>This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
!!!!!Code
***/
//{{{
version.extensions.goto= {major: 1, minor: 1, revision: 2, date: new Date(2006,5,10)};
config.macros.goto= { 
	handler:
	function(place,macroName,params) {
		var quiet=(params[0] && params[0]=="quiet"); if (quiet) params.shift();
		var instyle=params.shift(); if (!instyle) instyle="";
		var liststyle=params.shift(); if (!liststyle) liststyle="";
		createTiddlyElement(place,"span").innerHTML
			=this.html.replace(/%quiet%/,quiet).replace(/%instyle%/,instyle).replace(/%liststyle%/,liststyle);
	},

	html:
	'<form onsubmit="return false" style="display:inline;margin:0;padding:0">\
		<input name=goto type=text autocomplete="off" accesskey="G" style="%instyle%"\
			title="enter a tiddler title"\
			onfocus="this.setAttribute(\'accessKey\',\'G\'); this.select();"\
			onkeyup="return config.macros.goto.inputKeyHandler(event,this,this.form.list,%quiet%);">\
		<select name=list style="%liststyle%;display:none;position:absolute"\
			onchange="if (!this.selectedIndex) this.selectedIndex=1;"\
			onblur="this.style.display=\'none\';"\
			onkeyup="return config.macros.goto.selectKeyHandler(event,this,this.form.goto);"\
			onclick="return config.macros.goto.openItem(this.value,this.form.goto,this);">\
		</select>\
	</form>',
	
	getItems:
	function() {
		var items=[];
		var tiddlers=store.reverseLookup("tags","excludeSearch",false,"title");
		if (this.includeTiddlers) for(var t=0; t<tiddlers.length; t++) items.push(tiddlers[t].title);
		if (this.includeShadows) for (var t in config.shadowTiddlers) items.pushUnique(t);
		if (this.includeTags) { var tags=store.getTags(); for(var t=0; t<tags.length; t++) items.pushUnique(tags[t][0]); }
		return items;
	},
	includeTiddlers: true, includeShadows: true, includeTags: true,

	getItemSuffix:
	function(t) {
		if (store.tiddlerExists(t)) return "";  // tiddler
		if (store.isShadowTiddler(t)) return " (shadow)"; // shadow
		return " (tag)"; // tag 
	},

	openItem:
	function(title,here,list) {
		if (!title.length) return; here.value=title; list.style.display='none'; story.displayTiddler(null,title); return false;
	},

	inputKeyHandler:
	function(event,here,list,quiet) {
		var key=event.keyCode;
		// non-printing chars... bubble up, except: backspace=8, enter=13, escape=27, space=32, down=40, delete=46
		if (key<48) switch(key) { case 8: case 13: case 27: case 32: case 40: case 46: break; default: return true; }
		// escape... hide list (2nd esc=reset input)
		if (key==27) { if (list.style.display=="none") here.value=here.defaultValue; else list.style.display="none"; return false; }
		// blank input... if down/enter... fall through (list all)... else, hide list
		if (!here.value.length && !(key==40 || key==13)) { list.style.display="none"; return false; }
		// find matching items...
		var pattern=config.options.chkRegExpSearch?here.value:here.value.escapeRegExp();
		var re=new RegExp(pattern,config.options.chkCaseSensitiveSearch?"mg":"img");
		var found = []; var items=this.getItems(); for(var t=0; t<items.length; t++) if(items[t].search(re)!=-1) found.push(items[t]);
		// matched one item... enter... not *exact* match... autocomplete input field
		if (found.length==1 && quiet && key==13 && here.value!=found[0]) { list.style.display="none"; here.value=found[0]; return false; }
		// no match/exact match... enter... create/show it
		if (found.length<2 && key==13) return this.openItem(found.length?found[0]:here.value,here,list);
		// quiet/no match... hide list...
		list.style.display=(!quiet && found.length)?"block":"none";
		// no matches... key bubbles up
		if (!found.length) return true;
		// down/enter... show/move to list...
		if (key==40 || key==13)  { list.style.display="block"; list.focus(); }
		// list is showing... fill list...
		if (list.style.display!="none") {
			while (list.length > 0) list.options[0]=null; // clear list...
			found.sort();
			list.options[0]=new Option(found.length==1?this.listMatchMsg:this.listHeading.format([found.length]),"",false,false);
			for (var t=0; t<found.length; t++)  // fill list...
				list.options[t+1]=new Option(found[t]+this.getItemSuffix(found[t]),found[t],false,false);
			list.size=(found.length<this.listMaxSize?found.length:this.listMaxSize)+1; // resize list...
			list.selectedIndex=(key==40 || key==13)?1:0;
		}
		return true; // key bubbles up
	},
	listMaxSize: 10,
	listHeading: 'Found %0 matching titles:',
	listMatchMsg: 'Press enter to open tiddler...',

	selectKeyHandler:
	function(event,list,editfield) {
		if (event.keyCode==27) // escape... hide list, move to edit field
			{ editfield.focus(); list.style.display="none"; return false; }
		if (event.keyCode==13 && list.value.length) // enter... view selected item
			{ this.openItem(list.value,editfield,list); return false; }
		return true;
	}
}
//}}}
[[Homepage|http://shared.snapgrid.com/gtd_tiddlywiki.html]]
!Help links:
[[Formatting Test]]
[[Shadow Tiddlers]]

!SystemConfig
Recent posts:
<<list tagged content>>
<<importTiddlers inline>>
<<twupdate>> TiddlyWiki core
/***
|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");	
}
//}}}
!!Pre Packaged:
* [[AppFuse|http://raibledesigns.com/wiki/Wiki.jsp?page=AppFuse]]
* [[Equinox|https://equinox.dev.java.net/]]
!!Frameworks
* [[Spring|SpringFramework]]
* [[WebWork|http://www.opensymphony.com/webwork/wikidocs/WebWork.html]]
* [[Tapestry|http://tapestry.apache.org/]]
* [[Grails|http://grails.codehaus.org/]]
* FreeMarker + Spring
* [[Echo2|http://www.nextapp.com/platform/echo2/echo/]]
* Struts
* [[GWT|http://code.google.com/webtoolkit/]]
* JSP/ Servlets
* JSF/ MyFaces + [[AjaxAnywhere|http://ajaxanywhere.sourceforge.net/jsf.html]]
* [[DWR|http://getahead.ltd.uk/dwr/overview/dwr]]
* [[JRuby|http://jruby.sourceforge.net/]]
!Ajax
[[Ajax for java|http://www-128.ibm.com/developerworks/views/java/libraryview.jsp?search_by=Ajax+Java%20developers]]
//{{{
config.macros.list.tagged = {}
config.macros.list.tagged.innerHandler = function(tagList, allTags)
{
	var tiddlers = store.getTaggedTiddlers(tagList[0]);

	if (allTags) {
		var results = [];
		for (var i = 0; i < tiddlers.length; i++) {
			var tiddler = tiddlers[i], hasAllTags = true;
			for (var j = 1; hasAllTags && j < tagList.length; j++) {
				// hasAllTags &= _GTD.tiddlerHasTag(tiddler, tagList[j]);
				hasAllTags &= (tagList[j].charAt(0) == '-') ? !_GTD.tiddlerHasTag(tiddler, tagList[j].substr(1)) : _GTD.tiddlerHasTag(tiddler, tagList[j])
			}
			if (hasAllTags) results.push(tiddlers[i]);
		}
		return results;
	}
	else {
		for (var i = 1; i < tagList.length; i++) {
			var more = store.getTaggedTiddlers(tagList[i]);
			for (var j = 0; j < more.length; j++)
				tiddlers.pushUnique(more[j]);
		}
		return tiddlers;
	}
}
config.macros.list.tagged.handler = function(params) 
{
	var tags = params[1].readBracketedList();
	if (tags.length == 1) {
		if (config.options[tags[0]] == undefined)
			return store.getTaggedTiddlers(tags[0]);
		else
			return store.getTaggedTiddlers(config.options[tags[0]]);
	}
	else if (tags.length > 1) {
		var allTags = (params[2] == undefined || params[2] == 'all');
		var tiddlers = this.innerHandler(tags, allTags);
		tiddlers.sort(function (a,b) {if(a.title == b.title) return(0); else return (a.title < b.title) ? -1 : +1; });
		return tiddlers;
	}
}
//}}}
* [[Home]]
* [[Original Content]]
* [[Articles]]
* [[Editorial]]
* [[Code Tricks]]
* [[About]]
++++![Calendar]
<<calendar thismonth>>
===
*[[Help]]
Maven doesn't necessarily solve world hunger, but one thing it does do well is dependency management.  This is a godsend if you are using multiple frameworks that have a lot of dependencies of their own.  

Many people still want to use Ant for their builds, and luckily Maven can be used within Ant //just for the dependency management// while staying out of the way for the rest.  

So what I did was put together a simple Maven taskdef bootstrap that will download the maven tasks and then generate classpaths, ''even if the user doesn't already have Maven installed''.  Handy, huh?

! Your Ant Script...
{{{
<project name="myProject" basedir="." 
  xmlns:mvn="antlib:org.apache.maven.artifact.ant">

  <import file="maven-bootstrap.xml" />
  <target name="init" depends="maven-init" if="use.maven">
    <echo>Building dependency tree from POM files...</echo>
    <mvn:pom id="pom.all" file="pom.xml" />
    <mvn:dependencies pathid="cp.all" pomrefid="pom.all" usescope="compile" />
    <mvn:dependencies pathid="cp.test" pomrefid="pom.all" usescope="test" />
    <mvn:dependencies filesetid="deps.ear" pomrefid="pom.all" usescope="runtime" />
  </target>
</build>
}}}

At this point you would have two classpaths (cp.all, cp.test) defined which you could use for compiling and running tests.  The third item, (deps.ear) is a fileset you could use to copy all of your runtime dependencies when packaging your EAR or WAR.

+++!![ maven-bootstrap.xml...]
{{{
<project name="maven-bootstrap" default="maven-init" 
    xmlns:mvn="antlib:org.apache.maven.artifact.ant">
	
  <description>
    This Ant buildfile will attempt to download Maven-Ant JAR so that Maven
    may be used within Ant buildfiles.
  </description>

  <property name="maven.bootstrap.server"
    value="http://ibiblio.org/pub/packages/maven2/org/apache/maven/maven-ant-tasks/2.0.7/" />
  <property name="maven.bootstrap.artifact.ant"
    value="maven-ant-tasks-2.0.7.jar" />

  <property name="maven.settings.dir" value="${user.home}/.m2/"/>
  <property name="maven.settings.file" value="settings.xml" />
  <!-- For company-specific settings, i.e. your own in-house repo if you have one. -->
  <property name="maven.settings.loc" 
    value="http://internalserver.mycompany.com/uploads/Maven/"/>
  
  <!-- This is where the Maven jar will be put: -->
  <property name="maven.bootstrap.directory" value="${maven.settings.dir}/ant/" />
  
  <target name="maven-download" unless="offline">
    <mkdir dir="${maven.bootstrap.directory}" />
    <get src="${maven.bootstrap.server}${maven.bootstrap.artifact.ant}"
      dest="${maven.bootstrap.directory}${maven.bootstrap.artifact.ant}"
      usetimestamp="true" />
  </target>
  
  <available file="${maven.settings.dir}${maven.settings.file}" 
    property="maven.configured" />

  <target name="maven-settings-download" unless="maven.configured">
    <mkdir dir="${maven.settings.dir}" />
    <get src="${maven.settings.loc}${maven.settings.file}"
      dest="${maven.settings.dir}${maven.settings.file}"
      usetimestamp="true" />    
  </target>

  <target name="maven-taskdef" depends="maven-download">
    <taskdef resource="org/apache/maven/artifact/ant/antlib.xml"
      uri="antlib:org.apache.maven.artifact.ant" 
   	  classpath="${maven.bootstrap.directory}${maven.bootstrap.artifact.ant}" />
    <echo>Maven tasks defined.</echo>
  </target>
  
  <target name="maven-init" depends="maven-settings-download, maven-taskdef" />
</project>
}}}
===
/***
''NestedSlidersPlugin for TiddlyWiki version 1.2.x and 2.0''
^^author: Eric Shulman
source: http://www.elsdesign.com/tiddlywiki/#NestedSlidersPlugin
license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^

Quickly make any tiddler content into an expandable 'slider' panel, without needing to create a separate tiddler to contain the slider content.  Optional syntax allows ''default to open'', ''custom button label/tooltip'' and ''automatic blockquote formatting.''

You can also 'nest' these sliders as deep as you like (see complex nesting example below), so that expandable 'tree-like' hierarchical displays can be created.  This is most useful when converting existing in-line text content to create in-line annotations, footnotes, context-sensitive help, or other subordinate information displays.

For more details, please click on a section headline below:
++++!!!!![Configuration]>
Debugging messages for 'lazy sliders' deferred rendering:
<<option chkDebugLazySliderDefer>> show debugging alert when deferring slider rendering
<<option chkDebugLazySliderRender>> show debugging alert when deferred slider is actually rendered
//''note: Enabling these settings may produce unexpected results. Use at your own risk.''//
===
++++!!!!![Usage]>
When installed, this plugin adds new wiki syntax for embedding 'slider' panels directly into tiddler content.  Use {{{+++}}} and {{{===}}} to delimit the slider content.  Additional optional syntax elements let you specify 'default to open', 'cookiename', 'heading level', 'custom label/tooltip', 'automatic blockquote' and 'deferred rendering'.
//{{{
++++(cookiename)!!!!![label|tooltip]>...
content goes here
===
//}}}
where:
* {{{+++}}} (or {{{++++}}}) and {{{===}}}^^
marks the start and end of the slider definition, respectively.  When the extra {{{+}}} is used, the slider will be open when initially displayed.^^
* {{{(cookiename)}}}^^
save the slider opened/closed state, and restore this state whenever the slider is re-rendered.^^
* {{{!}}} through {{{!!!!!}}}^^
displays the slider label using a formatted headline (Hn) style instead of a button/link style^^
* {{{[label]}}} or {{{[label|tooltip]}}}^^
uses custom label/tooltip.  (defaults are: ">/more..." and "</less...")^^
* {{{">"}}} //(without the quotes)//^^
automatically adds blockquote formatting to slider content^^
* {{{"..."}}} //(without the quotes)//^^
defers rendering of closed sliders until the first time they are opened.  //Note: deferred rendering may produce unexpected results in some cases.  Use with care.//^^

//Note: to make slider definitions easier to read and recognize when editing a tiddler, newlines immediately following the {{{+++}}} 'start slider' or preceding the {{{===}}} 'end slider' sequence are automatically supressed so that excess whitespace is eliminated from the output.//
===
++++!!!!![Examples]>
simple in-line slider: 
{{{
+++
   content
===
}}}
+++
   content
===
----
default to open: 
{{{
++++
  content
===
}}}
++++
   content
===
----
use a custom label: 
{{{
+++[label]
   content
===
}}}
+++[label]
   content
===
----
use a custom label and tooltip: 
{{{
+++[label|tooltip]
   content
===
}}}
+++[label|tooltip]
   content
===
----
content automatically blockquoted: 
{{{
+++>
   content
===
}}}
+++>
   content
===
----
all options combined //(default open, custom label/tooltip, blockquoted)//
{{{
++++(testcookie)[label|tooltip]>
   content
===
}}}
++++(testcookie)[label|tooltip]>
   content
===
----
complex nesting example:
{{{
+++[get info...|click for information]>
   put some general information here, plus a slider with more specific info:
   +++[view details...|click for details]>
      put some detail here, which could include some +++[definitions]>explaining technical terms===
   ===
===
}}}
+++[get info...|click for information]>
   put some general information here, plus a slider with more specific info:
   +++[view details...|click for details]>
      put some detail here, which could include some +++[definitions]>explaining technical terms===
   === 
=== 
===
+++!!!!![Installation]>
import (or copy/paste) the following tiddlers into your document:
''NestedSlidersPlugin'' (tagged with <<tag systemConfig>>)
===
+++!!!!![Revision History]>

++++[2006.01.03 - 1.6.2]
When using optional "!" heading style, instead of creating a clickable "Hn" element, create an "A" element inside the "Hn" element.  (allows click-through in SlideShowPlugin, which captures nearly all click events, except for hyperlinks)
===

+++[2005.12.15 - 1.6.1]
added optional "..." syntax to invoke deferred ('lazy') rendering for initially hidden sliders
removed checkbox option for 'global' application of lazy sliders
===

+++[2005.11.25 - 1.6.0]
added optional handling for 'lazy sliders' (deferred rendering for initially hidden sliders)
===

+++[2005.11.21 - 1.5.1]
revised regular expressions: if present, a single newline //preceding// and/or //following// a slider definition will be suppressed so start/end syntax can be place on separate lines in the tiddler 'source' for improved readability.  Similarly, any whitespace (newlines, tabs, spaces, etc.) trailing the 'start slider' syntax or preceding the 'end slider' syntax is also suppressed.
===

+++[2005.11.20 - 1.5.0]
   added (cookiename) syntax for optional tracking and restoring of slider open/close state
===

+++[2005.11.11 - 1.4.0]
   added !!!!! syntax to render slider label as a header (Hn) style instead of a button/link style
===

+++[2005.11.07 - 1.3.0]
   removed alternative syntax {{{(((}}} and {{{)))}}} (so they can be used by other
   formatting extensions) and simplified/improved regular expressions to trim multiple excess newlines
===

+++[2005.11.05 - 1.2.1]
   changed name to NestedSlidersPlugin
   more documentation
===

+++[2005.11.04 - 1.2.0]
   added alternative character-mode syntax {{{(((}}} and {{{)))}}}
   tweaked "eat newlines" logic for line-mode {{{+++}}} and {{{===}}} syntax
===

+++[2005.11.03 - 1.1.1]
   fixed toggling of default tooltips ("more..." and "less...") when a non-default button label is used
   code cleanup, added documentation
===

+++[2005.11.03 - 1.1.0]
   changed delimiter syntax from {{{(((}}} and {{{)))}}} to {{{+++}}} and {{{===}}}
   changed name to EasySlidersPlugin
===

+++[2005.11.03 - 1.0.0]
   initial public release
===

===
+++!!!!![Credits]>
This feature was implemented by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]] based on considerable research, programming and suggestions from RodneyGomes, GeoffSlocock, and PaulPetterson
===
***/
// //+++!!!!![Code]
//{{{
version.extensions.nestedSliders = {major: 1, minor: 6, revision: 2, date: new Date(2006,1,3)};
//}}}

//{{{
// options for deferred rendering of sliders that are not initially displayed
if (config.options.chkDebugLazySliderDefer==undefined) config.options.chkDebugLazySliderDefer=false;
if (config.options.chkDebugLazySliderRender==undefined) config.options.chkDebugLazySliderRender=false;
//}}}

//{{{
config.formatters.push( {
	name: "nestedSliders",
	match: "\\n?\\+{3}",
	terminator: "\\s*\\={3}\\n?",
	lookahead: "\\n?\\+{3}(\\+)?(\\([^\\)]*\\))?(\\!*)?(\\[[^\\]]*\\])?(\\>?)(\\.\\.\\.)?\\s*",
	handler: function(w)
		{
			var lookaheadRegExp = new RegExp(this.lookahead,"mg");
			lookaheadRegExp.lastIndex = w.matchStart;
			var lookaheadMatch = lookaheadRegExp.exec(w.source)
			if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
			{
				// default to closed, no cookie
				var show="none"; var title=">"; var tooltip="show"; var cookie="";

				// extra "+", default to open
				if (lookaheadMatch[1])
					{ show="block"; title="<"; tooltip="hide"; }

				// cookie, use saved open/closed state
				if (lookaheadMatch[2]) {
					cookie=lookaheadMatch[2].trim().substr(1,lookaheadMatch[2].length-2);
					cookie="chkSlider"+cookie;
					if (config.options[cookie]==undefined)
						{ config.options[cookie] = (show=="block") }
					if (config.options[cookie])
						{ show="block"; title="<"; tooltip="hide"; }
					else
						{ show="none"; title=">"; tooltip="show"; }
				}

				// custom label/tooltip
				if (lookaheadMatch[4]) {
					title = lookaheadMatch[4].trim().substr(1,lookaheadMatch[4].length-2);
					if ((pos=title.indexOf("|")) != -1)
						{ tooltip = title.substr(pos+1,title.length); title = title.substr(0,pos); }
					else
						{ tooltip += " "+title; }
				}
				// use "Hn" header format instead of button/link
				if (lookaheadMatch[3]) {
					var lvl=(lookaheadMatch[3].length>6)?6:lookaheadMatch[3].length;
					var btn = createTiddlyElement(createTiddlyElement(w.output,"h"+lvl,null,null,null),"a",null,null,title);
					btn.onclick=onClickNestedSlider;
					btn.setAttribute("href","javascript:;");
					btn.setAttribute("title",tooltip);

				}
				else
					var btn = createTiddlyButton(w.output,title,tooltip,onClickNestedSlider);
				var panel = createTiddlyElement(w.output,"span",null,"sliderPanel",null);
				btn.sliderCookie = cookie;
				btn.sliderPanel = panel;
				panel.style.display = show;
				w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
				if (!lookaheadMatch[6] || show=="block") {
					w.subWikify(lookaheadMatch[5]?createTiddlyElement(panel,"blockquote"):panel,this.terminator);
				}
				else {
					var src = w.source.substr(w.nextMatch);
					var endpos=findMatchingDelimiter(src,"+++","===");
					panel.setAttribute("raw",src.substr(0,endpos));
					panel.setAttribute("blockquote",lookaheadMatch[5]?"true":"false");
					panel.setAttribute("rendered","false");
					w.nextMatch += endpos+3;
					if (w.source.substr(w.nextMatch,1)=="\n") w.nextMatch++;
					if (config.options.chkDebugLazySliderDefer)
						alert("deferred '"+title+"':\n\n"+panel.getAttribute("raw"));
				}
			}
		}
	}
)

// TBD: ignore 'quoted' delimiters (e.g., "{{{+++foo===}}}" isn't really a slider)
function findMatchingDelimiter(src,starttext,endtext) {
	var startpos = 0;
	var endpos = src.indexOf(endtext);
	// check for nested delimiters
	while (src.substring(startpos,endpos-1).indexOf(starttext)!=-1) {
		// count number of nested 'starts'
		var startcount=0;
		var temp = src.substring(startpos,endpos-1);
		var pos=temp.indexOf(starttext);
		while (pos!=-1)  { startcount++; pos=temp.indexOf(starttext,pos+starttext.length); }
		// set up to check for additional 'starts' after adjusting endpos
		startpos=endpos+endtext.length;
		// find endpos for corresponding number of matching 'ends'
		while (startcount && endpos!=-1) {
			endpos = src.indexOf(endtext,endpos+endtext.length);
			startcount--;
		}
	}
	return (endpos==-1)?src.length:endpos;
}
//}}}

//{{{
function onClickNestedSlider(e)
{
	if (!e) var e = window.event;
	var theTarget = resolveTarget(e);
	var theLabel = theTarget.firstChild.data;
	var theSlider = theTarget.sliderPanel
	var isOpen = theSlider.style.display!="none";
	// if using default button labels, toggle labels
	if (theLabel==">") theTarget.firstChild.data = "<";
	else if (theLabel=="<") theTarget.firstChild.data = ">";
	// if using default tooltips, toggle tooltips
	if (theTarget.getAttribute("title")=="show")
		theTarget.setAttribute("title","hide");
	else if (theTarget.getAttribute("title")=="hide")
		theTarget.setAttribute("title","show");
	if (theTarget.getAttribute("title")=="show "+theLabel)
		theTarget.setAttribute("title","hide "+theLabel);
	else if (theTarget.getAttribute("title")=="hide "+theLabel)
		theTarget.setAttribute("title","show "+theLabel);
	// deferred rendering (if needed)
	if (theSlider.getAttribute("rendered")=="false") {
		if (config.options.chkDebugLazySliderRender)
			alert("rendering '"+theLabel+"':\n\n"+theSlider.getAttribute("raw"));
		var place=theSlider;
		if (theSlider.getAttribute("blockquote")=="true")
			place=createTiddlyElement(place,"blockquote");
		wikify(theSlider.getAttribute("raw"),place);
		theSlider.setAttribute("rendered","true");
	}
	// show/hide the slider
// DISABLED: animation sets overflow:hidden, which clips nested sliders...
//	if(config.options.chkAnimate)
//		anim.startAnimating(new Slider(theSlider,!isOpen,e.shiftKey || e.altKey,"none"));
//	else
		theSlider.style.display = isOpen ? "none" : "block";
	if (this.sliderCookie && this.sliderCookie.length)
		{ config.options[this.sliderCookie]=!isOpen; saveOptionCookie(this.sliderCookie); }
	return false;
}
//}}}
// //===
Java's standard [[XPath | http://java.sun.com/j2se/1.5.0/docs/api/index.html?javax/xml/xpath/XPath.html]] handling isn't great, but it's there.  So I wrote a little utility library to make life easier.  This is great for one-off XPath evaluation, i.e. testing to validate some XML output.  Easy!

+++!![XPathUtil Code...]
{{{
public class XPathUtil {
  private static final ThreadLocal<Container> threadLocal = new ThreadLocal<Container>() {
    @Override protected Container initialValue() { return new Container(); }
  };
  
  // access to this guy is synchronized
  private static final XPathFactory factory = XPathFactory.newInstance();
  
  /**
   * This holds the last-used XPathExpression in order to improve performance
   * of (i.e.) the same expression used in a loop.  This can also use a 
   * LinkedHashMap if you want to maintain a cache of XPathExpressions
   */
  private static final class Container {
    String key = "";
    XPathExpression exp;
    final XPath xPath;
    { synchronized( factory ) { xPath = factory.newXPath(); } } //initializer
  }

  private XPathUtil() {}  // this is a static utility class

  /** The main evaluation method */
  private static Object eval( String q, Node n, QName type ) 
      throws XMLException {
    try {
      final Container container = threadLocal.get();
      XPathExpression exp = null;
      if( !container.key.equals( q ) ) { //cache miss
        exp = container.xPath.compile( q );
        container.key = q;
        container.exp = exp;
      }
      else exp = container.exp;
      return exp.evaluate( n, type );
    }
    catch( XPathExpressionException ex ) {
      // you should create your own RuntimeException subclass:
      throw new RuntimeException( ex );
    }
  }
  
  public static XPath getXPath() { return threadLocal.get().xPath; } 

  public static Node getNode( String exp, Node n ) {
    return (Node)eval(exp, n, XPathConstants.NODE);
  }

  public static NodeList getList( String exp, Node n ) {
    return (NodeList)eval(exp, n, XPathConstants.NODESET);
  }
  
  public static String getString( String exp, Node n ) {
    return (String)eval(exp, n, XPathConstants.STRING);
  }

  //other methods - getBool, getNumber, getElement, etc.

  /** Wrapper to make NodeList iterable */
  public static NodeIterator iterator( String exp, Node n ) {
    return new NodeIterator( getList( exp, n ) );
  }
}
}}}
===

+++!![NodeIterator code...]
{{{
public class NodeIterator implements Iterable<Node>, Iterator<Node> {

  private NodeList nl;
  private int currentIndex = 0;
  
  NodeIterator( NodeList nl ) {
    this.nl = nl;
  }
  
  public Iterator<Node> iterator() {
    return this;
  }

  public boolean hasNext() {
    return currentIndex < nl.getLength();
  }

  public Node next() throws NoSuchElementException {
    Node item = nl.item( currentIndex++ );
    if( item != null ) return item; 
    else throw new NoSuchElementException( "Iterator has no more elements" );
  }

  /** @see Iterator#remove() */
  public void remove() throws IllegalStateException {
    if( currentIndex > 0 ) {
      Node n = nl.item( currentIndex-1 );
      if( n == null ) throw new IllegalStateException( "No more elements" );
      Node p = n.getParentNode();
      if( p == null ) throw new IllegalStateException( "Node has already been removed" );
      p.removeChild( n );
    }
    else throw new IllegalStateException( 
        "Must call next() before calling remove()" );
  }
}
}}}
Username:
<<option txtUserName>>
<<option chkSaveBackups>> SaveBackups
<<option chkAutoSave>> AutoSave
<<option chkRegExpSearch>> RegExpSearch
<<option chkCaseSensitiveSearch>> CaseSensitiveSearch
<<option chkAnimate>> EnableAnimations

See AdvancedOptions
Or at least, I think it's original...  I can't help if someone else thought of it first.

* [[Code Tricks]]
* [[Software I Use]]
* [[Customizing Cygwin]]
* [[Quick Refs]]
<div id='header'>
 <div id='titleLine'>
  <span id='siteTitle' refresh='content' tiddler='SiteTitle'></span>
  <span id='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>
* [[Google Map|http://maps.google.com/maps/ms?ie=UTF8&hl=en&msa=0&msid=112932514428367708735.00000112f86b774bfff42]]

! Attractions
* [[ Mt. Washington | http://www.city.pittsburgh.pa.us/district2/html/mount_washington.html]] - [[Incline| http://incline.pghfree.net/]] - [[Restaurants| http://incline.pghfree.net/restaurants.htm]]
* [[Pittsburgh Restaurants|http://www.hellopittsburgh.com/Restaurants.cfm]]
* [[Cultural District restaurants| http://www.culturaldistrict.org/index.php?op=rest]]
* [[Penn Brewery|http://www.pennbrew.com/data/english/contact.htm]]
* [[Veg restaurants| http://www.vegetarian-restaurants.net/usa/PennPittsburgh.htm]]
* [[Radisson Greentree|http://www.radisson.com/pittsburghpa_greentree]]
* [[Zoo | http://www.pittsburghzoo.com/]]
* [[Science Museum | http://www.carnegiesciencecenter.org/]]
* [[Art house | http://www.mattress.org/]]
* [[Cabaret Theatre| http://www.clocabaret.com/default2.html]]
* [[Horseback riding | http://www.rolling-hills-ranch.com/]]
* [[Rapelling | http://www.laurelcaverns.com/rapelling.htm]]
<<plugins>>
Video of my dog running in circles:
http://video.google.com/videoplay?docid=-8484052489835829740&hl=en
A list of quick reference sheets:

! Code
* [[http://docs.codehaus.org/download/attachments/2715/groovy-reference-card.pdf?version=1| Groovy]] - [-[[http://www.google.com/search?q=cache:Qxm6CSzx__gJ:docs.codehaus.org/download/attachments/2715/groovy-reference-card.pdf%3Fversion%3D1 |(HTML)]]-]
* [[http://www.mulberrytech.com/quickref/XSLT_1quickref-v2.pdf|XSLT & XPath]]
* [[http://www.ilovejackdaniels.com/cheat-sheets/ruby-on-rails-cheat-sheet/| Ruby On Rails]], [[http://www.zenspider.com/Languages/Ruby/QuickRef.html| Ruby]]
* [[http://java.sun.com/docs/books/tutorial/java/nutsandbolts/datatypes.html|Java Primitive types]]
* [[http://www.yukoncollege.yk.ca/~ttopper/COMP118/rCheatSheet.html| Python]]

! Web
* [[http://cdburnerxp.se/htmlcheatsheet.pdf| XHTML]]
* [[http://lesliefranke.com/files/reference/csscheatsheet.html| CSS]] - [[http://www.ilovejackdaniels.com/css_cheat_sheet.png| 2]]
* [[http://www.snook.ca/files/prototype_1.5.0_snookca.png| Prototype.js 1.5.0]]
* [[http://www.ilovejackdaniels.com/javascript_cheat_sheet.png| JavaScript]]
* [[http://www.ilovejackdaniels.com/characters_cheat_sheet.png| HTML Character Codes]]

! Tools
* [[http://eclipse-tools.sourceforge.net/EclipseEmacsKeybindings_3_1.pdf| Eclipse]]
* [[http://tnerual.eriogerg.free.fr/vimqrc.pdf| Vim]] - [-[[http://www.google.com/search?q=cache:5RMMTk-azd8J:tnerual.eriogerg.free.fr/vimqrc.pdf |(HTML)]]-]
* [[http://www.digilife.be/quickreferences/QRC/Bash%20Quick%20Reference.pdf| Bash]]
* [[http://www.ilovejackdaniels.com/cheat-sheets/regular-expressions-cheat-sheet/| Regular Expressions]]

[[http://www.smashingmagazine.com/2006/10/30/cheat-sheet-round-up-ajax-css-latex-ruby/ | List of cheat sheets]]
/***
|Name|RearrangeTiddlersPlugin|
|Source|http://www.TiddlyTools.com/#RearrangeTiddlersPlugin|
|Version|0.0.0|
|Author|Joe Raii|
|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|Story.prototype.refreshTiddler|
|Description|drag tiddlers by title to re-order story column display|

adapted from: http://www.cs.utexas.edu/~joeraii/dragn/#Draggable
changes by ELS:
* hijack refreshTiddler() instead of overridding createTiddler()
* find title element by className instead of elementID
* set cursor style via code instead of stylesheet
* set tooltip help text
* set tiddler "position:relative" when starting drag event, restore saved value when drag ends
* update 2006.08.07: use getElementsByTagName("*") to find title element, even when it is 'buried' deep in tiddler DOM elements (due to custom template usage)
* update 2007.03.01: use apply() to invoke hijacked core function

***/
//{{{

Story.prototype.rearrangeTiddlersHijack_refreshTiddler = Story.prototype.refreshTiddler;
Story.prototype.refreshTiddler = function(title,template,unused1,unused2,unused3,unused4,unused5)
{
	this.rearrangeTiddlersHijack_refreshTiddler.apply(this,arguments);
	var theTiddler = document.getElementById(this.idPrefix + title); if (!theTiddler) return;
	var theHandle;
	var children=theTiddler.getElementsByTagName("*");
	for (var i=0; i<children.length; i++) if (hasClass(children[i],"title")) { theHandle=children[i]; break; }
	if (!theHandle) return theTiddler;

	Drag.init(theHandle, theTiddler, 0, 0, null, null);
	theHandle.style.cursor="move";
	theHandle.title="drag title to re-arrange tiddlers"
	theTiddler.onDrag = function(x,y,myElem) {
		if (this.style.position!="relative")
			{ this.savedstyle=this.style.position; this.style.position="relative"; }
		y = myElem.offsetTop;
		var next = myElem.nextSibling;
		var prev = myElem.previousSibling;
		if (next && y + myElem.offsetHeight > next.offsetTop + next.offsetHeight/2) { 
			myElem.parentNode.removeChild(myElem);
			next.parentNode.insertBefore(myElem, next.nextSibling);//elems[pos+1]);
			myElem.style["top"] = -next.offsetHeight/2+"px";
		}
		if (prev && y < prev.offsetTop + prev.offsetHeight/2) { 
			myElem.parentNode.removeChild(myElem);
			prev.parentNode.insertBefore(myElem, prev);
			myElem.style["top"] = prev.offsetHeight/2+"px";
		}
	};
	theTiddler.onDragEnd = function(x,y,myElem) {
		myElem.style["top"] = "0px";
		if (this.savedstyle!=undefined)
			this.style.position=this.savedstyle;
	}
	return theTiddler;
}

/**************************************************
 * dom-drag.js
 * 09.25.2001
 * www.youngpup.net
 **************************************************
 * 10.28.2001 - fixed minor bug where events
 * sometimes fired off the handle, not the root.
 **************************************************/

var Drag = {
	obj:null,

	init:
	function(o, oRoot, minX, maxX, minY, maxY) {
		o.onmousedown = Drag.start;
		o.root = oRoot && oRoot != null ? oRoot : o ;
		if (isNaN(parseInt(o.root.style.left))) o.root.style.left="0px";
		if (isNaN(parseInt(o.root.style.top))) o.root.style.top="0px";
		o.minX = typeof minX != 'undefined' ? minX : null;
		o.minY = typeof minY != 'undefined' ? minY : null;
		o.maxX = typeof maxX != 'undefined' ? maxX : null;
		o.maxY = typeof maxY != 'undefined' ? maxY : null;
		o.root.onDragStart = new Function();
		o.root.onDragEnd = new Function();
		o.root.onDrag = new Function();
	},

	start:
	function(e) {
		var o = Drag.obj = this;
		e = Drag.fixE(e);
		var y = parseInt(o.root.style.top);
		var x = parseInt(o.root.style.left);
		o.root.onDragStart(x, y, Drag.obj.root);
		o.lastMouseX = e.clientX;
		o.lastMouseY = e.clientY;
		if (o.minX != null) o.minMouseX = e.clientX - x + o.minX;
		if (o.maxX != null) o.maxMouseX = o.minMouseX + o.maxX - o.minX;
		if (o.minY != null) o.minMouseY = e.clientY - y + o.minY;
		if (o.maxY != null) o.maxMouseY = o.minMouseY + o.maxY - o.minY;
		document.onmousemove = Drag.drag;
		document.onmouseup = Drag.end;
		Drag.obj.root.style["z-index"] = "10";
		return false;
	},

	drag:
	function(e) {
		e = Drag.fixE(e);
		var o = Drag.obj;
		var ey = e.clientY;
		var ex = e.clientX;
		var y = parseInt(o.root.style.top);
		var x = parseInt(o.root.style.left);
		var nx, ny;
		if (o.minX != null) ex = Math.max(ex, o.minMouseX);
		if (o.maxX != null) ex = Math.min(ex, o.maxMouseX);
		if (o.minY != null) ey = Math.max(ey, o.minMouseY);
		if (o.maxY != null) ey = Math.min(ey, o.maxMouseY);
		nx = x + (ex - o.lastMouseX);
		ny = y + (ey - o.lastMouseY);
		Drag.obj.root.style["left"] = nx + "px";
		Drag.obj.root.style["top"] = ny + "px";
		Drag.obj.lastMouseX = ex;
		Drag.obj.lastMouseY = ey;
		Drag.obj.root.onDrag(nx, ny, Drag.obj.root);
		return false;
	},

	end:
	function() {
		document.onmousemove = null;
		document.onmouseup = null;
		Drag.obj.root.style["z-index"] = "0";
		Drag.obj.root.onDragEnd(parseInt(Drag.obj.root.style["left"]), parseInt(Drag.obj.root.style["top"]), Drag.obj.root);
		Drag.obj = null;
	},

	fixE:
	function(e) {
		if (typeof e == 'undefined') e = window.event;
		if (typeof e.layerX == 'undefined') e.layerX = e.offsetX;
		if (typeof e.layerY == 'undefined') e.layerY = e.offsetY;
		return e;
	}
};
//}}}
!Reference sites:
* [[Programming Ruby|http://www.rubycentral.com/book/]]
* [[Rails Docs|http://www.rubyonrails.org/docs]]
!Rails
* [[Rails onLamp|http://www.onlamp.com/lpt/a/5546]]
* [[Rails + Ajax|http://www.onlamp.com/lpt/a/5944]]
* [[Rails Tutorials|http://www.digitalmediaminute.com/article/1816/top-ruby-on-rails-tutorials]]
If you happen to live in upstate NY...

* [[Cherry Orchard B&B|http://www.cherryorchard.com/]] - [[Map|http://maps.google.com/maps?q=%204194%20STATE%20ROUTE%2014%20%20ROCK%20STREAM%2C%20NY%2014878]]
* [[Chalet Leon | http://www.chaletleonmotel.com/]]

* [[Rooser Fish|http://www.roosterfishbrewing.com/home.htm]] - reservations!

* [[Seneca lake wine trail|http://www.senecalakewine.com/jc/component/option,com_contxtd/catid,13/Itemid,2/]]
<<list shadowed>>
This is a great rule to follow.  If I ever see logic nesting more than two deep I start looking for ways to avoid it.  

Why do this:
{{{
if( some_condition ) {
  // do some logic ...
  // that might involve more 
  //    nested conditions
}
else {
  //throw some exception/ do error handling, etc.
}
}}}

When you could do this:
{{{
if( !some_condition ) {
  // fail-fast error handling - 
  // throw an exception or return some default value
}

//continue with logic here 
}}}

What do you think is more readable?

[[read | http://javathink.blogspot.com/2006/10/short-concise-and-readable-code-invert.html]]
<<search>>
<<goto width:9em>>Go<<collapseAll>><<expandAll>><<closeAll>><<permaview>>
So if you're doing any sort of non-trivial Java Applet,  you probably need to do more than the default sandbox permissions will allow.  I've collected the steps necessary to create a self-signed applet.

{{{
#!/bin/bash

keytool="$JAVA_HOME/bin/keytool.exe"
jarsigner="$JAVA_HOME/bin/jarsigner.exe"

keypass=password
CN="CN=Tom Nichols, OU=Division, O=Company, L=Rochester, ST=NY, C=US"
keystore=KeyStore.db
alias=jarCert
jarFile=target/MyApplet.jar
publicCert=MyApplet.cer

# First, create the self-signed cert
echo Creating KeyStore and certificate...
"$keytool" -genkey -keyalg rsa -alias $alias -keypass $keypass \
	-keystore $keystore -storepass $keypass -validity 99999 \
	-dname "$CN" 

"$keytool" -selfcert -validity 99999 -alias $alias \
	-keystore $keystore -storepass $keypass

# Sign the JAR - this may also be done by the Ant <signjar> task
echo Signing JAR...
"$jarsigner" -keystore $keystore -storepass $keypass $jarFile $alias

"$jarsigner" -verify -verbose -certs $jarFile

echo Exporting certificate...
# Export the cert, to be imported on the client's computer
# -- Open the Java plugin control panel, and go to the "Security" tab
"$keytool" -export -keystore $keystore -storepass $keypass \
	-alias $alias -file $publicCert
}}}

!!! Importing your certificate
Now you just have to take 'MyApplet.cer' and import it into your Java keystore.  This can be done from the Java control panel (Security tab -> Certificates -> Import) or by doing the following:
{{{
keytool -keystore "$JAVA_HOME\lib\security\cacerts" -import -file $publicCert -alias $alias
# password: 'changeit'
}}}
or, "yet another programmer blog..."
[[Medi@rcade|Home]]
http://mediarcade.tiddlyspot.com/
Most of this software I use daily during my work routine.  A few things I've listed just don't apply to this environment.  Most (if not all) of it is free.  Hopefully you'll see something on here you haven't already tried.  Or if you'd like, [[mailto:tom.nichols@thomson.com|suggest something]].  Thanks. 

Note that for most of the attached configuration files, you'll have to rename to .xxx or _xxx (e.g. vimrc.txt -> .vimrc or _vimrc) and put it in your home folder.


! Development Tools

* [[http://www.eclipse.org|Eclipse]] - The end-all Java (and [[http://www.radrails.org/|Ruby]]... and [[http://pydev.sourceforge.net/|Python]]) IDE.  
** Groovy plugin [[http://groovy.codehaus.org/Eclipse+Plugin|here]].
* [[http://www.groovy.org|Groovy]] - A powerful dynamic language for the Java platform.  Useful for shell scripting and full-featured [[http://grails.org/|J2EE applications]]
* [[http://www.cygwin.com|Cygwin]] - indispensable for [[http://www.codebox.8m.com/shellscripting.htm|scripting]].  \\
   Also handy just for its CLI, especially with my [[Attach:bashrc.txt|shell]] [[Attach:inputrc.txt|and]] [[Attach:profile.txt|environment]] [[Attach:bashrc-cygwin.txt|customizations]].
** Handy Cygwin packages: ssh/scp, svn, curl, wget, ncftp, cadaver
** The '*rxvt*' package gives the shell window your WinXP theme - see [[Attach:rxvt.bat.txt|rxvt.bat]]
** See also: [[CustomizingCygwin]]
* [[http://winmerge.org/|WinMerge]] Visual diff tool for Windows.  Create a [[http://lifehacker.com/software/windows/customize-windows-send-to-menu-136345.php|SendTo]] shortcut to easily diff files from Explorer.
* [[http://tortoisesvn.tigris.org/|Tortoise SVN]] - if we used Subversion in-house this would be a must-have.

!!! Free Text Editors
  For a while I had a hard time finding a decent free text editor.  No more!

* [[http://notepad-plus.sourceforge.net|Notepad++]] - a sophisticated, open-source Windows text editor based on [[http://www.scintilla.org/SciTE.html|SciTE]].  
** Also works as a windows notepad replacement.
* [[http://www.vim.org|Vim]] - see also my [[Attach:vimrc.txt|.vimrc]] and [[Attach:gvimrc.txt|.gvimrc]] files.  
** See also [[http://cream.sourceforge.net/|Cream for Vim]]
* [[http://www.jedit.org|JEdit]] - Java-based text editor with '*tons*' of scripting and plugin ability.


! Utilities

* [[http://www.7-zip.org/|7-Zip]] - Fast, free open-source windows archiver (to replace non-free tools like WinZip and WinRAR).
* [[http://www.autohotkey.com/|AutoHotKey]] - create keyboard shortcuts for anything.  
** See my [[Attach:myhotkeys.ahk.txt|AHK script]].
** Intro by [[http://lifehacker.com/software/how-to/automate-windows-with-autohotkey-278859.php|LifeHacker]]
* [[http://www.ntwind.com/software/taskswitchxp.html|TaskSwitchXP]] - Windows Alt-Tab replacement w/ preview.
* [[http://www.palma.com.au/winroll/|Winroll]] - Minimize Windows' windows.
* [[http://nonadmin.editme.com/DropMyRights|DropMyRights]] - run vulnerable applications (like IE and Outlook) with non-admin privileges.  Other [[http://nonadmin.editme.com/UsefulTools|useful tools]] 
* [[http://wipfw.sourceforge.net/|wipfw]] - super-simple firewall. See my [[Attach:rc.fw.txt|rc.fw]] script.
* [[http://www.steffengerlach.de/freeware/|Scanner]] - a disk usage visualization tool.  Makes it easy to find what's eating up disk space.
* [[http://sourceforge.net/projects/pdfcreator/|PDFCreator]] - Print anything in Windows to a PDF document.  Like [[http://www.cutepdf.com/Products/CutePDF/writer.asp|CutePDF]] except open source.
* [[http://keepass.info/|KeePass]] - Keep all those Linux server passwords and web logins in one place.


! Web-related Apps

* [[http://shared.snapgrid.com/index.html|GTDTiddlyWiki]] - A slick client-side wiki.  Basically a rich-text notepad and to-do list in your browser.
** See also [[http://www.tiddlytools.com/|TiddlyTools]] for many useful plugins.
* [[http://www.bloglines.com/|Bloglines.com]] - read your RSS feeds in a browser.
* [[http://mail.google.com|All]] [[http://calendar.google.com|of]] [[http://www.google.com/notebook/|the]]    [[http://docs.google.com|Google]] [[http://www.google.com/ig|tools]]
* [[http://gotapi.com/|GotAPI]] and [[http://www.jdocs.com/|JDocs]] -- Centralized API documentation.
* [[http://www.mozilla.com/en-US/firefox/|Firefox]] - see also [[FirefoxExtensionsIUse]]
* [[http://www.privoxy.org/|Privoxy]] - Ad-block and web content modification for all browsers.  
** See my customized [[Attach:default.filter.txt|filter file]] and [[Attach:user.action.txt|user.action file]].
* [[http://www.miranda-im.org/| Miranda IM]] - best IM client for Windows.  
**  See also [[Using Miranda IM]]
* [[http://filezilla.sourceforge.net/|FileZilla]] - FTP client.
* IE Plugins:
** [[http://www.codeproject.com/atl/MouseGestures.asp|Mouse Gestures]] - This is a lifesaver if I'm ever forced to use IE for any length of time.
** [[http://www.ieforge.com/InlineSearch/HomePage|Inline Search]] - Just like in FireFox


! Misc

Most of these don't get used here very often (simply because of the nature of the work) but they are handy tools nonetheless.

* [[http://www.inkscape.org|Inkscape]] - [[Wikipedia:SVG|SVG]] editor (great for creating slick [[MedicalLitigator/WorkspaceDirectoryStructure|diagrams]]).
* [[http://www.ubuntu.com/|Ubuntu Linux]]
* [[http://www.videolan.org/vlc/|VLC Media Player]]
* [[http://www.apachefriends.org/en/xampp-windows.html|XAMPP]] - Get Apache/PHP/MySQL running on Windows ''fast''.  I actually prefer [[http://www.apachefriends.org/en/xampp-windows.html#646|XAMPP-Lite]]
* [[http://winscp.net/eng/index.php|WinSCP]] - SFTP and SSH file transfer client.
* [[http://ultravnc.sourceforge.net/|UltraVNC]] - great Windows alternative to remote desktop.
* [[http://openvpn.net/|OpenVPN]]
* Many of the [[http://www.microsoft.com/technet/sysinternals/default.mspx|Sysinternals]] tools.  [[http://www.microsoft.com/technet/sysinternals/ProcessesAndThreads/Autoruns.mspx|Autoruns]] and [[http://www.microsoft.com/technet/sysinternals/ProcessesAndThreads/ProcessExplorer.mspx|Process Explorer]] are two of my favorites.


!! Further Reading

* [[Free Tools]] on the wiki
* [[http://www.samurize.com/|Samurize]] from this [[http://www.lifehacker.com/software/plain-text/geek-to-live--incorporate-text-files-onto-your-desktop-213280.php| LifeHacker article]].  \\
Looks like [[http://widgets.yahoo.com/|Yahoo Widgets]] (or Google Widgets, or OS X Dashboard widgets... etc.)
[[Spline.NET | http://sourceforge.net/projects/splinedotnet/]] is a project I wrote containing some .NET utilities and a simple persistence framework built on ADO.NET.  

''TODO list''
* Make generic methods
* Separate Annotation mapping from Data Objects?
* Cached key-field mapping
* [[Dynamic Methods|http://www.codeproject.com/csharp/DynamicCodeVsReflection.asp]]
/***
!GTD Style

!Generic rules /%==================================================================== %/
***/
/*{{{*/
body {
 background: #464646 url('http://shared.snapgrid.com/images/tiddlywiki/bodygradient.png') repeat-x top fixed;
 color: #000;
 font: .82em/1.25em  "Bitstream Vera Sans", Verdana, Helvetica, Arial, sans-serif;
/*"Lucida Sans Unicode", "Lucida Grande","Trebuchet MS", */
}
/*}}}*/

/***
!Header rules /%====================================================================== %/
***/
/*{{{*/
#contentWrapper {
 width: 80em;
 margin: 0 auto;
 position: relative;
}

#header {
 color: #fff;
 padding: 1.5em 1em .6em 0;
}

#siteTitle {
 font-size: 2.3em;
 margin: 0;
}

#siteSubtitle {
 font-size: 1em;
 padding-left: .8em;;
}

#titleLine {
 background: transparent;
 padding: 0;
}

#titleLine a {
 color: #cf6;
}
/*}}}*/

/***
!Tiddler display rules /%================================================================== %/
***/
/*{{{*/

#displayArea {
 width: 46em;
 margin: 0 0 0 16em;
}

.tiddler {
 margin: 0 0 .9em 0;
 padding: 0 1em;
 border-right: .25em solid #aaa;
 border-bottom: .25em solid #555;
 background: #fff;
}

.title {
 font-size: 1.5em;
 font-weight: bold;
 color: #900;
}

.toolbar {
 font-size: .8em;
 padding: .5em 0;
}

.toolbar .button{
 padding: .1em .3em;
 color: #000;
 border: .1em outset #cf6;
 background: #cf6;
 margin: .1em;
}

.toolbar .button:hover {
 background: #ef9;
 color: #000;
}

.toolbar .button:active {
 background: #ff0;
}

.titleCollapsed {
 position:relative;
 top:1em;
 background-color:#eee;
}

.toolbarCollapsed {
 position:relative;
 top:-1.6em;
}

/*}}}*/

/***
!Editor rules 
/% ------------------------------------------------------------------ %/
***/
/*{{{*/
.editor {
 font-size: .8em;
 color: #402C74;
 padding: .3em 0;
}

.editor input, .editor textarea {
 font: 1.1em/130% "Andale Mono", "Monaco", "Lucida Console", "Courier New", monospace;
 margin: 0;
 border: 1px inset #333;
 padding: 2px 0;
}

.editor textarea {
 width: 100%;
}

.editor input {
 margin-right: 3px;
 display: inline;
}

.titleEdit input {
 width: 15em;
}

.tagEdit input {
 width: 30em;
}

input:focus, textarea:focus {
 background: #ffe;
 border: 1px solid #000;
}

.footer {
 padding: .5em 0;
 margin: .5em 0;
 border-top: 1px solid #ddd;
 color: #555;
 text-align: center;	
}

/*}}}*/

/***
!Viewer rules 
/% ------------------------------------------------------------------ %/
***/
/*{{{*/
.viewer {
 line-height: 1.4em;
 font-size: 1em;
}

.viewer a:link, .viewer a:visited {
 color: #15b;
}

.viewer a:hover {
 color: #fff;
 background: #000;
}

.viewer .button{
 background: transparent;
 border-top: 1px solid #eee;
 border-left: 1px solid #eee;
 border-bottom: 1px solid #000;
 border-right: 1px solid #000;
}

.viewer .button:hover{
 background: #eee;
 color: #000;
}

.viewer .button:active{
 background: #ccc;
 border-bottom: 1px solid #eee;
 border-right: 1px solid #eee;
 border-top: 1px solid #111;
 border-left: 1px solid #111;
}


.viewer blockquote {
 border-left: 3px solid #777;
 margin: .3em;
 padding: .3em;
}

.viewer pre, .viewer code {
 color: #000;
 font-size: .9em;
 overflow: auto;
 border: 1px solid #fe8;
 background: #ffc;
}

.tagged {
 font-size: .8em;
}

.viewer ul {
 padding-left: 30px;
}

.viewer ol {
 padding-left: 30px;
}

ul {
 list-style-type: asquare;
}
ol { 
 list-style-type: decimal;
}

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

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

.viewer ul, .viewer ol, .viewer p {
 margin: .0;
}

.viewer li {
 margin: .2em 0;
}

h1,h2,h3,h4,h5,h6 {
 color: #000;
 font-weight: bold;
 background: #eee;
 padding: 2px 10px;
 margin: 5px 0;
}

.viewer h1 {font-size: 1.3em;}
.viewer h2 {font-size: 1.2em;}
.viewer h3 {font-size: 1.1em;}
.viewer h4 {font-size: 1em;}
.viewer h5 { font-size: .9em;}
.viewer h6 { font-size: .8em;}

.viewer table {
 border: 2px solid #303030;
 font-size: 11px;
 margin: 10px 0;
}

.viewer th, .viewer thead td {
 color: #000;
 background: #eee;
 border: 1px solid #aaa;
 padding: 0 3px;
}

.viewer td {
 border: 1px solid #aaa;
 padding: 0 3px;
}

.viewer caption {
 padding: 3px;
}

.viewer hr {
 border: none;
 border-top: dotted 1px #777;
 height: 1px;
 color: #777;
 margin: 7px 0;
}

.viewer {
 margin: .5em 0 0 0;
 padding: .5em 0;
 border-top: 1px solid #ccc;
}

.highlight {
 color: #000;
 background: #ffe72f;
}

/*}}}*/


/***
!Main menu rules /%=================================================================== %/
***/
/*{{{*/
#mainMenu {
 position: absolute;
 left: 0;
 width: 13em;
 background: #600;
 border-right: 3px solid #500;
 padding: 0;
 text-align: left;
 font-size: 1em;
}

#mainMenu h1{
 padding: 0;
 margin: 0;
 font-size: 1em;
 font-weight: normal;
}

#mainMenu ul {
 padding: 0;
 margin: 0;
 list-style: none;
}

#mainMenu h1 a,
#mainMenu li a,
#mainMenu li a.button {
 display: block;
 padding: 0 5px 0 10px;
 border: 0;
 border-bottom: 1px solid #500;
 border-top: 1px solid #900;
 margin: 0;
}

#mainMenu a,
#mainMenu a.button {
 height: 22px;
 height: 1.83em;
 line-height: 22px;
 color: #fff;
 background: #700;
 margin-left: 1em;
}

#mainMenu a:hover,
#mainMenu a.button:hover {
 background: #b00;
 color: #fff;
}
/*}}}*/
/***
!Sidebar options rules /%============================================================ %/
***/
/*{{{*/
#sidebar {
 width: 17em;
}

#sidebarOptions {
 background: #eeb;
 border-right: 3px solid #bb8;
 color: #B4C675;
 padding: .5em 0;
}

#sidebarOptions a {
 color: #700;
 margin: .2em .8em;
 padding: 0;
 border: 0;
}

#sidebarOptions a:hover, #sidebarOptions a:active {
 color: #fff;
 background: #700;
 border: 0;
}

#sidebarOptions input{
 margin: 2px 10px;
 border: 1px inset #333;
padding: 0;
}

#sidebarOptions .sliderPanel {
 background: #fff;
 color: #000;
 padding: 5px 10px;
 font-size: .9em;
}

#sidebarOptions .sliderPanel a{
 font-weight: normal;
 margin: 0;
}

#sidebarOptions .sliderPanel a:link,#sidebarOptions .sliderPanel a:visited {
 color: #700;
}

#sidebarOptions .sliderPanel a:hover,#sidebarOptions  .sliderPanel a:active  {
 color: #fff;
 background: #700;
}
/*}}}*/
/***
!Sidebar tabs rules /%===================================================================== %/
***/
/*{{{*/
#sidebarTabs {
 background: transparent;
 border-right: 3px solid #740;
 border-bottom: 3px solid #520;
 border: 0;
 padding: 0;
}

#contentWrapper #sidebarTabs a,
#contentWrapper #displayArea .tabContents a{
 color: #fff;
}

#contentWrapper #sidebarTabs a:hover,
#contentWrapper #displayArea .tabContents a:hover {
 background: #000;
 color: #fff;
}

#contentWrapper #sidebarTabs a:active,
#contentWrapper #displayArea .tabContents a:active {
 color: #000;
}

#contentWrapper  .tabSelected {
 background: #960;
}

#contentWrapper  .tabUnselected{
 background: #660;
}

#contentWrapper #sidebar .tabset{
 background: #eeb;
 border-right: 3px solid #bb8;
 padding: 0em;
}

#contentWrapper  .tabContents {
 font-size: .95em;
 background: #960;
 border:0;
 border-right: 3px solid #740;
 border-bottom: 3px solid #520;
 padding: .75em;
}

#contentWrapper  .tabContents {
 width: auto;
}

#contentWrapper #sidebarTabs .tabContents .tabset,
#contentWrapper .tabContents .tabset {
 border: 0;
 padding: 0;
 background: transparent;
}

#contentWrapper .tabContents .tabSelected,
#contentWrapper .tabContents .tabContents {
 background: #700;
 border: 0;
}

#contentWrapper .tabContents .tabUnselected {
 background: #440;
}

#contentWrapper .tabset a {
 color: #fff;
 padding: .2em .7em;
 margin: 0 .17em 0 0;
 height: 2em;
 position: static;
}

#contentWrapper .tabset a:hover {
 background: #000;
 color: #fff;
}

#contentWrapper .tabset a:active {
 color: #000;
}

#contentWrapper .tabContents ul {
 margin: 0;
 padding: 0;
 list-style: none;
}

#contentWrapper .tabContents .tabContents ul {
 color: #eeb;
}

.tabContents ul a,
.tabContents ul .button {
 color: #fff;
 display: block;
 padding: .1em 0 .1em .7em;
 background: transparent;
 border: 0;
}

.tabContents ul a:hover {
 color: #fff;
 background: #000;
}
/*}}}*/
/***
!Popup rules 
/%================================================================= %/
***/
/*{{{*/
.popup {
 font-size: .8em;
 padding: 0em;
 background: #333;
 border: 1px solid #000;
}

.popup hr {
 margin: 1px 0 0 0;
 visibility: hidden;
}

.popup li.disabled {
 color: #666;
}

.popup li a,
.popup li a:visited {
 color: #000;
 border: .1em outset #cf6;
 background: #cf6;
}

.popup li a:hover {
 border: .1em outset #cf6;
 background: #ef9;
 color: #000;
}
/*}}}*/
/***
!Message area rules /%================================================================= %/
***/
/*{{{*/
#messageArea {
 font-size: .9em;
 padding: .4em;
 background: #FFE72F;
 border-right: .25em solid #da1;
 border-bottom: .25em solid #a80;
 position: fixed;
 top: 10px;
 right: 10px;
 color: #000;
}

#contentWrapper #messageArea a {
 color: #00e;
 text-decoration: none;
}

#contentWrapper #messageArea a:hover {
 color: #00e;
 text-decoration: underline;
 background: transparent;
}

#contentWrapper #messageArea .messageToolbar a.button {
 border: 1px solid #da1;
}

#contentWrapper #messageArea .messageToolbar a.button:hover {
 color: #00e;
 text-decoration: none;
 border: 1px solid #000;
 background: #fff;
}
/*}}}*/


/***
!Calendar CSS
***/
/*{{{*/
.calendar{
 border-bottom: 1px solid #550000;
}

.viewer .calendar{
 width: 220px;
}

#mainMenu .calendar{
 font-size: 8px;
 cursor: pointer;
 width: 100%;
 border: 0;
 border-collapse: collapse;
}

#mainMenu .calendar .button{
 border: 0;
}

#mainMenu .calendar td{
 font-size: 8pt;
 padding: 0;
 background: #fff;
 border: 0;
}

#mainMenu .calendar a{
 margin: 0;
 color: #000;
 background: transparent;
}

#mainMenu .calendar a:hover{
 color: #000;
 background: transparent;
}

#mainMenu .calendarMonthname,
#mainMenu .calendar .calendarMonthTitle td a{
 color: #fff;
}

#mainMenu .calendarDaysOfWeek td{
 background: #500;
 color: #fff;
}

/*}}}*/


/***
!IE Display hacks 
/% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~%/
***/
/*{{{*/
body{
 _text-align: center;
}

#contentWrapper {
/* _width: 770px;  CSS UNDERSCORE HACK FOR PROPER WIN/IE DISPLAY */
 _text-align: left; /* CSS UNDERSCORE HACK FOR PROPER WIN/IE DISPLAY */	
}

#messageArea {
 _position: absolute;
}
/*}}}*/
/*{{{*/

@media print {
#mainMenu, #sidebar, #messageArea {display: none !important;}
#displayArea {margin: 1em 1em 0em 1em;}


/* LAYOUT ELEMENTS ========================================================== */
*
{
	margin: 0;
	padding: 0;
}

body {
    background: #fff;
	color: #000;
    font-size: 6.2pt;
	font-family: "Lucida Grande", "Bitstream Vera Sans", Helvetica, Verdana, Arial, sans-serif;
}

img {
	max-width: 2.2in;
	max-height: 4.3in;
}

#header, #side_container, #storeArea, #copyright, #floater, #messageArea, .save_accesskey, .site_description, #saveTest, .toolbar, .footer
{
	display: none;
}

#tiddlerDisplay, #displayArea
{
	display: inline;
}

.tiddler {
	margin: 0 0 2em 0;
	border-top: 1px solid #000;
	page-break-before: always;
}

.tiddler:first-child {
	page-break-before: avoid;
}

.title {
    font-size: 1.6em;
    font-weight: bold;
	margin-bottom: .3em;
	padding: .2em 0;
	border-bottom: 1px dotted #000;
}

p, blockquote, ul, li, ol, dt, dd, dl, table
{
	margin: 0 0 .3em 0;
}

h1, h2, h3, h4, h5, h6
{
	margin: .2em 0;
}  

h1
{
	font-size: 1.5em;
}

h2
{
	font-size: 1.3em;
}

h3
{
	font-size: 1.25em;
}

h4
{
	font-size: 1.15em;
}

h5
{
	font-size: 1.1em;
}

blockquote
{
	margin: .6em;
	padding-left: .6em;
	border-left: 1px solid #ccc;
}

ul
{
	list-style-type: circle;
}

li
{
	margin: .1em 0 .1em 2em;
	line-height: 1.4em;	
}

table
{
    border-collapse: collapse;
	font-size: 1em;
}

td, th
{
	border: 1px solid #999;
	padding: .2em;
}

hr {
    border: none;
    border-top: dotted 1px #777;
    height: 1px;
    color: #777;
	margin: .6em 0;
}
}
/*}}}*/
So have you ever been editing something where you copied something, then realized you needed to copy something else but hadn't yet pasted the first thing yet?  Dammit!  And yes, I know there are all sorts of clipboard apps, but who wants to run another application just for their clipboard??  

!! AutoHotKey to the rescue!

So [[AHK | http://www.autohotkey.com/]] is a great tool, and I managed to not only whip up a decent 'power clipboard', but also make that damn caps lock useful!  Basically the clipboard functions as a stack, with keys to clear and inspect the clipboard stack.

!!! Here's what it does:

| ''Key combo'' | ''Description'' |
| CapsLock  | push (copy) the highlighted text onto the clipboard |
| Shift+CapsLock | push (cut) the hightlighted text onto the clipboard |
| Win+CapsLock | pop the last item off the clipboard |
| CapsLock+Tilde | inspect the clipboard stack |
| CapsLock+(Number) | Copy to slot (number) |
| Win +(Number) | Paste from slot (number) |

+++!!![The Code]

{{{
; Clipboard controls:
CapsLock:: 		PushClip()
+CapsLock::		PushClip(true)
#CapsLock::		PopClip()
!CapsLock::		bufPtr:=1 ;reset the buffer stack
CapsLock & `::		InspectStack()

; To turn off capslock when it accidentally gets triggered
^!CapsLock::  
	suspend on
	send {CapsLock}
	suspend off
	return

; Copy to named buffers
CapsLock & 1:: ClipBuffer(1)
CapsLock & 2:: ClipBuffer(2)
CapsLock & 3:: ClipBuffer(3)
CapsLock & 4:: ClipBuffer(4)
#1:: PasteBuffer(1)
#2:: PasteBuffer(2)
#3:: PasteBuffer(3)
#4:: PasteBuffer(4)

; --------------------   Copy and paste routines   ----------------------
bufPtr:=0

; Cut or copy onto a stack, preserving the original clipboard contents
; Default operation is to copy
PushClip(cut=false) {
	global
	bufPtr++		
	ClipBuffer(bufPtr, cut)
}

; Paste the topmost item on the clipboard stack
PopClip() {
	global
	PasteBuffer(bufPtr)
	if( bufPtr > 1 )
		bufPtr--
}

; Copy/Cut to a specific slot
ClipBuffer( num, cut=false ) {
	global ;  NEED THIS else clip%num% is LOCAL
	SmartClip( clip%num%, cut )
}

; Paste a specific slot
PasteBuffer( num ) {
	local bak
	bak:=clipboard
	clipboard:=clip%num%
	Suspend On
	Send ^v
	Suspend Off
	clipboard:=bak
}

; Display the clipboard stack contents in a message box
InspectStack() {
	local str
	str:= "_____ Clipboard _______`n" . clipboard . "`n`n_____ Buffer :  (" . bufPtr . ") ______`n"
	Loop % bufPtr {
		str:= str . A_Index . ": __________`n" . clip%A_Index% . "`n"
	}
	MsgBox %str%
}
	
; Cut/copy only if there was something highlighted.
; Return that val and put the original contents back on the clipboard.
SmartClip( ByRef val, cut=false, raw=false ) {
	bak:= clipboardAll
	clipboard:= ""
	Suspend On
	if( cut )
		Send ^x
	else Send ^c
	ClipWait 0.1
	Suspend Off
	length:= StrLen( clipboardAll ) + StrLen( clipboard )
	if( length > 0 && raw ) 
		val:= clipboardAll
	else if( length > 0 )
		val:= clipboard
	clipboard:=bak
}
}}}
===
/***
|''Name:''|TWUpdatePlugin|
|''Description:''|Plugin to enable quick update of TW core|
|''Version:''|1.0.2|
|''Date:''|December 15, 2006|
|''Source:''|http://www.dcubed.ca/|
|''Author:''|Tom Otvos|
|''~CoreVersion:''|2.0.7|
|''Browser:''|Firefox 1.5+; InternetExplorer 6.0+|
***/

//{{{

version.extensions.twupdate = {major: 1, minor: 0, revision: 2, source: "http://www.dcubed.ca/"};

config.macros.twupdate = { 
	label: "update",
	sourceUrl: "http://www.tiddlywiki.com/empty.html", 
	lingo: {
		prompt: "Update this TiddlyWiki from TiddlyWiki.com", 
		warning: "Are you sure you want to update this document with the latest version of TiddlyWiki (and do you know that all your plugins are compatible)?\n\nIf you want to continue, your document will first be saved with a backup.",
		success: "Update was successful. Click on 'OK' to reload the document",
		errNoHttp: "Unable to allocate an HTTP request object for the update",
		errIncompatible: "This version of TiddlyWiki cannot be updated by this plugin. Sorry.",
		errIncompatible2: "This version of TiddlyWiki is the last version that can be updated by this plugin. Sorry.",
		progressLoading: "Getting update from TiddlyWiki.com...",
		progressLoadSuccess: "File successfully loaded",
		progressLoadFailure: "File was not loaded successfully (%0)",
		progressMerging: "Merging with existing document..."
	}
}

config.macros.twupdate.handler = function(place,macroName,params)
{
	if(!readOnly) {
		var label = params[0] ? params[0] : this.label;
		createTiddlyButton(place, label, this.lingo.prompt, this.onClick, null, null, null);
	}
}

config.macros.twupdate.onClick = function(e)
{
	if (version.major != 2 || version.minor > 1 || (version.minor == 0 && version.revision < 7)) {
		alert(config.macros.twupdate.lingo.errIncompatible);
		return;
	}
	
	//if (version.major != 2 || version.minor != 0 || version.revision == 11) {
	//	alert(config.macros.twupdate.lingo.errIncompatible2);
	//	return;
	//}
	
	if (!confirm(config.macros.twupdate.lingo.warning)) return;

	try {
		// force a save with backup
		var saveBackups = config.options.chkSaveBackups;
		config.options.chkSaveBackups = true;
		saveChanges();
		config.options.chkSaveBackups = saveBackups;
		
		var ajax = new AjaxHelper();
		displayMessage(config.macros.twupdate.lingo.progressLoading);
		ajax.getText(config.macros.twupdate.sourceUrl, config.macros.twupdate.performUpdate);		
	}
	catch (e) {
		alert(e);
	}

	return false;
}

// Courtesy of http://www.worldtimzone.com/res/encode/...
function utf8(wide) {
	var c, s;
	var enc = "";
	var i = 0;
	while(i<wide.length) {
		c= wide.charCodeAt(i++);
		// handle UTF-16 surrogates
		if (c>=0xDC00 && c<0xE000) continue;
		if (c>=0xD800 && c<0xDC00) {
			if (i>=wide.length) continue;
			s= wide.charCodeAt(i++);
			if (s<0xDC00 || c>=0xDE00) continue;
			c= ((c-0xD800)<<10)+(s-0xDC00)+0x10000;
		}
		// output value
		if (c<0x80) enc += String.fromCharCode(c);
		else if (c<0x800) enc += String.fromCharCode(0xC0+(c>>6),0x80+(c&0x3F));
		else if (c<0x10000) enc += String.fromCharCode(0xE0+(c>>12),0x80+(c>>6&0x3F),0x80+(c&0x3F));
		else enc += String.fromCharCode(0xF0+(c>>18),0x80+(c>>12&0x3F),0x80+(c>>6&0x3F),0x80+(c&0x3F));
	}
	return enc;
}

config.macros.twupdate.performUpdate = function(emptyHtml, status, statusText)
{
	// note that this is begin called from a callback from an event handler, so
	// "this" is most definitely not defined!
	
	if (status == 200)
		displayMessage(config.macros.twupdate.lingo.progressLoadSuccess);
	else {
		displayMessage(config.macros.twupdate.lingo.progressLoadFailure.format([statusText]));
		return;
	}
	displayMessage(config.macros.twupdate.lingo.progressMerging);
	
	// very important...convert the response to UTF-8 to be fully TW-compatible
	var re = /[^\u0000-\u007F]/g ;
	emptyHtml = emptyHtml.replace(re, function($0) {return(utf8($0));});
	
	// the bulk of this is cribbed from saveChanges()...
	var originalPath = document.location.toString();
	// Check we were loaded from a file URL
	if (originalPath.substr(0,5) != "file:") {
		alert(config.messages.notFileUrlError);
		if (store.tiddlerExists(config.messages.saveInstructions))
			displayTiddler(null,config.messages.saveInstructions);
		return;
	}
	var localPath = getLocalPath(originalPath);

	// Locate the storeArea div's
	var posOpeningDiv = emptyHtml.indexOf(startSaveArea);
	var limitClosingDiv = emptyHtml.indexOf("<!--POST-BODY-START--"+">");
	var posClosingDiv = emptyHtml.lastIndexOf(endSaveArea,limitClosingDiv == -1 ? emptyHtml.length : limitClosingDiv);
	if ((posOpeningDiv == -1) || (posClosingDiv == -1)) {
		alert(config.messages.invalidFileError.format(['empty.html']));
		return;
	}

	// Save new file
	var revised = emptyHtml.substr(0,posOpeningDiv + startSaveArea.length) + 
				convertUnicodeToUTF8(allTiddlersAsHtml()) + "\n\t\t" +
				emptyHtml.substr(posClosingDiv);
	var newSiteTitle = convertUnicodeToUTF8((wikifyPlain("SiteTitle") + " - " + wikifyPlain("SiteSubtitle")).htmlEncode());
	revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
	revised = revised.replaceChunk("<!--PRE-HEAD-START--"+">","<!--PRE-HEAD-END--"+">","\n" + store.getTiddlerText("MarkupPreHead","") + "\n");
	revised = revised.replaceChunk("<!--POST-HEAD-START--"+">","<!--POST-HEAD-END--"+">","\n" + store.getTiddlerText("MarkupPostHead","") + "\n");
	revised = revised.replaceChunk("<!--PRE-BODY-START--"+">","<!--PRE-BODY-END--"+">","\n" + store.getTiddlerText("MarkupPreBody","") + "\n");
	revised = revised.replaceChunk("<!--POST-BODY-START--"+">","<!--POST-BODY-END--"+">","\n" + store.getTiddlerText("MarkupPostBody","") + "\n");
	var save = saveFile(localPath, revised);
	if (save) {
		displayMessage(config.messages.mainSaved, "file://" + localPath);
		store.setDirty(false);
		alert(config.macros.twupdate.lingo.success);
		document.location.reload();
	}
	else
		alert(config.messages.mainFailed);
}

function AjaxHelper()
{
	this.http = null;
	
	try
	{
		this.http = new XMLHttpRequest()
	}
	
	catch(e)
	{
		// if we don't get an internal object, try allocating it using ActiveX, with successive
		// fallbacks to earlier MSXML versions as necessary
		try
		{
			this.http = new ActiveXObject("Msxml2.XMLHTTP.4.0")
		} 
		catch(e) 
		{
			try
			{
				this.http = new ActiveXObject("MSXML2.XMLHTTP")
			} 
			catch(e) 
			{
				try
				{
					this.http = new ActiveXObject("Microsoft.XMLHTTP")
				} 
				catch(e) 
				{
					this.http = null
				}
			}
		}
	}
		
	if (!this.http) throw 'Unable to allocate an HTTP request object';
}

AjaxHelper.prototype.getText = function(url, callback, async, force)
{
	if (!this.http) return;
	if (async == undefined) async = true;
	if (force == undefined) force = false;
	// ??? right now, we are not handling "forced" requests
	this._request("GET", url, callback, async, true, false);
}

AjaxHelper.prototype.getXML = function(url, callback, async, force)
{
	if (!this.http) return;
	if (async == undefined) async = true;
	if (force == undefined) force = false;
	// ??? right now, we are not handling "forced" requests
	this._request("GET", url, callback, async, true, true);
}

AjaxHelper.prototype.getHead = function(url, callback, async, force)
{
	if (!this.http) return;
	if (async == undefined) async = true;
	if (force == undefined) force = false;
	// ??? right now, we are not handling "forced" requests
	this._request("HEAD", url, callback, async, false, false);
}

AjaxHelper.prototype.abort = function()
{
	if (this.http) this.http.abort();
}

AjaxHelper.prototype.setRequestHeader = function(name, value)
{
	if (this.http) this.http.setRequestHeader(name, value);
}

AjaxHelper.prototype._request = function(method, url, callback, async, hasResponse, hasResponseXML)
{
	if (!this.http) return;
	
	// get reference to request object so we can use it in closure
	var xmlHttp = this.http, helper = this;
	xmlHttp.onreadystatechange = function()
	{
		if (!async) return;
		if (xmlHttp.readyState == 4)
			callback((hasResponse ? (hasResponseXML ? xmlHttp.responseXML : xmlHttp.responseText) : null), xmlHttp.status, xmlHttp.statusText, helper._parsedResponseHeaders());
	}
	
	try {
		// need some cross-domain privileges for Firefox
		try {
			netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
		} 
		catch (e) 
		{
		}
		
		xmlHttp.open(method, url, async);
		xmlHttp.send(null);
		if (!async) callback((hasResponse ? (hasResponseXML ? xmlHttp.responseXML : xmlHttp.responseText) : null), xmlHttp.status, xmlHttp.statusText, this._parsedResponseHeaders());
	}
	
	catch (e)
	{
		alert(e);
	}
}

AjaxHelper.prototype._parsedResponseHeaders = function()
{
	if (this.http) {
		var headersArray = new Array();
		var headers = this.http.getAllResponseHeaders().split("\n");
		for (var i = 0; i < headers.length; i++) {
			var h = headers[i].trim();
			if (h.length == 0) continue;
			// value can have ':' so do not use split here!
			var sep = h.indexOf(':');
			headersArray[h.substring(0, sep).trim()] = h.substr(sep + 1).trim();
		}
		return headersArray;
	}
	else
		return null;
}

//}}}
<<allTags>>
Anyone who does not think this is ''crucial'' to the survival of the Java platform has, quite frankly, totally lost perspective on modern information technology.  I mean, come on, what do you do more -- read or watch TV?  (Discounting the availability of quality progamming.)  

Video is a //richer experience//.  We've all got text and images figured out OK.  Now let's move on to the next level - audio and video.  If the Java platform can't keep up, it ''will'' get left behind.  ''Period''.

[[ This guy Gets It | http://www.oreillynet.com/onjava/blog/2007/05/java_se_media_or_not_at_javaon.html]]
<<tiddler About>>
This form is provided for uploading any file with the store.php script describe in UploadPlugin
----
<html><center>
<form enctype="multipart/form-data" action="http://tiddlywiki.bidix.info/store.php" method="post" target="_blank">
 <input type="hidden" name="MAX_FILE_SIZE" value="3000000" />
This file : <input name="userfile" type="file" /><p>
Options* : <input type="text" name="UploadPlugin" size=70 value="backupDir=backupDir;user=UPLOAD_USER;password=UPLOAD_PASSWORD;" /><p>
 <input type="submit" value="Upload" />
</form></center>
</html>
----
 * Don't save your real username and password in any tiddler
!Options used by UploadPlugin
Username: <<option txtUploadUserName>>
Password: <<option pasUploadPassword>>

Url of the [[store.php]] script^^(1)^^: <<option txtUploadStoreUrl 50>>
Relative Directory to store the file^^(2)^^: <<option txtUploadDir 50>>
Filename of the uploaded file^^(3)^^: <<option txtUploadFilename 40>>
Directory to backup file on webserver^^(4)^^: <<option txtUploadBackupDir>>

^^(1)^^Mandatory either in UploadOptions or in MacroParameter
^^(2)^^If empty stores in the [[store.php]] directory
^^(3)^^If empty takes the actual filename
^^(4)^^If empty existing the file with same name on webserver will be overwriten

!Upload
{{{<<upload [[UploadStoreUrl]}}}^^(1)^^{{{ [UploadFilename] [UploadBackupDir]]>>}}}

<<upload>>
/***
<<tiddler UploadPluginDoc>>
!Code
***/
//{{{
version.extensions.UploadPlugin = {
	major: 3, minor: 3, revision: 3, 
	date: new Date(2006,6,30),
	type: 'macro',
	source: 'http://tiddlywiki.bidix.info/#UploadPlugin',
	docs: 'http://tiddlywiki.bidix.info/#UploadPluginDoc'
};
//}}}

////+++!![config.lib.file]

//{{{
if (!config.lib) config.lib = {};
if (!config.lib.file) config.lib.file= {
	author: 'BidiX',
	version: {major: 0, minor: 1, revision: 0}, 
	date: new Date(2006,3,9)
};
config.lib.file.dirname = function (filePath) {
	var lastpos;
	if ((lastpos = filePath.lastIndexOf("/")) != -1) {
		return filePath.substring(0, lastpos);
	} else {
		return filePath.substring(0, filePath.lastIndexOf("\\"));
	}
};
config.lib.file.basename = function (filePath) {
	var lastpos;
	if ((lastpos = filePath.lastIndexOf("#")) != -1) 
		filePath = filePath.substring(0, lastpos);
	if ((lastpos = filePath.lastIndexOf("/")) != -1) {
		return filePath.substring(lastpos + 1);
	} else
		return filePath.substring(filePath.lastIndexOf("\\")+1);
};
window.basename = function() {return "@@deprecated@@";};
//}}}
////===

////+++!![config.lib.log]

//{{{
if (!config.lib) config.lib = {};
if (!config.lib.log) config.lib.log= {
	author: 'BidiX',
	version: {major: 0, minor: 1, revision: 0}, 
	date: new Date(2006,3,9)
};
config.lib.Log = function(tiddlerTitle, logHeader) {
	if (version.major < 2)
		this.tiddler = store.tiddlers[tiddlerTitle];
	else
		this.tiddler = store.getTiddler(tiddlerTitle);
	if (!this.tiddler) {
		this.tiddler = new Tiddler();
		this.tiddler.title = tiddlerTitle;
		this.tiddler.text = "| !date | !user | !location |" + logHeader;
		this.tiddler.created = new Date();
		this.tiddler.modifier = config.options.txtUserName;
		this.tiddler.modified = new Date();
	if (version.major < 2)
		store.tiddlers[tiddlerTitle] = this.tiddler;
	else
		store.addTiddler(this.tiddler);
	}
	return this;
};

config.lib.Log.prototype.newLine = function (line) {
	var now = new Date();
	var newText = "| ";
	newText += now.getDate()+"/"+(now.getMonth()+1)+"/"+now.getFullYear() + " ";
	newText += now.getHours()+":"+now.getMinutes()+":"+now.getSeconds()+" | ";
	newText += config.options.txtUserName + " | ";
	var location = document.location.toString();
	var filename = config.lib.file.basename(location);
	if (!filename) filename = '/';
	newText += "[["+filename+"|"+location + "]] |";
	this.tiddler.text = this.tiddler.text + "\n" + newText;
	this.addToLine(line);
};

config.lib.Log.prototype.addToLine = function (text) {
	this.tiddler.text = this.tiddler.text + text;
	this.tiddler.modifier = config.options.txtUserName;
	this.tiddler.modified = new Date();
	if (version.major < 2)
	store.tiddlers[this.tiddler.tittle] = this.tiddler;
	else {
		store.addTiddler(this.tiddler);
		story.refreshTiddler(this.tiddler.title);
		store.notify(this.tiddler.title, true);
	}
	if (version.major < 2)
		store.notifyAll(); 
};
//}}}
////===

////+++!![config.lib.options]

//{{{
if (!config.lib) config.lib = {};
if (!config.lib.options) config.lib.options = {
	author: 'BidiX',
	version: {major: 0, minor: 1, revision: 0}, 
	date: new Date(2006,3,9)
};

config.lib.options.init = function (name, defaultValue) {
	if (!config.options[name]) {
		config.options[name] = defaultValue;
		saveOptionCookie(name);
	}
};
//}}}
////===

////+++!![PasswordTweak]

//{{{
version.extensions.PasswordTweak = {
	major: 1, minor: 0, revision: 2, date: new Date(2006,3,11),
	type: 'tweak',
	source: 'http://tiddlywiki.bidix.info/#PasswordTweak'
};
//}}}
/***
!!config.macros.option
***/
//{{{
config.macros.option.passwordCheckboxLabel = "Save this password on this computer";
config.macros.option.passwordType = "password"; // password | text

config.macros.option.onChangeOption = function(e)
{
	var opt = this.getAttribute("option");
	var elementType,valueField;
	if(opt) {
		switch(opt.substr(0,3)) {
			case "txt":
				elementType = "input";
				valueField = "value";
				break;
			case "pas":
				elementType = "input";
				valueField = "value";
				break;
			case "chk":
				elementType = "input";
				valueField = "checked";
				break;
		}
		config.options[opt] = this[valueField];
		saveOptionCookie(opt);
		var nodes = document.getElementsByTagName(elementType);
		for(var t=0; t<nodes.length; t++) {
			var optNode = nodes[t].getAttribute("option");
			if (opt == optNode) 
				nodes[t][valueField] = this[valueField];
		}
	}
	return(true);
};

config.macros.option.handler = function(place,macroName,params)
{
    var opt = params[0];
	var size = 15;
	if (params[1])
		size = params[1];
    if(config.options[opt] === undefined) {
        return;}
    var c;
    switch(opt.substr(0,3)) {
		case "txt":
			c = document.createElement("input");
			c.onkeyup = this.onChangeOption;
			c.setAttribute ("option",opt);
			c.size = size;
			c.value = config.options[opt];
			place.appendChild(c);
			break;
		case "pas":
			// input password
			c = document.createElement ("input");
			c.setAttribute("type",config.macros.option.passwordType);
			c.onkeyup = this.onChangeOption;
			c.setAttribute("option",opt);
			c.size = size;
			c.value = config.options[opt];
			place.appendChild(c);
			// checkbox link with this password "save this password on this computer"
			c = document.createElement("input");
			c.setAttribute("type","checkbox");
			c.onclick = this.onChangeOption;
			c.setAttribute("option","chk"+opt);
			place.appendChild(c);
			c.checked = config.options["chk"+opt];
			// text savePasswordCheckboxLabel
			place.appendChild(document.createTextNode(config.macros.option.passwordCheckboxLabel));
			break;
		case "chk":
			c = document.createElement("input");
			c.setAttribute("type","checkbox");
			c.onclick = this.onChangeOption;
			c.setAttribute("option",opt);
			place.appendChild(c);
			c.checked = config.options[opt];
			break;
	}
};
//}}}
/***
!! Option cookie stuff
***/
//{{{
window.loadOptionsCookie_orig_PasswordTweak = window.loadOptionsCookie;
window.loadOptionsCookie = function()
{
	var cookies = document.cookie.split(";");
	for(var c=0; c<cookies.length; c++) {
		var p = cookies[c].indexOf("=");
		if(p != -1) {
			var name = cookies[c].substr(0,p).trim();
			var value = cookies[c].substr(p+1).trim();
			switch(name.substr(0,3)) {
				case "txt":
					config.options[name] = unescape(value);
					break;
				case "pas":
					config.options[name] = unescape(value);
					break;
				case "chk":
					config.options[name] = value == "true";
					break;
			}
		}
	}
};

window.saveOptionCookie_orig_PasswordTweak = window.saveOptionCookie;
window.saveOptionCookie = function(name)
{
	var c = name + "=";
	switch(name.substr(0,3)) {
		case "txt":
			c += escape(config.options[name].toString());
			break;
		case "chk":
			c += config.options[name] ? "true" : "false";
			// is there an option link with this chk ?
			if (config.options[name.substr(3)]) {
				saveOptionCookie(name.substr(3));
			}
			break;
		case "pas":
			if (config.options["chk"+name]) {
				c += escape(config.options[name].toString());
			} else {
				c += "";
			}
			break;
	}
	c += "; expires=Fri, 1 Jan 2038 12:00:00 UTC; path=/";
	document.cookie = c;
};
//}}}
/***
!! Initializations
***/
//{{{
// define config.options.pasPassword
if (!config.options.pasPassword) {
	config.options.pasPassword = 'defaultPassword';
	window.saveOptionCookie('pasPassword');
}
// since loadCookies is first called befor password definition
// we need to reload cookies
window.loadOptionsCookie();
//}}}
////===

////+++!![config.macros.upload]

//{{{
config.macros.upload = {
	accessKey: "U",
	formName: "UploadPlugin",
	contentType: "text/html;charset=UTF-8",
	defaultStoreScript: "store.php"
};

// only this two configs need to be translated
config.macros.upload.messages = {
	aboutToUpload: "About to upload TiddlyWiki to %0",
	errorDownloading: "Error downloading",
	errorUploadingContent: "Error uploading content",
	fileNotFound: "file to upload not found",
	fileNotUploaded: "File %0 NOT uploaded",
	mainFileUploaded: "Main TiddlyWiki file uploaded to %0",
	urlParamMissing: "url param missing",
	rssFileNotUploaded: "RssFile %0 NOT uploaded",
	rssFileUploaded: "Rss File uploaded to %0"
};

config.macros.upload.label = {
	promptOption: "Save and Upload this TiddlyWiki with UploadOptions",
	promptParamMacro: "Save and Upload this TiddlyWiki in %0",
	saveLabel: "save to web", 
	saveToDisk: "save to disk",
	uploadLabel: "upload"	
};

config.macros.upload.handler = function(place,macroName,params){
	// parameters initialization
	var storeUrl = params[0];
	var toFilename = params[1];
	var backupDir = params[2];
	var uploadDir = params[3];
	var username = params[4];
	var password; // for security reason no password as macro parameter
	var label;
	if (document.location.toString().substr(0,4) == "http")
		label = this.label.saveLabel;
	else
		label = this.label.uploadLabel;
	var prompt;
	if (storeUrl) {
		prompt = this.label.promptParamMacro.toString().format([this.dirname(storeUrl)]);
	}
	else {
		prompt = this.label.promptOption;
	}
	createTiddlyButton(place, label, prompt, 
						function () {
							config.macros.upload.upload(storeUrl, toFilename, uploadDir, backupDir, username, password); 
							return false;}, 
						null, null, this.accessKey);
};
config.macros.upload.UploadLog = function() {
	return new config.lib.Log('UploadLog', " !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |" );
};
config.macros.upload.UploadLog.prototype = config.lib.Log.prototype;
config.macros.upload.UploadLog.prototype.startUpload = function(storeUrl, toFilename, uploadDir,  backupDir) {
	var line = " [[" + config.lib.file.basename(storeUrl) + "|" + storeUrl + "]] | ";
	line += uploadDir + " | " + toFilename + " | " + backupDir + " |";
	this.newLine(line);
};
config.macros.upload.UploadLog.prototype.endUpload = function() {
	this.addToLine(" Ok |");
};
config.macros.upload.basename = config.lib.file.basename;
config.macros.upload.dirname = config.lib.file.dirname;
config.macros.upload.upload = function(storeUrl, toFilename, uploadDir, backupDir, username, password)
{
	// parameters initialization
	storeUrl = (storeUrl ? storeUrl : config.options.txtUploadStoreUrl);
	toFilename = (toFilename ? toFilename : config.options.txtUploadFilename);
	backupDir = (backupDir ? backupDir : config.options.txtUploadBackupDir);
	uploadDir = (uploadDir ? uploadDir : config.options.txtUploadDir);
	username = (username ? username : config.options.txtUploadUserName);
	password = config.options.pasUploadPassword; // for security reason no password as macro parameter
	if (storeUrl === '') {
		config.macros.upload.defaultStoreScript;
	}
	if (config.lib.file.dirname(storeUrl) === '') {
		storeUrl = config.lib.file.dirname(document.location.toString())+'/'+storeUrl;
	}
	if (toFilename === '') {
		toFilename = config.lib.file.basename(document.location.toString());
	}

	clearMessage();
	// only for forcing the message to display
	 if (version.major < 2)
		store.notifyAll();
	if (!storeUrl) {
		alert(config.macros.upload.messages.urlParamMissing);
		return;
	}
	
	var log = new this.UploadLog();
	log.startUpload(storeUrl, toFilename, uploadDir,  backupDir);
	if (document.location.toString().substr(0,5) == "file:") {
		saveChanges();
	}
	displayMessage(config.macros.upload.messages.aboutToUpload.format([this.dirname(storeUrl)]), this.dirname(storeUrl));
	this.uploadChanges(storeUrl, toFilename, uploadDir, backupDir, username, password);
	if(config.options.chkGenerateAnRssFeed) {
		//var rssContent = convertUnicodeToUTF8(generateRss());
		var rssContent = generateRss();
		var rssPath = toFilename.substr(0,toFilename.lastIndexOf(".")) + ".xml";
		this.uploadContent(rssContent, storeUrl, rssPath, uploadDir, '', username, password, 
			function (responseText) {
				if (responseText.substring(0,1) != '0') {
					displayMessage(config.macros.upload.messages.rssFileNotUploaded.format([rssPath]));
				}
				else {
					if (uploadDir) {
						rssPath = uploadDir + "/" + config.macros.upload.basename(rssPath);
					} else {
						rssPath = config.macros.upload.basename(rssPath);
					}
					displayMessage(config.macros.upload.messages.rssFileUploaded.format(
						[config.macros.upload.dirname(storeUrl)+"/"+rssPath]), config.macros.upload.dirname(storeUrl)+"/"+rssPath);
				}
				// for debugging store.php uncomment last line
				//DEBUG alert(responseText);
			});
	}
	return;
};

config.macros.upload.uploadChanges = function(storeUrl, toFilename, uploadDir, backupDir, 
		username, password) {
	var original;
	if (document.location.toString().substr(0,4) == "http") {
		original =  this.download(storeUrl, toFilename, uploadDir, backupDir, username, password);
		return;
	}
	else {
		// standard way : Local file
		
		original = loadFile(getLocalPath(document.location.toString()));
		if(window.Components) {
			// it's a mozilla browser
			try {
				netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
				var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
									.createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
				converter.charset = "UTF-8";
				original = converter.ConvertToUnicode(original);
			}
			catch(e) {
			}
		}
	}
	//DEBUG alert(original);
	this.uploadChangesFrom(original, storeUrl, toFilename, uploadDir, backupDir, 
		username, password);
};

config.macros.upload.uploadChangesFrom = function(original, storeUrl, toFilename, uploadDir, backupDir, 
		username, password) {
	var startSaveArea = '<div id="' + 'storeArea">'; // Split up into two so that indexOf() of this source doesn't find it
	var endSaveArea = '</d' + 'iv>';
	// Locate the storeArea div's
	var posOpeningDiv = original.indexOf(startSaveArea);
	var posClosingDiv = original.lastIndexOf(endSaveArea);
	if((posOpeningDiv == -1) || (posClosingDiv == -1))
		{
		alert(config.messages.invalidFileError.format([document.location.toString()]));
		return;
		}
	var revised = original.substr(0,posOpeningDiv + startSaveArea.length) + 
				allTiddlersAsHtml() + "\n\t\t" +
				original.substr(posClosingDiv);
	var newSiteTitle;
	if(version.major < 2){
		newSiteTitle = (getElementText("siteTitle") + " - " + getElementText("siteSubtitle")).htmlEncode();
	} else {
		newSiteTitle = (wikifyPlain ("SiteTitle") + " - " + wikifyPlain ("SiteSubtitle")).htmlEncode();
	}
	revised = revised.replace(new RegExp("<title>[^<]*</title>", "im"),"<title>"+ newSiteTitle +"</title>");
	var response = this.uploadContent(revised, storeUrl, toFilename, uploadDir, backupDir, 
		username, password, function (responseText) {
					if (responseText.substring(0,1) != '0') {
						alert(responseText);
						displayMessage(config.macros.upload.messages.fileNotUploaded.format([getLocalPath(document.location.toString())]));
					}
					else {
						if (uploadDir !== '') {
							toFilename = uploadDir + "/" + config.macros.upload.basename(toFilename);
						} else {
							toFilename = config.macros.upload.basename(toFilename);
						}
						displayMessage(config.macros.upload.messages.mainFileUploaded.format(
							[config.macros.upload.dirname(storeUrl)+"/"+toFilename]), config.macros.upload.dirname(storeUrl)+"/"+toFilename);
						var log = new config.macros.upload.UploadLog();
						log.endUpload();
						store.setDirty(false);
					}
					// for debugging store.php uncomment last line
					//DEBUG alert(responseText);
				}
			);
};

config.macros.upload.uploadContent = function(content, storeUrl, toFilename, uploadDir, backupDir, 
		username, password, callbackFn) {
	var boundary = "---------------------------"+"AaB03x";		
	var request;
	try {
		request = new XMLHttpRequest();
		} 
	catch (e) { 
		request = new ActiveXObject("Msxml2.XMLHTTP"); 
		}
	if (window.netscape){
			try {
				if (document.location.toString().substr(0,4) != "http") {
					netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserRead');}
			}
			catch (e) { }
		}		
	//DEBUG alert("user["+config.options.txtUploadUserName+"] password[" + config.options.pasUploadPassword + "]");
	// compose headers data
	var sheader = "";
	sheader += "--" + boundary + "\r\nContent-disposition: form-data; name=\"";
	sheader += config.macros.upload.formName +"\"\r\n\r\n";
	sheader += "backupDir="+backupDir
				+";user=" + username 
				+";password=" + password
				+";uploaddir=" + uploadDir
				+ ";;\r\n"; 
	sheader += "\r\n" + "--" + boundary + "\r\n";
	sheader += "Content-disposition: form-data; name=\"userfile\"; filename=\""+toFilename+"\"\r\n";
	sheader += "Content-Type: " + config.macros.upload.contentType + "\r\n";
	sheader += "Content-Length: " + content.length + "\r\n\r\n";
	// compose trailer data
	var strailer = new String();
	strailer = "\r\n--" + boundary + "--\r\n";
	var data;
	data = sheader + content + strailer;
	//request.open("POST", storeUrl, true, username, password);
	request.open("POST", storeUrl, true);
	request.onreadystatechange = function () {
				if (request.readyState == 4) {
				     if (request.status == 200)
						callbackFn(request.responseText);
					else
						alert(config.macros.upload.messages.errorUploadingContent);
				}
		};
	request.setRequestHeader("Content-Length",data.length);
	request.setRequestHeader("Content-Type","multipart/form-data; boundary="+boundary);
	request.send(data); 
};


config.macros.upload.download = function(uploadUrl, uploadToFilename, uploadDir, uploadBackupDir, 
	username, password) {
	var request;
	try {
		request = new XMLHttpRequest();
	} 
	catch (e) { 
		request = new ActiveXObject("Msxml2.XMLHTTP"); 
	}
	try {
		if (uploadUrl.substr(0,4) == "http") {
			netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
			}
		else {
			netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
		}
	} catch (e) { }
	//request.open("GET", document.location.toString(), true, username, password);
	request.open("GET", document.location.toString(), true);
	request.onreadystatechange = function () {
		if (request.readyState == 4) {
			if(request.status == 200) {
				config.macros.upload.uploadChangesFrom(request.responseText, uploadUrl, 
					uploadToFilename, uploadDir, uploadBackupDir, username, password);
			}
			else
				alert(config.macros.upload.messages.errorDownloading.format(
					[document.location.toString()]));
		}
	};
	request.send(null);
};

//}}}
////===

////+++!![Initializations]

//{{{
config.lib.options.init('txtUploadStoreUrl','store.php');
config.lib.options.init('txtUploadFilename','');
config.lib.options.init('txtUploadDir','');
config.lib.options.init('txtUploadBackupDir','');
config.lib.options.init('txtUploadUserName',config.options.txtUserName);
config.lib.options.init('pasUploadPassword','');
config.shadowTiddlers.UploadPluginDoc = "[[Full Documentation|http://tiddlywiki.bidix.info/l#UploadPluginDoc ]]\n"; 


//}}}
////===

////+++!![Core Hijacking]

//{{{
config.macros.saveChanges.label_orig_UploadPlugin = config.macros.saveChanges.label;
config.macros.saveChanges.label = config.macros.upload.label.saveToDisk;

config.macros.saveChanges.handler_orig_UploadPlugin = config.macros.saveChanges.handler;
/* TOM removed this!
config.macros.saveChanges.handler = function(place)
{
	if ((!readOnly) && (document.location.toString().substr(0,5) != "http"))
		createTiddlyButton(place,this.label,this.prompt,this.onClick,null,null,this.accessKey);
}
*/
//}}}
////===
/***


----
UploadPluginMsgEN provides the english translation for UploadPlugin.
----


***/
/***
|''Name:''|UploadPluginMsgEN|
|''Type:''|Translation|
|''Version:''|UploadPlugin 3.1.x (12/02/2006)|
|''Source:''|[[TiddlyWiki.BidiX.info|http://tiddlywiki.BidiX.info/#UploadPluginEN]]|
|''Author:''|BidiX[at]BidiX.info with modifications by [[Yann Perrin]]|
|''Compatibility:''|TW1.2 & TW2|
!Usage : 
For an english translation of UploadPlugin Message when using PolyGlotPlugin, import this tiddler in the TiddlyWiki. Make sure it is tagged with {{{en}}} and {{{linguo}}}. 
Comments and suggestions are welcome.
***/
//{{{
config.macros.upload.messages = {
	aboutToUpload: "About to upload TiddlyWiki to %0",
	errorDownloading: "Error downloading",
	errorUploadingContent: "Error uploading content",
	fileNotFound: "file to upload not found",
	fileNotUploaded: "File %0 NOT uploaded",
	mainFileUploaded: "Main TiddlyWiki file uploaded to %0",
	urlParamMissing: "url param missing",
	rssFileNotUploaded: "RssFile %0 NOT uploaded",
	rssFileUploaded: "Rss File uploaded to %0"
};

config.macros.upload.label = {
	promptOption: "Save and Upload this TiddlyWiki with UploadOptions",
	promptParamMacro: "Save and Upload this TiddlyWiki in %0",
	saveLabel: "save to web", 
	saveToDisk: "save to disk",
	uploadLabel: "upload"	
};
config.macros.saveChanges.label = config.macros.upload.label.saveToDisk;
//}}}
/***
<<tiddler UploadToFileMacroDoc>>
!Code
***/
//{{{
version.extensions.UploadToFileMacro = {
	major: 1, minor: 0, revision: 0, 
	date: new Date(2006,3,13),
	type: 'macro',
	source: 'http://tiddlywiki.bidix.info/#UploadToFilePlugin',
	docs: 'http://tiddlywiki.bidix.info/#UploadToFilePluginDoc'
};

config.macros.uploadToFile = {
	label: "uploadToFile",
	prompt: "upload tiddler '%0' to file '%1' ",
	warning: "Are you sure you want to upload '%0'?",
	dirname: config.lib.file.dirname,
	basename: config.lib.file.dirname
};

config.macros.uploadToFile.messages = {
	fileUploaded: "tiddler '%0' uploaded to file '%1'",
	fileNotUploaded: "tiddler '%0' NOT uploaded"
};

config.macros.uploadToFile.handler = function(place, macroName, params, wikifier,paramString, tiddler) {
	// parameters initialization
	var toFilename = params[0];
	var tiddlerTitle = params[1];
	if (!tiddlerTitle) { 
		tiddlerTitle = tiddler.title;
	} else {
		tiddler = store.getTiddler(tiddlerTitle);
	}
	if (!toFilename) { 
		toFilename = tiddlerTitle;
	} 
	var prompt = this.prompt.format([tiddlerTitle, toFilename]);
	createTiddlyButton(place, this.label, this.prompt.format([tiddlerTitle, toFilename]), 
						function () {
							config.macros.uploadToFile.upload(tiddler, toFilename); 
							return false;}, 
						null, null, null);
};

config.macros.uploadToFile.upload = function(tiddler, toFilename, storeUrl, toFilename, uploadDir, backupDir, username, password) {
		var uploadIt = true; 
		if (this.warning)
			uploadIt = confirm(this.warning.format([tiddler.title])); 
		if (uploadIt) {
			if (!tiddler)	{
				alert("Tiddler not found.");
				return;
			}
			if (!config.macros.upload.uploadContent) {
				alert ("no UploadPlugin extension");
				return;
			}
			// parameters initialization
			storeUrl = (storeUrl ? storeUrl : config.options.txtUploadStoreUrl);
			toFilename = (toFilename ? toFilename : tiddler.title);
			backupDir = (backupDir ? backupDir : config.options.txtUploadBackupDir);
			uploadDir = (uploadDir ? uploadDir : config.options.txtUploadDir);
			username = (username ? username : config.options.txtUploadUserName);
			password = (password ? password :config.options.pasUploadPassword); 
			config.macros.upload.uploadContent(tiddler.text, storeUrl, toFilename, uploadDir, backupDir, username, password, 
					function (responseText) {
						if (responseText.substring(0,1) != '0') {
							displayMessage(config.macros.uploadToFile.messages.fileNotUploaded.format([tiddler.title]));
						}
						else {
							if (uploadDir) {
								toFilename = uploadDir + "/" + config.macros.uploadToFile.basename(toFilename);
							} 
							displayMessage(config.macros.uploadToFile.messages.fileUploaded.format(
								[tiddler.title, config.macros.uploadToFile.dirname(storeUrl)+"/"+toFilename]), config.macros.upload.dirname(storeUrl)+"/"+toFilename);
						}
						// for debugging store.php uncomment last line
						//DEBUG alert(responseText);
					});
		}
		return false;
};

config.shadowTiddlers.UploadToFileMacroDoc= "[[Full Documentation|http://tiddlywiki.bidix.info/l#UploadToFileMacroDoc]]\n"; 
//}}}
<div class='toolbar' macro='toolbar +editTiddler permalink references jump collapseTiddler collapseOthers closeOthers +closeTiddler'></div>
<div class='tagged' macro='tags'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'>by <span macro='view modifier link'></span>, <span macro='view modified date [[DD MMM YYYY]]'></span> (created <span macro='view created date [[DD MMM YYYY]]'></span>)</div>
<div class='viewer' macro='view text wikified'></div>
If you have an SDN login, please vote for this bug.  It is a limitation to generics, and (more importantly) provides a proper mechanism for the [[ fluent interface | http://martinfowler.com/bliki/FluentInterface.html]] to be properly supported.  (For an overview of why it doesn't work well in Java [[see this bug | http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6373386]].)  Furthermore the JavaBean spec would have to be changed to allow non void setters, but I think if they went ahead with the above enhancement, they would (hopefully) allow it in the JavaBean spec too.  

[[ Vote for this Java enhancement! | http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6479372]]
!! Good ol' Windowz

Recently, the question came up of whether or not to use the forward slash "/" when specifying a file path under Windows.  

We had properties files that looked like this:
{{{
output.path.windows=\\changemanagement\\out
output.path=/changemanagement/out
}}}
So the double-backslashed version is both harder to type ''and'' read.


!! So is it "OK" to use forward slashes?  

After a little digging, I found this in the File JavaDoc:
>When a pathname string is converted into an abstract pathname, the names within it may be separated by the default name-separator character '*or by any other name-separator character that is supported by the underlying system*'. 


Now, I always assumed it was ''Java'' converting the forward slash to the proper format, but apparently not.  
To demonstrate, I can type @@cd C:/windows/system32@@ in a CMD prompt.  So ''Windows'' is smart enough to convert the path.  I believe this holds true for any "NT-based" version of Windows (NT,2k,XP,2003).  

This is very handy if you (for example) want to hard-code paths in your unit tests:
{{{
public class LoadFilesGeneratorTest extends TestCase {
	
  static String OUTPUT_DIR = "./target/test-output/LoadFilesGeneratorTest/";
  File outputDir = new File( OUTPUT_DIR );

  // ...etc
}}}

This way, when I run the build under Windows and Linux, the unit tests would still run correctly.  If I were to use "\\", Linux would interpret it as an escape character, and the resulting path as "targettestoutputLoadFilesGeneratorTest".

Not that generally I would condone hard-coding paths, but lets keep things as simple as possible, eh?
/***
Custom tweaks to config options.  This tiddler must be alphabetically last so it can override other Plugin options after they have been loaded.
***/

//{{{
config.autoOpenTiddlers = {
  count: 5,
  tag: 'content'
}

// General

config.commands.closeTiddler.text = " X "; // default is "close"
config.commands.closeOthers.text = "[x]";
//config.views.wikified.defaultText = ""; 
//config.options.chkHttpReadOnly = false; 
config.options.txtBackupFolder = "twBackups"; //default backup folder

// CollapseTiddlersPlugin buttons 

config.commands.collapseTiddler.text="=";
config.commands.expandTiddler.text="+";
config.commands.collapseOthers.text= "[=]";
config.macros.collapseAll.text = "[-]";
config.macros.expandAll.text = "[+]";

// UploadLog kill 

config.macros.upload.UploadLog = function() {return this;};
config.macros.upload.UploadLog.prototype.startUpload = function(storeUrl, toFilename, uploadDir,  backupDir) {};
config.macros.upload.UploadLog.prototype.endUpload = function() {};

//}}}