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

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

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

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

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

	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 need larger font sizes.
body {font-size:0.8em;}
#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}
.subtitle {font-size:0.8em;}
.viewer table.listView {font-size:0.95em;}
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none ! important;}
#displayArea {margin: 1em 1em 0em 1em;}
/* Fixes a feature in Firefox 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 class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
<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 id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></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

Tiddlywiki Write is an implementation of the Tiddlywiki concept and is an experiment in mind mapping for writers.

This TiddlyWiki is a modification based on the DevFire template, available here: http://tiddlythemes.com/#Home

Feel free to use/alter this tool as is useful to you.  I am not a computer programmer and have built TWW using numerous existing plugins available for tiddlywiki.  Tiddlywiki is an open source project.

The Tiddlywiki homepage is here:  http://www.tiddlywiki.com

<<newJournal 'DD MMM YYYY'>>
<<slider chkSideBarTabs SideBarTabs 'Side Bar »' 'Open Side Bar Tabs'>>
<<slider chkSliderOptionsPanel OptionsPanel 'options »' 'Change TiddlyWiki advanced options'>>
<<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>>
<<option chkInsertTabs>> Use tab key to insert tab characters instead of jumping to next field
Apple Cake
T. Susan Chang

This is adapted from Dotty Pascoe, who lives in Leverett, Mass., and works at the town post office. She, in turn, received it from her sister Elaine, who lives in Deerfield, Mass., and who may or may not have found it in a local women's cookbook. For 45 years, Dottie Pascoe was a secretary at the University of Massachusetts; she enjoys bowling and NASCAR.

Makes 8 servings

3 cups flour all-purpose flour
1 cup white sugar
1/2 cup brown sugar, firmly packed
1 teaspoon cinnamon
1 teaspoon salt
1 teaspoon baking soda
1 1/4 cups vegetable oil
3 eggs
1 teaspoon vanilla

4 cups apples, peeled and sliced or chopped (Russets, Golden Delicious, Granny Smiths, Baldwins and Cortlands all work well)
1 cup pecans (optional)

Preheat oven to 350 degrees.

In a large bowl, whisk together the dry ingredients (flour, sugars, cinnamon, salt and baking soda) until thoroughly combined.

Whisk together the wet ingredients (eggs, oil and vanilla) and stir into the dry ingredients. Stir to just combine. Fold in apples and pecans. The batter will be quite stiff. Add 1/4 cup water if desired.

Spread the batter into an ungreased tube pan or ungreased 9-inch-by-11-inch-by-2-inch baking dish. Bake 60 to 65 minutes, until a skewer inserted in the center comes out clean.
3 cups matzo farfel
3 eggs *well* beaten
1/2 cup sugar
1/2 tsp salt
1/4 cup melter butter or margarine
1 tsp cinnamon
1/2 cup chopped walnuts
4 large apples, peeled and chopped
1/2 cup raisins

Preheat oven to 350 degrees

Moisten farfel in a colander under water until just soft.  Drain excess water.  Set aside.

Beat eggs.

Add sugar, cinnamon, salt.  Mix.

Add melted butter/margarine and continue beating until well blended.

Stir in farfel, walnuts, apples, and raisins

Place mixture in well greased 2 quart casserole

Bake @ 350 45 minutes or until lightly browned.  

(From the back of the Manischewitz Matzoh Farfel box)+++!!!!![comment: Tuesday, April 03, 2007 9:48:16 AM]>
Excellent--like a cross between apple cake and apple crumble.  Needs more cinnamon.  Maybe sprinkle top with cinnamon/sugar/butter mixture?===
(aka Apple Yucky)                                                                   

1/2 # unsalted butter            
2 c. sugar                             
4 beaten eggs                       
4 t. vanilla
4 c. flour
4 t. baking powder

Mix crust ingredients to form a ball.  Press 
dough evenly into bottom and sides of buttered 
9x13” pyrex pan, reserving half for top.  

8-10 cortland apples, sliced  
1 pt sour cream                    
2+t. cinnamon
3/4 c. sugar

Mix cinnamon and sugar.  Place apples and half cinnamon-sugar mixture on crust.  Spread sour cream on apples and sprinkle with remaining  cinnamon-sugar.  Top with 
remaining crust (blobs or “spread”).

Bake at 375 ~ 1 hour (test apples).  Store in refrigerator.
|''Version:''|1.0.1 (3-Mar-2006)|
Hijacks core backupPath function, replacing the datetime naming system
with a rotating set of backups, whose number is limited by the user.

* Possible to insert an option into the AdvancedOptions Tiddler? That'd
make editing the number of backups that much easier.

!Revision History
* 1.0.1 (3-Mar-2006)
** Wrote code, totally stole [[Simon|SimonBaird]]'s documentation

config.options.txtBackupFolder = "twBackups";

if (!config.options.txtMaxBackups) config.options.txtMaxBackups="5";
if (!config.options.txtCurrentBackup)

getBackupPath = function(localPath)
 var backSlash = true;
 var dirPathPos = localPath.lastIndexOf("\\");
 if(dirPathPos == -1)
 dirPathPos = localPath.lastIndexOf("/");
 backSlash = false;
 var backupFolder = config.options.txtBackupFolder;
 if(!backupFolder || backupFolder == "")
 backupFolder = ".";
 var backupPath = localPath.substr(0,dirPathPos) + (backSlash ? "\\" :
"/") + backupFolder + localPath.substr(dirPathPos);
 backupNum = config.options["txtCurrentBackup"];
 if (backupNum>config.options["txtMaxBackups"]) {
 backupPath = backupPath.substr(0,backupPath.lastIndexOf(".")) + "." +
backupNum + ".html";
 return backupPath;

Makes one 9-inch loaf

Greasing and flouring only the bottom of a regular loaf pan causes the bread to cling to the sides and rise higher. If using a nonstick loaf pan, on which the sides are very slick, grease and flour sides as well as the bottom. 
2 cups all-purpose flour
3/4 cup sugar
3/4 teaspoon baking soda
1/2 teaspoon salt
1 1/4 cups toasted walnuts, chopped coarse (about 1 cup)
3 very ripe, soft, darkly speckled large bananas,  mashed well (about 1 1/2 cups)
1/4 cup plain yogurt
2 large eggs, beaten lightly
6 tablespoons butter, melted and cooled
1 teaspoon vanilla extract

1. Adjust oven rack to lower middle position and heat oven to 350 degrees. 

2. Grease bottom only of regular loaf pan, or grease and flour bottom and sides of nonstick 9x5x3-inch loaf pan; set aside. 

3. Combine first five ingredients together in large bowl; set aside.
4. Mix mashed bananas, yogurt, eggs, butter, and vanilla with wooden spoon in medium bowl. 

5. Lightly fold banana mixture into dry ingredients with rubber spatula until just combined and batter looks thick and chunky. 

6. Scrape batter into prepared loaf pan; bake until loaf is golden brown and toothpick inserted in center comes out clean, about 55 minutes. Cool in pan for 5 minutes, then transfer to wire rack. Serve warm or at room temperature.

Follow recipe for The Best Banana Bread, reducing sugar to 10 tablespoons and mixing 2 1/2 ounces grated bittersweet chocolate (a heaping 1/2 cup) into dry ingredients.

Adjust oven rack to middle position and heat oven to 350 degrees. Toast 1/2 cup flaked, sweetened coconut and 1 cup chopped macadamia nuts on small cookie sheet, stirring every 2 minutes, until golden brown, about 6 minutes. Follow recipe for the Best Banana Bread, substituting toasted macadamias and coconut for walnuts. 

Follow recipe for The Best Banana Bread, adding 1 teaspoon ground cinnamon, 1/4 teaspoon grated nutmeg, and 2 tablespoons grated orange zest to dry ingredients.

Cooks Illustrated March, 1998
Original article and recipes by Elizabeth Gunas Crampton
- 1/3 cup packed fresh basil leaves 
- 1/3 cup fresh parsley 
- 1/4 cup pine nuts 
- 1 teaspoon chopped fresh oregano leaves 
- 2 teaspoons finely chopped fresh garlic 
- 1/4 teaspoon pepper 
- 1/3 cup olive <html><b>or</b></html> vegetable oil 
- 1/2 cup freshly grated Parmesan cheese 

<html><b>Cooking Instructions:</b></html>
Place basil leaves, parsley, pine nuts, oregano leaves, garlic and pepper in food processor bowl with metal blade or 5-cup blender container.  

Cover; process until herbs are finely chopped (30 to 45 seconds).

While food processor is running, slowly add oil through feed tube until well mixed (20 to 30 seconds).  

Stir in Parmesan cheese by hand.  Store refrigerated.

To serve, stir into hot cooked penne, fettuccine or fusilli.

  Pesto can be frozen up to 6 months.
Yield: 3/4 cup
Preparation: 15 minutes
3 lbs boneless beef chuck roast
1 TBS dijon mustard
1 clove garlic, finely chopped
1 tsp dried oregano
pinch of crushed red pepper
salt and black pepper to taste
1 large spanish onion, halved and thinly sliced
1 green bell pepper, cored, seeded, and cut into 1 inch pieces
1 cup write wine
1 cup chicken stock
2 cups whole peeled canned tomatoes, crushed
1 TBS worcestershire sauce
1 TBS brown sugar

Set oven at 350 degrees

Place beef in heavy-based casserole (4 quart)

In a bowl, stir together the mustard, garlic, oregano, red pepper, salt, and black pepper.  Rub the mixture over the beef.

Tuck the onion and bell pepper around the meat, pour the wine in at the sides.

In a bowl, combine the chicken stock, tomatoes, worcestershire, and brown sugar.  Pour it over the meat.

Cover and roast the meat and vegetables for 2 hours, turning halfway through cooking, or until a fork inserted in the beef comes out easily.  remove the lid and cook for another 30 minutes.

Remove the meat from the pan and transfer it to a cutting board; cover and keep warm.  With a slotted spoon, transfer the onions and peppers to a deep serving platter.  Cover the vegetables and keep warm.

With a large spoon, skim off and discard the fat on the surface of the cooking liquid.  Set the pan on a burner and bring the liquid to a boil.

Slice the meat and transfer it to the serving platter.

Spoon the cooking juices on top, serve immediately.

From the Boston Globe Magazine, march 12, 2006, p. 51
      20 servings
      2 hours 20 minutes (20 min prep, 2 hrs cooking)

      Easy quick appetizer. Make several and freeze them for use at a later time.

             8  ounces softened cream cheese 
             1  cup sour cream (low fat is fine) 
             1  cup shredded monterey jack cheese 
              1/4  cup chopped pimento stuffed olives 
              1/4  cup chopped red onions 
              1/2  teaspoon salt 
              1/2  teaspoon garlic powder 
             1  (15 ounce) can black beans, drained 
             5  flour tortillas 

            1. Beat cream cheese and sour cream until well blended. 
            2. Stir in Monterey Jack cheese, olives, onion, salt& garlic powder. 
            3. Cover and refrigerate for 2 hours. 
            4. Process beans in a food processor until smooth. 
            5. Spread each tortilla with a thin layer of beans. 
            6. Spread layer of cream cheese mixture over beans. 
            7. Roll up tightly, wrap in plastic wrap and refrigerate until well chilled. 
            8. Cut into 3/4 inch slices (on a bias?). 
            9. Serve with salsa. 

Makes 12 muffins

Do not overmix the batter. 

2 cups (10 ounces) unbleached all-purpose flour
1 tablespoon baking powder
1/2 teaspoon salt
1 large egg
1 cup (7 ounces) sugar
4 tablespoons unsalted butter, melted and cooled slightly
1 1/4 cups (10 ounces) sour cream or plain yogurt
1 1/2 cups frozen blueberries, preferably wild 

1. Adjust oven rack to middle position and heat oven to 350 degrees. Spray standard muffin tin with nonstick vegetable cooking spray.

2. Whisk flour, baking powder, and salt in medium bowl until combined. 

3. Whisk egg in second medium bowl until well-combined and light-colored, about 20 seconds. Add sugar and whisk vigorously until thick and homogenous, about 30 seconds; add melted butter in 2 or 3 steps, whisking to combine after each addition. Add sour cream in 2 steps, whisking just to combine.

4. Add frozen berries to dry ingredients and gently toss to combine. Add sour cream mixture and fold with rubber spatula until batter comes together and 
berries are evenly distributed, 25 to 30 seconds (small spots of flour may remain and batter will be thick). Do not overmix.

5. Use ice cream scoop or large spoon to drop batter into greased muffin tin. 

6. Bake until light golden brown and toothpick or skewer inserted into center of muffin comes out clean, 25 to 30 minutes, rotating pan from front to back 
halfway through baking time. Invert muffins onto wire rack, stand muffins upright, and cool 5 minutes. 

Cooks Illustrated
1 cup brown sugar 
1 cup white sugar 
3 cups flour 
1 tsp. baking soda 
1 tsp. baking powder 
1 tsp. salt 
1 tsp. cinnamon 
3 oz. grated Hershey bar 
1 cup mini chocolate chips 
1/2 cup nuts (optional) 

Mix separately and then add to dry ingredients: 
3 eggs beaten 
3 tsp. vanilla 
1 cup oil 
2 1/2 cups grated zucchini 

Preheat oven to 425 degrees 

Fill muffin cups about 3/4 full 

Bake at 425 degrees for 18-20 minutes (Don't over bake) 

Makes approximately 26-28 muffins. 

More recipes at: http://www.travelguides.com/bandb/recipes/
Caramel Matzoh Crunch

    4- to 6- unsalted matzohs
    1 cup (2 sticks) unsalted butter, cut into chunks
    1 cup firmly-packed light brown sugar
    1 cup semisweet or bittersweet chocolate chips, or coarsely chopped chocolate

1. Line a 11" x 17" baking sheet (with sides) completely with foil. You can use a similar sized pan. Cover the foil with a sheet of parchment paper. 

Preheat the oven to 375 degrees.

2. Line the bottom of the sheet with matzoh, breaking extra pieces as necessary to fill in any spaces.

3. In a 3- to 4-quart heavy duty saucepan, combine the butter and brown sugar and cook over medium heat, stirring, until the butter is melted and the mixture is beginning to boil. Boil for 3 minutes, stirring constantly. Remove from heat and pour over matzoh, spreading with a heatproof utensil.

4. Put the pan in the oven and reduce the heat to 350 degrees. Bake for 15 minutes. As it bakes, it will bubble up. Make sure it's not burning every few minutes. If so, remove from oven and reduce the heat to 325 degrees, then replace the pan.

5. Remove from oven and immediately cover with chocolate chunks. Let stand 5 minutes, then spread with an offset spatula.

6. Let cool about 15 minutes, then break or cut into pieces. Cool completely, then store in an airtight container until ready to serve.+++!!!!![comment: Saturday, March 31, 2007 4:53:15 PM]>
If you use margarine, it takes *much* longer for the brown sugar to enter solution===
+++!!!!![comment: Saturday, March 31, 2007 4:53:41 PM]>
Silicone brush for the sugar mixture.  Spread chocolate with cake knife.===
Adapted Joan Nathan's Jewish Holiday Kitchen

6 apples, chopped
4 TBS sweet wine
2/3 cup chopped walnuts or almonds
3 TBS sugar or Honey
1/2 tsp cinnamon
lemon juice
grated lemon rind (optional)

Mix all lightly in a food processor


2 large tart apples peeled and cored
1/2 c. raisins
1/2 cup pitted dates
1/4 c. each almonds and walnuts
1/2 tsp. cinnamon and ginger
red wine

Finely chop raisins and dates.  

Coarsly chop apples.  

Combine all ingredients and add wine to make spreadable.  

Refrigerate at least 4 hours.

This is from our rabbi’s wife.  Apparantly, this is how they do it in Israel! We love it!

From Linda

4 skinless, boneless chicken breast halves
1 3/4 cups Swanson® Chicken Broth
1/4 tsp. garlic powder OR 2 cloves garlic, minced
2 cups broccoli flowerets
1/4 cup all-purpose flour
1/2 cup milk
1 tbsp. Dijon-style mustard
4 cups hot cooked medium egg noodles, cooked without salt


COOK chicken in nonstick skillet until browned.

ADD broth, garlic powder and broccoli. Heat to a boil. Cover and cook over low heat 10 min. or until done. Remove chicken.

MIX flour, milk and mustard. Gradually add to broth mixture. Cook and stir until mixture boils and thickens. Serve with chicken and noodles.

From: Campbell's Kitchen
Prep/Cook Time: 25 minutes
Serves: 4

Easy Chicken Broccoli Divine

2 cups broccoli bits
2 cups baby carrots
About 1 lb. boneless/skinless chicken -cut into bite-sized pieces
1 cup apricot preserves
1 cup salsa

In a large skillet cook meat in small amount of olive oil until partially done. Then add veggies, stir well. Cover and cook over low heat until meat is done and veggies are tender crisp. Add preserves and salsa, stir and heat through. Serve over rice.

+++!!!!![comment: Tuesday, March 13, 2007]>
Used frozen broccoli===

Chicken with Rice

Source: The 60-Minute Gourmet by Pierre Franey

2 whole chicken breasts, split in half about 2 lbs. total weight (4 pieces)
Salt and freshly ground pepper to taste
2 tablespoons    butter or oil
1/2 cup    finely chopped onion
1 teaspoon      chopped garlic
1/3 pound  fresh mushrooms, left whole if very small, otherwise sliced or quartered, depending on size
1          bay leaf
1/2 cup    dry white wine (or apple juice)
1/2 cup    raw white rice
1 cup      chicken broth (or water -- if you use water, you will need some extra salt)

1.  Do not skin or bone the chicken breasts.  Sprinkle the chicken pieces with salt and pepper.

2.  Heat the butter (or oil) in a heavy pot and add the chicken pieces, skin side down.  Brown about 5 minutes and turn.

3.  Scatter the onion and garlic between the chicken pieces and add the mushrooms and bay leaf.  Cook about 5 minutes.

4.  Add the wine and cook until it is almost all evaporated.   Add the rice and broth, taking care that the rice does not rest on top of the chicken pieces.  Cover closely and cook
about 20 minutes or until rice is tender.

Yield: Four servings

(also nice with red peppers instead of mushrooms, tarragon or rosemary are also nice spices with this dish.)
|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/]]|
%//%comment%/+++[add a comment...]>
	<html><textarea id="comment" rows="3" style="width:100%"></textarea>
	<input type="button" value="submit comment" onclick="addTiddlerComment(this.previousSibling.previousSibling);"></html><script>
		place.lastChild.firstChild.value="Enter your comment text here";
window.addTiddlerComment = function(place) {
	if (!window.story) window.story=window; if (!store.getTiddler) store.getTiddler=function(title) {return this.tiddlers[title]}
	var title = story.findContainingTiddler(place).id.substr(7);
	var tiddler=store.getTiddler(title);
	var pos=tiddler.text.indexOf("/%"+place.id+"%/");
	if (pos==-1) pos=tiddler.text.length;
	var heading="comment: "+(new Date()).toLocaleString();
	var newtxt=tiddler.text.substr(0,pos);
per serving:

1 egg
1/3 cup cottage cheese
1/3 cup milk
1/3 cup matzoh meal

Mix egg, cottage cheese, and milk with a whisk

Add matzoh meal, salt, sugar and cinnamon

Saute in butter/oil

Serve with sour cream and/or apple sauce
|''Description:''|Support for cryptographic functions|
if(!version.extensions.CryptoFunctionsPlugin) {
version.extensions.CryptoFunctionsPlugin = {installed:true};

//-- Crypto functions and associated conversion routines

// Crypto "namespace"
function Crypto() {}

// Convert a string to an array of big-endian 32-bit words
Crypto.strToBe32s = function(str)
	var be = Array();
	var len = Math.floor(str.length/4);
	var i, j;
	for(i=0, j=0; i<len; i++, j+=4) {
		be[i] = ((str.charCodeAt(j)&0xff) << 24)|((str.charCodeAt(j+1)&0xff) << 16)|((str.charCodeAt(j+2)&0xff) << 8)|(str.charCodeAt(j+3)&0xff);
	while (j<str.length) {
		be[j>>2] |= (str.charCodeAt(j)&0xff)<<(24-(j*8)%32);
	return be;

// Convert an array of big-endian 32-bit words to a string
Crypto.be32sToStr = function(be)
	var str = "";
	for(var i=0;i<be.length*32;i+=8)
		str += String.fromCharCode((be[i>>5]>>>(24-i%32)) & 0xff);
	return str;

// Convert an array of big-endian 32-bit words to a hex string
Crypto.be32sToHex = function(be)
	var hex = "0123456789ABCDEF";
	var str = "";
	for(var i=0;i<be.length*4;i++)
		str += hex.charAt((be[i>>2]>>((3-i%4)*8+4))&0xF) + hex.charAt((be[i>>2]>>((3-i%4)*8))&0xF);
	return str;

// Return, in hex, the SHA-1 hash of a string
Crypto.hexSha1Str = function(str)
	return Crypto.be32sToHex(Crypto.sha1Str(str));

// Return the SHA-1 hash of a string
Crypto.sha1Str = function(str)
	return Crypto.sha1(Crypto.strToBe32s(str),str.length);

// Calculate the SHA-1 hash of an array of blen bytes of big-endian 32-bit words
Crypto.sha1 = function(x,blen)
	// Add 32-bit integers, wrapping at 32 bits
	add32 = function(a,b)
		var lsw = (a&0xFFFF)+(b&0xFFFF);
		var msw = (a>>16)+(b>>16)+(lsw>>16);
		return (msw<<16)|(lsw&0xFFFF);
	// Add five 32-bit integers, wrapping at 32 bits
	add32x5 = function(a,b,c,d,e)
		var lsw = (a&0xFFFF)+(b&0xFFFF)+(c&0xFFFF)+(d&0xFFFF)+(e&0xFFFF);
		var msw = (a>>16)+(b>>16)+(c>>16)+(d>>16)+(e>>16)+(lsw>>16);
		return (msw<<16)|(lsw&0xFFFF);
	// Bitwise rotate left a 32-bit integer by 1 bit
	rol32 = function(n)
		return (n>>>31)|(n<<1);

	var len = blen*8;
	// Append padding so length in bits is 448 mod 512
	x[len>>5] |= 0x80 << (24-len%32);
	// Append length
	x[((len+64>>9)<<4)+15] = len;
	var w = Array(80);

	var k1 = 0x5A827999;
	var k2 = 0x6ED9EBA1;
	var k3 = 0x8F1BBCDC;
	var k4 = 0xCA62C1D6;

	var h0 = 0x67452301;
	var h1 = 0xEFCDAB89;
	var h2 = 0x98BADCFE;
	var h3 = 0x10325476;
	var h4 = 0xC3D2E1F0;

	for(var i=0;i<x.length;i+=16) {
		var j,t;
		var a = h0;
		var b = h1;
		var c = h2;
		var d = h3;
		var e = h4;
		for(j = 0;j<16;j++) {
			w[j] = x[i+j];
			t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
			e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
		for(j=16;j<20;j++) {
			w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
			t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
			e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
		for(j=20;j<40;j++) {
			w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
			t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k2);
			e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
		for(j=40;j<60;j++) {
			w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
			t = add32x5(e,(a>>>27)|(a<<5),(b&c)|(d&(b|c)),w[j],k3);
			e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
		for(j=60;j<80;j++) {
			w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
			t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k4);
			e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;

		h0 = add32(h0,a);
		h1 = add32(h1,b);
		h2 = add32(h2,c);
		h3 = add32(h3,d);
		h4 = add32(h4,e);
	return Array(h0,h1,h2,h3,h4);

|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/]]|

There are quite a few calendar generators, reminders, to-do lists, 'dated tiddlers' journals, blog-makers and GTD-like schedule managers that have been built around TW.  While they all have different purposes, and vary in format, interaction, and style, in one way or another each of these plugins displays and/or uses date-based information to make finding, accessing and managing relevant tiddlers easier.  This plugin provides a general approach to embedding dates and date-based links/menus within tiddler content.

This plugin display formatted dates, for the specified year, month, day using number values or mathematical expressions such as (Y+1) or (D+30).  Optionally, you can create a link from the formatted output to a 'dated tiddler' for quick blogging or create a popup menu that includes the dated tiddler link plus links to changes made on that date as well as links to any pending reminders for the coming 31 days (if the RemindersPlugin is installed).  This plugin also provides a public API for easily incorporating formatted date output (with or without the links/popups) into other plugins, such as calendar generators, etc.
When installed, this plugin defines a macro: {{{<<date [mode] [date] [format] [linkformat]>>}}}.  All of the macro parameters are optional and, in it's simplest form, {{{<<date>>}}}, it is equivalent to the ~TiddlyWiki core macro, {{{<<today>>}}}.

However, where {{{<<today>>}}} simply inserts the current date/time in a predefined format (or custom format, using {{{<<today [format]>>}}}), the {{{<<date>>}}} macro's parameters take it much further than that:
* [mode] is either ''display'', ''link'' or ''popup''.  If omitted, it defaults to ''display''.  This param let's you select between simply displaying a formatted date, or creating a link to a specific 'date titled' tiddler or a popup menu containing a dated tiddler link, plus links to changes and reminders.
* [date] lets you enter ANY date (not just today) as ''year, month, and day values or simple mathematical expressions'' using pre-defined variables, Y, M, and D for the current year, month and day, repectively.  You can display the modification date of the current tiddler by using the keyword: ''tiddler'' in place of the year, month and day parameters.  Use ''tiddler://name-of-tiddler//'' to display the modification date of a specific tiddler.  You can also use keywords ''today'' or ''filedate'' to refer to these //dynamically changing// date/time values.  
* [format] and [linkformat] uses standard ~TiddlyWiki date formatting syntax.  The default is "YYYY.0MM.0DD"
>^^''DDD'' - day of week in full (eg, "Monday"), ''DD'' - day of month, ''0DD'' - adds leading zero^^
>^^''MMM'' - month in full (eg, "July"), ''MM'' - month number, ''0MM'' - adds leading zero^^
>^^''YYYY'' - full year, ''YY'' - two digit year, ''hh'' - hours, ''mm'' - minutes, ''ss'' - seconds^^
>^^//note: use of hh, mm or ss format codes is only supported with ''tiddler'', ''today'' or ''filedate'' values//^^
* [linkformat] - specify an alternative date format so that the title of a 'dated tiddler' link can have a format that differs from the date's displayed format

In addition to the macro syntax, DatePlugin also provides a public javascript API so that other plugins that work with dates (such as calendar generators, etc.) can quickly incorporate date formatted links or popups into their output:

''{{{showDate(place, date, mode, format, linkformat, autostyle, weekend)}}}'' 

Note that in addition to the parameters provided by the macro interface, the javascript API also supports two optional true/false parameters:
* [autostyle] - when true, the font/background styles of formatted dates are automatically adjusted to show the date's status:  'today' is boxed, 'changes' are bold, 'reminders' are underlined, while weekends and holidays (as well as changes and reminders) can each have a different background color to make them more visibly distinct from each other.
* [weekend] - true indicates a weekend, false indicates a weekday.  When this parameter is omitted, the plugin uses internal defaults to automatically determine when a given date falls on a weekend.
The current date: <<date>>
The current time: <<date today "0hh:0mm:0ss">>
Today's blog: <<date link today "DDD, MMM DDth, YYYY">>
Recent blogs/changes/reminders: <<date popup Y M D-1 "yesterday">> <<date popup today "today">> <<date popup Y M D+1 "tomorrow">>
The first day of next month will be a <<date Y M+1 1 "DDD">>
This tiddler (DatePlugin) was last updated on: <<date tiddler "DDD, MMM DDth, YYYY">>
The SiteUrl was last updated on: <<date tiddler:SiteUrl "DDD, MMM DDth, YYYY">>
This document was last saved on <<date filedate "DDD, MMM DDth, YYYY at 0hh:0mm:0ss">>
<<date 2006 07 24 "MMM DDth, YYYY">> will be a <<date 2006 07 24 "DDD">>
import (or copy/paste) the following tiddlers into your document:
''DatePlugin'' (tagged with <<tag systemConfig>>)
!!!!!Revision History
''2006.05.09 [2.2.1]'' added "todaybg" handling to set background color of current date.  Also, honor excludeLists tag when getting lists of tiddlers.  Based on suggestions by Mark Hulme.
''2006.05.05 [2.2.0]'' added "linkedbg" handling to set background color when a 'dated tiddler' exists.  Based on a suggestion by Mark Hulme.
''2006.03.08 [2.1.2]'' add 'override leadtime' flag param in call to findTiddlersWithReminders(), and add "Enter a title" default text to new reminder handler.  Thanks to Jeremy Sheeley for these additional tweaks.
''2006.03.06 [2.1.0]'' hasReminders() nows uses window.reminderCacheForCalendar[] when present.  If calendar cache is not present, indexReminders() now uses findTiddlersWithReminders() with a 90-day look ahead to check for reminders.  Also, switched default background colors for autostyled dates: reminders are now greenish ("c0ffee") and holidays are now reddish ("ffaace").
''2006.02.14 [2.0.5]'' when readOnly is set (by TW core), omit "new reminders..." popup menu item and, if a "dated tiddler" does not already exist, display the date as simple text instead of a link.
''2006.02.05 [2.0.4]'' added var to variables that were unintentionally global.  Avoids FireFox crash bug when referencing global variables
''2006.01.18 [2.0.3]'' In 1.2.x the tiddler editor's text area control was given an element ID=("tiddlerBody"+title), so that it was easy to locate this field and programmatically modify its content.  With the addition of configuration templates in 2.x, the textarea no longer has an ID assigned.  To find this control we now look through all the child nodes of the tiddler editor to locate a "textarea" control where attribute("edit") equals "text", and then append the new reminder to the contents of that control.
''2006.01.11 [2.0.2]'' correct 'weekend' override detection logic in showDate()
''2006.01.10 [2.0.1]'' allow custom-defined weekend days (default defined in config.macros.date.weekend[] array)
added flag param to showDate() API to override internal weekend[] array
''2005.12.27 [2.0.0]'' Update for TW2.0
Added parameter handling for 'linkformat'
''2005.12.21 [1.2.2]'' FF's date.getYear() function returns 105 (for the current year, 2005).  When calculating a date value from Y M and D expressions, the plugin adds 1900 to the returned year value get the current year number.  But IE's date.getYear() already returns 2005.  As a result, plugin calculated date values on IE were incorrect (e.g., 3905 instead of 2005).  Adding +1900 is now conditional so the values will be correct on both browsers.
''2005.11.07 [1.2.1]'' added support for "tiddler" dynamic date parameter
''2005.11.06 [1.2.0]'' added support for "tiddler:title" dynamic date parameter
''2005.11.03 [1.1.2]'' when a reminder doesn't have a specified title parameter, use the title of the tiddler that contains the reminder as "fallback" text in the popup menu.  Based on a suggestion from BenjaminKudria.
''2005.11.03 [1.1.1]'' Temporarily bypass hasReminders() logic to avoid excessive overhead from generating the indexReminders() cache.  While reminders can still appear in the popup menu, they just won't be indicated by auto-styling the date number that is displayed.  This single change saves approx. 60% overhead (5 second delay reduced to under 2 seconds).
''2005.11.01 [1.1.0]'' corrected logic in hasModifieds() and hasReminders() so caching of indexed modifieds and reminders is done just once, as intended.  This should hopefully speed up calendar generators and other plugins that render multiple dates...
''2005.10.31 [1.0.1]'' documentation and code cleanup
''2005.10.31 [1.0.0]'' initial public release
''2005.10.30 [0.9.0]'' pre-release
This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]].
version.extensions.date = {major: 2, minor: 2, revision: 1, date: new Date(2006,5,9)};

config.macros.date = {
	format: "YYYY.0MM.0DD", // default date display format
	linkformat: "YYYY.0MM.0DD", // 'dated tiddler' link format
	linkedbg: "#babb1e", // "babble"
	todaybg: "#ffab1e", // "fable"
	weekendbg: "#c0c0c0", // "cocoa"
	holidaybg: "#ffaace", // "face"
	modifiedsbg: "#bbeeff", // "beef"
	remindersbg: "#c0ffee", // "coffee"
	holidays: [ "01/01", "07/04", "07/24", "11/24" ], // NewYearsDay, IndependenceDay(US), Eric's Birthday (hooray!), Thanksgiving(US)
	weekend: [ 1,0,0,0,0,0,1 ] // [ day index values: sun=0, mon=1, tue=2, wed=3, thu=4, fri=5, sat=6 ]

config.macros.date.handler = function(place,macroName,params)
	// do we want to see a link, a popup, or just a formatted date?
	var mode="display";
	if (params[0]=="display") { mode=params[0]; params.shift(); }
	if (params[0]=="popup") { mode=params[0]; params.shift(); }
	if (params[0]=="link") { mode=params[0]; params.shift(); }
	// get the date
	var now = new Date();
	var date = now;
	if (!params[0] || params[0]=="today")
		{ params.shift(); }
	else if (params[0]=="filedate")
		{ date=new Date(document.lastModified); params.shift(); }
	else if (params[0]=="tiddler")
		{ date=store.getTiddler(story.findContainingTiddler(place).id.substr(7)).modified; params.shift(); }
	else if (params[0].substr(0,8)=="tiddler:")
		{ var t; if ((t=store.getTiddler(params[0].substr(8)))) date=t.modified; params.shift(); }
	else {
		var y = eval(params.shift().replace(/Y/ig,(now.getYear()<1900)?now.getYear()+1900:now.getYear()));
		var m = eval(params.shift().replace(/M/ig,now.getMonth()+1));
		var d = eval(params.shift().replace(/D/ig,now.getDate()+0));
		date = new Date(y,m-1,d);
	// date format with optional custom override
	var format=this.format; if (params[0]) format=params.shift();
	var linkformat=this.linkformat; if (params[0]) linkformat=params.shift();

function showDate(place,date,mode,format,linkformat,autostyle,weekend)
	if (!mode) mode="display";
	if (!format) format=config.macros.date.format;
	if (!linkformat) linkformat=config.macros.date.linkformat;
	if (!autostyle) autostyle=false;

	// format the date output
	var title = date.formatString(format);
	var linkto = date.formatString(linkformat);

	// just show the formatted output
	if (mode=="display") { place.appendChild(document.createTextNode(title)); return; }

	// link to a 'dated tiddler'
	var link = createTiddlyLink(place, linkto, false);
	link.title = linkto;
	link.date = date;
	link.format = format;
	link.linkformat = linkformat;

	// if using a popup menu, replace click handler for dated tiddler link
	// with handler for popup and make link text non-italic (i.e., an 'existing link' look)
	if (mode=="popup") {
		link.onclick = onClickDatePopup;

	// format the popup link to show what kind of info it contains (for use with calendar generators)
	if (!autostyle) return;
	if (hasModifieds(date))
		{ link.style.fontStyle="normal"; link.style.fontWeight="bold"; }
	if (hasReminders(date))
		{ link.style.textDecoration="underline"; }
		{ link.style.border="1px solid black"; }

	if( (weekend!=undefined?weekend:isWeekend(date)) && (config.macros.date.weekendbg!="") )
		{ place.style.background = config.macros.date.weekendbg; }
		{ place.style.background = config.macros.date.holidaybg; }
	if (hasModifieds(date)&&(config.macros.date.modifiedsbg!=""))
		{ place.style.background = config.macros.date.modifiedsbg; }
	if (store.tiddlerExists(linkto)&&(config.macros.date.linkedbg!=""))
		{ place.style.background = config.macros.date.linkedbg; }
	if (hasReminders(date)&&(config.macros.date.remindersbg!=""))
		{ place.style.background = config.macros.date.remindersbg; }
		{ place.style.background = config.macros.date.todaybg; }

function isToday(date) // returns true if date is today
	{ var now=new Date(); return ((now-date>=0) && (now-date<86400000)); }

function isWeekend(date) // returns true if date is a weekend
	{ return (config.macros.date.weekend[date.getDay()]); }

function isHoliday(date) // returns true if date is a holiday
	var longHoliday = date.formatString("0MM/0DD/YYYY");
	var shortHoliday = date.formatString("0MM/0DD");
	for(var i = 0; i < config.macros.date.holidays.length; i++) {
		var holiday=config.macros.date.holidays[i];
		if (holiday==longHoliday||holiday==shortHoliday) return true;
	return false;

// Event handler for clicking on a day popup
function onClickDatePopup(e)
	if (!e) var e = window.event;
	var theTarget = resolveTarget(e);
	var popup = createTiddlerPopup(this);
	if(popup) {
		// always show dated tiddler link (or just date, if readOnly) at the top...
		if (!readOnly || store.tiddlerExists(this.date.formatString(this.linkformat)))
	e.cancelBubble = true;
	if (e.stopPropagation) e.stopPropagation();

function indexModifieds() // build list of tiddlers, hash indexed by modification date
	var modifieds= { };
	var tiddlers = store.getTiddlers("title","excludeLists");
	for (var t = 0; t < tiddlers.length; t++) {
		var date = tiddlers[t].modified.formatString("YYYY0MM0DD")
		if (!modifieds[date])
			modifieds[date]=new Array();
	return modifieds;
function hasModifieds(date) // returns true if date has modified tiddlers
	if (!config.macros.date.modifieds) config.macros.date.modifieds = indexModifieds();
	return (config.macros.date.modifieds[date.formatString("YYYY0MM0DD")]!=undefined);

function addModifiedsToPopup(popup,when,format)
	if (!config.macros.date.modifieds) config.macros.date.modifieds = indexModifieds();
	var indent=String.fromCharCode(160)+String.fromCharCode(160);
	var mods = config.macros.date.modifieds[when.formatString("YYYY0MM0DD")];
	if (mods) {
		var e=createTiddlyElement(popup,"div",null,null,"changes:");
		for(var t=0; t<mods.length; t++) {
			var link=createTiddlyLink(popup,mods[t],false);

function indexReminders(date,leadtime) // build list of tiddlers with reminders, hash indexed by reminder date
	var reminders = { };
	if(window.findTiddlersWithReminders!=undefined) { // reminder plugin is installed
		// DEBUG var starttime=new Date();
		var t = findTiddlersWithReminders(date, [0,leadtime], null, null, 1);
		for(var i=0; i<t.length; i++) reminders[t[i].matchedDate]=true;
		// DEBUG var out="Found "+t.length+" reminders in "+((new Date())-starttime+1)+"ms\n";
		// DEBUG out+="startdate: "+date.toLocaleDateString()+"\n"+"leadtime: "+leadtime+" days\n\n";
		// DEBUG for(var i=0; i<t.length; i++) { out+=t[i].matchedDate.toLocaleDateString()+" "+t[i].params.title+"\n"; }
		// DEBUG alert(out);
	return reminders;

function hasReminders(date) // returns true if date has reminders
	if (window.reminderCacheForCalendar)
		return window.reminderCacheForCalendar[date]; // use calendar cache
	if (!config.macros.date.reminders)
		config.macros.date.reminders = indexReminders(date,90); // create a 90-day leadtime reminder cache
	return (config.macros.date.reminders[date]);

function addRemindersToPopup(popup,when,format)
	if(window.findTiddlersWithReminders==undefined) return; // reminder plugin not installed

	var indent = String.fromCharCode(160)+String.fromCharCode(160);
	var reminders=findTiddlersWithReminders(when, [0,31],null,null,1);
	var e=createTiddlyElement(popup,"div",null,null,"reminders:"+(!reminders.length?" none":""));
	for(var t=0; t<reminders.length; t++) {
		link = createTiddlyLink(popup,reminders[t].tiddler,false);
		var diff=reminders[t].diff;
		diff=(diff<1)?"Today":((diff==1)?"Tomorrow":diff+" days");
		var txt=(reminders[t].params["title"])?reminders[t].params["title"]:reminders[t].tiddler;
		link.appendChild(document.createTextNode(indent+diff+" - "+txt));
	if (readOnly) return;	// omit "new reminder..." link
	var link = createTiddlyLink(popup,indent+"new reminder...",true); createTiddlyElement(popup,"br");
	var title = when.formatString(format);
	link.title="add a reminder to '"+title+"'";
	link.onclick = function() {
		// show tiddler editor
		story.displayTiddler(null, title, 2, null, null, false, false);
		// find body 'textarea'
		var c =document.getElementById("tiddler" + title).getElementsByTagName("*");
		for (var i=0; i<c.length; i++) if ((c[i].tagName.toLowerCase()=="textarea") && (c[i].getAttribute("edit")=="text")) break;
		// append reminder macro to tiddler content
		if (i<c.length) {
			if (store.tiddlerExists(title)) c[i].value+="\n"; else c[i].value="";
			c[i].value += "<<reminder";
			c[i].value += " day:"+when.getDate();
			c[i].value += " month:"+(when.getMonth()+1);
			c[i].value += " year:"+when.getFullYear();
			c[i].value += ' title:"Enter a title" >>';

|''Description:''|Support for deprecated functions removed from core|
if(!version.extensions.DeprecatedFunctionsPlugin) {
version.extensions.DeprecatedFunctionsPlugin = {installed:true};

//-- Deprecated code

// @Deprecated: Use createElementAndWikify and this.termRegExp instead
config.formatterHelpers.charFormatHelper = function(w)

// @Deprecated: Use enclosedTextHelper and this.lookaheadRegExp instead
config.formatterHelpers.monospacedByLineHelper = function(w)
	var lookaheadRegExp = new RegExp(this.lookahead,"mg");
	lookaheadRegExp.lastIndex = w.matchStart;
	var lookaheadMatch = lookaheadRegExp.exec(w.source);
	if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
		var text = lookaheadMatch[1];
			text = text.replace(/\n/g,"\r");
		w.nextMatch = lookaheadRegExp.lastIndex;

// @Deprecated: Use <br> or <br /> instead of <<br>>
config.macros.br = {};
config.macros.br.handler = function(place)

// Find an entry in an array. Returns the array index or null
// @Deprecated: Use indexOf instead
Array.prototype.find = function(item)
	var i = this.indexOf(item);
	return i == -1 ? null : i;

// Load a tiddler from an HTML DIV. The caller should make sure to later call Tiddler.changed()
// @Deprecated: Use store.getLoader().internalizeTiddler instead
Tiddler.prototype.loadFromDiv = function(divRef,title)
	return store.getLoader().internalizeTiddler(store,this,title,divRef);

// Format the text for storage in an HTML DIV
// @Deprecated Use store.getSaver().externalizeTiddler instead.
Tiddler.prototype.saveToDiv = function()
	return store.getSaver().externalizeTiddler(store,this);

// @Deprecated: Use store.allTiddlersAsHtml() instead
function allTiddlersAsHtml()
	return store.allTiddlersAsHtml();

// @Deprecated: Use refreshPageTemplate instead
function applyPageTemplate(title)

// @Deprecated: Use story.displayTiddlers instead
function displayTiddlers(srcElement,titles,template,unused1,unused2,animate,unused3)

// @Deprecated: Use story.displayTiddler instead
function displayTiddler(srcElement,title,template,unused1,unused2,animate,unused3)

// @Deprecated: Use functions on right hand side directly instead
var createTiddlerPopup = Popup.create;
var scrollToTiddlerPopup = Popup.show;
var hideTiddlerPopup = Popup.remove;

// @Deprecated: Use right hand side directly instead
var regexpBackSlashEn = new RegExp("\\\\n","mg");
var regexpBackSlash = new RegExp("\\\\","mg");
var regexpBackSlashEss = new RegExp("\\\\s","mg");
var regexpNewLine = new RegExp("\n","mg");
var regexpCarriageReturn = new RegExp("\r","mg");

1 onion, halved and sliced thickly (¼ to 1/3 inch)
6-8 boneless chicken tenderloins, or 3-4 boneless breasts, chunked
4 large tomatoes, chunked
3 peppers, quartered
4-5 carrots, sliced thickly (½ to ¾ inch)
4-5 potatoes, chunked
a dozen cabbage leaves 
whole coriander

Cover bottom of a heavy pot with oil
layer sliced onion over oil
layer chicken over onions
salt chicken generously, add a handful of whole coriander
layer tomatoes over the chicken
layer peppers
layer carrots
layer potatoes, generously salt the potatoes
cover the food with layers of cabbage leaves

cover the pot, cook on high until it starts to cook vigorously, then turn down to medium high for a total of 45 minutes cooking time.
<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></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>

//configuration settings
config.fontSize.settings =
            defaultSize : 100,  // all sizes in %
            maxSize : 200,
            minSize : 40,
            stepSize : 10

//startup code
var fontSettings = config.fontSize.settings;

if (!config.options.txtFontSize)
            {config.options.txtFontSize = fontSettings.defaultSize;
setStylesheet(".tiddler .viewer {font-size:"+config.options.txtFontSize+"%;}\n","fontResizerStyles");
setStylesheet("#contentWrapper .fontResizer .button {display:inline;font-size:105%; font-weight:bold; margin:0 1px; padding: 0 3px; text-align:center !important;}\n .fontResizer {margin:0 0.5em;}","fontResizerButtonStyles");

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

               var sp = createTiddlyElement(place,"span",null,"fontResizer");
               if (params[0])
               createTiddlyButton(sp,"+","increase font-size",this.incFont);
               createTiddlyButton(sp,"=","reset font-size",this.resetFont);
               createTiddlyButton(sp,"–","decrease font-size",this.decFont);

config.macros.fontSize.onDblClick = function (e)
             if (!e) var e = window.event;
             e.cancelBubble = true;
             if (e.stopPropagation) e.stopPropagation();
             return false;

config.macros.fontSize.setFont = function ()
               setStylesheet(".tiddler .viewer {font-size:"+config.options.txtFontSize+"%;}\n","fontResizerStyles");

               if (config.options.txtFontSize < fontSettings.maxSize)
                  config.options.txtFontSize = (config.options.txtFontSize*1)+fontSettings.stepSize;


               if (config.options.txtFontSize > fontSettings.minSize)
                  config.options.txtFontSize = (config.options.txtFontSize*1) - fontSettings.stepSize;



config.paramifiers.font =
               onstart: function(v)
                   config.options.txtFontSize = v;
If you've never used a [[TiddlyWiki | http://www.tiddlywiki.com/#HelloThere]] before, don't panic.  It's sort of a cross between a blog and a website in that like a blog, the material is presented continuously on one page and like a website, you can navigate to parts of the page.

Each 'tiddler' (a box with stuff in it) is a piece of micro content.  This ~GettingStarted is a tiddler.  If you mouse over the top of this tiddler, you will see a hover menu with the options:

<html><b><u>close</b></u></html>   closes the current tiddler
<html><b><u>close others </b></u></html>   closes all except for the current tiddler
<html><b><u>view</b></u></html>    the source text or code for a tiddler
<html><b><u>permalink</b></></html>    a permanent link to the current tiddler
<html><b><u>references</b></u></html>   other tiddlers that refer to this one
<html><b><u>jump</b></u></html>   to any other open tiddlers

By opening different tiddlers in different orders, you experience a different webpage.  Sort of a dynamic experience with static content.  

This implementation of the Tiddlywiki concept is an experiment in mind mapping for writers.

This ~TiddlyWiki is a modification based on the DevFire template, available here: http://tiddlythemes.com/#Home

If you wish to use this tool, go to your browser controls and click file/save as so you can download a copy to your computer.

The file is a single html file.  <html><b><u>You can only edit/customize this tiddlywiki on your own computer.  By default, it hides all editing tools when it is shown over the web.</b></i></html>

<html><b><i>Add New Tiddlers:</b></i></html>
Click on 'Admin Tools' in the right hand panel.  Click 'new tiddler'.  Follow the prompts to name your tiddler, add content, and add tags.  Tagging is a powerful organizational too and lets you view your Tiddlywiki as a dynamic creation.

You can access a  calendar in order to harness the power of tagging tiddlers by date.  Use for a submission calendar, for example.  Click on any number date on the calendar to create a tiddler linked to that date. 

Because the Tiddlywiki is a SINGLE webpage, you cannot navigate it with the forward/back buttons on your browser.  Use the 'close' button at the top of any tiddler to close that tiddler.  Use the 'close all' button on the right hand panel to close all open tiddlers.  You can also search by tag (TagSearch) which will open any particular tiddler with a specific tag or all tiddlers with a specific tag.

<html><b><i>On or Off Line?</b></i></html>
Unlike most wikis, TiddyWiki is not mainly a collaborative tool.  Use it on a local computer (it's small enough that you can place the html file on a usb storage device and take it with you) or upload it to a webspace and have access to your data wherever you happen to be.

<html><i>Using Tags</i></html>
Tags are what make Tiddlywiki relational.  If you use tags efficiently, you can sort by tag and see all data relating to a specific tag.  As an example, open TagSearch and click on ~TheForgetting.  Select the 'open all' option. 

<html><i>Adding Tags</i></html>
To add a tag to a tiddler, type the text of the tag in the footer.  You can use wikiwords (words smashed together with a capital in the middle) or single words surrounded by double square brackets.  Then edit the TagSearch tiddler to include your new tag.


|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/]]|
|Description|Insert Javascript executable code directly into your tiddler content.|

''Call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
When installed, this plugin adds new wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be treated as embedded javascript and executed each time the tiddler is rendered.

''Deferred execution from an 'onClick' link''
By including a {{{label="..."}}} parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.  You may also include a {{{title="..."}}} parameter to specify the 'tooltip' text that will appear whenever the mouse is moved over the onClick link text

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

''Display script source in tiddler output''
By including the keyword parameter "show", in the initial {{{<script>}}} marker, the plugin will include the script source code in the output that it displays in the tiddler.

''Defining javascript functions and libraries:''
Although the external javascript file is loaded while the tiddler content is being rendered, any functions it defines will not be available for use until //after// the rendering has been completed.  Thus, you cannot load a library and //immediately// use it's functions within the same tiddler.  However, once that tiddler has been loaded, the library functions can be freely used in any tiddler (even the one in which it was initially loaded).

To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that will be rendered as soon as your TiddlyWiki document is opened.  For example, you could put your {{{<script src="..."></script>}}} syntax into a tiddler called LoadScripts, and then add {{{<<tiddler LoadScripts>>}}} in your MainMenu tiddler.

Since the MainMenu is always rendered immediately upon opening your document, the library will always be loaded before any other tiddlers that rely upon the functions it defines.  Loading an external javascript library does not produce any direct output in the tiddler, so these definitions should have no impact on the appearance of your MainMenu.

''Creating dynamic tiddler content''
An important difference between this implementation of embedded scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document:
* In a typical web document, you use the document.write() function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
* However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and completely replaces the entire ~TiddlyWiki document in your browser window.
* To allow these scripts to work unmodified, the plugin automatically converts all occurences of document.write() so that the output is inserted into the tiddler content instead of replacing the entire ~TiddlyWiki document.

If your script does not use document.write() to create dynamically embedded content within a tiddler, your javascript can, as an alternative, explicitly return a text value that the plugin can then pass through the wikify() rendering engine to insert into the tiddler display.  For example, using {{{return "thistext"}}} will produce the same output as {{{document.write("thistext")}}}.

//Note: your script code is automatically 'wrapped' inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler.  To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.//

''Accessing the ~TiddlyWiki DOM''
The plugin provides one pre-defined variable, 'place', that is passed in to your javascript code so that it can have direct access to the containing DOM element into which the tiddler output is currently being rendered.

Access to this DOM element allows you to create scripts that can:
* vary their actions based upon the specific location in which they are embedded
* access 'tiddler-relative' information (use findContainingTiddler(place))
* perform direct DOM manipulations (when returning wikified text is not enough)
an "alert" message box:
><script show>
	alert('InlineJavascriptPlugin: this is a demonstration message');
dynamic output:
><script show>
	return (new Date()).toString();
wikified dynamic output:
><script show>
	return "link to current user: [["+config.options.txtUserName+"]]";
dynamic output using 'place' to get size information for current tiddler:
><script show>
   if (!window.story) window.story=window;
   var title=story.findContainingTiddler(place).id.substr(7);
   return title+" is using "+store.getTiddlerText(title).length+" bytes";
creating an 'onclick' button/link that runs a script:
><script label="click here" title="clicking this link will show an 'alert' box" show>
   if (!window.story) window.story=window;
   alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
loading a script from a source url:
>http://www.TiddlyTools.com/demo.js contains:
>>{{{function demo() { alert('this output is from demo(), defined in demo.js') } }}}
>>{{{alert('InlineJavascriptPlugin: demo.js has been loaded'); }}}
><script src="demo.js" show>
	return "loading demo.js..."
><script label="click to execute demo() function" show>
import (or copy/paste) the following tiddlers into your document:
''InlineJavascriptPlugin'' (tagged with <<tag systemConfig>>)
!!!!!Revision History
''2007.02.19 [1.6.0]'' added support for title="..." to specify mouseover tooltip when using an onclick (label="...") script
''2006.10.16 [1.5.2]'' add newline before closing '}' in 'function out_' wrapper.  Fixes error caused when last line of script is a comment.
''2006.06.01 [1.5.1]'' when calling wikify() on script return value, pass hightlightRegExp and tiddler params so macros that rely on these values can render properly
''2006.04.19 [1.5.0]'' added 'show' parameter to force display of javascript source code in tiddler output
''2006.01.05 [1.4.0]'' added support 'onclick' scripts.  When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked.  'place' value is set to match the clicked button/link element.
''2005.12.13 [1.3.1]'' when catching eval error in IE, e.description contains the error text, instead of e.toString().  Fixed error reporting so IE shows the correct response text.  Based on a suggestion by UdoBorkowski
''2005.11.09 [1.3.0]'' for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content.  Based on a suggestion by BradleyMeck
''2005.11.08 [1.2.0]'' handle loading of javascript from an external URL via src="..." syntax
''2005.11.08 [1.1.0]'' pass 'place' param into scripts to provide direct DOM access 
''2005.11.08 [1.0.0]'' initial release
This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
version.extensions.inlineJavascript= {major: 1, minor: 6, revision: 0, date: new Date(2007,2,19)};

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

	handler: function(w) {
		var lookaheadRegExp = new RegExp(this.lookahead,"mg");
		lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = lookaheadRegExp.exec(w.source)
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
			if (lookaheadMatch[1]) { // load a script library
				// make script tag, set src, add to body to execute, then remove for cleanup
				var script = document.createElement("script"); script.src = lookaheadMatch[1];
				document.body.appendChild(script); document.body.removeChild(script);
			if (lookaheadMatch[5]) { // there is script code
				if (lookaheadMatch[4]) // show inline script code in tiddler output
				if (lookaheadMatch[2]) { // create a link to an 'onclick' script
					// add a link, define click handler, save code in link (pass 'place'), set link attributes
					var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",lookaheadMatch[2]);
					link.code="function _out(place){"+lookaheadMatch[5]+"\n};_out(this);"
				else { // run inline script code
					var code="function _out(place){"+lookaheadMatch[5]+"\n};_out(w.output);"
					try { var out = eval(code); } catch(e) { out = e.description?e.description:e.toString(); }
					if (out && out.length) wikify(out,w.output,w.highlightRegExp,w.tiddler);
			w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
} )
There are a wide variety of ways to make jambalaya, with chicken and sausage, or shrimp, or ham, or even duck or alligator. Some involve tomatoes and tomato sauce, some use chicken or beef stock instead. This first one uses both tomatoes and chicken stock, and is a New Orleans Creole-style "red" jambalaya, as opposed to one made with only stock, a more Cajun-style"brown" jambalaya (like Dee Gautreau's or Marc Savoy's).

By the way, it's pronounced <jahm-buh-LIE-uh> or <jum-buh-LIE-uh>.

    * 1 lb. boneless chicken, cubed; AND/OR
          o 1 lb. shrimp, boiled in Zatarain's and peeled; OR
          o 1 lb. leftover holiday turkey, cubed; OR
          o 1 lb. of any kind of poultry or fish, cubed; OR
          o Any combination of the above 
    * 1 lb. (hot) smoked sausage, andouille or chaurice, sliced on the bias; OR
          o 1 lb. diced smoked ham 
    * 1 large onion, chopped
    * 1 bell pepper, chopped
    * 3 - 6 cloves garlic, minced (amount to taste; I like lots)
    * 4 ribs celery, chopped
    * 3 small cans tomato paste
    * 4 large Creole tomatoes, peeled, seeded and diced; OR
          o 1 28-oz. can tomatoes 
    * 8 cups good dark homemade chicken stock
    * Creole seasoning blend to taste (or 2 - 3 tablespoons); OR
          o 2 teaspoons cayenne, 2 teaspoons black pepper, 1 teaspoon white pepper, 1 teaspoon oregano, 1/2 teapsoon thyme 
    * 2 bay leaves
    * Salt to taste
    * 4 cups long-grain white rice, uncooked (Some people like converted rice, others prefer good old Mahatma. I use Uncle Ben's converted, as the rice doesn't get sticky or lumpy that way.)

In a sauté or frying pan, brown the chicken, sprinkling with Tony Chachere's seasoning if you've got it; a bit of salt, black pepper and red pepper otherwise. Don't brown if using leftover cooked bird, but you still might want to season the meat. Tear or cut the meat into bite-size pieces.

Brown the sliced smoked sausage or andouille and pour off fat. In the pot, sauté the onions, garlic, peppers and celery in oil until onions begin to turn transparent.

In the same pot, while you're sautéing the "trinity", add the tomato paste and let it pincé, meaning to let it brown a little. What we're going for here is an additional depth of flavor by browning the tomato paste a little; the sugar in the tomato paste begins to caramelize, deepening the flavor and color. Keep it moving so that it browns but doesn't burn. Some friends of mine hate this step, so you can skip it if you want, but then it won't be Chuck's jambalaya. :^)

Once the vegetables are translucent and the tomato paste achives sort of a red mahogany color, deglaze the pan with the about 2 cups of the stock, scraping the bottom of the pan to mix up any browned bits, and stir until smooth, making sure the sautéed vegetables, paste and stock are combined thoroughly. It should be fairly thick.

Add the Creole seasoning, tomatoes and salt to taste. Cook over low-medium heat for about 10 minutes. Add the meat and/or seafood and cook another 10 minutes; if you're using seafood, be careful not to overcook it.

Add the rest of the stock, check seasonings, and stir in the rice, combining thoroughly. Cook for about 20-25 minutes, or until the rice has absorbed all the liquid and is cooked through. If you haven't checked your seasonings before adding the rice, it's too late! It's much better for the rice to absorb the seasonings while it's cooking. Check seasoning anyway, then turn the heat down to low-medium and let the sauce thicken up a bit, with the pot uncovered, stirring frequently, for about 10 minutes. Stir thoroughly to combine all ingredients. When the jambalaya has thickened up a bit and has reached the "right" consistency (you'll know), it's done.

Serve with salad and French bread.

|''Description:''|Support for legacy (pre 2.1) strike through formatting|
|''Date:''|Jul 21, 2006|
|''Author:''|MartinBudden (mjbudden (at) gmail (dot) com)|
|''License:''|[[BSD open source license]]|

// Ensure that the LegacyStrikeThrough Plugin is only installed once.
if(!version.extensions.LegacyStrikeThroughPlugin) {
version.extensions.LegacyStrikeThroughPlugin = {installed:true};

	name: "legacyStrikeByChar",
	match: "==",
	termRegExp: /(==)/mg,
	element: "strike",
	handler: config.formatterHelpers.createElementAndWikify

} //# end of "install only once"
 <<fontSize "font-size:">> 

<<allTagsExcept excludeLists excludeSearch systemConfig AdminTools DevFireTheme script>>

Mediterranean Pot Roast
CDKitchen http://www.cdkitchen.com

Category: Roast
Serves/Makes: 6    |   Difficulty Level: 3    |   Ready In: > 5 hrs


3 beef boneless chuck roast
1 teaspoon salt
1 tablespoon Italian seasoning
1 clove garlic, finely chopped
1/3 cup sun-dried tomatoes in oil, drained and chopped
1/2 cup sliced pitted Kalamata or ripe olives
1/2 cup beef broth
1/2 cup frozen pearl onions

Spray 12-inch skillet with cooking spray; heat over medium-high heat. Cook beef in skillet about 5 minutes, turning once, until brown. Sprinkle with salt, Italian seasoning and garlic; remove from skillet.

Place beef, seasoned side up, in 4-to 5-quart slow cooker. Spread tomatoes and olives over roast. Add broth and onions.

Cover and cook on low heat setting 5 to 6 hours or until beef is tender.

Remove beef from slow cooker; cover and let stand 15 minutes. Slice beef; serve with beef juice and onions from slow cooker.

Recipe Location: http://www.cdkitchen.com/recipes/recs/364/Mediterranean-Pot-Roast86931.shtml
Recipe ID: 57804
+++!!!!![comment: Monday, April 02, 2007 10:58:31 AM]>
Used a large vidalia onion, chopped and more than 1 cup of beef broth with about 3 tbs of concentrated tomato paste===
+++!!!!![comment: Tuesday, April 03, 2007 9:50:00 AM]>
Absolutely remove the cooked meat from the crockpot or the meat will get tough/dried out.  Keep tented in foil if need be.===
60 meatballs
      1 hour 50 minutes (20 min prep, 1 hr 30 min cooking)

 2  lbs lean ground beef 
 1  cup challah bread crumbs 
 2  large eggs 
              1/4  cup fresh parsley, chopped 
             2  cloves garlic, minced 
             2  teaspoons onion powder 
             1  teaspoon fresh basil, minced 
              1/4  teaspoon cayenne pepper 
             1  can whole-berry cranberry sauce 
             1 1/2  cups chili sauce or catsup 
             2  tablespoons brown sugar 
              1/2  teaspoon citric acid or 1 tablespoon lemon juice 

            1. In a large bowl, combine the ground meat, bread crumbs, parsley,             eggs, garlic, onion powder, basil and pepper. 
            2. Mix well but do not overwork or meat will become tough when cooked. 
            3. Form into small balls the size of a walnut or smaller. 
            4. Place meatballs in a slow oven of 300 degrees F and bake for 25 minutes. 
            5. In the meantime, in a crock pot set at 325 degrees F, mix together the cranberry sauce (if you so desire, you can use jellied. I prefer the whole berry.) Add remaining sauce ingredients. 
            6. When meatballs are baked, transfer them to the crock pot and stir             into the sauce. 
            7. Cook in crock pot for 1 hour. 
            8. Lower heat to keep warm for serving.

Mocha Pudding for Passover

Yield: 6 Servings

Source: Torah Prep High School for Girls Passover booklet

  5 ts Instant coffee 
  1/2 c Boiling water 
  2 1/2 c Milk 
  1/4 c Potato starch 
  2/3 c Sugar 
  1/8 ts Salt 
  3 oz Chocolate, chopped 

Mix coffee and boiling water; add milk.

Stir potato starch, sugar and salt in top of double boiler. 

Slowly stir in milk mixture; add chocolate.

Place over boiling water; stir until blended (about 4 minutes). Cover; cook for another 10 minutes, stirring occasionally. Pour into a serving bowl; cool.

From: Tzimmi@aol.com
      3 cups
      55 minutes (15 min prep, 40 min cooking)

      This is an easy delicious pate to serve!

             1  cup butter 
             2  lbs mushrooms, cleaned and finely chopped 
             1  teaspoon salt 
              1/2  teaspoon lemon pepper 
              1/2  teaspoon thyme 
             1  teaspoon cayenne pepper 
             3  egg yolks 
             1  tablespoon whipping cream or half-and-half cream 

            1. In a skillet melt the butter over medium heat. 
            2. Add mushrooms. 
            3. Cook until the mushrooms are well browned and the liquid has cooked down (about 35 minutes). 
            4. Stir in the lemon pepper. 
            5. thyme,cayenne& salt and remove from heat. 
            6. Combine egg yolks with cream. 
            7. Add 1/4 of the mushroom mixture to the egg yolks, stirring well. 
            8. Combine yolk mixture with mushrooms in the skillet. 
            9. Cook over low heat for 2-3 minutes. 
            10. Remove from heat and pour into a 3 cup container. 
            11. Chill& firm. 
            12. Serve at room temperature with crackers or slices of French            bread. 
|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/]]|
|Description|Make any tiddler content into an expandable 'slider' panel, without needing to create a separate tiddler to contain the slider content.|

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
When installed, this plugin adds new wiki syntax for embedding 'slider' panels directly into tiddler content.  Use {{{+++}}} and {{{===}}} to delimit the slider content.  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.

Additional optional syntax elements let you specify
*default to open
*heading level
*floater (with optional CSS width value)
*mouse auto rollover
*custom class/label/tooltip/accesskey
*automatic blockquote
*deferred rendering
The complete syntax, using all options, is:
content goes here
* {{{+++}}} (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)}}}^^
saves the slider opened/closed state, and restores 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^^
* {{{^width^}}} (or just {{{^}}})^^
makes the slider 'float' on top of other content rather than shifting that content downward.  'width' must be a valid CSS value (e.g., "30em", "180px", "50%", etc.).  If omitted, the default width is "auto" (i.e., fit to content)^^
* {{{*}}}^^
automatically opens/closes slider on "rollover" as well as when clicked^^
* {{{{{class{[label=key|tooltip]}}}}}}^^
uses custom label/tooltip/accesskey.  {{{{{class{...}}}}}}, {{{=key}}} and {{{|tooltip}}} are optional.  'class' is any valid CSS class name, used to style the slider label text.  'key' must be a ''single letter only''.  Default labels/tootips are: ">" (more) and "<" (less), with no default access key assignment.^^
* {{{">"}}} //(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.//
simple in-line slider: 
use a custom label and tooltip: 
content automatically blockquoted: 
all options combined //(default open, cookie, heading, sized floater, rollover, class, label/tooltip/key, blockquoted, deferred)//
++++(testcookie)!!!^30em^*{{big{[label=Z|click or press Alt-Z to open]}}}>...
++++(testcookie)!!!^30em^*{{big{[label=Z|click or press Alt-Z to open]}}}>...
complex nesting example:
+++^[get info...=I|click for information or press Alt-I]
   put some general information here, plus a floating slider with more specific info:
   +++^10em^[view details...|click for details]
      put some detail here, which could include a rollover with a +++^25em^*[glossary definition]explaining technical terms===
+++^[get info...=I|click for information or press Alt-I]
   put some general information here, plus a floating slider with more specific info:
   +++^10em^[view details...|click for details]
      put some detail here, which could include a rollover with a +++^25em^*[glossary definition]explaining technical terms===
import (or copy/paste) the following tiddlers into your document:
''NestedSlidersPlugin'' (tagged with <<tag systemConfig>>)
!!!!!Revision History
''2007.03.01 - 2.0.2'' for TW2.2+, hijack Morpher.prototype.stop so that "overflow:hidden" can be reset to "overflow:visible" after animation ends
''2007.03.01 - 2.0.1'' in hijack for Slider.prototype.stop, use apply() to pass params to core function
''2006.07.28 - 2.0.0'' added custom class syntax around label/tip/key syntax: {{{{{classname{[label=key|tip]}}}}}}
''2006.07.25 - 1.9.3'' when parsing slider, save default open/closed state in button element, then in onClickNestedSlider(), if slider state matches saved default, instead of saving cookie, delete it.  Significantly reduces the 'cookie overhead' when default slider states are used.
''2006.06.29 - 1.9.2'' in onClickNestedSlider(), when setting focus to first control, skip over type="hidden"
''2006.06.22 - 1.9.1'' added panel.defaultPanelWidth to save requested panel width, even after resizing has changed the style value
''2006.05.11 - 1.9.0'' added optional '^width^' syntax for floating sliders and '=key' syntax for setting an access key on a slider label
''2006.05.09 - 1.8.0'' in onClickNestedSlider(), when showing panel, set focus to first child input/textarea/select element
''2006.04.24 - 1.7.8'' in adjustSliderPos(), if floating panel is contained inside another floating panel, subtract offset of containing panel to find correct position
''2006.02.16 - 1.7.7'' corrected deferred rendering to account for use-case where show/hide state is tracked in a cookie
''2006.02.15 - 1.7.6'' in adjustSliderPos(), ensure that floating panel is positioned completely within the browser window (i.e., does not go beyond the right edge of the browser window)
''2006.02.04 - 1.7.5'' add 'var' to unintended global variable declarations to avoid FireFox crash bug when assigning to globals
''2006.01.18 - 1.7.4'' only define adjustSliderPos() function if it has not already been provided by another plugin.  This lets other plugins 'hijack' the function even when they are loaded first.
''2006.01.16 - 1.7.3'' added adjustSliderPos(place,btn,panel,panelClass) function to permit specialized logic for placement of floating panels.  While it provides improved placement for many uses of floating panels, it exhibits a relative offset positioning error when used within *nested* floating panels.  Short-term workaround is to only adjust the position for 'top-level' floaters.
''2006.01.16 - 1.7.2'' added button property to slider panel elements so that slider panel can tell which button it belongs to.  Also, re-activated and corrected animation handling so that nested sliders aren't clipped by hijacking Slider.prototype.stop so that "overflow:hidden" can be reset to "overflow:visible" after animation ends
''2006.01.14 - 1.7.1'' added optional "^" syntax for floating panels.  Defines new CSS class, ".floatingPanel", as an alternative for standard in-line ".sliderPanel" styles.
''2006.01.14 - 1.7.0'' added optional "*" syntax for rollover handling to show/hide slider without requiring a click (Based on a suggestion by tw4efl)
''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
''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
This feature was implemented by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]] with initial research and suggestions from RodneyGomes, GeoffSlocock, and PaulPetterson.
version.extensions.nestedSliders = {major: 2, minor: 0, revision: 2, date: new Date(2007,3,2)};

// 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;

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

config.formatters.push( {
	name: "nestedSliders",
	match: "\\n?\\+{3}",
	terminator: "\\s*\\={3}\\n?",
	lookahead: "\\n?\\+{3}(\\+)?(\\([^\\)]*\\))?(\\!*)?(\\^(?:[^\\^\\*\\[\\>]*\\^)?)?(\\*)?(?:\\{\\{([\\w]+[\\s\\w]*)\\{)?(\\[[^\\]]*\\])?(?:\\}{3})?(\\>)?(\\.\\.\\.)?\\s*",
	handler: function(w)
			// defopen=lookaheadMatch[1]
			// cookiename=lookaheadMatch[2]
			// header=lookaheadMatch[3]
			// panelwidth=lookaheadMatch[4]
			// rollover=lookaheadMatch[5]
			// class=lookaheadMatch[6]
			// label=lookaheadMatch[7]
			// blockquote=lookaheadMatch[8]
			// deferred=lookaheadMatch[9]

			 lookaheadRegExp = new RegExp(this.lookahead,"mg");
			lookaheadRegExp.lastIndex = w.matchStart;
			var lookaheadMatch = lookaheadRegExp.exec(w.source)
			if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
				// location for rendering button and panel
				var place=w.output;

				// default to closed, no cookie, no accesskey
				var show="none"; var title=">"; var tooltip="show"; var cookie=""; var key="";

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

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

				// parse custom label/tooltip/accesskey: [label=X|tooltip]
				if (lookaheadMatch[7]) {
					title = lookaheadMatch[7].trim().slice(1,-1);
					var pos=title.indexOf("|");
					if (pos!=-1) { tooltip = title.substr(pos+1,title.length); title=title.substr(0,pos); }
					if (title.substr(title.length-2,1)=="=") { key=title.substr(title.length-1,1); title=title.slice(0,-2); }
					if (pos==-1) tooltip += " "+title; // default tooltip: "show/hide <title>"

				// create the button
				if (lookaheadMatch[3]) { // use "Hn" header format instead of button/link
					var lvl=(lookaheadMatch[3].length>6)?6:lookaheadMatch[3].length;
					var btn = createTiddlyElement(createTiddlyElement(place,"h"+lvl,null,null,null),"a",null,lookaheadMatch[6],title);
					var btn = createTiddlyButton(place,title,tooltip,onClickNestedSlider,lookaheadMatch[6]);

				// set extra button attributes
				btn.sliderCookie = cookie; // save the cookiename (if any) in the button object
				btn.defOpen=lookaheadMatch[1]!=null; // save default open/closed state (boolean)
				btn.keyparam=key; // save the access key letter ("" if none)
				if (key.length) {
					btn.setAttribute("accessKey",key); // init access key
					btn.onfocus=function(){this.setAttribute("accessKey",this.keyparam);}; // **reclaim** access key on focus

				// "non-click" MouseOver open/close slider
				if (lookaheadMatch[5]) btn.onmouseover=onClickNestedSlider;

				// create slider panel
				var panelClass=lookaheadMatch[4]?"floatingPanel":"sliderPanel";
				var panel=createTiddlyElement(place,"div",null,panelClass,null);
				panel.button = btn; // so the slider panel know which button it belongs to
				panel.defaultPanelWidth=(lookaheadMatch[4] && lookaheadMatch[4].length>2)?lookaheadMatch[4].slice(1,-1):""; // save requested panel size
				panel.style.display = show;

				// render slider (or defer until shown) 
				w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
				if ((show=="block")||!lookaheadMatch[9]) {
					// render now if panel is supposed to be shown or NOT deferred rendering
					// align slider/floater position with button
				else {
					var src = w.source.substr(w.nextMatch);
					var endpos=findMatchingDelimiter(src,"+++","===");
					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
		// find endpos for corresponding number of matching 'ends'
		while (startcount && endpos!=-1) {
			endpos = src.indexOf(endtext,endpos+endtext.length);
	return (endpos==-1)?src.length:endpos;

	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")
	else if (theTarget.getAttribute("title")=="hide")
	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")
	// show/hide the slider
		anim.startAnimating(new Slider(theSlider,!isOpen,e.shiftKey || e.altKey,"none"));
		theSlider.style.display = isOpen ? "none" : "block";
	// reset to default width (might have been changed via plugin code)
	// align slider/floater position with target button
	if (!isOpen) window.adjustSliderPos(theSlider.parentNode,theTarget,theSlider,theSlider.className);
	// if showing panel, set focus to first 'focus-able' element in panel
	if (theSlider.style.display!="none") {
		var ctrls=theSlider.getElementsByTagName("*");
		for (var c=0; c<ctrls.length; c++) {
			var t=ctrls[c].tagName.toLowerCase();
			if ((t=="input" && ctrls[c].type!="hidden") || t=="textarea" || t=="select")
				{ ctrls[c].focus(); break; }
	if (this.sliderCookie && this.sliderCookie.length) {
		if (config.options[this.sliderCookie]!=this.defOpen)
		else { // remove cookie if slider is in default display state
			var ex=new Date(); ex.setTime(ex.getTime()-1000);
			document.cookie = this.sliderCookie+"=novalue; path=/; expires="+ex.toGMTString();
	return false;

// TW2.1 and earlier:
// hijack Slider animation handler 'stop' handler so overflow is visible after animation has completed
Slider.prototype.coreStop = Slider.prototype.stop;
Slider.prototype.stop = function()
	{ this.coreStop.apply(this,arguments); this.element.style.overflow = "visible"; }

// TW2.2+
// hijack Morpher animation handler 'stop' handler so overflow is visible after animation has completed
if (version.major+.1*version.minor+.01*version.revision>=2.2) {
	Morpher.prototype.coreStop = Morpher.prototype.stop;
	Morpher.prototype.stop = function()
		{ this.coreStop.apply(this,arguments); this.element.style.overflow = "visible"; }

// adjust panel position based on button position
if (window.adjustSliderPos==undefined) window.adjustSliderPos=function(place,btn,panel,panelClass) {
	if (panelClass=="floatingPanel") {
		var left=0;
		var top=btn.offsetHeight; 
		if (place.style.position!="relative") {
			var left=findPosX(btn);
			var top=findPosY(btn)+btn.offsetHeight;
			var p=place; while (p && p.className!='floatingPanel') p=p.parentNode;
			if (p) { left-=findPosX(p); top-=findPosY(p); }
		if (left+panel.offsetWidth > getWindowWidth()) left=getWindowWidth()-panel.offsetWidth-15;
		panel.style.left=left+"px"; panel.style.top=top+"px";

function getWindowWidth() {
		return document.width; // moz (FF)
	if(document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) )
		return document.documentElement.clientWidth; // IE6
	if(document.body && ( document.body.clientWidth || document.body.clientHeight ) )
		return document.body.clientWidth; // IE4
		return window.innerWidth; // IE - general
	return 0; // unknown
My variation of the basic recipe

Here's a lower yeast variation of the "master recipe" from "Artisan Bread in Five Minutes a Day: The Discovery that Revolutionizes Home Baking," by Jeff Hertzberg and Zoe Francois.

Yield: Four pounds of bread dough, enough for four round loaves.

3/4 tablespoon yeast

1 1/2 tablespoons coarse (kosher) salt

3 cups lukewarm water

6 1/2 cups flour, plus additional for dusting dough (I use a mix of 50% unbleached white flour and 50% king Arthur white whole wheat. Because whole wheat is heavier than white, I ended up only needing 5 1/2 cups of flour.)

Cornmeal, optional

1. Mix yeast and salt into the water in a large bowl or plastic container. Stir in the flour, mixing until there are no dry patches; the dough will be quite loose. Cover, but not airtight. Let the dough rise at room temperature 5+ hours.

2.   To bake immediately, sprinkle a little flour on the dough; cut off a grapefruit-sized piece. *Remember, this will make enough dough to bake 4 loaves. If you only need one, the rest of the dough will keep in the fridge for up to 2 weeks.* Turn dough in floured hands to lightly stretch the surface until smooth and round on top and lumpy on the bottom. (The *less* you handle the dough, the better.) 

3. Place the dough on a piece of *greased* parchment paper, let rise in a shallow bowl for 1-2 hours. (Since I used less yeast than the original master recipe, it required the longer rise, but I think that's a fair trade-off for the more complex flavor.)

4. Heat oven to 450 degrees.  Put a dutch oven, (enameled cast iron pot, round or oval) covered on the middle shelf as the oven heats.

5. When the rising time is finished and the oven reaches temperature, dust loaf with flour, slash top in a cross with a serrated knife. Lift parchment paper from the shallow bowl and put it carefully into the (now VERY HOT)  dutch oven.  Bake for about 20 minutes; remove cover of dutch oven, cook an additional 10 minutes, then cool completely before cutting.
Yield: 4 (1-pound) loaves, about 64 slices

Here's an adaptation of the "master recipe" from "Artisan Bread in Five Minutes a Day: The Discovery that Revolutionizes Home Baking," by Jeff Hertzberg and Zoe Francois. You'll get four pounds of bread dough, enough for four generous round loaves.

1 1/2 tablespoons each: yeast, coarse salt

3 cups lukewarm water

6 1/2 cups flour, plus additional for dusting dough

Cornmeal, optional

1. Mix yeast and salt into the water in a large bowl or plastic container. Stir in the flour, mixing until there are no dry patches; the dough will be quite loose. Cover, but not airtight. Let the dough rise at room temperature 2-5 hours.

2. Bake immediately, if desired, or refrigerate, loosely covered, for up to 2 weeks (the dough is easier to handle when it's cold). When ready to bake, sprinkle a little flour on the dough; cut off a grapefruit-sized piece. Turn dough in floured hands to lightly stretch the surface until smooth and round on top and lumpy on the bottom.

3. Heat oven to 450 degrees. Sprinkle a pizza peel with cornmeal. Place the dough on the peel; let rise 40 minutes. Repeat with remaining dough, or refrigerate. Place a broiler pan on bottom of oven. Place a baking stone on the middle rack. (If using a loaf pan without a baking stone, stretch rounded dough into an oval and place into a greased, non-stick loaf pan. Let rest 40 minutes if fresh, 1 hour if refrigerated. Heat oven to 450 degrees for 5 minutes.)

4. Dust loaf with flour, slash top in a cross with a serrated knife. Slide loaf onto baking stone or, if using loaf pan, place loaf pan on middle rack. Pour 1 cup water into the broiler pan; close oven door quickly to trap steam. Bake until well browned, about 30 minutes; cool completely before cutting.

Nutrition information per slice:

47 calories, 3% of calories from fat, 0 g fat, 0 g saturated fat, 0 mg cholesterol, 10 g carbohydrates, 1 g protein, 133 mg sodium, 0 g fiber


<div class='header' macro='gradient vert #390108 #900'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
<div id='mainMenu'>
<div refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
JCC Cookbook: Breads: Passover Blueberry Muffins

1/3 cup margarine
3/4 cup sugar
3 eggs
1/2 tsp. vanilla
1/2 cup matzo cake meal
1/4 cup potato starch
1/4 tsp. salt
1 cup blueberries (if frozen drain)

Cream sugar and margarine. 

Add eggs, one at a time, beating well. 

Add vanilla, matzo meal, potato starch and salt. 

Fold in blueberries. 

Bake in greased muffin tins for about 45 minutes at 350°. Makes 12 muffins. These can be frozen.

Beat: 4 eggs, 2 cups sugar

Blend in: 1/2 cup cocoa, 3/4 cup oil

Add: 1/2 cup strong black coffee,1 cup cake meal, 2 Tablespoons potato starch

Bake in 9x13" pan or 2 smaller pans for 25-30 min. in medium oven.

Bea's Mom is Lillian Skolnik of Bayonne, New Jersey
SaraBeth's Cream Cheese Brownies ·

1/4 cup butter (or unsalted margarine) ·
1/3 cup soft cream cheese (NOT the whipped kind) ·
1 cup sugar ·
2 beaten eggs ·
1/2 cup matzah cake flour ·
3 ounces bittersweet chocolate, melted and cooled ·
2 Tbsp. potato starch ·
1 cup finely chopped nuts 

Cream butter, cream cheese. 

Add sugar and eggs, mix well. 

Stir in cake flour, potato starch, cooled chocolate, chopped nuts (reserving some nuts for the top). 

Spread batter in buttered 9x9 inch pan. 

Sprinkle reserved nuts over top. 

Bake at 325~ for 30 minutes. 

Cut into squares while warm. 

Penina W. Freedenberg, Rockville, MD
Submitted by Betty Denenberg Adler

1 Box Matzo Farfel
1/3 C. Oil
1/2 C. Honey
1/2 Stick Butter or Margarine
1/2 C. Water
1 tsp. Cinnamon
1 oz. (+) Sunflower Seeds
4 oz. Walnuts
1 3/4 oz. Slivered Almonds
2-3 oz. Raisins
6 oz. Dried Fruit Bits
Grated Coconut (optional)

Melt honey and margarine on low heat. 

Add oil and water and pour over farfel. 

Add nuts, sunflower seeds, coconut and cinnamon. 

Spread mixture on large cookie sheet and bake at 400, stirring every 10 minutes until light brown — 25-30 minutes.

Add raisins and dried fruit after baking. 

After cooling, keep in airtight container to maintain crispness. Makes about 2 1/2 lbs. 
// --------------------------------------------------------------------
// Calendar
// --------------------------------------------------------------------

config.macros.calendar = {holidays: []};
config.macros.calendar.options = {
 // day week starts from (normally 0-Su or 1-Mo)
 calendarWeekStart: 1,
 calendarToday: "Today",
 calendarHoliday: "Holiday: ",
 calendarLongDateFormat: "0MM/0DD/YYYY",
 calendarShortDateFormat: "0MM/0DD",
 calendarTag: ["schedule"]

** Internal functions
var cldTag;

config.macros.calendar.calendarIsHoliday = function(date) {
 var cm = config.macros.calendar;
 var longHoliday = date.formatString(cm.options.calendarLongDateFormat);
 var shortHoliday = date.formatString(cm.options.calendarShortDateFormat);
 for(var i = 0; i < cm.holidays.length; i++) {
 if(cm.holidays[i][0] == longHoliday || cm.holidays[i][0] == shortHoliday) {
 return cm.holidays[i];
 return null;

config.macros.calendar.onClickOtherDay = function() {
 var day = this.getAttribute('tiddlylink');
 for(var i=0; i<cldTag.length;i++){
 Story.prototype.setTiddlerTag(day, cldTag[i], 1);

config.macros.calendar.getPopupText = function(title) {
 var popup_entries = store.getTiddlerText(title).split("\n");
 var popup = popup_entries[0];
 if(popup_entries.length>1) popup += " ...";
 return popup;

config.macros.calendar.findCalendar = function(child) {
 var parent;
 while (child && child.parentNode) {
 parent = child.parentNode;
 if (parent.id == "calendarWrapper") {
 return parent;
 child = parent;
 return null;

config.macros.calendar.selectDate = function(e) {
 if (!e) var e = window.event;
 var cm = config.macros.calendar;
 var calendar = cm.findCalendar(this);
 if (calendar) {
 var d = this.getAttribute("date");
 if (d != null) cm.makeCalendar(calendar, new Date(new Number(d)));
 e.cancelBubble = true;
 if (e.stopPropagation) e.stopPropagation();
 return false;

config.macros.calendar.makeCalendar = function(calendar, dt_current) {
 var cm = config.macros.calendar;
 var dt_today = new Date(new Number(calendar.getAttribute("today")));
 var currentDay = new Date(new Number(calendar.getAttribute("currentDay")));
 var setControls = calendar.getAttribute("setControls");
 calendar.setAttribute("date", dt_current.valueOf());

 while (calendar.hasChildNodes())

 // get same date in the previous year
 var dt_prev_year = new Date(dt_current);
 dt_prev_year.setFullYear(dt_prev_year.getFullYear() - 1);
 if (dt_prev_year.getDate() != dt_current.getDate())

 // get same date in the next year
 var dt_next_year = new Date(dt_current);
 dt_next_year.setFullYear(dt_next_year.getFullYear() + 1);
 if (dt_next_year.getDate() != dt_current.getDate())

 // get same date in the previous month
 var dt_prev_month = new Date(dt_current);
 dt_prev_month.setMonth(dt_prev_month.getMonth() - 1);
 if (dt_prev_month.getDate() != dt_current.getDate())

 // get same date in the next month
 var dt_next_month = new Date(dt_current);
 dt_next_month.setMonth(dt_next_month.getMonth() + 1);
 if (dt_next_month.getDate() != dt_current.getDate())

 // get first day to display in the grid for current month
 var dt_firstday = new Date(dt_current);
 dt_firstday.setDate(1 - (7 + dt_firstday.getDay() - cm.options.calendarWeekStart) % 7);

 var area, header;
 var line, cell, i;

 // 1 - calendar header table
 // 2 - print weekdays titles
 // 3 - calendar days table 
calendar.cellPadding = 0;
calendar.cellSpacing = 0;
area = createTiddlyElement(calendar, "tbody");

 // 1 - calendar header table
 header = createTiddlyElement(area,"tr", "calendarHeader");
 header.cellPadding = 0;
 header.cellSpacing = 0;

var headerValues = [
 [ "<<", "selectYear", dt_prev_year.valueOf() ],
 [ "<", "selectMonth", dt_prev_month.valueOf() ],
 [ config.messages.dates.months[dt_current.getMonth()] + ' ' + dt_current.getFullYear(),
 "selectToday", dt_today.valueOf() ],
 [ ">", "selectMonth", dt_next_month.valueOf() ],
 [ ">>", "selectYear", dt_next_year.valueOf() ]

 for (i = 0; i < headerValues.length; ++i) {
 var link = createTiddlyElement(header,"td", null, null, headerValues[i][0]);
 if(i==2) link.colSpan=3;
 link.onclick = cm.selectDate;
 link.setAttribute("date", headerValues[i][2]);
} else {
 var link = createTiddlyElement(header,"td", null, null, 
config.messages.dates.months[dt_current.getMonth()] + ' ' + dt_current.getFullYear());

 // 2 - print weekdays titles
 line = createTiddlyElement(area, "tr", "weekNames");
 for (var n = 0; n < 7; ++n) {
 createTiddlyElement(line, "td", null, null, config.messages.dates.shortDays[(cm.options.calendarWeekStart + n)%7]);

 // 3 - calendar days table
 var dt_current_day = new Date(dt_firstday);
 var day_class;
 var title;
 var holiday;
 var popup;
 var clickHandler;

 while (dt_current_day.getMonth() == dt_current.getMonth() ||
 dt_current_day.getMonth() == dt_firstday.getMonth()) {

 // print row header
 line = createTiddlyElement(area, "tr", "calendarLine", null, null);
 for (var n_current_wday = 0; n_current_wday < 7; ++n_current_wday) {
 title = dt_current_day.formatString(cm.options.calendarLongDateFormat);
 clickHandler = cm.onClickOtherDay;
 popup = null;
 holiday = cm.calendarIsHoliday(dt_current_day);

 if (holiday != null) {
 // holidays
 day_class = (holiday.length==3)? holiday[2]: "holiDay";
 popup = cm.options.calendarHoliday + holiday[1];
 } else if (dt_current_day.getDay() == 0 || dt_current_day.getDay() == 6) {
 // weekend days
 day_class = "weekDay";
 } else {
 // print working days of current month
 day_class = "workingDay";

if(dt_current_day.getMonth() == dt_current.getMonth()){
 if (currentDay.valueOf() == dt_current_day.valueOf()) {
 // print current date
 if (store.tiddlerExists(title)){
 // day has a tiddler associated with it
 day_class += " currentscheduledDay";
 clickHandler = onClickTiddlerLink;
 popup = cm.options.calendarToday + ": "+ cm.getPopupText(title);
 } else {
 day_class += " currentDay";
 popup = cm.options.calendarToday;

 if (store.tiddlerExists(title) && store.getTiddler(title).isTagged(cldTag[0]) && dt_current_day.valueOf() != dt_today.valueOf()) {
 // day has a tiddler associated with it
 day_class += " scheduledDay";
 clickHandler = onClickTiddlerLink;
 popup = cm.getPopupText(title);

 // extra formatting for days of previous or next month
 if (dt_current_day.getMonth() != dt_current.getMonth()) {
 day_class += " otherMonthDay";

 var text = dt_current_day.getDate();
 var cell = createTiddlyElement(line, "td", null, day_class, text);
 cell.setAttribute("date", dt_current_day.valueOf());
 cell.setAttribute("tiddlyLink", title);
 if(popup) cell.setAttribute("title", popup);


config.macros.calendar.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
 var start_date = new Array();
 var date = new Date();
 var cldParams = paramString.parseParams('calendarParams', null, true);
 var cldYear = (cldParams[0].year)?parseFloat(cldParams[0].year[0]): date.getFullYear();
 var cldMonth = (cldParams[0].month)?parseFloat(cldParams[0].month[0]): date.getMonth();
 var n_months = (cldParams[0].numberMonths)?parseFloat(cldParams[0].numberMonths[0]): 1;
 var n_cols = (cldParams[0].numberColumns)?parseFloat(cldParams[0].numberColumns[0]): 3;
 cldTag = (cldParams[0].tag)?cldParams[0].tag[0].split("#"): config.macros.calendar.options.calendarTag;
 for(var i = 0; i < n_months; i++){
 start_date[i] = new Date(cldYear, cldMonth+i, 1);
 var n_rows = Math.max(1,Math.ceil(n_months/n_cols));
 n_cols = Math.min(n_cols,n_months);
 var setControls=(n_months>1)? 0: 1;
 var currentDay = new Date();
 currentDay = new Date(currentDay.getFullYear(), currentDay.getMonth(), currentDay.getDate());
 var holder = createTiddlyElement(place, "table", null,"calendarHolder");
 var holderTable = createTiddlyElement(holder, "tbody");
 for(var i = 0; i < n_rows; i++){
 var holderLine = createTiddlyElement(holderTable, "tr");
 for(var j = 0; j < n_cols; j++){
 var holderCell = createTiddlyElement(holderLine, "td");
 var calendar = createTiddlyElement(holderCell, "table", "calendarWrapper");
 calendar.setAttribute("name", "calendarWrapper");
 calendar.setAttribute("setControls", setControls);
 calendar.setAttribute("today", start_date[n_cols*i+j].valueOf());
 calendar.setAttribute("currentDay", currentDay.valueOf());
 config.macros.calendar.makeCalendar(calendar, start_date[n_cols*i+j]);

function refreshCalendars(hint) {
 var calendars = document.getElementsByName("calendarWrapper");
 var i, c;
 for (i = 0; i < calendars.length; ++i) {
 c = calendars.item(i);
 if (c.id == "calendarWrapper") {
 config.macros.calendar.makeCalendar(c, new Date(new Number(c.getAttribute("date"))));

store.addNotification(null, refreshCalendars);

setStylesheet("/***\n!Calendar Styles\n***/\n/*{{{*/\n .viewer .calendarHolder {\n margin-left: auto;\n margin-right: auto;\n border: none;\n}\n\n .viewer .calendarHolder table {\n border: none;\n margin: 0;\n}\n\n .viewer .calendarHolder tr {\n border: none;\n vertical-align: top;\n}\n\n .viewer .calendarHolder td {\n border: none;\n vertical-align: top;\n}\n\n .viewer #calendarWrapper {\n width: 21em;\n border: 2px solid #4682b4;\n cursor: pointer;\n}\n\n #calendarWrapper #calendarLine td {\n height: 2.5em;\n}\n\n #calendarWrapper tr {\n border:none;\n}\n\n #calendarWrapper td {\n text-align: center;\n vertical-align: middle;\n width: 14.28%;\n border:none;\n}\n\n #calendarWrapper #calendarHeader td{\n color: #ffffff;\n background-color: #666;\n height: 2em;\n}\n\n #calendarWrapper #weekNames td {\n color: #ffffff;\n background-color: #87cefa;\n height: 2em;\n}\n\n #calendarWrapper .weekDay {\n background-color: #ccff99;\n}\n\n #calendarWrapper .holiDay {\n background-color: #9acd32;\n}\n\n #calendarWrapper .currentDay {\n border: solid #ff0000 2px;\n font-weight: bold;\n}\n\n #calendarWrapper .currentscheduledDay {\n border: solid #ff0000 2px;\n font-weight: bold;\n}\n\n #calendarWrapper .workingDay {\n background-color: #999;\n}\n\n #calendarWrapper .scheduledDay {\n border: solid orange 2px;\n}\n\n #calendarWrapper .otherMonthDay {\n background-color: #014;\n}\n\n/*}}}*/","CalendarStyles");
Beef pot roast

As winter, we hope, starts winding down toward spring in this part of the world, I'm still trying to get in a few more hearty cold-weather dishes before the flowers blossom and the trees start to bud.

One such favorite is beef pot roast, a dish that stands at the other end of the sophistication spectrum from tenderloin or standing rib, but one that can bring plenty of pleasure in its own right. Made from tougher cuts that can range from shoulder to rump, it's usually braised - simmered long and slow in a closed pot with aromatic liquids.

This time-consuming process may sacrifice the hot-pink pleasures of medium-rare beef, but it more than makes up for that in flavor, both from the beef itself - tough cuts tend to be more intensely flavored than the more tender, expensive and sought-after rib cuts and tenderloins - and from the braising liquids that infuse the meat with aromatics during the long, closed braising period.

The basic principle of pot roasting is so simple that it hardly requires a recipe: Brown a chunk of beef in a little oil, flavored if you wish with salt, pepper, garlic and onion. Pop it into a dutch oven or other heavy, ovenproof pot with a tight-fitting lid with a small amount of liquid, and cook at low temperature for a long time. During cooking, add vegetables if you like. That's pretty much the whole story, and you can even skip the browning step if you want to save a few calories, although you'll make some sacrifice in color and flavor.

I focused on the braising liquid in a pot roast the other day, fine-tuning it with a few non-traditional ingredients in almost homeopathic doses (tomato paste and soy sauce, to add richness and a touch of umami, the Japanese "fifth flavor;" and a dash of hot sauce just to liven things up a bit), and thought the result was exceptionally fine. I'll outline the process below, with the strong caveat that you're welcome to follow my exact proportions, but you'll likely achieve just as much success if you modify it with changes to suit your taste. In short, it's an easily altered and very forgiving recipe.

(Serves two)

2-4 pound (about 1 to 2 kilos) beef chuck or shoulder roast.
Black pepper
3 or 4 garlic cloves
2 to 4 tablespoons (30 to 60ml) olive oil
1/4 cup (60 ml) dry red wine
1 cup beef broth
1 tablespoon tomato paste
1 tablespoon soy sauce
1/2 teaspoon good-quality hot sauce (Cholula Mexican, Sriracha Vietnamese or Indonesian sambal oelek, for example)
1 stalk celery
1 bay leaf
1 medium or 2 small baking potatoes
2 carrots
1 medium-size sweet onion

1. Preheat oven to 325F (160C). Generously season the beef with salt and pepper. Peel and smash the garlic cloves and put them in the dutch oven with the olive oil. Cook over high heat until the garlic sizzles. Put in the beef, sear quickly on all sides, then reduce heat to medium-high and continue cooking, turning the meat often, until it's well browned.

2. When the meat is nicely browned, pour off a little of the accumulated beef fat and olive oil in the pan, if you're concerned about calories; then turn heat back to high and pour in the red wine, turning the meat once or twice and scraping the bottom of the pan to pick up any browned bits, until the liquid has reduced to a syrupy glaze.

3. While the meat is browning, mix together the beef broth, tomato paste, soy sauce and hot sauce. After you've reduced the red wine, turn heat to low and pour in this liquid, adding a little water if necessary so the level of liquid comes about halfway up the sides of the meat. Break the celery stalk in half and put the pieces in; add the bay leaf. Cover and place in the preheated oven and cook for a total of about two hours, turning the meat every half-hour or so.

4. While the meat simmers, peel the baking potatoes and the carrots. Cut the potatoes into 1/2-inch cubes and the carrots into thick "coins." Put them in a saucepan with lightly salted water to cover, and simmer for about 10 minutes or until they're crisp-tender. Peel the onion and cut it in half across its equator; cut each half into six or eight wedges. When you estimate that the meat has about 45 minutes to go, drain the potatoes and carrots and add them to the simmering liquid; put in the onion chunks.

5. Continue cooking the meat with the vegetables. For the last 30 minutes or so, particularly if there seems to be a lot of liquid in the pan, set the lid askew and turn the oven up to 375F (190C) so it will reduce a bit. (Keep an eye on it, though, and make sure it doesn't dry out.)

6. At the end of cooking, remove the beef from the liquid and carve it into thick slices. It should be almost falling-apart tender. I lift out the vegetables with a slotted spoon and serve them separately in a warm bowl, with the possible exception of the celery, which may be so cooked down that it needs to be discarded. You can put some of the liquid in a gravy boat or just use it as a light jus on the meat platter.

MATCHING WINE: Red wine with beef, of course, and just about any red would do, although I like to turn to "comfort wines" from Northern Italy or Southern France. This time around, it was a delight with the Languedoc red blend featured in the Feb. 28 30 Second Wine Advisor, Domaine d'Aupilhac 2004 Coteaux du Languedoc.

Pumpkin Mousse Cups made with Hershey'®s Mini Kisses® Brand Milk Chocolates

Makes 10 servings

1-3/4 	cups (10-ounce package) HERSHEY'®S MINI KISSES® Brand Milk Chocolates, divided
24 	marshmallows
1/2 	cup milk
1/2 	cup canned pumpkin
1 	teaspoon vanilla extract
1 	teaspoon pumpkin pie spice
1/3 	cup powdered sugar
1 	cup (1/2 pint) cold whipping cream
1. Line 10 muffin cups (2-1/2 inches in diameter) with paper bake cups. Reserve 1/2 cup chocolates or chocolate pieces. Place remaining 1-1/4 cups chocolates in small microwave-safe bowl; microwave at HIGH (100%) 1 minute or until melted when stirred. Mixture should be thick.

2. Very thickly coat inside pleated surfaces and bottoms of bake cups with melted chocolate using soft pastry brush. Refrigerate 10 minutes; recoat any thin spots with melted chocolate.* Refrigerate until firm, about 2 hours. Gently peel off paper; refrigerate until ready to fill.

3. Place marshmallows, milk, and pumpkin in medium microwave-safe bowl. Microwave at HIGH 1 minute; stir. Microwave additional 30 seconds at a time, stirring after each heating, until mixture is melted and smooth. Stir in vanilla and pumpkin pie spice. Cool completely.

4. Beat powdered sugar and whipping cream until stiff; fold into pumpkin mixture. Fill cups with pumpkin mousse; garnish with whipped cream and reserved chocolates pieces. Cover; refrigerate 2 hours or until firm.

*If reheating is needed, microwave chocolate at HIGH 15 seconds; stir.
config.renameTags = {

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

	removeTag: function(tag,tiddlers) {
		for (var i=0;i<tiddlers.length;i++) {

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

	storeMethods: {

		saveTiddler_orig_renameTags: TiddlyWiki.prototype.saveTiddler,

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

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

		removeTiddler_orig_renameTags: TiddlyWiki.prototype.removeTiddler,

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


	init: function() {

<html><i>Easy and fast roast chicken.  Dinner in under an hour.  YUM</i></html>

1  whole chicken , 3 1/2 to 4 lbs
2 1/2  	pounds  russet potatoes (4 to 5 medium), or Yukon Gold potatoes, peeled and sliced 1/8 to 1/4 inch thick
Vegetable cooking spray (nonstick)
1 1/2  	tablespoons  olive oil 
3/4  teaspoon  table salt (for potatoes)
Ground black pepper 

1. Adjust oven rack to lower-middle position and heat oven to 500 degrees. 

2.  Line bottom of broiler pan with foil and spray with nonstick vegetable cooking spray. 

3.  Toss potatoes with 1 tablespoon oil, salt, and pepper to taste in medium bowl. Spread potatoes in even layer in foil-lined broiler pan bottom.

4.  Cut out chicken's backbone and "butterfly" chicken, flattening the breastbone.  (optional:  Insert softened/melted butter with garlic or rosemary under the skin)

2.  Place chicken on broiler pan rack. Rub chicken with remaining 1 1/2 teaspoons oil and sprinkle with pepper.

3. Roast chicken until spotty brown, about 20 minutes. Rotate pan and continue to roast until skin has crisped and turned a deep brown and an instant-read thermometer registers 160 degrees in thickest part of breast, 20 to 25 minutes longer. 

4. Transfer chicken to cutting board. 

5.  Soak up excess grease from potatoes with several sheets paper towels. 

6.  Cut chicken into serving pieces and serve with potatoes.

(Adapted from <html><a href="http://www.cooksillustrated.com">Cooks Illustrated</a></html> -- my absolute favorite cooking source.  I *HIGHLY* recommend a subscription to the magazine and/or their website.)
Lisa and Neil's Recipes
Cook This!
makes about 1 1/4 cups

4 ounce sliced smoked salmon
1 large shallot, minced (about 3 tablespoons)
2 ounces cream cheese, softened
1 tablespoon lemon juice
1/4 cup creme fraiche or sour cream
Ground black pepper

1. Place salmon and shallot in food processor and process until mixture is finely chopped, scraping down bowl as necessary, about 10 seconds. Add cream cheese and lemon juice and process 
again until mixture forms a ball, again scraping down bowl as necessary. Add creme fraiche and pulse just to incorporate, 5 seconds. 

2. Turn mousse into bowl and season with pepper to taste. Use immediately or cover with plastic wrap and refrigerate up to 2 days.
|''Description:''|Sparklines macro|
if(!version.extensions.SparklinePlugin) {
version.extensions.SparklinePlugin = {installed:true};

//-- Sparklines

config.macros.sparkline = {};
config.macros.sparkline.handler = function(place,macroName,params)
	var data = [];
	var min = 0;
	var max = 0;
	var v;
	for(var t=0; t<params.length; t++) {
		v = parseInt(params[t]);
		if(v < min)
			min = v;
		if(v > max)
			max = v;
	if(data.length < 1)
	var box = createTiddlyElement(place,"span",null,"sparkline",String.fromCharCode(160));
	box.title = data.join(",");
	var w = box.offsetWidth;
	var h = box.offsetHeight;
	box.style.paddingRight = (data.length * 2 - w) + "px";
	box.style.position = "relative";
	for(var d=0; d<data.length; d++) {
		var tick = document.createElement("img");
		tick.border = 0;
		tick.className = "sparktick";
		tick.style.position = "absolute";
		tick.src = "data:image/gif,GIF89a%01%00%01%00%91%FF%00%FF%FF%FF%00%00%00%C0%C0%C0%00%00%00!%F9%04%01%00%00%02%00%2C%00%00%00%00%01%00%01%00%40%02%02T%01%00%3B";
		tick.style.left = d*2 + "px";
		tick.style.width = "2px";
		v = Math.floor(((data[d] - min)/(max-min)) * h);
		tick.style.top = (h-v) + "px";
		tick.style.height = v + "px";

Author: Clint Checketts

body {
background: #000;
!Link styles /% ============================================================= %/
#mainMenu a.button,
#sidebarOptions .sliderPanel a{
 color: #ffbf00;
 border: 0;
 background: transparent;

#mainMenu a.button:hover,
#sidebarOptions .sliderPanel a:hover
#sidebarOptions .sliderPanel a:active{
 color: #ff7f00;
 border: 0;
 border-bottom: #ff7f00 1px dashed;
 background: transparent;
 text-decoration: none;

#displayArea .button.highlight{
 color: #ffbf00;
 background: #4c4c4c;
!Header styles /% ============================================================= %/
 border-bottom: 2px solid #ffbf00;
 color: #fff;

.headerForeground a {
 color: #fff;

.header a:hover {
 border-bottom: 1px dashed #fff;
!Main menu styles /% ============================================================= %/
#mainMenu {color: #fff;}
#mainMenu h1{
 font-size: 1.1em;
#mainMenu li,#mainMenu ul{
 list-style: none;
 margin: 0;
 padding: 0;
!Sidebar styles /% ============================================================= %/
#sidebar {
 right: 0;
 color: #fff;
 border: 2px solid #ffbf00;
 border-width: 0 0 2px 2px;

#sidebarOptions {
 background-color: #4c4c4c;
 padding: 0;

#sidebarOptions a{
 margin: 0;
 color: #ffbf00;
 border: 0;
#sidebarOptions a:hover {
 color: #4c4c4c;
 background-color: #ffbf00;


#sidebarOptions a:active {
 color: #ffbf00;
 background-color: transparent;

#sidebarOptions .sliderPanel {
 background-color: #333;
 margin: 0;

#sidebarTabs {background-color: #4c4c4c;}
#sidebarTabs .tabSelected {
 padding: 3px 3px;
 cursor: default;
 color: #ffbf00;
 background-color: #666;
#sidebarTabs .tabUnselected {
 color: #ffbf00;
 background-color: #5f5f5f;
 padding: 0 4px;

#sidebarTabs .tabUnselected:hover,
#sidebarTabs .tabContents {
 background-color: #666;

.listTitle{color: #FFF;}
#sidebarTabs .tabContents a{
 color: #ffbf00;

#sidebarTabs .tabContents a:hover{
 color: #ff7f00;
 background: transparent;

#sidebarTabs .txtMoreTab .tabSelected,
#sidebarTabs .txtMoreTab .tab:hover,
#sidebarTabs .txtMoreTab .tabContents{
 color: #ffbf00;
 background: #4c4c4c;

#sidebarTabs .txtMoreTab .tabUnselected {
 color: #ffbf00;
 background: #5f5f5f;

.tab.tabSelected, .tab.tabSelected:hover{color: #ffbf00; border: 0; background-color: #4c4c4c;cursor:default;}
.tab.tabUnselected {background-color: #666;}
.tab.tabUnselected:hover{color:#ffbf00; border: 0;background-color: #4c4c4c;}
.tabContents {
 background-color: #4c4c4c;
 border: 0;
.tabContents .tabContents{background: #666;}
.tabContents .tabSelected{background: #666;}
.tabContents .tabUnselected{background: #5f5f5f;}
.tabContents .tab:hover{background: #666;}
!Message area styles /% ============================================================= %/
#messageArea {background-color: #666; color: #fff; border: 2px solid #ffbf00;}
#messageArea a:link, #messageArea a:visited {color: #ffbf00; text-decoration:none;}
#messageArea a:hover {color: #ff7f00;}
#messageArea a:active {color: #ff7f00;}
#messageArea .messageToolbar a{
 border: 1px solid #ffbf00;
 background: #4c4c4c;
!Popup styles /% ============================================================= %/
.popup {color: #fff; background-color: #4c4c4c; border: 1px solid #ffbf00;}
.popup li.disabled{color: #fff;}
.popup a {color: #ffbf00; }
.popup a:hover { background: transparent; color: #ff7f00; border: 0;}
.popup hr {color: #ffbf00; background: #ffbf00;}
!Tiddler Display styles /% ============================================================= %/
.title{color: #fff;}
h1, h2, h3, h4, h5 {
 color: #fff;
 background-color: transparent;
 border-bottom: 1px solid #333;

 color: #666;

.viewer {color: #fff; }

.viewer table{background: #666; color: #fff;}

.viewer th {background-color: #996; color: #fff;}

.viewer pre, .viewer code {color: #ddd; background-color: #4c4c4c; border: 1px solid #ffbf00;}

.viewer hr {color: #666;}

.tiddler .button {color: #4c4c4c;}
.tiddler .button:hover { color: #ffbf00; background-color: #4c4c4c;}
.tiddler .button:active {color: #ffbf00; background-color: #4c4c4c;}

.toolbar {
 color: #4c4c4c;

.toolbar a.button,
.toolbar a.button:hover,
.toolbar a.button:active,
.editorFooter a{
 border: 0;

.footer {
 color: #ddd;

.selected .footer {
 color: #888;

.highlight, .marked {
 color: #000;
 background-color: #ffe72f;
.editorFooter {
 color: #aaa;

-moz-border-radius-topleft: 3px;
-moz-border-radius-topright: 3px;

 background: #4c4c4c;
 border: 1px solid #4c4c4c; 

.selected .tagging,
.selected .tagged{
 background-color: #333;
 border: 1px solid #ffbf00;

.tagging .listTitle,
.tagged .listTitle{
 color: #fff;

.tagging .button,
.tagged .button{
 color: #ffbf00;
 border: 0;
 padding: 0;

.tagging .button:hover,
.tagged .button:hover{
background: transparent;

.selected .isTag .tagging.simple,
.selected .tagged.simple,
.isTag .tagging.simple,
.tagged.simple {
 float: none;
 display: inline;
 border: 0;
 background: transparent;
 color: #fff;
 margin: 0;

.cascade {
 background: #4c4c4c;
 color: #ddd;
 border: 1px solid #ffbf00;
|''Description:''|this plugin replace the content of the tag popup with the one from TagToolTemplate (a new shadowTiddler).<<br>>It allows for quick and easy modification of this popup content (including some [[batchTagTools]] macros for example)|
|''Author:''|[[Yann Perrin|YannPerrin]]|
|''License:''|[[BSD open source license]]|
|''Browser:''|Firefox 1.0.4+; Firefox 1.5; InternetExplorer 6.0|
function onClickTag(e)
 if (!e) var e = window.event;
 var theTarget = resolveTarget(e);
 var popup = Popup.create(this);
 var tag = this.getAttribute("tag");
 var title = this.getAttribute("tiddler");
 if(popup && tag) {
 wikify('<<tiddler TagToolTemplate with:"'+tag+'">>',popup);
 e.cancelBubble = true;
 if (e.stopPropagation) e.stopPropagation();
config.macros.openAll = {};
config.macros.openAll.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
 var lingo = config.views.wikified.tag;
 var openAll = createTiddlyButton(place,lingo.openAllText.format(params),lingo.openAllTooltip,onClickTagOpenAll);
////definition de TagToolTemplate
config.shadowTiddlers.TagToolTemplate = "[[Open tag '$1'|$1]]\n<<openAll $1>>\n<<tagging $1>><<newTiddler label:\"New $1 Tiddler\" tag:\"$1\">>";
               createTiddlyButton(place,"^","jump to top",this.onclick);

config.commands.top =
               text:" ^ ",
               tooltip:"jump to top"

config.commands.top.handler = function(event,src,title)
Required by Tiddlyspot

config.options.chkHttpReadOnly = false; // make it so you can by default see edit controls via http

if (window.location.protocol != "file:")
	config.options.chkGTDLazyAutoSave = false; // disable autosave in d3

config.tiddlyspotSiteId = 'ljcrecipes';

// probably will need to redo this for TW 2.2
with (config.shadowTiddlers) {
	SiteUrl = 'http://'+config.tiddlyspotSiteId+'.tiddlyspot.com';
	SideBarOptions = SideBarOptions.replace(/(<<saveChanges>>)/,"$1<<tiddler TspotSidebar>> <<slider chkSideBarTabs SideBarTabs 'Side Bar »' 'Open Side Bar Tabs'>>");
	OptionsPanel = OptionsPanel.replace(/^/,"<<tiddler TspotOptions>>");
	DefaultTiddlers = DefaultTiddlers.replace(/^/,"[[Welcome to Tiddlyspot]] ");
	MainMenu = MainMenu.replace(/^/,"[[Welcome to Tiddlyspot]] ");


'Welcome to Tiddlyspot':[
 "This document is a ~TiddlyWiki from tiddlyspot.com.  A ~TiddlyWiki is an electronic notebook that is great for managing todo lists, personal information, and all sorts of things.",
 "@@font-weight:bold;font-size:1.3em;color:#444; //What now?// &nbsp;&nbsp;@@ Before you can save any changes, you need to enter your password in the form below.  Then configure privacy and other site settings at your [[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]] (your control panel username is //" + config.tiddlyspotSiteId + "//).",
 "<<tiddler TspotControls>>",
 "See also GettingStarted.",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Working online// &nbsp;&nbsp;@@ You can edit this ~TiddlyWiki right now, and save your changes using the \"save to web\" button in the column on the right.",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Working offline// &nbsp;&nbsp;@@ A fully functioning copy of this ~TiddlyWiki can be saved onto your hard drive or USB stick.  You can make changes and save them locally without being connected to the Internet.  When you're ready to sync up again, just click \"upload\" and your ~TiddlyWiki will be saved back to tiddlyspot.com.",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Help!// &nbsp;&nbsp;@@ Find out more about ~TiddlyWiki at [[TiddlyWiki.com|http://tiddlywiki.com]].  Also visit [[TiddlyWiki Guides|http://tiddlywikiguides.org]] for documentation on learning and using ~TiddlyWiki. New users are especially welcome on the [[TiddlyWiki mailing list|http://groups.google.com/group/TiddlyWiki]], which is an excellent place to ask questions and get help.  If you have a tiddlyspot related problem email [[tiddlyspot support|mailto:support@tiddlyspot.com]].",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Enjoy :)// &nbsp;&nbsp;@@ We hope you like using your tiddlyspot.com site.  Please email [[feedback@tiddlyspot.com|mailto:feedback@tiddlyspot.com]] with any comments or suggestions."

 "| tiddlyspot password:|<<option pasUploadPassword>>|",
 "| site management:|<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . .  " + config.tiddlyspotSiteId + ">>//(requires tiddlyspot password)//<<br>>[[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]], [[download (go offline)|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download]]|",
 "| links:|[[tiddlyspot.com|http://tiddlyspot.com/]], [[FAQs|http://faq.tiddlyspot.com/]], [[announcements|http://announce.tiddlyspot.com/]], [[blog|http://tiddlyspot.com/blog/]], email [[support|mailto:support@tiddlyspot.com]] & [[feedback|mailto:feedback@tiddlyspot.com]], [[donate|http://tiddlyspot.com/?page=donate]]|"

 "<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . .  " + config.tiddlyspotSiteId + ">><html><a href='http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download' class='button'>download</a></html>"

 "tiddlyspot password:",
 "<<option pasUploadPassword>>",

| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |
| 31/3/2007 10:23:38 | YourName | [[/|http://ljcrecipes.tiddlyspot.com/]] | [[store.cgi|http://ljcrecipes.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |
| 31/3/2007 10:28:28 | ljc | [[/|http://ljcrecipes.tiddlyspot.com/]] | [[store.cgi|http://ljcrecipes.tiddlyspot.com/store.cgi]] | . | index.html | . |
| 31/3/2007 11:14:13 | ljc | [[ljcrecipes-2.html|file:///C:/DOCUME~1/LISACO~1/LOCALS~1/Temp/ljcrecipes-2.html]] | [[store.cgi|http://ljcrecipes.tiddlyspot.com/store.cgi]] | . | index.html | . |
| 31/3/2007 17:3:29 | ljc | [[ljcrecipes.html|file:///C:/DOCUME~1/LISACO~1/LOCALS~1/Temp/ljcrecipes.html]] | [[store.cgi|http://ljcrecipes.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |
| 31/3/2007 17:30:27 | ljc | [[ljcrecipes.html|file:///C:/Documents%20and%20Settings/Lisa%20Cohen/My%20Documents/computer_related/tiddly-wiki/ljcrecipes.html]] | [[store.cgi|http://ljcrecipes.tiddlyspot.com/store.cgi]] | . | index.html | . |
| 1/4/2007 20:18:4 | ljc | [[ljcrecipes.html|file:///C:/Documents%20and%20Settings/Lisa%20Cohen/My%20Documents/computer_related/tiddly-wiki/ljcrecipes.html]] | [[store.cgi|http://ljcrecipes.tiddlyspot.com/store.cgi]] | . | index.html | . |
| 2/4/2007 11:0:26 | ljc | [[ljcrecipes.html|file:///C:/Documents%20and%20Settings/Lisa%20Cohen/My%20Documents/computer_related/tiddly-wiki/ljcrecipes.html]] | [[store.cgi|http://ljcrecipes.tiddlyspot.com/store.cgi]] | . | index.html | . |
| 2/4/2007 16:58:43 | ljc | [[ljcrecipes.html|file:///C:/Documents%20and%20Settings/Lisa%20Cohen/My%20Documents/computer_related/tiddly-wiki/ljcrecipes.html]] | [[store.cgi|http://ljcrecipes.tiddlyspot.com/store.cgi]] | . | index.html | . |
| 3/4/2007 10:9:17 | ljc | [[ljcrecipes.html|file:///C:/Documents%20and%20Settings/Lisa%20Cohen/My%20Documents/computer_related/tiddly-wiki/ljcrecipes.html]] | [[store.cgi|http://ljcrecipes.tiddlyspot.com/store.cgi]] | . | index.html | . |
| 12/4/2007 12:30:39 | ljc | [[ljcrecipes.html|file:///C:/Documents%20and%20Settings/Lisa%20Cohen/My%20Documents/computer_related/tiddly-wiki/ljcrecipes.html]] | [[store.cgi|http://ljcrecipes.tiddlyspot.com/store.cgi]] | . | index.html | . |
| 24/12/2007 18:43:11 | ljc | [[ljcrecipes.html|file:///C:/Documents%20and%20Settings/Lisa%20Cohen/My%20Documents/computer_related/tiddly-wiki/ljcrecipes.html]] | [[store.cgi|http://ljcrecipes.tiddlyspot.com/store.cgi]] | . | index.html | . |
| 24/12/2007 18:43:17 | ljc | [[ljcrecipes.html|file:///C:/Documents%20and%20Settings/Lisa%20Cohen/My%20Documents/computer_related/tiddly-wiki/ljcrecipes.html]] | [[store.cgi|http://ljcrecipes.tiddlyspot.com/store.cgi]] | . | index.html | . |
| 18/1/2008 15:24:25 | ljc | [[TW-empty-v2-3-0.html|file:///C:/Documents%20and%20Settings/Lisa%20Cohen/Desktop/TW-empty-v2-3-0.html]] | [[store.cgi|http://ljcrecipes.tiddlyspot.com/store.cgi]] | . | index.html | . |
| 18/1/2008 15:26:19 | ljcblue | [[TW-empty-v2-3-0.html|file:///C:/Documents%20and%20Settings/Lisa%20Cohen/Desktop/TW-empty-v2-3-0.html]] | [[store.cgi|http://ljcrecipes.tiddlyspot.com/store.cgi]] | . | index.html | . |
| 18/1/2008 15:31:34 | ljcblue | [[TW-empty-v2-3-0.html|file:///C:/Documents%20and%20Settings/Lisa%20Cohen/Desktop/TW-empty-v2-3-0.html]] | [[store.cgi|http://ljcrecipes.tiddlyspot.com/store.cgi]] | . | index.html | . |
| 18/1/2008 15:31:41 | ljcblue | [[TW-empty-v2-3-0.html|file:///C:/Documents%20and%20Settings/Lisa%20Cohen/Desktop/TW-empty-v2-3-0.html]] | [[store.cgi|http://ljcrecipes.tiddlyspot.com/store.cgi]] | . | index.html | . |
| 18/1/2008 15:34:40 | ljcblue | [[ljcrecipes.html|file:///C:/Documents%20and%20Settings/Lisa%20Cohen/My%20Documents/computer_related/tiddly-wiki/ljcrecipes.html]] | [[store.cgi|http://ljcrecipes.tiddlyspot.com/store.cgi]] | . | index.html | . |
| 18/1/2008 15:37:24 | ljcblue | [[ljcrecipes.html|file:///C:/Documents%20and%20Settings/Lisa%20Cohen/My%20Documents/computer_related/tiddly-wiki/ljcrecipes.html]] | [[store.cgi|http://ljcrecipes.tiddlyspot.com/store.cgi]] | . | index.html | . |
| 18/1/2008 23:38:18 | ljcblue | [[ljcrecipes.html|file:///C:/Documents%20and%20Settings/Lisa%20Cohen/My%20Documents/computer_related/tiddly-wiki/ljcrecipes.html]] | [[store.cgi|http://ljcrecipes.tiddlyspot.com/store.cgi]] | . | index.html | . |
| 26/1/2008 16:17:48 | ljcblue | [[ljcrecipes.html|file:///C:/Documents%20and%20Settings/Lisa%20Cohen/My%20Documents/computer_related/tiddly-wiki/ljcrecipes.html]] | [[store.cgi|http://ljcrecipes.tiddlyspot.com/store.cgi]] | . | index.html | . |
|''Description:''|Save to web a TiddlyWiki|
|''Date:''|Oct 15, 2006|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''Browser:''|Firefox 1.5; InternetExplorer 6.0; Safari|
|''Include:''|config.lib.file; config.lib.log; config.lib.options; PasswordTweak|
version.extensions.UploadPlugin = {
	major: 3, minor: 4, revision: 5, 
	date: new Date(2006,9,15),
	source: 'http://tiddlywiki.bidix.info/#UploadPlugin',
	documentation: 'http://tiddlywiki.bidix.info/#UploadDoc',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	license: '[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D]]',
	coreVersion: '2.0.0',
	browser: 'Firefox 1.5; InternetExplorer 6.0; Safari'


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@@";};


if (!config.lib) config.lib = {};
if (!config.lib.log) config.lib.log= {
	author: 'BidiX',
	version: {major: 0, minor: 1, revision: 1}, 
	date: new Date(2006,8,19)
config.lib.Log = function(tiddlerTitle, logHeader) {
	if (version.major < 2)
		this.tiddler = store.tiddlers[tiddlerTitle];
		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;
	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;

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.notify(this.tiddler.title, true);
	if (version.major < 2)


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;


version.extensions.PasswordTweak = {
	major: 1, minor: 0, revision: 3, date: new Date(2006,8,30),
	type: 'tweak',
	source: 'http://tiddlywiki.bidix.info/#PasswordTweak'
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";
			case "pas":
				elementType = "input";
				valueField = "value";
			case "chk":
				elementType = "input";
				valueField = "checked";
		config.options[opt] = this[valueField];
		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];

config.macros.option.handler = function(place,macroName,params)
    var opt = params[0];
    if(config.options[opt] === undefined) {
    var c;
    switch(opt.substr(0,3)) {
		case "txt":
			c = document.createElement("input");
			c.onkeyup = this.onChangeOption;
			c.setAttribute ("option",opt);
			c.className = "txtOptionInput "+opt;
			c.value = config.options[opt];
		case "pas":
			// input password
			c = document.createElement ("input");
			c.onkeyup = this.onChangeOption;
			c.className = "pasOptionInput "+opt;
			c.value = config.options[opt];
			// checkbox link with this password "save this password on this computer"
			c = document.createElement("input");
			c.onclick = this.onChangeOption;
			c.className = "chkOptionInput "+opt;
			c.checked = config.options["chk"+opt];
			// text savePasswordCheckboxLabel
		case "chk":
			c = document.createElement("input");
			c.onclick = this.onChangeOption;
			c.className = "chkOptionInput "+opt;
			c.checked = config.options[opt];
!! 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);
				case "pas":
					config.options[name] = unescape(value);
				case "chk":
					config.options[name] = value == "true";

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());
		case "chk":
			c += config.options[name] ? "true" : "false";
			// is there an option link with this chk ?
			if (config.options[name.substr(3)]) {
		case "pas":
			if (config.options["chk"+name]) {
				c += escape(config.options[name].toString());
			} else {
				c += "";
	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';
// since loadCookies is first called befor password definition
// we need to reload cookies


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",
	backupFileStored: "Previous file backuped in %0",
	crossDomain: "Certainly a cross-domain isue: access to an other site isn't allowed",
	errorDownloading: "Error downloading",
	errorUploadingContent: "Error uploading content",
	fileLocked: "Files is locked: You are not allowed to Upload",
	fileNotFound: "file to upload not found",
	fileNotUploaded: "File %0 NOT uploaded",
	mainFileUploaded: "Main TiddlyWiki file uploaded to %0",
	passwordEmpty: "Unable to upload, your password is empty",
	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;
		label = this.label.uploadLabel;
	var prompt;
	if (storeUrl) {
		prompt = this.label.promptParamMacro.toString().format([this.toDirUrl(storeUrl, uploadDir, username)]);
	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 + " |";
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.toRootUrl = function (storeUrl, username)
	return root = (this.dirname(storeUrl)?this.dirname(storeUrl):this.dirname(document.location.toString()));
config.macros.upload.toDirUrl = function (storeUrl,  uploadDir, username)
	var root = this.toRootUrl(storeUrl, username);
	if (uploadDir && uploadDir != '.')
		root = root + '/' + uploadDir;
	return root;
config.macros.upload.toFileUrl = function (storeUrl, toFilename,  uploadDir, username)
	return this.toDirUrl(storeUrl, uploadDir, username) + '/' + toFilename;
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 (!password || password === '') {
	if (storeUrl === '') {
		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());

	// only for forcing the message to display
	 if (version.major < 2)
	if (!storeUrl) {
	// Check that file is not locked
	if (window.BidiX && BidiX.GroupAuthoring && BidiX.GroupAuthoring.lock) {
		if (BidiX.GroupAuthoring.lock.isLocked() && !BidiX.GroupAuthoring.lock.isMyLock()) {
	var log = new this.UploadLog();
	log.startUpload(storeUrl, toFilename, uploadDir,  backupDir);
	if (document.location.toString().substr(0,5) == "file:") {
	var toDir = config.macros.upload.toDirUrl(storeUrl, toFilename, uploadDir, username);
	displayMessage(config.macros.upload.messages.aboutToUpload.format([toDir]), toDir);
	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') {
				else {
					var toFileUrl = config.macros.upload.toFileUrl(storeUrl, rssPath, uploadDir, username);
						[toFileUrl]), toFileUrl);
				// for debugging store.php uncomment last line
				//DEBUG alert(responseText);

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);
	else {
		// standard way : Local file
		original = loadFile(getLocalPath(document.location.toString()));
		if(window.Components) {
			// it's a mozilla browser
			try {
				var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
				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))
	var revised = original.substr(0,posOpeningDiv + startSaveArea.length) + 
				allTiddlersAsHtml() + "\n\t\t" +
	var newSiteTitle;
	if(version.major < 2){
		newSiteTitle = (getElementText("siteTitle") + " - " + getElementText("siteSubtitle")).htmlEncode();
	} else {
		newSiteTitle = (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 response = this.uploadContent(revised, storeUrl, toFilename, uploadDir, backupDir, 
		username, password, function (responseText) {
					if (responseText.substring(0,1) != '0') {
					else {
						if (uploadDir !== '') {
							toFilename = uploadDir + "/" + config.macros.upload.basename(toFilename);
						} else {
							toFilename = config.macros.upload.basename(toFilename);
						var toFileUrl = config.macros.upload.toFileUrl(storeUrl, toFilename, uploadDir, username);
						if (responseText.indexOf("destfile:") > 0) {
							var destfile = responseText.substring(responseText.indexOf("destfile:")+9, 
							responseText.indexOf("\n", responseText.indexOf("destfile:")));
							toFileUrl = config.macros.upload.toRootUrl(storeUrl, username) + '/' + destfile;
						else {
							toFileUrl = config.macros.upload.toFileUrl(storeUrl, toFilename, uploadDir, username);
							[toFileUrl]), toFileUrl);
						if (backupDir && responseText.indexOf("backupfile:") > 0) {
							var backupFile = responseText.substring(responseText.indexOf("backupfile:")+11, 
							responseText.indexOf("\n", responseText.indexOf("backupfile:")));
							toBackupUrl = config.macros.upload.toRootUrl(storeUrl, username) + '/' + backupFile;
								[toBackupUrl]), toBackupUrl);
						var log = new config.macros.upload.UploadLog();
						// erase local lock
						if (window.BidiX && BidiX.GroupAuthoring && BidiX.GroupAuthoring.lock) {
							// change mtime with new mtime after upload
							var mtime = responseText.substr(responseText.indexOf("mtime:")+6);
							BidiX.GroupAuthoring.lock.mtime = mtime;
					// 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") {
			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;
	// add lock attributes to sheader
	if (window.BidiX && BidiX.GroupAuthoring && BidiX.GroupAuthoring.lock) {
		var l = BidiX.GroupAuthoring.lock.myLock;
		sheader += ";lockuser=" + l.user
				+ ";mtime=" + l.mtime
				+ ";locktime=" + l.locktime;
	sheader += ";;\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";
	//strailer = "--" + boundary + "--\r\n";
	var data;
	data = sheader + content + strailer;
	//request.open("POST", storeUrl, true, username, password);
	try {
		request.open("POST", storeUrl, true);		
	catch(e) {
		alert(config.macros.upload.messages.crossDomain + "\nError:" +e);
	request.onreadystatechange = function () {
				if (request.readyState == 4) {
				     if (request.status == 200)
						alert(config.macros.upload.messages.errorUploadingContent + "\nStatus: "+request.status.statusText);
	request.setRequestHeader("Content-Type","multipart/form-data; boundary="+boundary);

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") {
		else {
	} catch (e) { }
	//request.open("GET", document.location.toString(), true, username, password);
	try {
		request.open("GET", document.location.toString(), true);
	catch(e) {
		alert(config.macros.upload.messages.crossDomain + "\nError:" +e);
	request.onreadystatechange = function () {
		if (request.readyState == 4) {
			if(request.status == 200) {
				config.macros.upload.uploadChangesFrom(request.responseText, uploadUrl, 
					uploadToFilename, uploadDir, uploadBackupDir, username, password);
					[document.location.toString()]) + "\nStatus: "+request.status.statusText);



	".pasOptionInput {width: 11em;}\n"+
	".txtOptionInput.txtUploadStoreUrl {width: 25em;}\n"+
	".txtOptionInput.txtUploadFilename {width: 25em;}\n"+
	".txtOptionInput.txtUploadDir {width: 25em;}\n"+
	".txtOptionInput.txtUploadBackupDir {width: 25em;}\n"+
if (document.location.toString().substr(0,4) == "http") {
	config.options.chkAutoSave = false; 
config.shadowTiddlers.UploadDoc = "[[Full Documentation|http://tiddlywiki.bidix.info/l#UploadDoc ]]\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;

config.macros.saveChanges.handler = function(place)
	if ((!readOnly) && (document.location.toString().substr(0,4) != "http"))


Serves 4

4 large carrots, thickly sliced
8 small red potatoes, halved
1 lb ready-peeled and seeded butternut squash, cut into 2 inch pieces
1/2 lb white mushrooms or shiitakes, stemmed and halved or quartered
1 bunch radishes, root ends trimmed
1/2 lb green beans, stem ends trimmed
1 large sweet onion, cut into 8 wedges
salt, pepper to taste
1/2 cup chicken stock or water
2 TBS chopped fresh parsley

Set oven at 400 degrees

Lightly oil a heavy-based casserole (2 1/4 qt capacity)

Layer the vegetables in the pan, (firm to light) carrots, potatoes, squash, radishes, greenbeans, onion, and mushroom, sprinkling each layer with oil, salt, and pepper.  

Pour the stock or water into the pan at the sides.

Cover pan and roast the vegetables for 1 1/2 hours, basting them several times with the juices in the pan, or until all vegetables are tender.

From The Boston Globe Magazine, March 12, 2006, p.51

Sprinkle with parsley and serve.+++!!!!![comment: Monday, April 02, 2007 4:47:55 PM]>
Pretty much any veggies can be used, just layer them with the firmest, most slowly cooking ones on the bottom.===
<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler 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 [[DD MMM YYYY]]'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date [[DD MMM YYYY]]'></span>)</div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class="viewer" macro="tiddler CommentScript"></div> 
<div class='tagClear'></div>
|''Version:''|2.1.0 (2006-10-12)|
|''Source:''|http://tiddlywiki.abego-software.de/#YourSearchPlugin ([[del.icio.us|http://del.icio.us/post?url=http://tiddlywiki.abego-software.de/index.html%23YourSearchPlugin]])|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]|
|''Copyright:''|&copy; 2005-2006 [[abego Software|http://www.abego-software.de]]|
|''Browser:''|Firefox 1.0.4+; Firefox 1.5; ~InternetExplorer 6.0|
!About YourSearch
YourSearch gives you a bunch of new features to simplify and speed up your daily searches in TiddlyWiki. It seamlessly integrates into the standard TiddlyWiki search: just start typing into the 'search' field and explore!

For more information see [[Help|YourSearch Help]].
This plugin requires TiddlyWiki 2.1. 
Check the [[archive|http://tiddlywiki.abego-software.de/archive]] for ~YourSearchPlugins supporting older versions of TiddlyWiki.
!Revision history
* v2.1.0 (2006-10-12)
** Release version with TiddlyWiki 2.1 support
*** Support (Extended) Field search
*** Support parenthesis in Boolean Search
*** Support direct regular expression input
*** Support JavaScript Expressions for filtering
*** "new tiddler" feature (create tiddler based on search text)
* v2.0.2 (2006-02-13)
** Bugfix for Firefox related to the "Show prefix" checkbox. Thanks to Ted Pavlic for reporting and to BramChen for fixing. 
** Internal
*** Make "JSLint" conform
* v2.0.1 (2006-02-05)
** Support "Exact Word Match" (use '=' to prefix word)
** Support default filter settings (when no filter flags are given in search term)
** Rework on the "less than 3 chars search text" feature (thanks to EricShulman)
** Better support SinglePageMode when doing "Open all tiddlers" (thanks to EricShulman)
** Support Firefox
** Bug: Fixed a hilite bug in "classic search mode" (thanks to EricShulman)
* v2.0.0 (2006-01-16)
** Add User Interface
* v1.0.1 (2006-01-06)
** Support TiddlyWiki 2.0
* v1.0.0 (2005-12-28)
** initial version
!Source Code
//                           YourSearchPlugin

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

version.extensions.YourSearchPlugin = {
	major: 2, minor: 1, revision: 0,
	source: "http://tiddlywiki.abego-software.de/#YourSearchPlugin",
	licence: "[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]",
	copyright: "Copyright (c) abego Software GmbH, 2005-2006 (www.abego-software.de)"

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

// define the Array forEach when not yet defined (e.g. by Mozilla)
if (!Array.forEach) {
    Array.forEach = function(obj, callback, thisObj) {
        for (var i = 0,len = obj.length; i < len; i++)
            callback.call(thisObj, obj[i], i, obj);
    Array.prototype.forEach = function(callback, thisObj) {
        for (var i = 0,len = this.length; i < len; i++)
            callback.call(thisObj,  this[i], i, this);

abego.toInt = function(s, defaultValue) {
	if (!s) return defaultValue;
	var n = parseInt(s);
	return (n == NaN) ? defaultValue : n;

abego.createEllipsis = function(place) {
	var e = createTiddlyElement(place,"span");
	e.innerHTML = "&hellip;";

//#concept Object
abego.shallowCopy = function(object) {
	if (!object)
		return object;
	var result = {};
	for (var n in object) 
		result[n] = object[n];
	return result;

// Returns a shallow copy of the options, or a new, empty object if options is null/undefined.
// @param options [may be null/undefined]
//#concept Object, Options
//#import abego.shallowCopy
abego.copyOptions = function(options) {
	return !options ? {} : abego.shallowCopy(options);

//#import abego.define-namespace
// returns the number of occurances of s in the text
abego.countStrings = function(text, s) {
	if (!s)
		return 0;
	var len = s.length;
	var n = 0;
	var lastIndex = 0;
	while (1) {
		var i = text.indexOf(s, lastIndex);
		if (i < 0)
			return n;
		lastIndex = i+len;
	return n;
};// Returns the content of the first "braced" text {...}
// Also takes care of nested braces
// Returns undefined when no braced text is found or it is not properly nested
// @param [optional] when defined and a braced text is found lastIndexRef.lastIndex will contain the index of the char following the (final) closing brace on return.
abego.getBracedText = function(text, offset,lastIndexRef) {
	if (!offset) offset = 0;
	var re = /\{([^\}]*)\}/gm;
	re.lastIndex = offset;
	var m = re.exec(text);
	if (m) {
		// The matching stopped at the first closing brace.
		// But if the matched text contains opening braces 
		// this is not the final closing brace.
		// Handle this case specially, find the "corresponding" closing brace
		var s = m[1];
		var nExtraOpenBrace = abego.countStrings(s,"{");
		if (!nExtraOpenBrace) {
			if (lastIndexRef)
				lastIndexRef.lastIndex = re.lastIndex;
			// simple case: no nested braces
			return s;

		// special case: "nested braces"
		var len = text.length;
		for (var i = re.lastIndex; i < len && nExtraOpenBrace; i++) {
			var c = text.charAt(i);
			if (c == "{") 
			else if (c == "}")
		if (!nExtraOpenBrace) {
			// found the corresponding "}".
			if (lastIndexRef)
				lastIndexRef.lastIndex = i-1;
			return text.substring(m.index+1, i-1);
	// no return means: return undefined;

// Returns an array with those items from the array that pass the given test
// @param test an one-arg boolean function that returns true when the item should be added.
// @param testObj [optional] the receiver for the test function (global if undefined or null)
// @param result [optional] an array. When define the selected items are added to this array, otherwise a new array is used.
//#import Array.prototype.forEach
abego.select = function(array,test,testObj,result) {
	if (!result) result = [];
	array.forEach(function(t) {
		if (test.call(testObj,t)) 
	return result;

// Class abego.TiddlerFilterTerm =================================================================
// Used to check if a tiddler contains a given text.
// A list of fields (standard and/or extended) may be specified to restrict the search to certain fields. 
// When no explicit fields are given the fields defined by defaultFields are checked, plus all extended 
// fields (when options.withExtendedFields is true).
// @param options [may be null/undefined]
//		options.fields @seeParam abego.MultiFieldRegExpTester.fields
// 		options.withExtendedFields @seeParam abego.MultiFieldRegExpTester.withExtendedFields  
// 		options.caseSensitive [Default: false]
// 		options.fullWordMatch [Default: false]
// 		options.textIsRegExp [Default: false] when true the given text is already a regExp
//#import abego.MultiFieldRegExpTester
abego.TiddlerFilterTerm = function(text,options) {
	if (!options) options = {};

	var reText = text;
	if (!options.textIsRegExp) {
		reText = text.escapeRegExp();
		if (options.fullWordMatch) 
			reText = "\\b"+reText+"\\b";
	var regExp = new RegExp(reText, "m"+(options.caseSensitive ? "" : "i"));

	this.tester = new abego.MultiFieldRegExpTester(regExp, options.fields, options.withExtendedFields);

abego.TiddlerFilterTerm.prototype.test = function(tiddler) {
	return this.tester.test(tiddler);

//#import abego.define-namespace
// Recognize a string like
//     "Some Title. Some content text #Tag1 #Tag2 Tag3"
// with the tags and the text being optional.
// Also the period at the end of the title is optional when no content text is specified)
// Returns the result in an object with properties "title" and "params",
// with "params" following the parseParams format, containing the "tag" and "text" arguments.
abego.parseNewTiddlerCommandLine = function(s) {
	var m = /(.*?)\.(?:\s+|$)([^#]*)(#.*)?/.exec(s);
	if (!m) 
		m = /([^#]*)()(#.*)?/.exec(s);
	if (m) {
		var r;
		if (m[3]) {
			var s2 = m[3].replace(/#/g,"");
			r = s2.parseParams("tag");
		} else
			r = [[]];
		// add the text parameter
		var text = m[2]?m[2].trim():"";
		r.push({name: "text", value: text});
		r[0].text = [text];
		return {title: m[1].trim(), params: r}; 
	} else
		return {title: s.trim(),params: [[]]};
// 		options.defaultFields [@seeOptionDefault abego.TiddlerFilterTerm.fields] fields to check when no fields are explicitly specified in queryText.
// 		options.withExtendedFields [@seeOptionDefault abego.TiddlerFilterTerm.withExtendedFields] when true and no fields are explicitly specified in queryText also the extended fields are considered (in addition to the ones in defaultFields).
// @seeOptions abego.TiddlerFilterTerm (-fields -fullWordMatch -withExtendedFields)
//#import abego.getBracedText
//#import abego.copyOptions
//#import abego.TiddlerFilterTerm
abego.parseTiddlerFilterTerm = function(queryText,offset,options) {
	// group 1: {...} 		(JavaScript expression)
	// group 2: '=' 		(full word match (optional))
	// group 3: [!%#] 		(field selection short cuts)
	// group 4: fieldName ':'
	// group 5: String literal "..."
	// group 6: RegExp literal /.../
	// group 7: scheme '://' nonSpaceChars
	// group 8: word
	var re = /\s*(?:(?:\{([^\}]*)\})|(?:(=)|([#%!])|(?:(\w+)\s*\:(?!\/\/))|(?:(?:("(?:(?:\\")|[^"])+")|(?:\/((?:(?:\\\/)|[^\/])+)\/)|(\w+\:\/\/[^\s]+)|([^\s\)\-\"]+)))))/mg;
	var shortCuts = {'!':'title','%':'text','#':'tags'};
	var fieldNames = {};
	var fullWordMatch;
	re.lastIndex = offset;
	while (1) {
		var i = re.lastIndex;
		var m = re.exec(queryText);
		if (!m || m.index != i) 
			throw "Word or String literal expected";
		if (m[1]) {
			var lastIndexRef = {};
			var code = abego.getBracedText(queryText,0,lastIndexRef);
			if (!code)
				throw "Invalid {...} syntax";
			var f = Function("tiddler","return ("+code+");");
			return {func: f,
					markRE: null};
		if (m[2])
			fullWordMatch = true;
		else if (m[3]) 
			fieldNames[shortCuts[m[3]]] = 1;
		else if (m[4]) 
			fieldNames[m[4]] = 1;
		else {
			var textIsRegExp = m[6];
			var text = m[5] ? window.eval(m[5]) : m[6] ? m[6] :  m[7] ? m[7] : m[8];
			var options = abego.copyOptions(options);
			options.fullWordMatch = fullWordMatch;
			options.textIsRegExp = textIsRegExp;

			var fields = [];
			for (var n in fieldNames)
			if (fields.length == 0) {
				options.fields = options.defaultFields;
			} else {
				options.fields = fields;
				options.withExtendedFields	= false;
			var term = new abego.TiddlerFilterTerm(text,options);
			var markREText = textIsRegExp ? text : text.escapeRegExp();
			if (markREText && fullWordMatch)
				markREText = "\\b"+markREText+"\\b";
			return {func: function(tiddler) {return term.test(tiddler);},
					markRE: markREText ? "(?:"+markREText+")" : null};

// Class abego.BoolExp =================================================================
// Allows the execution/evaluation of a boolean expression, according to this syntax:
// boolExpression    : unaryExpression (("AND"|"OR"|"&&"|"||")? unaryExpression)*
//                   ;
// unaryExpression   : ("not"|"-")? primaryExpression
//                   ;
// primaryExpression : "(" boolExpression ")" 
//                   | Term
//                   ;
// For flexibility the Term syntax is defined by a separate parse function.
// Notice that there is no precedence between "AND" and "OR" operators, i.e. they are evaluated from left to right.
// To evaluate the expression in a given context use code like this:
//	var be = new abego.BoolExp(s, termParseFunc);
//  var result = be.exec(context);
// @param s the text defining the expression 
// @param parseTermFunc a Function(text,offset,options) that parses the text starting at offset for a "Term" and returns an object with properties {func: Function(context), lastIndex: ...}. func is the function to be used to evaluate the term in the given context.
// @param options [may be null/undefined] (is also passed to the parseTermFunc)
// 			options.defaultOperationIs_OR [Default: false] When true the concatenation of unaryExpressions (without an operator) is interpreted as an "OR", otherwise as an "AND".
// 			options.caseSensitive [default: false]
abego.BoolExp = function(s, parseTermFunc, options) {
	this.s = s;
	var defaultOperationIs_OR = options && options.defaultOperationIs_OR;
	var reStart = /\s*(?:(\-|not)|(\())/gi; 		// group 1: NOT, group2 "("
	var reCloseParenthesis = /\s*\)/g;  			// match )
	var reAndOr = /\s*(?:(and|\&\&)|(or|\|\|))/gi; 	// group 1: AND, group 2: OR
	var reNonWhiteSpace = /\s*[^\)\s]/g;
	var reNot_Parenthesis = /\s*(\-|not)?(\s*\()?/gi;
	var parseUnaryExpression = function(offset) {
		reNot_Parenthesis.lastIndex = offset;
		var m = reNot_Parenthesis.exec(s);
		var negate;
		var result;
		if (m && m.index == offset) {
			offset = reNot_Parenthesis.lastIndex;
			negate = m[1];
			if (m[2]) {
				// case:  (...)
				var e = parseBoolExpression(offset);
				reCloseParenthesis.lastIndex = e.lastIndex;
				if (!reCloseParenthesis.exec(s))
					throw "Missing ')'";
				result = {func: e.func, lastIndex: reCloseParenthesis.lastIndex};
		if (!result)
			result = parseTermFunc(s,offset,options);

		if (negate) {
			result.func = (function(f){return function(context) {return !f(context);}})(result.func);
			// don't mark patterns that are negated
			// (This is essential since the marking may also be used to calculate "ranks". If we
			// would also count the negated matches (i.e. that should not exist) the rank may get too high)
			result.markRE = null;
		return result;

	var parseBoolExpression = function(offset) {
		var result = parseUnaryExpression(offset);
		while (1) {
			var l = result.lastIndex;
			reAndOr.lastIndex = l;
			var m = reAndOr.exec(s);
			var isOrCase;
			var nextExp;
			if (m && m.index == l) {
				isOrCase = !m[1];
				nextExp = parseUnaryExpression(reAndOr.lastIndex);
			} else {
				// no "AND" or "OR" found. 
				// Maybe it is a concatenations of parseUnaryExpression without operators
				try {
					nextExp = parseUnaryExpression(l);
				} catch (e) {
					// no unary expression follows. We are done
					return result;
				isOrCase = defaultOperationIs_OR;
			result.func = (function(func1, func2, isOrCase) {
					return isOrCase
						? function(context) {return func1(context) || func2(context);}
						: function(context) {return func1(context) && func2(context);};
			result.lastIndex = nextExp.lastIndex;
			if (!result.markRE)
				result.markRE = nextExp.markRE;
			else if (nextExp.markRE) 
				result.markRE = result.markRE + "|" + nextExp.markRE;
	var expr = parseBoolExpression(0);
	this.evalFunc = expr.func;
	if (expr.markRE)
		this.markRegExp = new RegExp(expr.markRE, options.caseSensitive ? "mg" : "img");

abego.BoolExp.prototype.exec = function() {
	return this.evalFunc.apply(this,arguments);

abego.BoolExp.prototype.getMarkRegExp = function() {
	return this.markRegExp;

abego.BoolExp.prototype.toString = function() {
	return this.s;

// Class abego.MultiFieldRegExpTester ==================================================================
// @param fields [optional; Default: ["title","text","tags"]] array of names of fields to be considered
// @param withExtendedFields [optional; Default: false] when true also extended fields are considered (in addition to the ones given in 'fields')
abego.MultiFieldRegExpTester = function(re, fields, withExtendedFields) {
	this.re = re;
	this.fields = fields ? fields : ["title","text","tags"];
	this.withExtendedFields = withExtendedFields;

// Returns the name of the first field found that value succeeds the given test,
// or null when no such field is found
abego.MultiFieldRegExpTester.prototype.test = function(tiddler) {
	var re = this.re;
	// Check the fields explicitly specified
	for (var i = 0; i < this.fields.length; i++) {
		var s = store.getValue(tiddler, this.fields[i]);
		if (typeof s == "string" && re.test(s))
			return this.fields[i];		
	// Check the extended fields (if required)
	if (this.withExtendedFields) 
		return store.forEachField(
				function(tiddler, fieldName, value) {
					return typeof value == "string" && re.test(value)?fieldName:null;
				}, true);
	return null;

// Class abego.TiddlerQuery ==================================================================
//#import abego.select
//#import abego.MultiFieldRegExpTester
abego.TiddlerQuery = function(queryText,caseSensitive,useRegExp,defaultFields,withExtendedFields) {
	if (useRegExp) {
		this.regExp = new RegExp(queryText, caseSensitive ? "mg" : "img");
		this.tester = new abego.MultiFieldRegExpTester(this.regExp, defaultFields, withExtendedFields);
	} else {
		this.expr = new abego.BoolExp(
				abego.parseTiddlerFilterTerm, {
				defaultFields: defaultFields,
				caseSensitive: caseSensitive,
				withExtendedFields: withExtendedFields});
	this.getQueryText = function() {
		return queryText;
	this.getUseRegExp = function() {
		return useRegExp;
	this.getCaseSensitive = function() {
		return caseSensitive;
	this.getDefaultFields = function() {
		return defaultFields;
	this.getWithExtendedFields = function() {
		return withExtendedFields;

// Returns true iff the query includes the given tiddler
// @param tiddler [may be null/undefined]
abego.TiddlerQuery.prototype.test = function(tiddler) {
	if (!tiddler) return false;
	if (this.regExp) {
		return this.tester.test(tiddler);
	return this.expr.exec(tiddler);

// Returns an array with those tiddlers from the tiddlers array that match the query.
abego.TiddlerQuery.prototype.filter = function(tiddlers) {
	return abego.select(tiddlers,this.test,this);

abego.TiddlerQuery.prototype.getMarkRegExp = function() {
	if (this.regExp) {
		// Only use the regExp for marking when it does not match the empty string.
		return "".search(this.regExp) >= 0 ? null :  this.regExp;
	return this.expr.getMarkRegExp();

abego.TiddlerQuery.prototype.toString = function() {
	return (this.regExp ? this.regExp : this.expr).toString();

// Class abego.PageWiseRenderer ================================================
// Subclass or instance must implement getItemsPerPage function;
// They should also implement onPageChanged and refresh the container of the
// PageWiseRenderer on that event.
//#import abego.toInt
abego.PageWiseRenderer = function() {
	this.firstIndexOnPage = 0; // The index of the first item of the lastResults list displayed on the search result page

merge(abego.PageWiseRenderer.prototype, {
	setItems: function(items) {
		this.items = items;
	// Maximum number of pages listed in the navigation bar (before or after the current page)
	getMaxPagesInNavigation: function() {
		return 10;
	getItemsCount: function(items) {
		return this.items ? this.items.length : 0;
	getCurrentPageIndex: function() {
		return Math.floor(this.firstIndexOnPage / this.getItemsPerPage());
	getLastPageIndex: function() {
		return Math.floor((this.getItemsCount()-1) / this.getItemsPerPage())
	setFirstIndexOnPage: function(index) {
		this.firstIndexOnPage = Math.min(Math.max(0, index), this.getItemsCount()-1);
	getFirstIndexOnPage: function() {
		// Ensure that the firstIndexOnPage is really a page start. 
		// This may have become violated when getItemsPerPage has changed,
		// (e.g. when switching between previewText and simple mode.)
		this.firstIndexOnPage = Math.floor(this.firstIndexOnPage / this.getItemsPerPage()) * this.getItemsPerPage();
		return this.firstIndexOnPage;
	getLastIndexOnPage: function() {
		return Math.min(this.getFirstIndexOnPage()+this.getItemsPerPage()-1, this.getItemsCount()-1);
	onPageChanged: function(pageIndex,oldPageIndex) {
	renderPage: function(itemRenderer) {
		if (itemRenderer.beginRendering)
		try {
			// When there are items found add them to the result page (pagewise)
			if (this.getItemsCount()) {
				// Add the items of the current page
				var lastIndex = this.getLastIndexOnPage();
				var iInPage = -1;
				for (var i=this.getFirstIndexOnPage(); i <= lastIndex; i++) {
		} finally {
			if (itemRenderer.endRendering)
	addPageNavigation: function(place) {
		if (!this.getItemsCount()) return;
		var self = this;
		var onNaviButtonClick = function(e) {
			if (!e) var e = window.event;
			var pageIndex = abego.toInt(this.getAttribute("page"),0);
			var oldPageIndex = self.getCurrentPageIndex();
			if (pageIndex == oldPageIndex)
			var index = pageIndex * self.getItemsPerPage();
		var button;
		var currentPageIndex = this.getCurrentPageIndex();
		var lastPageIndex = this.getLastPageIndex();
		if (currentPageIndex > 0) {
			button = createTiddlyButton(place, "Previous", "Go to previous page (Shortcut: Alt-'<')", onNaviButtonClick, "prev");
		for (var i = -this.getMaxPagesInNavigation(); i < this.getMaxPagesInNavigation(); i++) {
			var pageIndex = currentPageIndex+i;
			if (pageIndex < 0) continue;
			if (pageIndex > lastPageIndex) break;
			var pageNo = (i+currentPageIndex+1).toString();
			var buttonClass = pageIndex == currentPageIndex ? "currentPage" : "otherPage";
			button = createTiddlyButton(place, pageNo, "Go to page %0".format([pageNo]), onNaviButtonClick, buttonClass);
		if (currentPageIndex < lastPageIndex) {
			button = createTiddlyButton(place, "Next", "Go to next page (Shortcut: Alt-'>')", onNaviButtonClick, "next");

// Class abego.LimitedTextRenderer ===========================================================
// Renders a given text, ensuring that a given limit of number of characters 
// is not exceeded.
// A "markRegExp" may be specified. Substring matching this regular expression 
// ("matched strings") are rendered with the class "marked". 
// if the given text is longer than the limit the matched strings are preferred 
// to be included in the rendered text (with some leading and trailing "context text"). 
// Example:
//     var renderer = new abego.LimitedTextRenderer();
//     var place = ... // a DOM element that should contain the rendered (limited) text
//     var s = "This is another 'Hello World' example, as saying 'Hello' is always nice. So let's say it again: >Hello!<";
//     var maxLen = 50;
//     var markRE = /hello/gi;
//     renderer.render(place,s,maxLen,markRE);
//#import abego.createEllipsis
abego.LimitedTextRenderer = function() {
	var minMatchWithContextSize = 40; 
	var maxMovementForWordCorrection = 4; // When a "match" context starts or end on a word the context borders may be changed to at most this amount to include or exclude the word.
	// Ranges
	// Objects with a "start" and "end" property (not a specific class). 
	// In a corresponding "Ranges array" these objects are sorted by their start 
	// and no Range object intersects/touches any other in the array.
	// Adds the Range [startIndex,endIndex[ to the ranges, ensuring that the Ranges
	// in the array are sorted by their start and no Range object 
	// intersects/touches any other in the array (i.e. possibly the new Range is 
	// "merged" with existing ranges)
	// @param ranges array of Range objects
	var addRange = function(ranges, startIndex, endIndex) {
		var n = ranges.length;
		// When there are no ranges in ranges, just add it.
		if (n == 0) {
			ranges.push({start: startIndex, end: endIndex});
		var i = 0;
		for (; i < n; i++) {
			var range = ranges[i];
			// find the first range that intersects or "touches" [startIndex, endIndex[
			if (range.start <= endIndex && startIndex <= range.end) {
				// Found.
				var r;
				// find the first range behind the new range that does not interfere
				var rIndex = i+1;
				for (; rIndex < n; rIndex++) {
					r = ranges[rIndex];
					if (r.start > endIndex || startIndex > range.end) {
				// Replace the ranges i to rIndex-1 with the union of the new range with these ranges.
				var unionStart = startIndex;
				var unionEnd = endIndex;
				for (var j = i; j < rIndex; j++) {
					r = ranges[j];
					unionStart = Math.min(unionStart, r.start);
					unionEnd = Math.max(unionEnd, r.end);
				ranges.splice(i, rIndex-i, {start: unionStart, end: unionEnd});
			// if we found a range R that is right of the new range there is no
			// intersection and we can insert the new range before R.
			if (range.start > endIndex) {
		// When we are here the new range does not interfere with any range in ranges and
		// i is the index of the first range right to it (or ranges.length, when the new range
		// becomes the right most range). 
		ranges.splice(i, 0, {start: startIndex, end: endIndex});
	// Returns the total size of all Ranges in ranges
	var getTotalRangesSize = function(ranges) {
		var totalRangeSize = 0;
		for (var i=0; i < ranges.length; i++) {
			var range = ranges[i];
			totalRangeSize += range.end-range.start;
		return totalRangeSize;
	var isWordChar = function(c) {
		return (c >= "a" && c <= "z") || (c >= "A" && c <= "Z") || c == "_";
	// Returns the bounds of the word in s around offset as a {start: , end:} object.
	// Returns null when the char at offset is not a word char.
	var getWordBounds = function(s, offset) {
		// Handle the "offset is not in word" case
		if (!isWordChar(s[offset])) return null;
		for (var i = offset-1; i >= 0 && isWordChar(s[i]); i--) 
		var startIndex = i+1;
		var n = s.length;
		for (i = offset+1; i < n && isWordChar(s[i]); i++) 
		return {start: startIndex, end: i};
	var moveToWordBorder = function(s, offset, isStartOffset) {
		var wordBounds;
		if (isStartOffset) {
			wordBounds = getWordBounds(s, offset);
		} else {
			if (offset <= 0) return offset;
			wordBounds = getWordBounds(s, offset-1);
		if (!wordBounds) return offset;
		if (isStartOffset) {
			if (wordBounds.start >= offset-maxMovementForWordCorrection) return wordBounds.start;
			if (wordBounds.end <= offset+maxMovementForWordCorrection) return wordBounds.end;
		} else {
			if (wordBounds.end <= offset+maxMovementForWordCorrection) return wordBounds.end;
			if (wordBounds.start >= offset-maxMovementForWordCorrection) return wordBounds.start;
		return offset;
	// Splits s into a sequence of "matched" and "unmatched" substrings, using the 
	// matchRegExp to do the matching.
	// Returns an array of objects with a "text" property containing the substring text. 
	// Substrings that are "matches" also contain a boolean "isMatch" property set to true.
	// @param matchRegExp [may be null] when null no matching is performed and the returned 
	// 			array just contains one item with s as its text
	var getTextAndMatchArray = function(s, matchRegExp) {
		var result = [];
		if (matchRegExp) {
			var startIndex = 0;
			var n = s.length;
			var currentLen = 0;
			do {
				matchRegExp.lastIndex = startIndex;
				var match = matchRegExp.exec(s);
				if (match) {
					if (startIndex < match.index) {
						var t = s.substring(startIndex, match.index);
					result.push({text:match[0], isMatch:true});
					startIndex = match.index + match[0].length;
				} else {
					result.push({text: s.substr(startIndex)});
			} while (true);
		} else {
			result.push({text: s});
		return result;
	var getMatchedTextCount = function(textAndMatches) {
		var result = 0;
		for (var i=0; i < textAndMatches.length; i++) {
			if (textAndMatches[i].isMatch) {
		return result;	
	var getContextRangeAround = function(s, startIndex, endIndex, matchCount, maxLen) {
		// Partition the available space into equal sized areas for each match and one 
		// for the text start.
		// But the size should not go below a certain limit
		var size = Math.max(Math.floor(maxLen/(matchCount+1)), minMatchWithContextSize);
		// Substract the size of the range to get the size of the context.
		var contextSize = Math.max(size-(endIndex-startIndex), 0);
		// Two thirds of the context should be before the match, one third after.
		var contextEnd = Math.min(Math.floor(endIndex+contextSize/3), s.length);
		var contextStart = Math.max(contextEnd - size, 0);
		// If the contextStart/End is inside a word and the end of the word is
		// close move the pointers accordingly to make the text more readable.
		contextStart = moveToWordBorder(s, contextStart, true);
		contextEnd = moveToWordBorder(s, contextEnd, false);
		return {start: contextStart, end: contextEnd};
	// Get all ranges around matched substrings with their contexts
	var getMatchedTextWithContextRanges = function(textAndMatches, s, maxLen) {
		var ranges = [];
		var matchCount = getMatchedTextCount(textAndMatches);
		var pos = 0;
		for (var i=0; i < textAndMatches.length; i++) {
			var t = textAndMatches[i];
			var text = t.text;
			if (t.isMatch) {
				var range = getContextRangeAround(s, pos, pos+text.length, matchCount, maxLen);
				addRange(ranges, range.start, range.end);
			pos += text.length;
		return ranges;
	var fillUpRanges = function(s, ranges, maxLen) {
		var remainingLen = maxLen - getTotalRangesSize(ranges);
		while (remainingLen > 0) {
			if (ranges.length == 0) {
				// No matches added yet. Make one large range.
				addRange(ranges, 0, moveToWordBorder(s, maxLen, false));
			} else {
				var range = ranges[0];
				var startIndex;
				var maxEndIndex;
				if (range.start == 0) {
					// The first range already starts at the beginning of the string.
					// When there is a second range fill to the next range start or to the maxLen.
					startIndex = range.end;
					if (ranges.length > 1) {
						maxEndIndex =  ranges[1].start;
					} else {
						// Only one range. Add a range after that with the complete remaining len 
						// (corrected to "beautify" the output)
						addRange(ranges, startIndex, moveToWordBorder(s, startIndex+remainingLen, false));
				} else {
					// There is unused space between the start of the text and the first range.
					startIndex = 0;
					maxEndIndex = range.start;
				var endIndex = Math.min(maxEndIndex, startIndex+remainingLen);
				addRange(ranges, startIndex, endIndex);
				remainingLen -= (endIndex-startIndex);
	// Write the given ranges of s, using textAndMatches for marking portions of the text.
	var writeRanges = function(place, s, textAndMatches, ranges, maxLen) {
		if (ranges.length == 0) return;
		// Processes the text between startIndex and endIndex of the textAndMatches
		// "writes" them (as DOM elements) at the given place, possibly as "marked" text.
		// When endIndex is not the end of the full text an ellisis is appended. 
		var writeTextAndMatchRange = function(place, s, textAndMatches, startIndex, endIndex) {
			var t;
			var text;
			// find the first text item to write
			var pos = 0;
			var i = 0;
			var offset = 0;
			for (;i < textAndMatches.length; i++) {
				t = textAndMatches[i];
				text = t.text;
				if (startIndex < pos+text.length) {
					offset = startIndex - pos;
				pos += text.length;
			var remainingLen = endIndex - startIndex;
			for (; i < textAndMatches.length && remainingLen > 0; i++) {
				t = textAndMatches[i];
				text = t.text.substr(offset);
				offset = 0;
				if (text.length > remainingLen) text = text.substr(0,remainingLen);
				if (t.isMatch) {
				} else {
					createTiddlyText(place, text);
				remainingLen -= text.length;
			if (endIndex < s.length) {
		// When the first range is not at the start of the text write an ellipsis("...")
		// (Ellipses between ranges are written in the writeTextAndMatchRange method)
		if (ranges[0].start > 0) abego.createEllipsis(place);
		var remainingLen = maxLen;
		for (var i = 0; i < ranges.length && remainingLen > 0; i++) {
			var range = ranges[i];
			var len = Math.min(range.end - range.start, remainingLen);
			writeTextAndMatchRange(place, s, textAndMatches, range.start, range.start+len);
			remainingLen -= len;
	this.render = function(place,s,maxLen,markRegExp) {
		if (s.length < maxLen) maxLen = s.length;
		var textAndMatches = getTextAndMatchArray(s, markRegExp);
		var ranges = getMatchedTextWithContextRanges(textAndMatches, s, maxLen);
		// When the maxLen is not yet reached add more ranges 
		// starting from the beginning until either maxLen or 
		// the end of the string is reached.
		fillUpRanges(s, ranges, maxLen);
		writeRanges(place, s, textAndMatches, ranges, maxLen);

(function() {

function alertAndThrow(msg) {
	throw msg;

if (version.major < 2 || (version.major == 2 && version.minor < 1)) 
	alertAndThrow("YourSearchPlugin requires TiddlyWiki 2.1 or newer.\n\nCheck the archive for YourSearch plugins\nsupporting older versions of TiddlyWiki.\n\nArchive: http://tiddlywiki.abego-software.de/archive");

abego.YourSearch = {};

// The Search Core

// Model Variables
var lastResults; // Array of tiddlers that matched the last search
var lastQuery; // The last Search query (TiddlerQuery)

var setLastResults = function(array) {
	lastResults = array;

var getLastResults = function() {
	return lastResults ? lastResults : [];

var getLastResultsCount = function() {
	return lastResults ? lastResults.length : 0;

// Standard Ranking Weights
var matchInTitleWeight = 4;
var precisionInTitleWeight = 10;
var matchInTagsWeight = 2;

var getMatchCount = function(s, re) {
	var m = s.match(re);
	return m ? m.length : 0;

var standardRankFunction = function(tiddler, query) {	
	// Count the matches in the title and the tags
	var markRE = query.getMarkRegExp();
	if (!markRE) return 1;
	var matchesInTitle = tiddler.title.match(markRE);
	var nMatchesInTitle =  matchesInTitle ? matchesInTitle.length : 0;
	var nMatchesInTags = getMatchCount(tiddler.getTags(), markRE);

	// Calculate the "precision" of the matches in the title as the ratio of
	// the length of the matches to the total length of the title.
	var lengthOfMatchesInTitle = matchesInTitle ? matchesInTitle.join("").length : 0;
	var precisionInTitle = tiddler.title.length > 0 ? lengthOfMatchesInTitle/tiddler.title.length : 0;
	// calculate a weighted score
	var rank= nMatchesInTitle * matchInTitleWeight 
			+ nMatchesInTags * matchInTagsWeight 
			+ precisionInTitle * precisionInTitleWeight 
			+ 1;

	return rank;

// @return Tiddler[]
var findMatches = function(store, searchText,caseSensitive,useRegExp,sortField,excludeTag) {
	lastQuery = null;
	var candidates = store.reverseLookup("tags",excludeTag,false);
	try {
		var defaultFields = [];
		if (config.options.chkSearchInTitle) defaultFields.push("title");
		if (config.options.chkSearchInText) defaultFields.push("text");
		if (config.options.chkSearchInTags) defaultFields.push("tags");
		lastQuery = new abego.TiddlerQuery(
				searchText,caseSensitive, useRegExp,defaultFields,config.options.chkSearchExtendedFields); 
	} catch (e) {
		// when an invalid query is given no tiddlers are matched
		return [];

	var results = lastQuery.filter(candidates);

	// Rank the results
	var rankFunction = abego.YourSearch.getRankFunction();
	for (var i = 0; i < results.length; i++) {
		var tiddler = results[i];
		var rank = rankFunction(tiddler, lastQuery);
		// Add the rank information to the tiddler.
		// This is used during the sorting, but it may also
		// be used in the result, e.g. to display some "relevance" 
		// information in the result	
		tiddler.searchRank = rank;	
	// sort the result, taking care of the rank and the sortField	
	if(!sortField) {
		sortField = "title";
	var sortFunction = function (a,b) {
		var searchRankDiff = a.searchRank - b.searchRank;
		if (searchRankDiff == 0) {
			if (a[sortField] == b[sortField]) {
			} else {
				return (a[sortField] < b[sortField]) ? -1 : +1; 
		} else {
			return (searchRankDiff > 0) ? -1 : +1; 
	return results;

// The Search UI (Result page)

// Visual appearance of the result page
var maxCharsInTitle = 80;
var maxCharsInTags = 50;
var maxCharsInText = 250;
var maxCharsInField = 50;

var itemsPerPageDefault = 25; // Default maximum number of items on one search result page
var itemsPerPageWithPreviewDefault = 10; // Default maximum number of items on one search result page when PreviewText is on

// DOM IDs
var yourSearchResultID = "yourSearchResult";
var yourSearchResultItemsID = "yourSearchResultItems";

var lastSearchText; // The last search text, as passed to findMatches

var resultElement; // The (popup) DOM element containing the search result [may be null]
var searchInputField; // The "search" input field
var searchButton; // The "search" button
var lastNewTiddlerButton;

var initStylesheet = function() {
	if (version.extensions.YourSearchPlugin.styleSheetInited) 
	version.extensions.YourSearchPlugin.styleSheetInited = true;

var isResultOpen = function() {
	return resultElement != null && resultElement.parentNode == document.body;

var closeResult = function() {
	if (isResultOpen()) {

// Closes the Search Result window and displays the tiddler 
// defined by the "tiddlyLink" attribute of this element
var closeResultAndDisplayTiddler = function(e)
	var title = this.getAttribute("tiddlyLink");
	if(title) {
		var withHilite = this.getAttribute("withHilite");
		var oldHighlightHack = highlightHack;
		if (withHilite && withHilite=="true" && lastQuery) {
			highlightHack = lastQuery.getMarkRegExp();
		highlightHack = oldHighlightHack;

// Adjusts the resultElement's size and position, relative to the search input field.
var adjustResultPositionAndSize = function() {
	if (!searchInputField) return;
	var root = searchInputField;
	// Position the result below the root and resize it if necessary.
	var rootLeft = findPosX(root);
	var rootTop = findPosY(root);
	var rootHeight = root.offsetHeight;
	var popupLeft = rootLeft;
	var popupTop = rootTop + rootHeight;

	// Make sure the result is not wider than the window
	var winWidth = findWindowWidth();
	if (winWidth < resultElement.offsetWidth) {
		resultElement.style.width = (winWidth - 100)+"px";
		winWidth = findWindowWidth();

	// Ensure that the left and right of the result are not
	// clipped by the window. Move it to the left or right, if necessary.	
	var popupWidth = resultElement.offsetWidth;
	if(popupLeft + popupWidth > winWidth)
		popupLeft = winWidth - popupWidth-30;
	if (popupLeft < 0) popupLeft = 0;
	// Do the actual moving
	resultElement.style.left = popupLeft + "px";
	resultElement.style.top = popupTop + "px";
	resultElement.style.display = "block";

var scrollVisible = function() {
	// Scroll the window to make the result page (and the search Input field) visible.
	if (resultElement) window.scrollTo(0,ensureVisible(resultElement));
	if (searchInputField) window.scrollTo(0,ensureVisible(searchInputField));

// Makes sure the result page has a good size and position and visible
// (may scroll the window)
var	ensureResultIsDisplayedNicely = function() {

var indexInPage; // The index (in the current page) of the tiddler currently rendered.
var currentTiddler; // While rendering the page the tiddler that is currently rendered.

var pager = new abego.PageWiseRenderer();

var MyItemRenderer = function(parent) {
	// Load the template how to display the items that represent a found tiddler
	this.itemHtml = store.getTiddlerText("YourSearchItemTemplate");
	if (!this.itemHtml) alertAndThrow("YourSearchItemTemplate not found");
	// Locate the node that shall contain the list of found tiddlers
	this.place = document.getElementById(yourSearchResultItemsID);
		this.place = createTiddlyElement(parent,"div",yourSearchResultItemsID);

	render: function(pager,object,index,indexOnPage) {
		// Define global variables, referenced by macros during applyHtmlMacros
		indexInPage = indexOnPage;
		currentTiddler = object;
		var item = createTiddlyElement(this.place,"div",null, "yourSearchItem");
		item.innerHTML = this.itemHtml;

	endRendering: function(pager) {
		// The currentTiddler must only be defined while rendering the found tiddlers
		currentTiddler = null;

// Refreshes the content of the result with the current search result
// of the selected page.
// Assumes that the result is already open. 
var refreshResult = function() {
	if (!resultElement || !searchInputField) return;

	// Load the template for the YourSearchResult
	var html = store.getTiddlerText("YourSearchResultTemplate");
	if (!html) html = "<b>Tiddler YourSearchResultTemplate not found</b>";
	resultElement.innerHTML = html;

	// Expand the template macros etc.
	var itemRenderer = new MyItemRenderer(resultElement);


pager.getItemsPerPage = function() {
	var n = (config.options.chkPreviewText) 
			? abego.toInt(config.options.txtItemsPerPageWithPreview, itemsPerPageWithPreviewDefault) 
			: abego.toInt(config.options.txtItemsPerPage, itemsPerPageDefault);
	return (n > 0) ? n : 1;

pager.onPageChanged = function() {

var showResult = function() {
	if (!resultElement) {
		resultElement = createTiddlyElement(document.body,"div",yourSearchResultID,"yourSearchResult");
	} else if (resultElement.parentNode != document.body) {


var	reopenResultIfApplicable = function() {
	if (searchInputField == null || !config.options.chkUseYourSearch) return;
	if ((searchInputField.value == lastSearchText) && lastSearchText && !isResultOpen()) {
		// For speedup we check re-use the previously created resultElement, if possible.
		if (resultElement && (resultElement.parentNode != document.body)) {
		} else {

var invalidateResult = function() {
	resultElement = null;
	lastSearchText = null;

// Close the search result page when the user clicks on the document
// (and not into the searchInputField, on the search button or in the result)
// or presses the ESC key

// Returns true if e is either self or a descendant (child, grandchild,...) of self.
// @param self DOM:Element
// @param e DOM:Element or null
var isDescendantOrSelf = function(self, e) {
	while (e != null) {
		if (self == e) return true;
		e = e.parentNode;
	return false;

var onDocumentClick = function(e) {
	if (e.target == searchInputField) return; 
	if (e.target == searchButton) return; 
	if (resultElement && isDescendantOrSelf(resultElement, e.target)) return; 

var onDocumentKeyup = function(e) {
	// Close the search result page when the user presses "ESC"
	if (e.keyCode == 27) closeResult();

// Our Search Macro Hijack Function ==========================================

// Helper
var myStorySearch = function(text,useCaseSensitive,useRegExp)
	lastSearchText = text;
	setLastResults(findMatches(store, text,useCaseSensitive,useRegExp,"title","excludeSearch"));

	highlightHack = lastQuery ? lastQuery.getMarkRegExp() : null;
	highlightHack = null;

var myMacroSearchHandler = function(place,macroName,params,wikifier,paramString,tiddler)

	lastSearchText = "";
	var searchTimeout = null;
	var doSearch = function(txt)
		if (config.options.chkUseYourSearch)
		lastSearchText = txt.value;
	var clickHandler = function(e)
		return false;
	var keyHandler = function(e)
		if (!e) var e = window.event;
		searchInputField = this;
			case 13:
				if (e.ctrlKey && lastNewTiddlerButton && isResultOpen())
			case 27:
				// When the result is open, close it, 
				// otherwise clear the content of the input field
				if (isResultOpen()) {
				} else {
					this.value = "";
		if (String.fromCharCode(e.keyCode) == this.accessKey || e.altKey) 

		if(this.value.length<3 && searchTimeout) clearTimeout(searchTimeout);
		if(this.value.length > 2)
		 	if (this.value != lastSearchText)
				if (!config.options.chkUseYourSearch || config.options.chkSearchAsYouType)
					var txt = this;
					searchTimeout = setTimeout(function() {doSearch(txt);},500);
		if (this.value.length == 0) 

	var focusHandler = function(e)

	var args = paramString.parseParams("list",null,true);
	var buttonAtRight = getFlag(args, "buttonAtRight");
	var sizeTextbox = getParam(args, "sizeTextbox", this.sizeTextbox);
	var btn;
	if (!buttonAtRight)
		btn = createTiddlyButton(place,this.label,this.prompt,clickHandler);
	var txt = createTiddlyElement(place,"input",null,null,null);
		txt.value = params[0];
	txt.onkeyup = keyHandler;
	txt.onfocus = focusHandler;

	if (buttonAtRight)
		btn = createTiddlyButton(place,this.label,this.prompt,clickHandler);

	searchInputField = txt;
	searchButton = btn;

// Support for Macros

var openAllFoundTiddlers = function() {
	var results = getLastResults();
	var n = results.length;
	if (n) {
		var titles=[];
		for(var i = 0; i<n; i++)

var createOptionWithRefresh = function(place, optionParams, wikifier,tiddler) {
	// The option macro appended the component at the end of the place.
	var elem = place.lastChild;
	var oldOnClick = elem.onclick;
	elem.onclick = function(e) {
		var result = oldOnClick.apply(this, arguments);
		return result;
	return elem;

var removeTextDecoration = function(s) {
	var removeThis = ["''", "{{{", "}}}", "//", "<<<", "/***", "***/"];
	var reText = "";
	for (var i = 0; i < removeThis.length; i++) {
		if (i != 0) reText += "|";
		reText += "("+removeThis[i].escapeRegExp()+")";
	return s.replace(new RegExp(reText, "mg"), "").trim();

// Returns the "shortcut number" of the currentTiddler. 
// I.e. When the user presses Alt-n the given tiddler is opened/display.
// @return 0-9 or -1 when no number is defined
var getShortCutNumber = function() {
	var i = indexInPage;
	return (i >= 0 && i <= 9) 
		? (i < 9 ? (i+1) : 0)
		: -1;

var limitedTextRenderer = new abego.LimitedTextRenderer();
var renderLimitedText = function(place, s, maxLen) {

// When any tiddler are changed reset the result.
var oldTiddlyWikiSaveTiddler = TiddlyWiki.prototype.saveTiddler;
TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags,fields) {
	oldTiddlyWikiSaveTiddler.apply(this, arguments);
var oldTiddlyWikiRemoveTiddler = TiddlyWiki.prototype.removeTiddler;
TiddlyWiki.prototype.removeTiddler = function(title) {
	oldTiddlyWikiRemoveTiddler.apply(this, arguments);

// Macros

// ====Macro yourSearch ================================================

config.macros.yourSearch = {
	// Standard Properties
	label: "yourSearch",
	prompt: "Gives access to the current/last YourSearch result",
	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		if (params.length == 0) return;
		var name = params[0];
		var func = config.macros.yourSearch.funcs[name];
		if (func) func(place,macroName,params,wikifier,paramString,tiddler);
	tests: {
		"true" : function() {return true;},
		"false" : function() {return false;},
		"found" : function() {return getLastResultsCount() > 0;},
		"previewText" : function() {return config.options.chkPreviewText;}

	funcs: {
		itemRange: function(place) {
			if (getLastResultsCount()) {
				var lastIndex = pager.getLastIndexOnPage();
				var s = "%0 - %1".format([pager.getFirstIndexOnPage()+1,lastIndex+1]);
				createTiddlyText(place, s);
		count: function(place) {
			createTiddlyText(place, getLastResultsCount().toString());
		query: function(place) {
			if (lastQuery) {
				createTiddlyText(place, lastQuery.toString());
		version: function(place) {
			var t = "YourSearch %0.%1.%2".format(
			var e = createTiddlyElement(place, "a");
			e.setAttribute("href", "http://tiddlywiki.abego-software.de/#YourSearchPlugin");
			e.innerHTML = '<font color="black" face="Arial, Helvetica, sans-serif">'+t+'<font>';
		copyright: function(place) {
			var e = createTiddlyElement(place, "a");
			e.setAttribute("href", "http://www.abego-software.de");
			e.innerHTML = '<font color="black" face="Arial, Helvetica, sans-serif">&copy; 2005-2006 <b><font color="red">abego</font></b> Software<font>';
		newTiddlerButton: function(place) {
			if (lastQuery) {
				var r = abego.parseNewTiddlerCommandLine(lastQuery.getQueryText());
				var btn = config.macros.newTiddler.createNewTiddlerButton(place,r.title,r.params,"new tiddler","Create a new tiddler based on search text. (Shortcut: Ctrl-Enter; Separators: '.', '#')",null,"text");				
				// Close the result before the new tiddler is created.
				var oldOnClick = btn.onclick;
				btn.onclick = function() {
				lastNewTiddlerButton = btn;
		linkButton: function(place,macroName,params,wikifier,paramString,tiddler) {
			if (params < 2) return;
			var	tiddlyLink = params[1];
			var text = params < 3 ? tiddlyLink : params[2];
			var tooltip = params < 4 ? text : params[3];
			var accessKey = params < 5 ? null : params[4];
			var btn = createTiddlyButton(place,text,tooltip,closeResultAndDisplayTiddler,null,null, accessKey);
		closeButton: function(place,macroName,params,wikifier,paramString,tiddler) {
			var button = createTiddlyButton(place, "close", "Close the Search Results (Shortcut: ESC)", closeResult);
		openAllButton: function(place,macroName,params,wikifier,paramString,tiddler) {
			var n = getLastResultsCount();
			if (n == 0) return;
			var title = n == 1 ? "open tiddler" : "open all %0 tiddlers".format([n]);
			var button = createTiddlyButton(place, title, "Open all found tiddlers (Shortcut: Alt-O)", openAllFoundTiddlers);
		naviBar: function(place,macroName,params,wikifier,paramString,tiddler) {
		"if": function(place,macroName,params,wikifier,paramString,tiddler) {
			if (params.length < 2) return;
			var testName = params[1];
			var negate = (testName == "not");
			if (negate) {
				if (params.length < 3) return;
				testName = params[2];
			var test = config.macros.yourSearch.tests[testName];
			var showIt = false;
			try {
				if (test) {
					showIt = test(place,macroName,params,wikifier,paramString,tiddler) != negate;
				} else {
					// When no predefined test is specified try to evaluate it as a JavaScript expression.
					showIt = (!eval(testName)) == negate;
			} catch (ex) {
			if (!showIt) {
		chkPreviewText: function(place,macroName,params,wikifier,paramString,tiddler) {
			var optionParams = params.slice(1).join(" ");
			var elem = createOptionWithRefresh(place, "chkPreviewText", wikifier,tiddler);
			elem.setAttribute("accessKey", "P");
			elem.title = "Show text preview of found tiddlers (Shortcut: Alt-P)";	
			return elem;

// ====Macro foundTiddler ================================================

config.macros.foundTiddler = {
	// Standard Properties
	label: "foundTiddler",
	prompt: "Provides information on the tiddler currently processed on the YourSearch result page",
	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		var name = params[0];
		var func = config.macros.foundTiddler.funcs[name];
		if (func) func(place,macroName,params,wikifier,paramString,tiddler);
	funcs: {
		title: function(place,macroName,params,wikifier,paramString,tiddler) {
			if (!currentTiddler) return;
			var shortcutNumber = getShortCutNumber();
			var tooltip = shortcutNumber >= 0 
					? "Open tiddler (Shortcut: Alt-%0)".format([shortcutNumber.toString()])
					: "Open tiddler";
			var btn = createTiddlyButton(place,null,tooltip,closeResultAndDisplayTiddler,null);
			renderLimitedText(btn, currentTiddler.title, maxCharsInTitle);
			if (shortcutNumber >= 0) {
		tags: function(place,macroName,params,wikifier,paramString,tiddler) {
			if (!currentTiddler) return;
			renderLimitedText(place, currentTiddler.getTags(), maxCharsInTags);
		text: function(place,macroName,params,wikifier,paramString,tiddler) {
			if (!currentTiddler) return;
			renderLimitedText(place, removeTextDecoration(currentTiddler.text), maxCharsInText);
		field:  function(place,macroName,params,wikifier,paramString,tiddler) {
			if (!currentTiddler) return;
			var	name = params[1];
			var len = params.length > 2 ? abego.toInt(params[2],maxCharsInField) : maxCharsInField;
			var v = store.getValue(currentTiddler,name);
			if (v)
				renderLimitedText(place, removeTextDecoration(v), len);
		// Renders the "shortcut number" of the current tiddler, to indicate to the user
		// what number to "Alt-press" to open the tiddler.
		number: function(place,macroName,params,wikifier,paramString,tiddler) {
			var numberToDisplay = getShortCutNumber();
			if (numberToDisplay >= 0) {
				var text = "%0)".format([numberToDisplay.toString()]);

// Configuration Stuff

var opts = {chkUseYourSearch:true,
for (var n in opts) 
	if (config.options[n] == undefined) config.options[n] = opts[n];

// Shadow Tiddlers

config.shadowTiddlers.AdvancedOptions += "\n<<option chkUseYourSearch>> Use 'Your Search' //([[more options|YourSearch Options]]) ([[help|YourSearch Help]])// ";

config.shadowTiddlers["YourSearch Help"] =
"!Field Search\nWith the Field Search you can restrict your search to certain fields of a tiddler, e.g"+
" only search the tags or only the titles. The general form is //fieldname//'':''//textToSearch// (e."+
"g. {{{title:intro}}}). In addition one-character shortcuts are also supported for the standard field"+
"s {{{title}}}, {{{text}}} and {{{tags}}}:\n|!What you want|!What you type|!Example|\n|Search ''titles "+
"only''|start word with ''!''|{{{!jonny}}} (shortcut for {{{title:jonny}}})|\n|Search ''contents/text "+
"only''|start word with ''%''|{{{%football}}} (shortcut for {{{text:football}}})|\n|Search ''tags only"+
"''|start word with ''#''|{{{#Plugin}}} (shortcut for {{{tags:Plugin}}})|\n\nUsing this feature you may"+
" also search the extended fields (\"Metadata\") introduced with TiddlyWiki 2.1, e.g. use {{{priority:1"+
"}}} to find all tiddlers with the priority field set to \"1\".\n\nYou may search a word in more than one"+
" field. E.g. {{{!#Plugin}}} (or {{{title:tags:Plugin}}} in the \"long form\") finds tiddlers containin"+
"g \"Plugin\" either in the title or in the tags (but does not look for \"Plugin\" in the text). \n\n!Boole"+
"an Search\nThe Boolean Search is useful when searching for multiple words.\n|!What you want|!What you "+
"type|!Example|\n|''All words'' must exist|List of words|{{{jonny jeremy}}} (or {{{jonny and jeremy}}}"+
")|\n|''At least one word'' must exist|Separate words by ''or''|{{{jonny or jeremy}}}|\n|A word ''must "+
"not exist''|Start word with ''-''|{{{-jonny}}} (or {{{not jonny}}})|\n\n''Note:'' When you specify two"+
" words, separated with a space, YourSearch finds all tiddlers that contain both words, but not neces"+
"sarily next to each other. If you want to find a sequence of word, e.g. '{{{John Brown}}}', you need"+
" to put the words into quotes. I.e. you type: {{{\"john brown\"}}}.\n\nUsing parenthesis you may change "+
"the default \"left to right\" evaluation of the boolean search. E.g. {{{not (jonny or jeremy)}}} finds"+
" all tiddlers that contain neither \"jonny\" nor \"jeremy. In contrast to this {{{not jonny or jeremy}}"+
"} (i.e. without parenthesis) finds all tiddlers that either don't contain \"jonny\" or that contain \"j"+
"eremy\".\n\n!'Exact Word' Search\nBy default a search result all matches that 'contain' the searched tex"+
"t. E.g. if you search for {{{Task}}} you will get all tiddlers containing 'Task', but also '~Complet"+
"edTask', '~TaskForce' etc.\n\nIf you only want to get the tiddlers that contain 'exactly the word' you"+
" need to prefix it with a '='. E.g. typing '=Task' will find the tiddlers that contain the word 'Tas"+
"k', ignoring words that just contain 'Task' as a substring.\n\n!~CaseSensitiveSearch and ~RegExpSearch"+
"\nThe standard search options ~CaseSensitiveSearch and ~RegExpSearch are fully supported by YourSearc"+
"h. However when ''~RegExpSearch'' is on Filtered and Boolean Search are disabled.\n\nIn addition you m"+
"ay do a \"regular expression\" search even with the ''~RegExpSearch'' set to false by directly enterin"+
"g the regular expression into the search field, framed with {{{/.../}}}. \n\nExample: {{{/m[ae][iy]er/"+
"}}} will find all tiddlers that contain either \"maier\", \"mayer\", \"meier\" or \"meyer\".\n\n!~JavaScript E"+
"xpression Filtering\nIf you are familiar with JavaScript programming and know some TiddlyWiki interna"+
"ls you may also use JavaScript expression for the search. Just enter a JavaScript boolean expression"+
" into the search field, framed with {{{ { ... } }}}. In the code refer to the variable tiddler and e"+
"valuate to {{{true}}} when the given tiddler should be included in the result. \n\nExample: {{{ { tidd"+
"ler.modified > new Date(\"Jul 4, 2005\")} }}} returns all tiddler modified after July 4th, 2005.\n\n!Com"+
"bined Search\nYou are free to combine the various search options. \n\n''Examples''\n|!What you type|!Res"+
"ult|\n|{{{!jonny !jeremy -%football}}}|all tiddlers with both {{{jonny}}} and {{{jeremy}}} in its tit"+
"les, but no {{{football}}} in content.|\n|{{{#=Task}}}|All tiddlers tagged with 'Task' (the exact wor"+
"d). Tags named '~CompletedTask', '~TaskForce' etc. are not considered.|\n\n!Access Keys\nYou are encour"+
"aged to use the access keys (also called \"shortcut\" keys) for the most frequently used operations. F"+
"or quick reference these shortcuts are also mentioned in the tooltip for the various buttons etc.\n\n|"+
"!Key|!Operation|\n|{{{Alt-F}}}|''The most important keystroke'': It moves the cursor to the search in"+
"put field so you can directly start typing your query. Pressing {{{Alt-F}}} will also display the pr"+
"evious search result. This way you can quickly display multiple tiddlers using \"Press {{{Alt-F}}}. S"+
"elect tiddler.\" sequences.|\n|{{{ESC}}}|Closes the [[YourSearch Result]]. When the [[YourSearch Resul"+
"t]] is already closed and the cursor is in the search input field the field's content is cleared so "+
"you start a new query.|\n|{{{Alt-1}}}, {{{Alt-2}}},... |Pressing these keys opens the first, second e"+
"tc. tiddler from the result list.|\n|{{{Alt-O}}}|Opens all found tiddlers.|\n|{{{Alt-P}}}|Toggles the "+
"'Preview Text' mode.|\n|{{{Alt-'<'}}}, {{{Alt-'>'}}}|Displays the previous or next page in the [[Your"+
"Search Result]].|\n|{{{Return}}}|When you have turned off the 'as you type' search mode pressing the "+
"{{{Return}}} key actually starts the search (as does pressing the 'search' button).|\n\n//If some of t"+
"hese shortcuts don't work for you check your browser if you have other extensions installed that alr"+
"eady \"use\" these shortcuts.//";

config.shadowTiddlers["YourSearch Options"] =
"|>|!YourSearch Options|\n|>|<<option chkUseYourSearch>> Use 'Your Search'|\n|!|<<option chkPreviewText"+
">> Show Text Preview|\n|!|<<option chkSearchAsYouType>> 'Search As You Type' Mode (No RETURN required"+
" to start search)|\n|!|Default Search Filter:<<option chkSearchInTitle>>Title ('!')     <<option chk"+
"SearchInText>>Text ('%')     <<option chkSearchInTags>>Tags ('#')    <<option chkSearchExtendedFiel"+
"ds>>Extended Fields<html><br><font size=\"-2\">The fields of a tiddlers that are searched when you don"+
"'t explicitly specify a filter in the search text <br>(Explictly specify fields using one or more '!"+
"', '%', '#' or 'fieldname:' prefix before the word/text to find).</font></html>|\n|!|Number of items "+
"on search result page: <<option txtItemsPerPage>>|\n|!|Number of items on search result page with pre"+
"view text: <<option txtItemsPerPageWithPreview>>|\n";
config.shadowTiddlers["YourSearchStyleSheet"] = 
"/***\n!~YourSearchResult Stylesheet\n***/\n/*{{{*/\n.yourSearchResult {\n\tposition: absolute;\n\twidth: 800"+
"px;\n\n\tpadding: 0.2em;\n\tlist-style: none;\n\tmargin: 0;\n\n\tbackground: #ffd;\n\tborder: 1px solid DarkGra"+
"y;\n}\n\n/*}}}*/\n/***\n!!Summary Section\n***/\n/*{{{*/\n.yourSearchResult .summary {\n\tborder-bottom-width:"+
" thin;\n\tborder-bottom-style: solid;\n\tborder-bottom-color: #999999;\n\tpadding-bottom: 4px;\n}\n\n.yourSea"+
"rchRange, .yourSearchCount, .yourSearchQuery   {\n\tfont-weight: bold;\n}\n\n.yourSearchResult .summary ."+
"button {\n\tfont-size: 10px;\n\n\tpadding-left: 0.3em;\n\tpadding-right: 0.3em;\n}\n\n.yourSearchResult .summa"+
"ry .chkBoxLabel {\n\tfont-size: 10px;\n\n\tpadding-right: 0.3em;\n}\n\n/*}}}*/\n/***\n!!Items Area\n***/\n/*{{{*"+
"/\n.yourSearchResult .marked {\n\tbackground: none;\n\tfont-weight: bold;\n}\n\n.yourSearchItem {\n\tmargin-to"+
"p: 2px;\n}\n\n.yourSearchNumber {\n\tcolor: #808080;\n}\n\n\n.yourSearchTags {\n\tcolor: #008000;\n}\n\n.yourSearc"+
"hText {\n\tcolor: #808080;\n\tmargin-bottom: 6px;\n}\n\n/*}}}*/\n/***\n!!Footer\n***/\n/*{{{*/\n.yourSearchFoote"+
"r {\n\tmargin-top: 8px;\n\tborder-top-width: thin;\n\tborder-top-style: solid;\n\tborder-top-color: #999999;"+
"\n}\n\n.yourSearchFooter a:hover{\n\tbackground: none;\n\tcolor: none;\n}\n/*}}}*/\n/***\n!!Navigation Bar\n***/"+
"\n/*{{{*/\n.yourSearchNaviBar a {\n\tfont-size: 16px;\n\tmargin-left: 4px;\n\tmargin-right: 4px;\n\tcolor: bla"+
"ck;\n\ttext-decoration: underline;\n}\n\n.yourSearchNaviBar a:hover {\n\tbackground-color: none;\n}\n\n.yourSe"+
"archNaviBar .prev {\n\tfont-weight: bold;\n\tcolor: blue;\n}\n\n.yourSearchNaviBar .currentPage {\n\tcolor: #"+
"FF0000;\n\tfont-weight: bold;\n\ttext-decoration: none;\n}\n\n.yourSearchNaviBar .next {\n\tfont-weight: bold"+
";\n\tcolor: blue;\n}\n/*}}}*/\n";

config.shadowTiddlers["YourSearchResultTemplate"] =
"<!--\n{{{\n-->\n<span macro=\"yourSearch if found\">\n<!-- The Summary Header ============================"+
"================ -->\n<table class=\"summary\" border=\"0\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\">"+
"<tbody>\n  <tr>\n\t<td align=\"left\">\n\t\tYourSearch Result <span class=\"yourSearchRange\" macro=\"yourSearc"+
"h itemRange\"></span>\n\t\t&nbsp;of&nbsp;<span class=\"yourSearchCount\" macro=\"yourSearch count\"></span>\n"+
"\t\tfor&nbsp;<span class=\"yourSearchQuery\" macro=\"yourSearch query\"></span>\n\t</td>\n\t<td class=\"yourSea"+
"rchButtons\" align=\"right\">\n\t\t<span macro=\"yourSearch chkPreviewText\"></span><span class=\"chkBoxLabel"+
"\">preview text</span>\n\t\t<span macro=\"yourSearch newTiddlerButton\"></span>\n\t\t<span macro=\"yourSearch openAllButton\"></span>\n\t\t<span macro=\"yourSearch lin"+
"kButton 'YourSearch Options' options 'Configure YourSearch'\"></span>\n\t\t<span macro=\"yourSearch linkB"+
"utton 'YourSearch Help' help 'Get help how to use YourSearch'\"></span>\n\t\t<span macro=\"yourSearch clo"+
"seButton\"></span>\n\t</td>\n  </tr>\n</tbody></table>\n\n<!-- The List of Found Tiddlers ================="+
"=========================== -->\n<div id=\"yourSearchResultItems\" itemsPerPage=\"25\" itemsPerPageWithPr"+
"eview=\"10\"></div>\n\n<!-- The Footer (with the Navigation) ==========================================="+
"= -->\n<table class=\"yourSearchFooter\" border=\"0\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\"><tbody"+
">\n  <tr>\n\t<td align=\"left\">\n\t\tResult page: <span class=\"yourSearchNaviBar\" macro=\"yourSearch naviBar"+
"\"></span>\n\t</td>\n\t<td align=\"right\"><span macro=\"yourSearch version\"></span>, <span macro=\"yourSearc"+
"h copyright\"></span>\n\t</td>\n  </tr>\n</tbody></table>\n<!-- end of the 'tiddlers found' case ========="+
"================================== -->\n</span>\n\n\n<!-- The \"No tiddlers found\" case ================="+
"========================== -->\n<span macro=\"yourSearch if not found\">\n<table class=\"summary\" border="+
"\"0\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\"><tbody>\n  <tr>\n\t<td align=\"left\">\n\t\tYourSearch Resu"+
"lt: No tiddlers found for <span class=\"yourSearchQuery\" macro=\"yourSearch query\"></span>.\n\t</td>\n\t<t"+
"d class=\"yourSearchButtons\" align=\"right\">\n\t\t<span macro=\"yourSearch newTiddlerButton\"></span>\n\t\t<span macro=\"yourSearch linkButton 'YourSearch Options'"+
" options 'Configure YourSearch'\"></span>\n\t\t<span macro=\"yourSearch linkButton 'YourSearch Help' help"+
" 'Get help how to use YourSearch'\"></span>\n\t\t<span macro=\"yourSearch closeButton\"></span>\n\t</td>\n  <"+

config.shadowTiddlers["YourSearchItemTemplate"] = 
"<!--\n{{{\n-->\n<span class='yourSearchNumber' macro='foundTiddler number'></span>\n<span class='yourSea"+
"rchTitle' macro='foundTiddler title'/></span>&nbsp;-&nbsp;\n<span class='yourSearchTags' macro='found"+
"Tiddler field tags 50'/></span>\n<span macro=\"yourSearch if previewText\"><div class='yourSearchText' macro='fo"+
"undTiddler field text 250'/></div></span>\n<!--\n}}}\n-->";

config.shadowTiddlers["YourSearch"] = "<<tiddler [[YourSearch Help]]>>";

config.shadowTiddlers["YourSearch Result"] = "The popup-like window displaying the result of a YourSearch query.";

// Install YourSearch

// Overwrite the TiddlyWiki search handler and verify after a while 
// that nobody else has overwritten it.
config.macros.search.handler = myMacroSearchHandler;

var checkForOtherHijacker = function() {
	// Check that still our search handler is installed
    if (config.macros.search.handler != myMacroSearchHandler) {
"Message from YourSearchPlugin:\n\n\nAnother plugin has disabled the 'Your Search' features.\n\n\nYou may "+
"disable the other plugin or change the load order of \nthe plugins (by changing the names of the tidd"+
"lers)\nto enable the 'Your Search' features.");

setTimeout(checkForOtherHijacker, 5000);

// === Public API =================================

abego.YourSearch.getStandardRankFunction = function() {
	return standardRankFunction;

abego.YourSearch.getRankFunction = function() {
	return abego.YourSearch.getStandardRankFunction();

abego.YourSearch.getCurrentTiddler = function() {
	return currentTiddler;

abego.YourSearch.closeResult = function() {

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

// ... JavaScript Core
/*global 	alert,clearTimeout,confirm */
// ... TiddlyWiki Core
/*global 	Tiddler, applyHtmlMacros, clearMessage, createTiddlyElement, createTiddlyButton, createTiddlyText, ensureVisible ,findPosX, highlightHack, findPosY,findWindowWidth, invokeMacro, saveChanges, refreshElements, story */
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2005-2006 ([[www.abego-software.de|http://www.abego-software.de]])

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

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

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

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


Macro: allTagsExcept
Author: Clint Checketts
Version: 1.0 Sept 8, 2005

usage: {{{<< allTagsExcept systemConfig systemTiddlers >>}}} This will show all tags but those listed (e.g. systemConfig and systemTiddlers

<<allTagsExcept systemConfig systemTiddlers >>
version.extensions.allTagsExcept = {major: 0, minor: 1, revision: 0, date: new Date(2005,8,15)};
config.macros.allTagsExcept = {tooltip: "Show tiddlers tagged with '%0'",noTags: "There are no tags to display"};

config.macros.allTagsExcept.handler = function(place,macroName,params)
	var tags = store.getTags();
	var theTagList = createTiddlyElement(place,"ul",null,null,null);
	if(tags.length == 0)
	for (var t=0; t<tags.length; t++) {
            var includeTag = true;
            for (var p=0;p<params.length; p++) if (tags[t][0] == params[p]) includeTag = false;
            if (includeTag){
		var theListItem =createTiddlyElement(theTagList,"li",null,null,null);
		var theTag = createTiddlyButton(theListItem,tags[t][0] + " (" + tags[t][1] + ")",this.tooltip.format([tags[t][0]]),onClickTag);
Type the text for '[dessert]'