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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

hr {height:1px;}

a {text-decoration:none;}

dt {font-weight:bold;}

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

.txtOptionInput {width:11em;}

#contentWrapper .chkOptionInput {border:0;}

.externalLink {text-decoration:underline;}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/
/***
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which need larger font sizes.
***/
/*{{{*/
body {font-size:0.8em;}
#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}
.subtitle {font-size:0.8em;}
.viewer table.listView {font-size:0.95em;}
/*}}}*/
/*{{{*/
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none !important;}
#displayArea {margin: 1em 1em 0em;}
noscript {display:none;} /* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
}
/*}}}*/
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='edit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser excludeLists'></span></div>
<!--}}}-->
To get started with this blank [[TiddlyWiki]], you'll need to modify the following tiddlers:
* [[SiteTitle]] & [[SiteSubtitle]]: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* [[MainMenu]]: The menu (usually on the left)
* [[DefaultTiddlers]]: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: <<option txtUserName>>
These [[InterfaceOptions]] for customising [[TiddlyWiki]] are saved in your browser

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

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

----
Also see [[AdvancedOptions]]
<<importTiddlers>>
Scrumptious
conversations about websites
<<newTiddler label:"new bookmark" prompt:"create a new bookmark" template:bookmarkEditTemplate title:"New Bookmark" tag:"bookmark">>

[[Home]]
[[Trails]]
[[Bookmarks]]
<<overlay launchLabel:"Trail Player">>
[[About]]

<<search>>
<<closeAll>>
AdvancedOptions
<<slider chkTiddlerTabs SideBarTabs "tiddlers ยป" "manage tiddlers">>
/*
Theme - http://www.colourlovers.com/palette/60028/Night_Sky
#001848 dark blue
#301860 dark violet
#483078 violet
#604878 pink-violet
#906090 violet-pink
*/

@font-face {
  font-family: "punkass";
  src: url("images/punkass_.ttf");
}

.header { text-align: center; font-size: 100%; padding: 0.7em 0; width: 100%; color: white; }
.siteTitle { font-family: punkass, ariel, helvetica, sans serif; text-shadow: black 1px 1px 0; font-size: 5em; }
.siteSubtitle { font-weight: bold; font-family: times new roman, serif; }
.clearance { clear: both; height: 1px; }
.title, h1, h2, h3, h4, h5, h6 { color: [[ColorPalette::PrimaryMid]]; }
#scrumptiousWrapper { text-align: center; }
#scrumptiousMain { margin: 0 auto; width: 960px; }
#mainMenu { width: 150px; position: static; margin: 0; padding: 0; float: left; }
#dontCollapse { width: 1px; overflow: hidden; }
#displayArea { width: 540px; margin: 0 30px; padding: 0; text-align: left; float: left; position: static; }
#sidebar { width: 210px; font-size: 1em; float: left; position: static; margin: 0; padding: 0; text-align: left; }

#sidebar, #mainMenu { margin-top: 1em; }
h1,h2,h3,h4,h5,h6 { border: 0; }

.tabsetWrapper { font-size: 80%; overflow: hidden;}

.trailViewTiddler, .trailEditTiddler, .trailLink a {
  background-color: #88f;
  filter: progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#9999ff,EndColorStr=#ddddff);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#99f), to(#ddf));
  background-image: -moz-linear-gradient(top, #c8d, #edf);

  border: 1px solid #99f;
}

.trailTiddler table { border-collapse: collapse; }
.trailTiddler td { padding: 0.6em 0; }
.trailViewTiddler td { padding: 1em 0; }
.trailTiddler td input { display: inline; }
.trailTiddler td.bookmarkLinkCell { text-align: right; }
.trailTiddler .bookmarkLink { cursor: move; margin-right: 5px; }
.trailEditTiddler .bookmarkNote { width: 250px; height: 2em; }

.bookmarkTiddler, .bookmarkEditTiddler, .bookmarkLink { 
  background-color: #77f;
  filter: progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#9999ff,EndColorStr=#ddddff);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#99f), to(#ddf));
  background-image: -moz-linear-gradient(top, #77f, #eef);

  border: 1px solid #77f;
}

.dataTiddler { 
  padding: 0 10px 5px 20px;
  -moz-border-radius: 8px; -webkit-border-radius: 8px;
}
.dataCategory { font-size: 0.6em; color: black; font-style: italic; font-weight: normal; }

.trailViewTiddler .title td { padding: 0; vertical-align: bottom; }
.trailViewTiddler .title td.playCell { padding-bottom: 3px; }
.playTrail { border: 1px solid [[ColorPalette::PrimaryDarker]]; background: [[ColorPalette::PrimaryPale]]; font-size: 0.5em; color: black; padding: 1px; margin-left: 4px; font-weight: normal; cursor: pointer; }

.toolbar { padding-top: 5px; }
.toolbar a:hover { font-weight: bold; }
.dataTiddler .button, .selected .toolbar a  { color: [[ColorPalette::PrimaryDarker]]; border: 0; background: inherit; }
.dataTiddler .button:hover, .selected .dataTiddler .toolbar a:hover { color: white; }
.dataTiddler .tagged .button { color: #505; }
.dataTiddler .comments textarea { width: 90%; }
.dataTiddler .comments .addComment { display: block; float: left; border: 1px solid [[ColorPalette::PrimaryMid]]; padding: 2px; background: #99f; color: black; }
.dataTiddler .comments .addComment:hover { color: white; }
.dataTiddler .comments h1 { color: #000; text-decoration: underline; font-size: 1em; font-weight: normal; }
.dataTiddler .comment .heading { background: #99f; }
.dataTiddler .tag a { color: [[ColorPalette::PrimaryMid]]; }
.dataTiddler .url { font-size: 1.2em; color: #20c; }
.dataTiddler .description { font-size: 1.2em; margin-top: 0.3em; }

.trailViewTiddler .bookmarks { margin-top: 1.5em; }

/* .normalTiddler { border-top: 1px solid #99f; border-bottom: 1px solid #99f; } */
.normalTiddler, .tagTiddler { border-left: 5px solid #add; padding-left: 15px; }

.bookmarkLink { padding: 5px; color: black; margin-right: 10px; }
.bookmarkLink:hover { background-color: #88f; color: white; background-image: none; }
.bookmarkLink { color: black; }

.scrumptiousTagging { font-size: normal; margin-top: 1em; }
.scrumptiousTags { font-size: normal; margin: 1em 0; line-height: 3em; }

.bookmarkLink {
  -moz-border-radius: 5px; -webkit-border-radius: 5px;
}

.tag, .tagg, .tagName { background: [[ColorPalette::PrimaryPale]]; }
.tag:hover { background: [[ColorPalette::PrimaryMid]]; }
.tag:hover a { color: white; }

.tagsView { margin-bottom: 1em; }
.tagg, .tagName { padding: 0.2em; }
.tagCloud { line-height: 2em; }
.tag { padding: 0.5em; margin: 0.5em 0.5em 0.5em 0; }
.taggedTiddlers { margin: 1em 0 2em; }
.taggingIntro { margin-bottom: 2em; }
.tagCategory { font-size: 0.8em; font-style: italic; }

.clearance { height: 1px; clear: both; }


table { border: 1px; }
<div class="dataTiddler trailTiddler trailViewTiddler">
  <div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
  <div class='title'><table><tr><td><span macro='view title'></span></td><td class="playCell"><span class="playTrail" macro="playTrail" /></td></tr></table></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='scrumptiousTags' macro='scrumptiousTags'></div>
  <div class='description' macro='view Description wikified'></div>
  <div class='bookmarksView' macro='bookmarksView'></div>
  <div class='comments' macro="comments expandHeading:true heading:'Comments (%c):'"></div>
  <div class='tagClear'></div>
</div>
<div class="dataTiddler trailTiddler trailEditTiddler">
  <div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'></div>
  <div class='title'><span macro='view title'></span> <span class="dataCategory">trail</span></div>
  <div class='editor' macro='edit title'></div>
  <div macro='annotations'></div>
  <h2>Trail Tags</h2>
  <div class='tagSuggest' macro='tagSuggest prefix:user_'></div>
  <div class='editor' macro='edit tags' style='display:none;></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser excludeLists'></span></div>
  <div class='editor' macro='edit text' style="display: none;"></div>
  <div class='editTrail' macro='editTrail'></div>
  <div class="clearance">&nbsp;</div>
</div>
<div class="dataTiddler bookmarkTiddler">
  <div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
  <div class='title'><span macro='view title'></span> <span class="dataCategory">bookmark</span></div>
  <div class='bookmarkURL' macro='bookmarkURL'></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='scrumptiousTags' macro='scrumptiousTags'></div>
  <div class='description' macro='view Description wikified'></div>
  <div class='comments' macro="comments expandHeading:true heading:'Comments (%c):'"></div>
  <div class='tagClear'></div>
</div>
<div class="bookmarkEditTiddler dataTiddler">
  <div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'></div>
  <div class='title'><span macro='view title'></span> <span class="dataCategory">bookmark</span></div>
  <div class='editor' macro='edit title'></div>
  <div macro='annotations'></div>
  <div class='editor' macro='edit text' style="display: none;"></div>
  <div class='editBookmark' macro='editBookmark'></div>
  <h2>Bookmark Tags</h2>
  <div class='tagSuggest' macro='tagSuggest filterTag:bookmark prefix:user_'></div>
  <div class='editor' macro='edit tags' style='display:none;></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser excludeLists'></span></div>
  <div class="clearance">&nbsp;</div>
</div>
<div class="tagTiddler">
  <div class='clearance'>&nbsp;</div>
  <div class='scrumptiousTagging' macro='scrumptiousTagging'></div>
  <div class='clearance'>&nbsp;</div>
</div>
<div class="normalTiddler">
  <div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
  <div class='title' macro='view title'></div>
  <div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
  <div class='scrumptiousTagging' macro='scrumptiousTagging'></div>
  <div class='clearance'></div>
  <div class='tagged' macro='tags'></div>
  <div class='viewer' macro='view text wikified'></div>
  <div class='tagClear'></div>
</div>
[[Home]]
[[News]]
[[user_tech]]
[[Reddit]]
[[Digg]]
[[Bookmarks]]
[[About]]
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryDark]]'>
  <span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
  <span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div id="scrumptiousWrapper">
  <div id="scrumptiousMain">
    <div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
    <div id='displayArea'>
      <div id='messageArea'></div>
      <div id="dontCollapse">&nbsp;</div>
      <div id='tiddlerDisplay'></div>
    </div>
    <div id='sidebar' refresh='content' force='true' tiddler='SideBar'></div>
  </div>
</div>
<!--}}}-->
Background: #fff
Foreground: #000
PrimaryPale: #b8b
PrimaryLight: #604878 
PrimaryMid: #483078
PrimaryDark: #301860
PrimaryDarker: #001848
SecondaryPale: #bdf
SecondaryLight: #8df
SecondaryMid: #39c
SecondaryDark: #059
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88

PrimarySourceURL - http://www.colourlovers.com/palette/328418/rosso_rosso
|~ViewToolbar|+editTiddler closeTiddler > closeOthers fields syncing permalink references jump deleteTiddler|
|~EditToolbar|+saveTiddler -cancelTiddler|
/*
(function(){
  // http://paulirish.com/2009/fighting-the-font-face-fout/
  // if firefox 3.5+, hide content till load (or 3 seconds) to prevent FOUT
  var d = document, e = d.documentElement, s = d.createElement('style');
  if (e.style.MozTransform === ''){ // gecko 1.9.1 inference
    s.textContent = 'body{visibility:hidden}';
    e.firstChild.appendChild(s);
    function f(){ s.parentNode && s.parentNode.removeChild(s); }
    addEventListener('load',f,false);
    setTimeout(f,2000); 
  }
})();
*/

if (version.extensions.TrailPlayer.syncFromFragmentID()) {
  version.extensions.overlay.toggle();
}

config.options.chkAutoSave = true;
config.options.chkTiddlerTabs = false;

(function($) {

  /******************************************************************
   * Bookmark
   ******************************************************************/

  var editBookmark = config.macros.editBookmark = {
    handler: function(place,macroName,params,wikifier,paramString,tiddler) {
      $("<div>URL: </div>").appendTo(place);
      $("<input class='bookmarkURL'/>")
      .val(store.getTiddlerText(tiddler.title+"::url")||"")
      .keyup(function() {
        editBookmark.updateText(this);
      })
      .appendTo(place);

      $("<div>Description (optional): </div>").appendTo(place);
      $("<input class='bookmarkDescription'/>")
      .val(store.getTiddlerText(tiddler.title+"##Description")||"")
      .keyup(function() {
        editBookmark.updateText(this);
      })
      .appendTo(place);

    },
    updateText: function(src) {
      var $bookmarkTiddler = $(src).parents(".bookmarkEditTiddler");
      var text = "url: " + $bookmarkTiddler.find(".bookmarkURL").val();
      var description = $bookmarkTiddler.find(".bookmarkDescription").val();
      if ($.trim(description).length) text+="\n!Description\n"+description;
      $bookmarkTiddler.find("textArea").val(text);
    }
  }

  config.macros.bookmarkURL = {
    handler: function(place,macroName,params,wikifier,paramString,tiddler) {
      var url = store.getTiddlerText(tiddler.title+"::url");
      $("<a class='url'/>").appendTo(place).attr("href", url).html(url);
    }
  }

  config.macros.bookmarkDescription = {
    handler: function(place,macroName,params,wikifier,paramString,tiddler) {
      $("<div class='description'/>")
        .appendTo(place)
        .html(store.getTiddlerText(tiddler.title+"##Description"));
    }
  }

  /******************************************************************
   * Trail
   ******************************************************************/

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

      $("<div>Description (optional): </div>").appendTo(place);
      $("<input class='trailDescription'/>")
      .val(store.getTiddlerText(tiddler.title+"##Description")||"")
      .keyup(function() {
        editTrail.updateText(this);
      })
      .appendTo(place);

      renderBookmarks(place, tiddler, true);

    },

    updateText: function(src) {

      var $trailTiddler = $(src).parents(".trailEditTiddler");
      console.log($trailTiddler);
      var text = "";

      var description = $trailTiddler.find(".trailDescription").val();
      if ($.trim(description).length) text+="\n!Description\n"+description;

      var $bookmarkLinks = $trailTiddler.find(".bookmarkLink");
      if ($bookmarkLinks.length) {
        text += "\n!Bookmarks\n";
        $bookmarkLinks.each(function(i, bookmarkLink) {
          text += "[[" + $(bookmarkLink).html() + "]]";
          var note = $(bookmarkLink).parents("tr").find(".bookmarkNote").val();
          console.log(note);
          if (note.length) text+=" " + note;
          text += "\n";
        });
      }

      $trailTiddler.find(".editor").find("textArea").val(text);

    }

  }

  config.macros.bookmarksView = {
    handler: function(place,macroName,params,wikifier,paramString,tiddler) {
      renderBookmarks(place, tiddler, false);
    }
  }

  function renderBookmarks(place, tiddler, editable) {
      var trail = version.extensions.TrailPlugin.extractTrail(tiddler);
      $("<h2>Bookmarks on this Trail</h2>").appendTo(place);
      var $bookmarkTable = $("<table/>").appendTo(place);
      var $bookmarkList = $("<tbody/>").appendTo($bookmarkTable);
      $.each(trail.bookmarks, function(i, bookmark) {
        var $bookmarkItem = $("<tr/>").appendTo($bookmarkList);
        $bookmarkLinkCell = $("<td class='bookmarkLinkCell' />").appendTo($bookmarkItem);
        var $bookmarkLink;
        if (editable) {
          $bookmarkLink = $("<span/>").appendTo($bookmarkLinkCell);
        } else {
          $bookmarkLink = $(createTiddlyLink($bookmarkLinkCell.get(0), bookmark.title, true));
          console.log($bookmarkLink)
        }
        $bookmarkLink.addClass("bookmarkLink").html(bookmark.title)

        var $noteCell = $("<td/>").appendTo($bookmarkItem);
        var $note;
        if (editable) {
          var $note = $("<textarea type='text' class='bookmarkNote'/>")
            .val(bookmark.note)
            .keyup(function() {
              editTrail.updateText(this);
            })
        } else {
          $note = $("<span>"+bookmark.note+"</span>")
            // this is a "hidden feature" where the user can click on bookmark text
            // and when they launch the player, it 
            .click(function() {
              tiddler.recentBookmark = bookmark;
            });
        }
        $note.appendTo($noteCell)
      });

      if (editable) {
        $bookmarkTable.tableDnD({
          onDrop: function(table, row) {
            editTrail.updateText(row);
          },
          dragHandle: ".bookmarkLink"
        });
      }

  }

  /******************************************************************
   * General Utility
   ******************************************************************/

  function log() { if (console && console.log) console.log.apply(console, arguments); }

  _getValue = store.getValue;
  store.getValue = function(tiddler, field, value) {
    tiddler = store.resolveTiddler(tiddler);
    var sectionContent = store.getTiddlerText(tiddler.title+"##"+field);
    if (sectionContent) return sectionContent;
    var sliceContent = store.getTiddlerText(tiddler.title+"::"+field);
    if (sliceContent) return sliceContent;
    return _getValue.apply(this, arguments);
  }

})(jQuery);
(function($) {

  config.macros.scrumptiousTagTitle = {
    handler: function(place,macroName,params,wikifier,paramString,tiddler) {
      $("<div class='tagTitle'/>").html(tiddler.title.substr(5)).appendTo(place);
    }
  };

  config.macros.scrumptiousTagging = {
    handler: function(place,macroName,params,wikifier,paramString,tiddler) {
      var tagName = tiddler.title;
      if (tagName.substr(0,5)!="user_") return;
      tagName = tagName.substr(5);
      var $tagging = $("<div/>").appendTo(place);
      showTiddlersInCategory("bookmark", "user_"+tagName, "bookmarks", $tagging);
      showTiddlersInCategory("trail", "user_"+tagName, "trails", $tagging);
    }
  };

  function showTiddlersInCategory(categoryTag, userTag, pluralLabel, $tagging) {
    console.log("-----", userTag,store.getTaggedTiddlers(userTag));
    var tiddlersInCategory = _.filter(store.getTaggedTiddlers(userTag), function(tiddler) {
      return tiddler.isTagged(categoryTag);
    });
    if (!tiddlersInCategory.length) return;
    // TODO show tag as tiddlylink (for styling consistency) ie use createTiddlyLink here
    $tagging.append("<div class='taggingIntro'><span class='tag'>" + userTag.substr(5) + "</span> <span class='tagCategory'>" + pluralLabel + " with this tag</span></div>");
    var $taggedTiddler = $("<div class='taggedTiddlers' />").appendTo($tagging);
    $.each(tiddlersInCategory, function(i,tiddler) {
      $link = $("<span />").appendTo($taggedTiddler);
      var link = createTiddlyLink($link.get(0), tiddler.title, true);
      link.className = categoryTag+"Link";
      $("<span> </span>").appendTo($taggedTiddler);
    });
  }

  config.macros.scrumptiousTags = {
    handler: function(place,macroName,params,wikifier,paramString,tiddler) {
      $.each(tiddler.tags, function(i, tag) {
        if (tag.substr(0,5)=="user_") {
          var $tag = $("<span class='tag' />").appendTo(place);
          var link = createTiddlyLink($tag.get(0), tag, true);
          link.innerHTML = tag.substr(5);
          $("<span> </span>").appendTo(place);
        }
      });
    }
  };

  var _chooseTemplateForTiddler = Story.prototype.chooseTemplateForTiddler;
  Story.prototype.chooseTemplateForTiddler = function(title,template) {
    //console.log("choose", arguments);
    if (title.substr(0,5)=="user_") return "userTagViewTemplate";
    //console.log("returning");
    return _chooseTemplateForTiddler.apply(this, arguments);
  };

})(jQuery);
(function($) {
  config.macros.playTrail = {
    handler: function(place,macroName,params,wikifier,paramString,tiddler) {
      var title = params[0] || "Play This Trail!";
      $("<span>"+title+"</span>")
      .click(function() {
        version.extensions.overlay.toggle();
        $.switchTrailPlayer(tiddler.title, tiddler.recentBookmark.title);
        // recentBookmark is a "hidden feature" - see renderBookmarks()
      })
      .appendTo(place);
    }
  }
})(jQuery);
/**
 * TableDnD plug-in for JQuery, allows you to drag and drop table rows
 * You can set up various options to control how the system will work
 * Copyright (c) Denis Howlett <denish@isocra.com>
 * Licensed like jQuery, see http://docs.jquery.com/License.
 *
 * Configuration options:
 * 
 * onDragStyle
 *     This is the style that is assigned to the row during drag. There are limitations to the styles that can be
 *     associated with a row (such as you can't assign a border--well you can, but it won't be
 *     displayed). (So instead consider using onDragClass.) The CSS style to apply is specified as
 *     a map (as used in the jQuery css(...) function).
 * onDropStyle
 *     This is the style that is assigned to the row when it is dropped. As for onDragStyle, there are limitations
 *     to what you can do. Also this replaces the original style, so again consider using onDragClass which
 *     is simply added and then removed on drop.
 * onDragClass
 *     This class is added for the duration of the drag and then removed when the row is dropped. It is more
 *     flexible than using onDragStyle since it can be inherited by the row cells and other content. The default
 *     is class is tDnD_whileDrag. So to use the default, simply customise this CSS class in your
 *     stylesheet.
 * onDrop
 *     Pass a function that will be called when the row is dropped. The function takes 2 parameters: the table
 *     and the row that was dropped. You can work out the new order of the rows by using
 *     table.rows.
 * onDragStart
 *     Pass a function that will be called when the user starts dragging. The function takes 2 parameters: the
 *     table and the row which the user has started to drag.
 * onAllowDrop
 *     Pass a function that will be called as a row is over another row. If the function returns true, allow 
 *     dropping on that row, otherwise not. The function takes 2 parameters: the dragged row and the row under
 *     the cursor. It returns a boolean: true allows the drop, false doesn't allow it.
 * scrollAmount
 *     This is the number of pixels to scroll if the user moves the mouse cursor to the top or bottom of the
 *     window. The page should automatically scroll up or down as appropriate (tested in IE6, IE7, Safari, FF2,
 *     FF3 beta
 * dragHandle
 *     This is the name of a class that you assign to one or more cells in each row that is draggable. If you
 *     specify this class, then you are responsible for setting cursor: move in the CSS and only these cells
 *     will have the drag behaviour. If you do not specify a dragHandle, then you get the old behaviour where
 *     the whole row is draggable.
 * 
 * Other ways to control behaviour:
 *
 * Add class="nodrop" to any rows for which you don't want to allow dropping, and class="nodrag" to any rows
 * that you don't want to be draggable.
 *
 * Inside the onDrop method you can also call $.tableDnD.serialize() this returns a string of the form
 * <tableID>[]=<rowID1>&<tableID>[]=<rowID2> so that you can send this back to the server. The table must have
 * an ID as must all the rows.
 *
 * Other methods:
 *
 * $("...").tableDnDUpdate() 
 * Will update all the matching tables, that is it will reapply the mousedown method to the rows (or handle cells).
 * This is useful if you have updated the table rows using Ajax and you want to make the table draggable again.
 * The table maintains the original configuration (so you don't have to specify it again).
 *
 * $("...").tableDnDSerialize()
 * Will serialize and return the serialized string as above, but for each of the matching tables--so it can be
 * called from anywhere and isn't dependent on the currentTable being set up correctly before calling
 *
 * Known problems:
 * - Auto-scoll has some problems with IE7  (it scrolls even when it shouldn't), work-around: set scrollAmount to 0
 * 
 * Version 0.2: 2008-02-20 First public version
 * Version 0.3: 2008-02-07 Added onDragStart option
 *                         Made the scroll amount configurable (default is 5 as before)
 * Version 0.4: 2008-03-15 Changed the noDrag/noDrop attributes to nodrag/nodrop classes
 *                         Added onAllowDrop to control dropping
 *                         Fixed a bug which meant that you couldn't set the scroll amount in both directions
 *                         Added serialize method
 * Version 0.5: 2008-05-16 Changed so that if you specify a dragHandle class it doesn't make the whole row
 *                         draggable
 *                         Improved the serialize method to use a default (and settable) regular expression.
 *                         Added tableDnDupate() and tableDnDSerialize() to be called when you are outside the table
 */
jQuery.tableDnD = {
    /** Keep hold of the current table being dragged */
    currentTable : null,
    /** Keep hold of the current drag object if any */
    dragObject: null,
    /** The current mouse offset */
    mouseOffset: null,
    /** Remember the old value of Y so that we don't do too much processing */
    oldY: 0,

    /** Actually build the structure */
    build: function(options) {
        // Set up the defaults if any

        this.each(function() {
            // This is bound to each matching table, set up the defaults and override with user options
            this.tableDnDConfig = jQuery.extend({
                onDragStyle: null,
                onDropStyle: null,
				// Add in the default class for whileDragging
				onDragClass: "tDnD_whileDrag",
                onDrop: null,
                onDragStart: null,
                scrollAmount: 5,
				serializeRegexp: /[^\-]*$/, // The regular expression to use to trim row IDs
				serializeParamName: null, // If you want to specify another parameter name instead of the table ID
                dragHandle: null // If you give the name of a class here, then only Cells with this class will be draggable
            }, options || {});
            // Now make the rows draggable
            jQuery.tableDnD.makeDraggable(this);
        });

        // Now we need to capture the mouse up and mouse move event
        // We can use bind so that we don't interfere with other event handlers
        jQuery(document)
            .bind('mousemove', jQuery.tableDnD.mousemove)
            .bind('mouseup', jQuery.tableDnD.mouseup);

        // Don't break the chain
        return this;
    },

    /** This function makes all the rows on the table draggable apart from those marked as "NoDrag" */
    makeDraggable: function(table) {
        var config = table.tableDnDConfig;
    // mahemoff - patched this so dragHandle is *child* of a TD, not a TD itself
    // also, dragHandle is now a jQuery selector, not a class name
		if (table.tableDnDConfig.dragHandle) {
			// We only need to add the event to the specified cells
			jQuery(table.tableDnDConfig.dragHandle, table).each(function() {
				// The cell is bound to "this"
                var cell = this.parentNode;
                console.log("cell", cell);
                jQuery(cell).mousedown(function(ev) {
                    jQuery.tableDnD.dragObject = this.parentNode;
                    jQuery.tableDnD.currentTable = table;
                    jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev);
                    if (config.onDragStart) {
                        // Call the onDrop method if there is one
                        config.onDragStart(table, this);
                    }
                    return false;
                });
			})
		} else {
      // mahemoff - I patched this but am no longer using it (and it's probably wrong anyway)
      // using the "true" branch above
			// For backwards compatibility, we add the event to the whole row
	        var rows = jQuery("tr", table); // get all the rows as a wrapped set
	        rows.each(function() {
				// Iterate through each row, the row is bound to "this"
				var row = jQuery(this);
				if (! row.hasClass("nodrag")) {
	                row.mousedown(function(ev) {
                      var $target = jQuery(ev.target);
                      if (! $target.hasClass("bookmarkLink")) return false;
                      row = $target.parents("tr").get(0);
                      console.log("row", row);
                      jQuery.tableDnD.dragObject = row;
                      jQuery.tableDnD.currentTable = table;
                      jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(row, ev);
                      if (config.onDragStart) {
                          // Call the onDrop method if there is one
                          config.onDragStart(table, row);
                      }
                      return true;
	                }).css("cursor", "move"); // Store the tableDnD object
				}
			});
		}
	},

	updateTables: function() {
		this.each(function() {
			// this is now bound to each matching table
			if (this.tableDnDConfig) {
				jQuery.tableDnD.makeDraggable(this);
			}
		})
	},

    /** Get the mouse coordinates from the event (allowing for browser differences) */
    mouseCoords: function(ev){
        if(ev.pageX || ev.pageY){
            return {x:ev.pageX, y:ev.pageY};
        }
        return {
            x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
            y:ev.clientY + document.body.scrollTop  - document.body.clientTop
        };
    },

    /** Given a target element and a mouse event, get the mouse offset from that element.
        To do this we need the element's position and the mouse position */
    getMouseOffset: function(target, ev) {
        ev = ev || window.event;

        var docPos    = this.getPosition(target);
        var mousePos  = this.mouseCoords(ev);
        return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};
    },

    /** Get the position of an element by going up the DOM tree and adding up all the offsets */
    getPosition: function(e){
        var left = 0;
        var top  = 0;
        /** Safari fix -- thanks to Luis Chato for this! */
        if (e.offsetHeight == 0) {
            /** Safari 2 doesn't correctly grab the offsetTop of a table row
            this is detailed here:
            http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/
            the solution is likewise noted there, grab the offset of a table cell in the row - the firstChild.
            note that firefox will return a text node as a first child, so designing a more thorough
            solution may need to take that into account, for now this seems to work in firefox, safari, ie */
            e = e.firstChild; // a table cell
        }

        while (e.offsetParent){
            left += e.offsetLeft;
            top  += e.offsetTop;
            e     = e.offsetParent;
        }

        left += e.offsetLeft;
        top  += e.offsetTop;

        return {x:left, y:top};
    },

    mousemove: function(ev) {
        if (jQuery.tableDnD.dragObject == null) {
            return;
        }

        var dragObj = jQuery(jQuery.tableDnD.dragObject);
        var config = jQuery.tableDnD.currentTable.tableDnDConfig;
        var mousePos = jQuery.tableDnD.mouseCoords(ev);
        var y = mousePos.y - jQuery.tableDnD.mouseOffset.y;
        //auto scroll the window
	    var yOffset = window.pageYOffset;
	 	if (document.all) {
	        // Windows version
	        //yOffset=document.body.scrollTop;
	        if (typeof document.compatMode != 'undefined' &&
	             document.compatMode != 'BackCompat') {
	           yOffset = document.documentElement.scrollTop;
	        }
	        else if (typeof document.body != 'undefined') {
	           yOffset=document.body.scrollTop;
	        }

	    }
		    
		if (mousePos.y-yOffset < config.scrollAmount) {
	    	window.scrollBy(0, -config.scrollAmount);
	    } else {
            var windowHeight = window.innerHeight ? window.innerHeight
                    : document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight;
            if (windowHeight-(mousePos.y-yOffset) < config.scrollAmount) {
                window.scrollBy(0, config.scrollAmount);
            }
        }


        if (y != jQuery.tableDnD.oldY) {
            // work out if we're going up or down...
            var movingDown = y > jQuery.tableDnD.oldY;
            // update the old value
            jQuery.tableDnD.oldY = y;
            // update the style to show we're dragging
			if (config.onDragClass) {
				dragObj.addClass(config.onDragClass);
			} else {
	            dragObj.css(config.onDragStyle);
			}
            // If we're over a row then move the dragged row to there so that the user sees the
            // effect dynamically
            var currentRow = jQuery.tableDnD.findDropTargetRow(dragObj, y);
            if (currentRow) {
                // TODO worry about what happens when there are multiple TBODIES
                if (movingDown && jQuery.tableDnD.dragObject != currentRow) {
                    jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow.nextSibling);
                } else if (! movingDown && jQuery.tableDnD.dragObject != currentRow) {
                    jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow);
                }
            }
        }

        return false;
    },

    /** We're only worried about the y position really, because we can only move rows up and down */
    findDropTargetRow: function(draggedRow, y) {
        var rows = jQuery.tableDnD.currentTable.rows;
        for (var i=0; i<rows.length; i++) {
            var row = rows[i];
            var rowY    = this.getPosition(row).y;
            var rowHeight = parseInt(row.offsetHeight)/2;
            if (row.offsetHeight == 0) {
                rowY = this.getPosition(row.firstChild).y;
                rowHeight = parseInt(row.firstChild.offsetHeight)/2;
            }
            // Because we always have to insert before, we need to offset the height a bit
            if ((y > rowY - rowHeight) && (y < (rowY + rowHeight))) {
                // that's the row we're over
				// If it's the same as the current row, ignore it
				if (row == draggedRow) {return null;}
                var config = jQuery.tableDnD.currentTable.tableDnDConfig;
                if (config.onAllowDrop) {
                    if (config.onAllowDrop(draggedRow, row)) {
                        return row;
                    } else {
                        return null;
                    }
                } else {
					// If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic)
                    var nodrop = jQuery(row).hasClass("nodrop");
                    if (! nodrop) {
                        return row;
                    } else {
                        return null;
                    }
                }
                return row;
            }
        }
        return null;
    },

    mouseup: function(e) {
        if (jQuery.tableDnD.currentTable && jQuery.tableDnD.dragObject) {
            var droppedRow = jQuery.tableDnD.dragObject;
            var config = jQuery.tableDnD.currentTable.tableDnDConfig;
            // If we have a dragObject, then we need to release it,
            // The row will already have been moved to the right place so we just reset stuff
			if (config.onDragClass) {
	            jQuery(droppedRow).removeClass(config.onDragClass);
			} else {
	            jQuery(droppedRow).css(config.onDropStyle);
			}
            jQuery.tableDnD.dragObject   = null;
            if (config.onDrop) {
                // Call the onDrop method if there is one
                config.onDrop(jQuery.tableDnD.currentTable, droppedRow);
            }
            jQuery.tableDnD.currentTable = null; // let go of the table too
        }
    },

    serialize: function() {
        if (jQuery.tableDnD.currentTable) {
            return jQuery.tableDnD.serializeTable(jQuery.tableDnD.currentTable);
        } else {
            return "Error: No Table id set, you need to set an id on your table and every row";
        }
    },

	serializeTable: function(table) {
        var result = "";
        var tableId = table.id;
        var rows = table.rows;
        for (var i=0; i<rows.length; i++) {
            if (result.length > 0) result += "&";
            var rowId = rows[i].id;
            if (rowId && rowId && table.tableDnDConfig && table.tableDnDConfig.serializeRegexp) {
                rowId = rowId.match(table.tableDnDConfig.serializeRegexp)[0];
            }

            result += tableId + '[]=' + rowId;
        }
        return result;
	},

	serializeTables: function() {
        var result = "";
        this.each(function() {
			// this is now bound to each matching table
			result += jQuery.tableDnD.serializeTable(this);
		});
        return result;
    }

}

jQuery.fn.extend(
	{
		tableDnD : jQuery.tableDnD.build,
		tableDnDUpdate : jQuery.tableDnD.updateTables,
		tableDnDSerialize: jQuery.tableDnD.serializeTables
	}
);
Welcome!

TODO show recently added bookmarks and trails
hello
<<trails>>
<<list filter [tag[bookmark]] [sort[modified]] >>
Welcome to Scrumptious. Here you can store bookmarks and build up "trails" of websites for others to follow. You can leave a description for each bookmark and trail, and there are also user comments attached to them. More info at [[scrumptious.tv|http://scrumptious.tv]].

Scrumptious was built by [[Michael Mahemoff|http://mahemoff.com]] at [[Osmosoft|http://osmosoft.com]] and is free for you to use under the [[permissive MIT License|http://creativecommons.org/licenses/MIT/]].

!Bookmarks
<<tagCloud filterTag:bookmark quantity:20 prefix:"user_">>

!Trails
<<tagCloud filterTag:trail quantity:20 prefix:"user_">>
Scrumptious is a bookmarking service focused on collaboration and flexibility. Its similar to services like Delicious, but has its own unique take, and has a certain aesthetic that will be familiar to anyone who's used TiddlyWiki.

!Features for Users

* Tagging, descriptions, and nested comments for each bookmark.
* Supports not just bookmarks, but trails. Trails are lists of bookmarks, each with an optional annotation. Like bookmarks, trails hve tags, descriptions, and nested comments.
* Trail Player - Scrumptious can flip back and forth between regular mode and Trail Player mode. Through the magic of "iFrames"&#0153;, the player lets you easily navigate through a trail - hit the next button to jump to the next site in the trail. Each site you see is accompanied by notes from the 
* Tag clouds. Shiny.
* Searching and browsing.

!Features for Installers and Extenders

* Works in standalone mode, on a local file:// URL; running on a server as published, static, HTML content; or as a multi-user, interactive, web application.
* Completely hackable. All the interaction is coded in client-side Javascript as Scrumptious is a TiddlyWiki vertical, making it easy to customise. You can even customise simple things like the [[SiteTitle]], [[DefaultTiddlers]], and [[ColorPalette]] without even knowing Javascript. And you can also introduce TiddlyWiki plugins from elsewhere.
* Open source, under the permissive [[MIT License]], which means you can happily install it on your own server, and modify it to your heart's content.
* Compatible with all major browsers (note: IE6 will be supported, but still needs work).

!Philosophy: Surfing with Intent

Scrumptious shares a similar philosophy to the rest of the TiddlyVerse. I envsion two styles of Scrumptious instance:
* Task-focused: When we use the web, we're often "Surfing With Intent" (Paul Downey's term). We're hunting for specific information and collecting a bunch of resources along the way. Scrumptious is ideal for gathering those resources. e.g. a house-hunting family (or individual) would create a space with all the interesting homes they've spotted on the web.
* Individual: You can also use Scrumptious as a repository of your own bookmarks. In the context of TiddlyWiki hosting environments like the in-progress TiddlySpace, others will be able to "follow" your bookmarks this way. You could, for example, build up a collection of bookmarks in your team by aggregating everyone's bookmarks (perhaps filtering on a particular tag).

!Limitations

At this stage, the expectation is that each Scrumptious instance will include at most a few hundred bookmarks. This is consistent with the philosophy expressed above. It's always trivial to create a new space for a new set of bookmarks, whether in file:// or collaboritive, online, mode (once we've built http://tiddlyspace.com !)

!TODO

* "Play this trail" (with hidden feature - if you click on a bookmark in the trail tiddler first, trail starts on that bookmark)
* add/remove bookmarks in trail
* Bookmarklet
* Comments shown on trail and can submit
* Better "list of trails/bookmarks" support - order by name, date, etc
* Also list of comments - esp. recent comments
* "This bookmark appears in these trails"
* Delicious/Instapaper sync.
* IE-compatible

!More Info

See also http://scrumptious.tv.
/*
  BetterListPlugin
*/

/***
|Name|BetterListPlugin|
|Description||
|Source||
|Documentation||
|Version|0.1|
|Author|Michael Mahemoff, Osmosoft|
|''License:''|[[BSD open source license]]|
|~CoreVersion|2.2|
***/

(function($) {

  version.extensions.BetterListPlugin = {installed:true};

  var macro = config.macros.betterList = {


  init: function() {
    // var stylesheet = store.getTiddlerText(tiddler.title + "##StyleSheet");
    // config.shadowTiddlers["StyleSheetBetterListPlugin"] = stylesheet;
    // store.addNotification("StyleSheetBetterListPlugin", refreshStyles);
  },

  handler: function(place,macroName,params,wikifier,paramString,tiddler) {
    var format, macroParams = paramString.parseParams();
    
    var matches = paramString.match(/^(.*)(format:)(\S+)(.*)$/);
    console.log("par", paramString);
    if (matches) {
      console.log("matches", matches);
      format = matches[3]; 
      paramString = matches[1]+matches[4];
      console.log("for", format);
    }
    console.log("pam", paramString);
    params = paramString.split(/\s+/);
    var type = params[0] || "all";

    $(place).addClass("betterList");
    if (!config.macros.list[type].handler) return;
    var tiddlers = config.macros.list[type].handler(params);
    if (tiddlers.length) {
      if (format=="raw") {
        $.each(tiddlers, function(i, tiddler) { $(place).append(tiddler.title+"\n"); })
      } else {
        var $list = $("<ul/>").appendTo(place);
        $.each(tiddlers, function(i, tiddler) {
          var $li = $("<li/>").appendTo($list);
          createTiddlyLink($li.get(0), tiddler.title || tiddler, true);
        });
      }
    } else {
      $("<div/>").text(getParam(macroParams, "emptyMessage") || "no matches").appendTo(place);
    }
  }


}
})(jQuery);


/***
!StyleSheet

!(end of StyleSheet)

***/

/*}}}*/
/***
|Name|TrailPlugin|
|Description||
|Source||
|Documentation||
|Version|0.1|
|Author|Michael Mahemoff, Osmosoft|
|''License:''|[[BSD open source license]]|
|~CoreVersion|2.2|

!TrailTemplate
<h3 class="tiddlerLink" tiddlerTitle="<%= trail.name %>"><%= trail.name %></h3>
<strong><%= trail.description %></strong>
<dl class="trail">
<% for (var i=0; i<trail.bookmarks.length; i++) { bookmark = trail.bookmarks[i]; %>
 <div class="bookmark">
   <dt><a href="<%= bookmark.url %>"><%= bookmark.name %></a></dt>
   <dd>
     <%= bookmark.note %>
     <span class="tiddlerLink" tiddlerTitle="<%= bookmark.title %>">#</span>
   </dd>
 </div>
<% } %>
</dl>

!StyleSheet
.bookmark { margin: 20px 0; }
.trail dt { margin: 25px 0; padding: 5px; font-weight: normal; background: #008; color: white; display: inline; }
.trail dt a { color: white; }
.trail dd { margin-left: 0; color: #008; padding: 5px 0; display: inline; }
.trail dd .tiddlerLink { padding: 8px 8px 8px 2px; opacity: 0.5; cursor: pointer; font-size: 0.7em; font-style: italic; }
!Javascript
{{{
***/

(function($) {

  var plugin = version.extensions.TrailPlugin = {installed:true};

  var macro = config.macros.trail = {

    init: function() {
      var stylesheet = store.getTiddlerText("TrailPlugin##StyleSheet");
      config.shadowTiddlers["StyleSheetTrailPlugin"] = stylesheet;
      store.addNotification("StyleSheetTrailPlugin", refreshStyles);
    },

    handler: function(place,macroName,params,wikifier,paramString,tiddler) {
      tiddler = store.getTiddler(paramString);
      var trail = version.extensions.TrailPlugin.extractTrail(tiddler);
      $(place).append(tmpl("TrailPlugin##TrailTemplate", {trail:trail }));
      $(".tiddlerLink").click(function() {
        story.displayTiddler("top", this.getAttribute("tiddlerTitle"));
      });
    }

  };

  config.macros.trails = {
    handler: function(place) {
      // $.each(store.getTaggedTiddlers("trail"), function(i, trailTiddler) {
      $.each(plugin.findTrails(["todo"]), function(i, trailTiddler) {
        invokeMacro($("<div/>").appendTo(place).get(0), "trail", trailTiddler.title);
      });
    }
  };

  // If "tags" argument is a list (with at least one element), will only return trails matching at least
  // one of those tags. Implementation of filtering is slower than it could be.
  version.extensions.TrailPlugin.findTrails = function(userTags) {
    if (!userTags) userTags = [];
    if (typeof(userTags)=="string") userTags = [userTags];
    var tags = _.map(userTags, function(userTag) { return "user_"+userTag.replace(/^user_/, "") });
    var allTrails = store.getTaggedTiddlers("trail");
    if (!tags.length) return allTrails;
    return _.select(allTrails, function(trail) {
      return _.any(trail.tags, function(tag) { return tags.indexOf(tag)!=-1; });
    });
  }

  version.extensions.TrailPlugin.extractTrail = function(trailTiddler) {
    trailTiddler = store.getTiddler(trailTiddler.title) || trailTiddler;
    var trail = {
      description: store.getTiddlerText(trailTiddler.title+"##Description"),
      name: trailTiddler.title,
      bookmarks: []
    };
    var bookmarkSpec = /\[\[(.*?)\]\](.*)/g;
    var bookmarkMatch;
    while (bookmarkMatch = bookmarkSpec.exec(store.getTiddlerText(trailTiddler.title+"##Bookmarks"))) {
      var bookmarkTitle = bookmarkMatch[1];
      trail.bookmarks.push({
        title: bookmarkTitle,
        url: slice(bookmarkTitle, "url"),
        name: slice(bookmarkTitle, "name") || bookmarkTitle,
        note: $.trim(bookmarkMatch[2]),
        description: section(bookmarkTitle, "Description"),
      });
    };
    return trail;
  }

  var cache = {};
  function tmpl(str, data){

    // Figure out if we're getting a template, or if we need to
    // load the template - and be sure to cache the result.
    var fn = !/\W/.test(str) ?
      cache[str] = cache[str] ||
        // tmpl(document.getElementById(str).innerHTML) :
        tmpl(store.getTiddlerText(str)) :
      
      // Generate a reusable function that will serve as a template
      // generator (and which will be cached).
      new Function("obj",
        "var p=[],print=function(){p.push.apply(p,arguments);};" +
        
        // Introduce the data as local variables using with(){}
        "with(obj){p.push('" +
        
        // Convert the template into pure JavaScript
        store.getTiddlerText(str)
          .replace(/[\r\t\n]/g, " ")
          .split("<%").join("\t")
          .replace(/((^|%>)[^\t]*)'/g, "$1\r")
          .replace(/\t=(.*?)%>/g, "',$1,'")
          .split("\t").join("');")
          .split("%>").join("p.push('")
          .split("\r").join("\\'")
      + "');}return p.join('');");
    
    // Provide some basic currying to the user
    return data ? fn( data ) : fn;
  };
  version.extensions.microtemplate = tmpl;

  function log() { if (window.console) console.log.apply(console, arguments); }
  function slice(tiddler, key) { return store.getTiddlerText(tiddler+"::"+key); }
  function section(tiddler, key) { return store.getTiddlerText(tiddler+"##"+key); }

})(jQuery);

// underscore
(function(){var j=this,n=j._,i=function(a){this._wrapped=a},m=typeof StopIteration!=="undefined"?StopIteration:"__break__",b=j._=function(a){return new i(a)};if(typeof exports!=="undefined")exports._=b;var k=Array.prototype.slice,o=Array.prototype.unshift,p=Object.prototype.toString,q=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;b.VERSION="0.5.7";b.each=function(a,c,d){try{if(a.forEach)a.forEach(c,d);else if(b.isArray(a)||b.isArguments(a))for(var e=0,f=a.length;e<f;e++)c.call(d,
a[e],e,a);else{var g=b.keys(a);f=g.length;for(e=0;e<f;e++)c.call(d,a[g[e]],g[e],a)}}catch(h){if(h!=m)throw h;}return a};b.map=function(a,c,d){if(a&&b.isFunction(a.map))return a.map(c,d);var e=[];b.each(a,function(f,g,h){e.push(c.call(d,f,g,h))});return e};b.reduce=function(a,c,d,e){if(a&&b.isFunction(a.reduce))return a.reduce(b.bind(d,e),c);b.each(a,function(f,g,h){c=d.call(e,c,f,g,h)});return c};b.reduceRight=function(a,c,d,e){if(a&&b.isFunction(a.reduceRight))return a.reduceRight(b.bind(d,e),c);
var f=b.clone(b.toArray(a)).reverse();b.each(f,function(g,h){c=d.call(e,c,g,h,a)});return c};b.detect=function(a,c,d){var e;b.each(a,function(f,g,h){if(c.call(d,f,g,h)){e=f;b.breakLoop()}});return e};b.select=function(a,c,d){if(a&&b.isFunction(a.filter))return a.filter(c,d);var e=[];b.each(a,function(f,g,h){c.call(d,f,g,h)&&e.push(f)});return e};b.reject=function(a,c,d){var e=[];b.each(a,function(f,g,h){!c.call(d,f,g,h)&&e.push(f)});return e};b.all=function(a,c,d){c=c||b.identity;if(a&&b.isFunction(a.every))return a.every(c,
d);var e=true;b.each(a,function(f,g,h){(e=e&&c.call(d,f,g,h))||b.breakLoop()});return e};b.any=function(a,c,d){c=c||b.identity;if(a&&b.isFunction(a.some))return a.some(c,d);var e=false;b.each(a,function(f,g,h){if(e=c.call(d,f,g,h))b.breakLoop()});return e};b.include=function(a,c){if(b.isArray(a))return b.indexOf(a,c)!=-1;var d=false;b.each(a,function(e){if(d=e===c)b.breakLoop()});return d};b.invoke=function(a,c){var d=b.rest(arguments,2);return b.map(a,function(e){return(c?e[c]:e).apply(e,d)})};b.pluck=
function(a,c){return b.map(a,function(d){return d[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);var e={computed:-Infinity};b.each(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g>=e.computed&&(e={value:f,computed:g})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};b.each(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g<e.computed&&(e={value:f,computed:g})});return e.value};b.sortBy=function(a,c,d){return b.pluck(b.map(a,
function(e,f,g){return{value:e,criteria:c.call(d,e,f,g)}}).sort(function(e,f){e=e.criteria;f=f.criteria;return e<f?-1:e>f?1:0}),"value")};b.sortedIndex=function(a,c,d){d=d||b.identity;for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?(e=g+1):(f=g)}return e};b.toArray=function(a){if(!a)return[];if(a.toArray)return a.toArray();if(b.isArray(a))return a;if(b.isArguments(a))return k.call(a);return b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=function(a,c,d){return c&&!d?k.call(a,
0,c):a[0]};b.rest=function(a,c,d){return k.call(a,b.isUndefined(c)||d?1:c)};b.last=function(a){return a[a.length-1]};b.compact=function(a){return b.select(a,function(c){return!!c})};b.flatten=function(a){return b.reduce(a,[],function(c,d){if(b.isArray(d))return c.concat(b.flatten(d));c.push(d);return c})};b.without=function(a){var c=b.rest(arguments);return b.select(a,function(d){return!b.include(c,d)})};b.uniq=function(a,c){return b.reduce(a,[],function(d,e,f){if(0==f||(c===true?b.last(d)!=e:!b.include(d,
e)))d.push(e);return d})};b.intersect=function(a){var c=b.rest(arguments);return b.select(b.uniq(a),function(d){return b.all(c,function(e){return b.indexOf(e,d)>=0})})};b.zip=function(){for(var a=b.toArray(arguments),c=b.max(b.pluck(a,"length")),d=new Array(c),e=0;e<c;e++)d[e]=b.pluck(a,String(e));return d};b.indexOf=function(a,c){if(a.indexOf)return a.indexOf(c);for(var d=0,e=a.length;d<e;d++)if(a[d]===c)return d;return-1};b.lastIndexOf=function(a,c){if(a.lastIndexOf)return a.lastIndexOf(c);for(var d=
a.length;d--;)if(a[d]===c)return d;return-1};b.range=function(a,c,d){var e=b.toArray(arguments),f=e.length<=1;a=f?0:e[0];c=f?e[0]:e[1];d=e[2]||1;e=Math.ceil((c-a)/d);if(e<=0)return[];e=new Array(e);f=a;for(var g=0;;f+=d){if((d>0?f-c:c-f)>=0)return e;e[g++]=f}};b.bind=function(a,c){var d=b.rest(arguments,2);return function(){return a.apply(c||j,d.concat(b.toArray(arguments)))}};b.bindAll=function(a){var c=b.rest(arguments);if(c.length==0)c=b.functions(a);b.each(c,function(d){a[d]=b.bind(a[d],a)});
return a};b.delay=function(a,c){var d=b.rest(arguments,2);return setTimeout(function(){return a.apply(a,d)},c)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(b.rest(arguments)))};b.wrap=function(a,c){return function(){var d=[a].concat(b.toArray(arguments));return c.apply(c,d)}};b.compose=function(){var a=b.toArray(arguments);return function(){for(var c=b.toArray(arguments),d=a.length-1;d>=0;d--)c=[a[d].apply(this,c)];return c[0]}};b.keys=function(a){if(b.isArray(a))return b.range(0,a.length);
var c=[];for(var d in a)q.call(a,d)&&c.push(d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=function(a){return b.select(b.keys(a),function(c){return b.isFunction(a[c])}).sort()};b.extend=function(a,c){for(var d in c)a[d]=c[d];return a};b.clone=function(a){if(b.isArray(a))return a.slice(0);return b.extend({},a)};b.tap=function(a,c){c(a);return a};b.isEqual=function(a,c){if(a===c)return true;var d=typeof a;if(d!=typeof c)return false;if(a==c)return true;if(!a&&c||a&&!c)return false;
if(a.isEqual)return a.isEqual(c);if(b.isDate(a)&&b.isDate(c))return a.getTime()===c.getTime();if(b.isNaN(a)&&b.isNaN(c))return true;if(b.isRegExp(a)&&b.isRegExp(c))return a.source===c.source&&a.global===c.global&&a.ignoreCase===c.ignoreCase&&a.multiline===c.multiline;if(d!=="object")return false;if(a.length&&a.length!==c.length)return false;d=b.keys(a);var e=b.keys(c);if(d.length!=e.length)return false;for(var f in a)if(!b.isEqual(a[f],c[f]))return false;return true};b.isEmpty=function(a){return b.keys(a).length==
0};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=function(a){return!!(a&&a.concat&&a.unshift)};b.isArguments=function(a){return a&&b.isNumber(a.length)&&!a.concat&&!a.substr&&!a.apply&&!r.call(a,"length")};b.isFunction=function(a){return!!(a&&a.constructor&&a.call&&a.apply)};b.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};b.isNumber=function(a){return a===+a||p.call(a)==="[object Number]"};b.isDate=function(a){return!!(a&&a.getTimezoneOffset&&a.setUTCFullYear)};
b.isRegExp=function(a){return!!(a&&a.test&&a.exec&&(a.ignoreCase||a.ignoreCase===false))};b.isNaN=function(a){return b.isNumber(a)&&isNaN(a)};b.isNull=function(a){return a===null};b.isUndefined=function(a){return typeof a=="undefined"};b.noConflict=function(){j._=n;return this};b.identity=function(a){return a};b.breakLoop=function(){throw m;};var s=0;b.uniqueId=function(a){var c=s++;return a?a+c:c};b.templateSettings={start:"<%",end:"%>",interpolate:/<%=(.+?)%>/g};b.template=function(a,c){var d=b.templateSettings;
a=new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g," ").replace(new RegExp("'(?=[^"+d.end[0]+"]*"+d.end+")","g"),"\t").split("'").join("\\'").split("\t").join("'").replace(d.interpolate,"',$1,'").split(d.start).join("');").split(d.end).join("p.push('")+"');}return p.join('');");return c?a(c):a};b.forEach=b.each;b.foldl=b.inject=b.reduce;b.foldr=b.reduceRight;b.filter=b.select;b.every=b.all;b.some=b.any;b.head=b.first;b.tail=b.rest;
b.methods=b.functions;var l=function(a,c){return c?b(a).chain():a};b.each(b.functions(b),function(a){var c=b[a];i.prototype[a]=function(){var d=b.toArray(arguments);o.call(d,this._wrapped);return l(c.apply(b,d),this._chain)}});b.each(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var c=Array.prototype[a];i.prototype[a]=function(){c.apply(this._wrapped,arguments);return l(this._wrapped,this._chain)}});b.each(["concat","join","slice"],function(a){var c=Array.prototype[a];i.prototype[a]=
function(){return l(c.apply(this._wrapped,arguments),this._chain)}});i.prototype.chain=function(){this._chain=true;return this};i.prototype.value=function(){return this._wrapped}})();

/*}}}*/
(function($) {
  $.modal = {}
  $.modal.show  = function(message, options) {

    $.fn.attach = function(html) { return this.append(html).children(":last"); };

    var defaults = {
      dialogWidth: 400,
      dialogHeight: 300,
      close: function() { return $(this); }
    }
    var settings = $.extend(defaults, options);

    var CUSHION_LENGTH = 20000;
    FADE_DURATION = 500;
    var mask = $("<div/>")
      .animate({opacity: 0.85}, FADE_DURATION)
      .css({
        position: "absolute",
        top: -CUSHION_LENGTH,
        left: -CUSHION_LENGTH,
        background: "#999",
        zIndex: 9999998,
        height: 2*CUSHION_LENGTH+$(window).height(),
        width: 2*CUSHION_LENGTH+$(window).width(),
        opacity: 0.01
      })
      .click(function(ev) {
        $.modal.close(ev);
      })
      .appendTo($(document.body));

    var dialog = $("<div class='modal'/>")
      .fadeIn(FADE_DURATION)
      .css({
        position: "absolute",
        background: "#fff",
        border: "1px solid #666",
        // "-moz-border-radius": 5,
        // "-webkit-border-radius": 5,
        width: settings.dialogWidth,
        height: settings.dialogHeight,
        zIndex: 9999999,
        top: $(window.body).scrollTop()+$(window).height()/2-settings.dialogHeight/2,
        left: $(window).width()/2-settings.dialogWidth/2
      })
      .attach("<div/>")
        .css({
          margin: "10px"
        })
        .append(message)
      .end()
      .attach("<div/>")
        .css({
          position: "absolute",
          top: "-1.2em",
          right: "4px",
          cursor: "pointer"
        })
        .html("X")
        .click(function(ev) {
          $.modal.close(ev);
        })
      .end()
      .data("close", settings.close)
      .appendTo($(document.body));

    $(window.body).keyup(function(ev) { 
      if (ev.charCode==27 || ev.keyCode==27) $.modal.close(ev);
    });

    $.modal.close = function(ev) {
      if (ev) ev.stopPropagation();
      var close = dialog.data("close");
      dialog.slideUp(FADE_DURATION, function() { dialog.remove(); });
      mask.fadeOut(FADE_DURATION, function() {
        mask.remove();
        if (close) close.apply(dialog);
      });
    }
    return dialog;

  }
})(jQuery);
/***
|Name|portal|
|Description||
|Source||
|Documentation||
|Version|0.1|
|Author|Michael Mahemoff, Osmosoft|
|''License:''|[[BSD open source license]]|
|~CoreVersion|2.2|

!code
{{{
***/

(function($) {

  version.extensions.overlay = {installed:true};
  var $overlay, allStylesFrag;

  var macro = config.macros.overlay = {
    handler: function(place,macroName,params,wikifier,paramString,tiddler) {
      var macroParams = paramString.parseParams();
      createTiddlyButton(place,getParam(macroParams,"launchLabel")||"launch",null,version.extensions.overlay.toggle);
    }
  };

  function makeOverlay() {
    if (! $("#overlay").length) {
      var overlays = store.getTaggedTiddlers("overlay");
      if (!overlays.length) return;
      // var content = store.getTiddlerText(getParam(macroParams, "contentTiddler"));
      overlay = overlays[0];
      $overlay = $("<div id='overlay'/>").hide().html(store.getTiddlerText(overlay.title+"##Content")).appendTo("body");
      call(config[overlay.title].onLoad);
      // $("#closeOverlay").click(version.extensions.overlay.toggle);
    }
  }

  version.extensions.overlay.toggle = function() {

    var overlayTiddlerTitle = store.getTaggedTiddlers("overlay")[0].title;
    if ($overlay.css("display")=="none") {
      scroll(0,0);
      $.twStylesheet(store.getTiddlerText(overlayTiddlerTitle+"##StyleSheet"),
                     {id: "overlayStyleSheet"});
      overlayBaseStylesheet = $("style:last")[0];
      call(config[overlayTiddlerTitle].onOpen);
      $overlay.fadeIn(1000, function() {
        allStylesFrag = document.createDocumentFragment();
        $("style").each(function() {
          if (this!=overlayBaseStylesheet) allStylesFrag.appendChild(this);
        });
        // $.twStylesheet("#copyright,#saveTest,#backstage,#backstageCloak,#backstageArea,#backstageButton,#storeArea,#shadowArea,#contentWrapper { display: none; }", {id:"main"});
        $.twStylesheet("#copyright,#saveTest,#backstage,#backstageCloak,#backstageArea,#backstageButton,#storeArea,#shadowArea,#contentWrapper { height: 0; overflow: hidden; }", {id:"main"});
        $("#backstageArea,#backstageButton").hide();
      });
    } else {
      $.twStylesheet.remove({id:"main"});
      $(allStylesFrag).children().each(function() {
        $("head").append(this);
      });
      $("#backstageArea,#backstageButton").show();
      call(config[overlayTiddlerTitle].onClose);
      $("#overlay").fadeOut(function() {
        $.twStylesheet.remove({id:"overlayStyleSheet"});
        $(this).hide();
      });
    }

  }

  function call(fn) { return fn ? fn() : false; }

  makeOverlay();

})(jQuery);

/*}}}*/
/*
 * IFrame Loader Plugin for JQuery
 * - Notifies your event handler when iframe has finished loading
 * - Your event handler receives loading duration (as well as iframe)
 * - Optionally calls your timeout handler
 *
 * http://project.ajaxpatterns.org/jquery-iframe
 *
 * The MIT License
 *
 * Copyright (c) 2009, Michael Mahemoff
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

(function($) {

  var timer;

  $.fn.src = function(url, onLoad, options) {
    setIFrames($(this), onLoad, options, function() {
      this.src = url;
    });
    return $(this);
  }

  $.fn.squirt = function(content, onLoad, options) {

    setIFrames($(this), onLoad, options, function() {
      // var doc = this.contentDocument || this.contentWindow.document;
  doc = this.document;
	if(this.contentDocument)
		doc = this.contentDocument; // For NS6
	else if(this.contentWindow)
		doc = this.contentWindow.document; // For IE5.5 and IE6

      doc.open();
      doc.writeln(content);
      doc.close();
    });
    return this;

  }

  function setIFrames(iframes, onLoad, options, iFrameSetter) {
    iframes.each(function() {
      if (this.tagName=="IFRAME") setIFrame(this, onLoad, options, iFrameSetter);
    });
  }

  function setIFrame(iframe, onLoad, options, iFrameSetter) {

    var iframe;
    iframe.onload = null;
    if (timer) clearTimeout(timer);

    var defaults = {
      timeoutDuration: 0,
      timeout: null
    };
    var opts = $.extend(defaults, options);
    if (opts.timeout && !opts.timeoutDuration) opts.timeoutDuration = 60000;

    opts.frameactive = true;
    var startTime = (new Date()).getTime();
    if (opts.timeout) {
      var timer = setTimeout(function() {
        opts.frameactive=false; 
        iframe.onload=null;
        if (opts.timeout) opts.timeout(iframe, opts.timeout);
      }, opts.timeoutDuration);
    };

    var onloadHandler = function() {
      var duration=(new Date()).getTime()-startTime;
      if (timer) clearTimeout(timer);
      if (onLoad && opts.frameactive) onLoad.apply(iframe,[duration]);
      opts.frameactive=false;
    }
    iFrameSetter.apply(iframe);
    iframe.onload = onloadHandler;
    opts.completeReadyStateChanges=0;
    iframe.onreadystatechange = function() { // IE ftw
	    if (++(opts.completeReadyStateChanges)==3) onloadHandler();
    }

    return iframe;

  };

})(jQuery);
/***
|Name|TrailPlayer|
|Description||
|Source||
|Documentation||
|Version|0.1|
|Author|Michael Mahemoff, Osmosoft|
|''License:''|[[BSD open source license]]|
|~CoreVersion|2.2|

!Content
<div id="topBar">

  <div id="playControls">

    <!-- TRAIL INFO -->

    <select id="trail"></select>
    <div id="infoLink">&nbsp;</div>

    <!-- FLOATING LEFT -->

    <div id="nav">
      <span id="start" class="navigator">&nbsp;</span>
      <span id="prev" class="navigator">&nbsp;</span>
      <span id="next" class="navigator">&nbsp;</span>
      <span id="end" class="navigator">&nbsp;</span>
    </div>

    <select id="bookmark"></select>

    <div id="progressHolder">
      <img id="progress" src="images/progress.gif" />
      <img id="reload" src="images/reload.gif" />
    </div>

  </div>

  <!-- FLOATING RIGHT -->

  <a id="close" class="clickster">
    <div class="icon">X</div>
    <div class="prompt">close</div>
  </a>

  <div id="hide" class="clickster">
    <div class="icon">&uarr;</div>
    <div class="prompt">hide</div>
  </div>

  <div id="noteControl" class="clickster showing">
    <div title="note" class="icon">&nbsp;</div>
    <div class="prompt">note</div>
    <div id="commentStat" class="stat"><span id="commentCount">--</span> comments from users</div>
  </div>

</div>

<div id="miniBar">
  <div id="controls">
    <span id="restore">&darr;trail</span>
    <span id="miniPrev" class="navigator">&laquo;</span>
    <span id="miniNext" class="navigator">&raquo;</span>
    <!-- <a id="miniLink">#</a> -->
  </div>
</div>


<div id="note">
  <div id="noteHide">&uarr; hide note and comments &uarr;</div>
  <div class="existingNote">
    <h5><a href="#"></a></h5>
      <div class="content"></div>
      <div class="absentContent">[no note]</div>
      <h4>comments</h4>
        <div id="comments"></div>
  </div>
</div>

<div id="resourceView">
  <div id="initialBacking">---</div>
</div>

!StyleSheet
#overlay { background: black; font-family: sans-serif; overflow: hidden; }
.pseudoLink, a, a:visited { text-decoration: none; color: #00b; cursor: pointer; }
li { list-style-type: none; }

#topBar { position: absolute; top: 0; left: 0; width: 100%; height: 46px;
          z-index: 90000;
          border-bottom: 2px solid #666;
          line-height: 1; overflow: hidden;
          font-family: sans-serif;
          background-color: #a8dae5;
          background-image: -webkit-gradient(linear, left top, left bottom, from(#d3ffff), to(#58dae5));
          background-image: -moz-linear-gradient(top, #d3ffff, #58dae5);
          filter:progid:DXImageTransform.Microsoft.Gradient (GradientType=0, StartColorStr='#d3ffff', EndColorStr='#58dae5'); }

#infoLink { margin-left: 5px; }
#infoLink { background-image: url(images/info16.png); background-position: 5px 0; background-repeat: no-repeat; width:25px; float: left; height: 36px; margin-top: 2px; cursor: pointer; }

.modal { font-family: Gill Sans, sans-serif; }
#info button {
  cursor: pointer;
  border: 1px solid black;
  background: #ff9; 
  margin: 5px 0 10px; -moz-border-radius: 5px; -webkit-border-radius: 5px;
}
#info button:hover { background: #dd7; }
#info .selected { background: #fed; }
#info #options { margin: 10px 10px 0 0; font-size: x-small; font-family: serif; text-align: right; }
#info .note { background: #000; color: #fff; padding: 10px; font-size: small;
                 -moz-border-radius: 5px; -webkit-border-radius: 5px;
                 margin: 5px 10px 10px; }
#info li { padding: 5px; cursor: pointer; }
#info li:hover { background: #e9e9ff; }
.infoURL { font-size: small; color: #00b; }

#playControls { margin: 12px 0 0 20px; float: left; }

#nav { float: left; margin: -3px 10px 0 50px; }
#nav span { float: left; cursor: pointer; width: 38px; height: 30px; background-position: 0 0; background-repeat: no-repeat; }
#next { background-image: url("images/next.png"); }
#next:hover { background-image: url("images/next-hovered.png"); }
#prev { background-image: url("images/prev.png"); }
#prev:hover { background-image: url("images/prev-hovered.png"); }
#end { background-image: url("images/end.png"); }
#end:hover { background-image: url("images/end-hovered.png"); }
#start { background-image: url("images/start.png"); }
#start:hover { background-image: url("images/start-hovered.png"); }

select { width: 100px; float: left; text-align: center; }
select#bookmark { width: 200px; }

// #progress { float: left; width: 16px; height: 16px; padding-left: 10px; visibility: hidden; }
#progressHolder { float: left; width: 16px; height: 16px; padding-left: 10px; cursor: pointer; }

#noteExpand { position: absolute; top: 28px; height: 16px; opacity: 0; }
#noteHide { text-align: center; width: 100%; color: #06b; cursor: pointer; }
#note {  background: #000; }
#note { position: absolute; top: 48px; right: 5px; width: 200px; 
        border: 1px solid #444; border-top-width: 0;
        opacity: 0.9; filter:alpha(opacity=90);
        -moz-border-radius: 0 0 5px 5px; -webkit-border-radius: 0 0 5px 5px;
        padding: 2px 10px 10px; z-index: 999992; color: #fff; font-size: 12px; }
#note h4 { margin: 5px 0; text-align: right; font-style: italic; color: #333; cursor: pointer; }
#note h5 a, #note h5 a:visited { color: #fff; margin: 10px 0; font-weight: bold; }
#note h5 a:hover { text-decoration: underline; }
.absentContent { opacity: 0.2; }

#noteControl .icon { background-image: url("images/note.png"); background-repeat: no-repeat; background-position: 9px 0; }
* html #noteControl .icon { background-image: url("images/note.gif"); }
#noteControl.showing, #noteControl.showing div { background-color: #5bbac7; color: #333; }
.stat { color: #777; text-align: right; padding-right: 5px; }

#miniBar { position: absolute; top: 0; right: 0;
           text-align: right; z-index: 1; }
#miniBar #restore { font-size: small; }
#miniBar #controls { background: #a8dae5; opacity: 0.9; padding: 4px;
                     border: 2px solid #38626b; border-top-width: 0; border-right-width: 0; }
#miniBar #controls, #miniBar a { color: #000; }
#miniBar #controls span:hover { color: #0000dd; }
#miniBar #controls span { margin: 0 4px; cursor: pointer; }

.clickster { width: 40px; height: 100%; float: right; text-align: center; }
.clickster { font-size: 14px; float: right; padding: 2px 1px; cursor: pointer; width: 44px; height: 46px; text-align: center; float: right; }
.clickster:hover,a.clickster:hover { background: #5081b2; }
.clickster div, .clickster a { text-align: center; font-size: small; color: #000; }
.clickster div.icon { font-size: 20px; margin: 5px 0 2px; }

* html #resourceView { top: 48px; }
body { overflow: hidden; }
#resourceView { position: absolute; width: 100%; height: 100%; top: 0; left: 0; margin-top: 48px; }
#resourceView iframe { position: relative; top: 0; left: 0; width: 100%; height: 100%; z-index: 2; }

!InfoTemplate
<div id="info">
  <h3><%= trail.name %></h3>
  <p><%= trail.description %></p>
  <ol id="bookmarks" class="xoxo">
    <% jQuery.each(trail.bookmarks, function(i, bookmark) { %>
      <% var selectedString = (i==trail.selectedIndex ? " selected" : ""); %>
      <li class="bookmarkItem<%= selectedString %>">
          <a class="bookmark" href="<%= bookmark.url %>" target="bookmarkView" title="<%= bookmark.name %>"><%= bookmark.name %></a>
          <div class="infoURL"><%= bookmark.url %></div>
        <% if (jQuery.trim(bookmark.note).length) { %>
          <div class="note"><%= bookmark.note %></div>
        <% } %>
      </li>
    <% }); %>
  </ol>
</div>
!Javascript
{{{
***/

(function($) {

  var player = version.extensions.TrailPlayer = {installed:true};

  config.TrailPlayer = {
    onLoad: function() {

      $("#noteHide,#noteControl").click(toggleNote);
      $("#bookmark").change(switchBookmark);
      wireNavButtons();

      $("#progressHolder").click(function() { loadIframe(); });

      $("#infoLink").click(showInfo);
      $("#hide").click(function() { toggleTopBar(false); });
      $("#restore").click(function() { toggleTopBar(true); });

      $("#close").click(version.extensions.overlay.toggle);
      // if (!store.getTaggedTiddlers("trail").length) return false;
      // player.syncFromFragmentID();
      // return $("#trail option").length;

     populateDropdowns();

    },
    onOpen: function() {
     // player.syncToFragmentID();
     populateDropdowns();
    },
    onClose: function() {
      if (!this.closedBefore) {
        this.closedBefore = true;
        story.closeAllTiddlers();
      }
      story.displayTiddler("top", getCurrentTrail().name);
      story.displayTiddler("top", getCurrentBookmark().name);
      player.clearFragmentID();
    },
  };

  function populateDropdowns() {
    $("#trail").empty();
    $.each(store.getTaggedTiddlers("trail"), function(i,trailTiddler) {
      $("<option>"+trailTiddler.title+"</option>")
      .val(trailTiddler.title)
      .appendTo($("#trail"));
    });
    $("#trail").change(function() {
      switchTrail();
    });
  } 

  version.extensions.TrailPlayer.syncFromFragmentID = function() {
    var matches = document.location.href.match(/#\[trail\[(.*?)(\/(.*)\])?\]$/);
    if (matches) {
      var trailTitle=decodeURI(matches[1]), bookmarkTitle = decodeURI(matches[3]);
      $.switchTrailPlayer(trailTitle, bookmarkTitle);
      /*
      $("#trail option[value="+trailTitle+"]").attr("selected", true);
      var bookmarkIndex = _.pluck(getCurrentTrail().bookmarks, "name").indexOf(bookmarkTitle);
      switchTrail(bookmarkIndex==-1 ? 0 : bookmarkIndex);
      */
      return true;
    } else {
      switchTrail();
      return false;
    }
  };

  version.extensions.TrailPlayer.syncToFragmentID = function() {
    document.location.hash = "#[trail[" + encodeURI(getCurrentTrail().name) + "/" + encodeURI(getCurrentBookmark().name)+"]]";
  };

  version.extensions.TrailPlayer.clearFragmentID = function() {
    document.location.hash = "";
  }

  var navButtonCalculators = {
    next:  function(siteIndex) { return siteIndex+1; },
    prev:  function(siteIndex) { return siteIndex-1; },
    start: function(siteIndex) { return 0; },
    end:   function(siteIndex) { return $("#bookmark option").length-1; }
  };
  navButtonCalculators.miniNext = navButtonCalculators.next;
  navButtonCalculators.miniPrev = navButtonCalculators.prev;

  function wireNavButtons() {
    $(".navigator").click(function() {
      // var bookmarkIndex = $("#bookmark option:selected").prevAll().length;
      var bookmarkIndex = getCurrentBookmarkIndex();
      var newIndex = navButtonCalculators[this.id](parseInt(bookmarkIndex, 10));
      if (newIndex!=bookmarkIndex && newIndex>=0 && newIndex<$("#bookmark option").length) {
        $("#bookmark option").get(newIndex).selected = true;
        switchBookmark();
      }
    });
  }

  function showInfo() {
    var modalOptions = {
      dialogWidth: 600,
      dialogHeight: 600
    };
    var trail = getCurrentTrail();
    trail.selectedIndex = getCurrentBookmarkIndex();
    var infoMessage = version.extensions.microtemplate("TrailPlayer##InfoTemplate", {
      trail: trail
    });
    $.modal.show($("<div/>").html(infoMessage), modalOptions);
    $("#info ol").click(function(ev) {
      var firstButton = (ev.which===1 || ev.button===0);
      if (!firstButton) return;
      var $target = $(ev.target).closest("li");
      if ($target.length) {
        $(".modal").data("close", null);
        $.modal.close();
        $("#bookmark option").get($target.prevAll().length).selected = true;
        switchBookmark();
      }
    });
  }

  function toggleNote() {
    var willBeHidden = $("#note").isDisplayed();
    if (willBeHidden) {
      $("#noteControl").removeClass("showing");
    } else {
      $("#noteControl").addClass("showing");
      $("#noteHide").css("opacity", 1);
    }
    $("#note").slideToggle();
  }

  function toggleTopBar(shouldShow) {
    var DELAY=300;
    if (shouldShow) {
        $("#note").css("opacity",0).animate({opacity:1}, DELAY);
        $("#topBar").slideDown(DELAY);
        $("#resourceView").animate({"marginTop": 48}, DELAY, function() {
          // $("#miniBar").removeClass("available");
          $("#miniBar").css({zIndex: -1, opacity: 0});
        });
    } else {
        $("#note").css("opacity",1).animate({opacity:0}, DELAY);
        $("#topBar").slideUp(DELAY);
        $("#resourceView").animate({"marginTop": 0}, DELAY, function() {
          $("#miniBar").addClass("available").css("zIndex", 999999).blink(). hover(function() {
            $(this).css("opacity", 1);
          }, function() {
            $(this).css("opacity", 0.05);
          });
        });
    }
  }

  function switchTrail(bookmarkIndex) {
    if (!$("#trail option").length) return;
    $("#bookmark").empty();
    var trailTiddler = store.getTiddler($("#trail").val());
    var trail = version.extensions.TrailPlugin.extractTrail(trailTiddler);
    $.each(trail.bookmarks, function(i,bookmark) {
      $("<option/>").html(bookmark.name).val(bookmark.name).appendTo($("#bookmark"));
    });
    if (trail.bookmarks.length) $("#bookmark option").get(bookmarkIndex||0).selected = true;
    switchBookmark();
  }

  function switchBookmark() {
    // $('#resourceView').empty().create("<iframe/>").src(url, function() { $("#progress").visible(false); });
    // var bookmark = $("#bookmark").val();
    // $("#progress").visible();
    // $('#resourceView').empty().attach("<iframe/>").src(getCurrentBookmark().url,
    var bookmark = getCurrentBookmark();
    $("#note .content").showIf(!isWhitespace(bookmark.note));
    $("#note .absentContent").showIf(isWhitespace(bookmark.note));
    $("#note .content").html(bookmark.note);
    // $.trim(bookmark.note).length
    // $("#resourceView iframe").src(bookmark.url, function() { $("#progress").hidden(); });
    loadIframe();
    if ($("#overlay").isDisplayed()) player.syncToFragmentID();
  }

  function loadIframe(url) {
    // if (!url) url = $("#bookmark
    var url = getCurrentBookmark().url;
    showProgress();
    $("#resourceView").html($("<iframe/>"));
    $("#resourceView iframe").src(url, function() { hideProgress(); });
  }

  function getCurrentTrail() {
    var trailTiddler = store.getTiddler($("#trail").val());
    return version.extensions.TrailPlugin.extractTrail(trailTiddler);
  }

  function getCurrentBookmark() {
    // var bookmarkIndex = $("#bookmark").val();
    return getCurrentTrail().bookmarks[getCurrentBookmarkIndex()];
  }

  function getCurrentBookmarkIndex() { // TODO inline
    // return $("#bookmark").val();
    return $("#bookmark option:selected").prevAll().length;
  }

  function log() {
    if (console && console.log) console.log.apply(console, arguments);
  }

  $.fn.attach  = function(html) { return this.append(html).children(":last"); };
  $.fn.visible = function() { return this.css("visibility", "visible"); };
  $.fn.hidden  = function() { return this.css("visibility", "hidden"); };
  function showProgress() { $("#progress").show(); $("#reload").hide(); }
  function hideProgress() { $("#progress").hide(); $("#reload").show(); }
  $.fn.showIf = function(shouldShow, options) {
    var settings = $.extend({
      showEffect: function() { $(this).show(); },
      hideEffect: function() { $(this).hide(); }
    }, options);
    shouldShow ? settings.showEffect.apply(this) : settings.hideEffect.apply(this);
    return $(this);
  };
  $.fn.isDisplayed = function() { return $(this).css("display")!="none"; };
  $.fn.slideToggle = function() { return $(this)[$(this).isDisplayed() ? "slideUp":"slideDown"](200); };
  $.fn.toggleClass = function(test, klass) { test ? this.addClass(klass):this.removeClass(klass); };
  $.fn.blink = function() {
    for (var i=0; i<2; i++) $(this).fadeTo(200, 1).fadeTo(i==2?1000:200, 0.05);
    return $(this);
  };
  function isWhitespace(s) { return (/^[ \t\n]*$/).test(s); }
  $.switchTrailPlayer = function(trailTitle, bookmarkTitle) {
    $("#trail option[value="+trailTitle+"]").attr("selected", true);
    var bookmarkIndex = _.pluck(getCurrentTrail().bookmarks, "name").indexOf(bookmarkTitle);
    switchTrail(bookmarkIndex==-1 ? 0 : bookmarkIndex);
  }

   // http://lanitdev.wordpress.com/2009/09/22/jquery-custom-selector-for-selecting-elements-by-exact-text-textequals/


})(jQuery);

/*}}}*/

/***
|Name|TagCloud|
|Description||
|Source||
|Documentation||
|Version|0.1|
|Author|Michael Mahemoff, Osmosoft|
|''License:''|[[BSD open source license]]|
|~CoreVersion|2.2|

!StyleSheet
.tagCloud .tagg { margin-right: 8px; cursor: pointer; line-height: 2em; }
.tagCloud .tag0 { font-size: 0.8em; }
.tagCloud .tag1 { font-size: 1em; }
.tagCloud .tag2 { font-size: 1.15em; }
.tagCloud .tag3 { font-size: 1.3em; }
.tagCloud .tag4 { font-size: 1.6em; }
.tagCloud .clearance { height: 1px; clear: left; }

!Javascript
{{{
***/

(function($) {

  version.extensions.tagCloud = {installed:true};

  version.extensions.tagCloud.getTagRecords = function(options) {

    var settings = $.extend({
      filterTag: null,
      excludeTags: ["systemConfig", "systemTheme", "excludeSearch", "excludeLists"],
      prefix: null
    }, options);

    var tiddlers = settings.filterTag ? store.getTaggedTiddlers(settings.filterTag) : store.getTiddlers();
    var tagRecords = [];
    _.each(tiddlers, function(tiddler) {
      _.each(tiddler.tags, function(tag) {
        if (  (!settings.excludeTags || settings.excludeTags.indexOf(tag)==-1)
           && (!settings.prefix || tag.substr(0,settings.prefix.length)==settings.prefix) ) {
          var tagRecord =
            _.detect(tagRecords, function(tagRecord) { return tagRecord.tag==tag; }) 
            || tagRecords.push({ tag: tag, count: 0 });
          tagRecord.count++;
        }
      });
    });
    if (options.quantity) {
      tagRecords.sort(function(a,b) { return a.count > b.count ? 1 : -1; });
      tagRecords = tagRecords.slice(0,options.quantity);
    }
    tagRecords.sort(function(a,b) { return a.tag > b.tag ? 1 : -1; });
    return tagRecords;
  };

  version.extensions.tagCloud.getTags = function(filterTag, excludeTags) {
    return _.pluck(version.extensions.tagSuggest.getTagRecords(filterTag, excludeTags), "tag");
  }

  version.extensions.tagCloud.clicked = function(tag) {
    story.displayTiddler("top", tag);
  }

  var macro = config.macros.tagCloud = {

    init: function() {
      var stylesheet = store.getTiddlerText("TagCloud##StyleSheet");
      config.shadowTiddlers["StyleSheetTagCloud"] = stylesheet;
      store.addNotification("StyleSheetTagCloud", refreshStyles);
    },

    handler: function(place,macroName,params,wikifier,paramString,tiddler) {

      var p=getParam;

      var params = paramString.parseParams();
      var filterTag = p(params, "filterTag");
      var excludeTags = (p(params, "excludeTags") || "").split(",");
      var prefix = p(params, "prefix");
      var quantity = p(params, "quantity");
      var format = p(params, "format") || "%t";

      var tagRecords = version.extensions.tagCloud.getTagRecords({
        filterTag: filterTag,
        excludeTags: excludeTags,
        prefix: prefix,
        quantity: quantity
      });
      var orderedTagRecords = _.clone(tagRecords).sort(function(a,b) { return a.count > b.count ? 1 : -1; });

      var buckets = [];
      var bucketAmount = 5;
      $.each(orderedTagRecords, function(i, tagRecord) {
        tagRecord.bucket = Math.round((i/orderedTagRecords.length)*bucketAmount);
      });
      b=buckets;
      t=tagRecords;

      var $tagCloud = $("<div class='tagCloud'/>").appendTo(place);
      $.each(tagRecords, function(i, tagRecord) {
        var message = format.replace("%t", tagRecord.tag.replace(prefix,"")).replace("%c", tagRecord.count); // TODO only if prefix at start
        $("<a/>").html(message)
        .addClass("tagg").addClass("tag"+tagRecord.bucket)
        .data("tag", tagRecord.tag)
        .appendTo($tagCloud);
        $tagCloud.append(" ");
      });
      $("<div class='clearance'>").appendTo($tagCloud);
      $(place).click(function(ev) { 
        var $tag = $(ev.target);
        if ($tag.hasClass("tagg")) version.extensions.tagCloud.clicked($tag.data("tag"));
      })
    }

  };

  // TODO delete and test
  function _map(list, fn) {
    var mapped = [];
    $.each(list, function(i, item) { mapped.push(fn(item)); });
    return mapped;
  }

  // TODO delete and test
  function _clone(list, fn) {
    return _map(list, function(item) { return item; });
  }

  function log() {
    console.log.apply(console, arguments);
  }

// underscore
(function(){var j=this,n=j._,i=function(a){this._wrapped=a},m=typeof StopIteration!=="undefined"?StopIteration:"__break__",b=j._=function(a){return new i(a)};if(typeof exports!=="undefined")exports._=b;var k=Array.prototype.slice,o=Array.prototype.unshift,p=Object.prototype.toString,q=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;b.VERSION="0.5.7";b.each=function(a,c,d){try{if(a.forEach)a.forEach(c,d);else if(b.isArray(a)||b.isArguments(a))for(var e=0,f=a.length;e<f;e++)c.call(d,
a[e],e,a);else{var g=b.keys(a);f=g.length;for(e=0;e<f;e++)c.call(d,a[g[e]],g[e],a)}}catch(h){if(h!=m)throw h;}return a};b.map=function(a,c,d){if(a&&b.isFunction(a.map))return a.map(c,d);var e=[];b.each(a,function(f,g,h){e.push(c.call(d,f,g,h))});return e};b.reduce=function(a,c,d,e){if(a&&b.isFunction(a.reduce))return a.reduce(b.bind(d,e),c);b.each(a,function(f,g,h){c=d.call(e,c,f,g,h)});return c};b.reduceRight=function(a,c,d,e){if(a&&b.isFunction(a.reduceRight))return a.reduceRight(b.bind(d,e),c);
var f=b.clone(b.toArray(a)).reverse();b.each(f,function(g,h){c=d.call(e,c,g,h,a)});return c};b.detect=function(a,c,d){var e;b.each(a,function(f,g,h){if(c.call(d,f,g,h)){e=f;b.breakLoop()}});return e};b.select=function(a,c,d){if(a&&b.isFunction(a.filter))return a.filter(c,d);var e=[];b.each(a,function(f,g,h){c.call(d,f,g,h)&&e.push(f)});return e};b.reject=function(a,c,d){var e=[];b.each(a,function(f,g,h){!c.call(d,f,g,h)&&e.push(f)});return e};b.all=function(a,c,d){c=c||b.identity;if(a&&b.isFunction(a.every))return a.every(c,
d);var e=true;b.each(a,function(f,g,h){(e=e&&c.call(d,f,g,h))||b.breakLoop()});return e};b.any=function(a,c,d){c=c||b.identity;if(a&&b.isFunction(a.some))return a.some(c,d);var e=false;b.each(a,function(f,g,h){if(e=c.call(d,f,g,h))b.breakLoop()});return e};b.include=function(a,c){if(b.isArray(a))return b.indexOf(a,c)!=-1;var d=false;b.each(a,function(e){if(d=e===c)b.breakLoop()});return d};b.invoke=function(a,c){var d=b.rest(arguments,2);return b.map(a,function(e){return(c?e[c]:e).apply(e,d)})};b.pluck=
function(a,c){return b.map(a,function(d){return d[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);var e={computed:-Infinity};b.each(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g>=e.computed&&(e={value:f,computed:g})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};b.each(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g<e.computed&&(e={value:f,computed:g})});return e.value};b.sortBy=function(a,c,d){return b.pluck(b.map(a,
function(e,f,g){return{value:e,criteria:c.call(d,e,f,g)}}).sort(function(e,f){e=e.criteria;f=f.criteria;return e<f?-1:e>f?1:0}),"value")};b.sortedIndex=function(a,c,d){d=d||b.identity;for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?(e=g+1):(f=g)}return e};b.toArray=function(a){if(!a)return[];if(a.toArray)return a.toArray();if(b.isArray(a))return a;if(b.isArguments(a))return k.call(a);return b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=function(a,c,d){return c&&!d?k.call(a,
0,c):a[0]};b.rest=function(a,c,d){return k.call(a,b.isUndefined(c)||d?1:c)};b.last=function(a){return a[a.length-1]};b.compact=function(a){return b.select(a,function(c){return!!c})};b.flatten=function(a){return b.reduce(a,[],function(c,d){if(b.isArray(d))return c.concat(b.flatten(d));c.push(d);return c})};b.without=function(a){var c=b.rest(arguments);return b.select(a,function(d){return!b.include(c,d)})};b.uniq=function(a,c){return b.reduce(a,[],function(d,e,f){if(0==f||(c===true?b.last(d)!=e:!b.include(d,
e)))d.push(e);return d})};b.intersect=function(a){var c=b.rest(arguments);return b.select(b.uniq(a),function(d){return b.all(c,function(e){return b.indexOf(e,d)>=0})})};b.zip=function(){for(var a=b.toArray(arguments),c=b.max(b.pluck(a,"length")),d=new Array(c),e=0;e<c;e++)d[e]=b.pluck(a,String(e));return d};b.indexOf=function(a,c){if(a.indexOf)return a.indexOf(c);for(var d=0,e=a.length;d<e;d++)if(a[d]===c)return d;return-1};b.lastIndexOf=function(a,c){if(a.lastIndexOf)return a.lastIndexOf(c);for(var d=
a.length;d--;)if(a[d]===c)return d;return-1};b.range=function(a,c,d){var e=b.toArray(arguments),f=e.length<=1;a=f?0:e[0];c=f?e[0]:e[1];d=e[2]||1;e=Math.ceil((c-a)/d);if(e<=0)return[];e=new Array(e);f=a;for(var g=0;;f+=d){if((d>0?f-c:c-f)>=0)return e;e[g++]=f}};b.bind=function(a,c){var d=b.rest(arguments,2);return function(){return a.apply(c||j,d.concat(b.toArray(arguments)))}};b.bindAll=function(a){var c=b.rest(arguments);if(c.length==0)c=b.functions(a);b.each(c,function(d){a[d]=b.bind(a[d],a)});
return a};b.delay=function(a,c){var d=b.rest(arguments,2);return setTimeout(function(){return a.apply(a,d)},c)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(b.rest(arguments)))};b.wrap=function(a,c){return function(){var d=[a].concat(b.toArray(arguments));return c.apply(c,d)}};b.compose=function(){var a=b.toArray(arguments);return function(){for(var c=b.toArray(arguments),d=a.length-1;d>=0;d--)c=[a[d].apply(this,c)];return c[0]}};b.keys=function(a){if(b.isArray(a))return b.range(0,a.length);
var c=[];for(var d in a)q.call(a,d)&&c.push(d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=function(a){return b.select(b.keys(a),function(c){return b.isFunction(a[c])}).sort()};b.extend=function(a,c){for(var d in c)a[d]=c[d];return a};b.clone=function(a){if(b.isArray(a))return a.slice(0);return b.extend({},a)};b.tap=function(a,c){c(a);return a};b.isEqual=function(a,c){if(a===c)return true;var d=typeof a;if(d!=typeof c)return false;if(a==c)return true;if(!a&&c||a&&!c)return false;
if(a.isEqual)return a.isEqual(c);if(b.isDate(a)&&b.isDate(c))return a.getTime()===c.getTime();if(b.isNaN(a)&&b.isNaN(c))return true;if(b.isRegExp(a)&&b.isRegExp(c))return a.source===c.source&&a.global===c.global&&a.ignoreCase===c.ignoreCase&&a.multiline===c.multiline;if(d!=="object")return false;if(a.length&&a.length!==c.length)return false;d=b.keys(a);var e=b.keys(c);if(d.length!=e.length)return false;for(var f in a)if(!b.isEqual(a[f],c[f]))return false;return true};b.isEmpty=function(a){return b.keys(a).length==
0};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=function(a){return!!(a&&a.concat&&a.unshift)};b.isArguments=function(a){return a&&b.isNumber(a.length)&&!a.concat&&!a.substr&&!a.apply&&!r.call(a,"length")};b.isFunction=function(a){return!!(a&&a.constructor&&a.call&&a.apply)};b.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};b.isNumber=function(a){return a===+a||p.call(a)==="[object Number]"};b.isDate=function(a){return!!(a&&a.getTimezoneOffset&&a.setUTCFullYear)};
b.isRegExp=function(a){return!!(a&&a.test&&a.exec&&(a.ignoreCase||a.ignoreCase===false))};b.isNaN=function(a){return b.isNumber(a)&&isNaN(a)};b.isNull=function(a){return a===null};b.isUndefined=function(a){return typeof a=="undefined"};b.noConflict=function(){j._=n;return this};b.identity=function(a){return a};b.breakLoop=function(){throw m;};var s=0;b.uniqueId=function(a){var c=s++;return a?a+c:c};b.templateSettings={start:"<%",end:"%>",interpolate:/<%=(.+?)%>/g};b.template=function(a,c){var d=b.templateSettings;
a=new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g," ").replace(new RegExp("'(?=[^"+d.end[0]+"]*"+d.end+")","g"),"\t").split("'").join("\\'").split("\t").join("'").replace(d.interpolate,"',$1,'").split(d.start).join("');").split(d.end).join("p.push('")+"');}return p.join('');");return c?a(c):a};b.forEach=b.each;b.foldl=b.inject=b.reduce;b.foldr=b.reduceRight;b.filter=b.select;b.every=b.all;b.some=b.any;b.head=b.first;b.tail=b.rest;
b.methods=b.functions;var l=function(a,c){return c?b(a).chain():a};b.each(b.functions(b),function(a){var c=b[a];i.prototype[a]=function(){var d=b.toArray(arguments);o.call(d,this._wrapped);return l(c.apply(b,d),this._chain)}});b.each(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var c=Array.prototype[a];i.prototype[a]=function(){c.apply(this._wrapped,arguments);return l(this._wrapped,this._chain)}});b.each(["concat","join","slice"],function(a){var c=Array.prototype[a];i.prototype[a]=
function(){return l(c.apply(this._wrapped,arguments),this._chain)}});i.prototype.chain=function(){this._chain=true;return this};i.prototype.value=function(){return this._wrapped}})();

})(jQuery);

/*}}}*/
/***
|Name|TagSuggest|
|Description||
|Source||
|Documentation||
|Version|0.1|
|Author|Michael Mahemoff, Osmosoft|
|''License:''|[[BSD open source license]]|
|~CoreVersion|2.2|

!StyleSheet
.ac_results {
	padding: 0px;
	border: 1px solid WindowFrame;
	background-color: Window;
	overflow: hidden;
}

.ac_results ul {
	width: 100%;
	list-style-position: outside;
	list-style: none;
	padding: 0;
	margin: 0;
}

.ac_results iframe {
	display:none;
	position:absolute;
	top:0;
	left:0;
	z-index:-1;
	filter:mask();
	width:3000px;
	height:3000px;
}

.ac_results li {
	margin: 0px;
	padding: 2px 5px;
	cursor: pointer;
	display: block;
	width: 100%;
	font: menu;
	font-size: 12px;
	overflow: hidden;
}

.ac_loading {
	background : Window url('./indicator.gif') right center no-repeat;
}

.ac_over {
	background-color: Highlight;
	color: HighlightText;
}

.tagSuggest { margin-top: 2px; }
.tagView { float: left; padding: 5px 10px 5px 0; }
.tagView div { float: left; }
.tagSuggest button { margin-left: 5px; }
.tagDelete { cursor: pointer; color: #00b; margin: auto 5px; }
.clearance { clear: both; height: 1px; }

!Javascript
{{{
***/

(function($) {

  version.extensions.tagSuggest = {installed:true};
  version.extensions.tagHelper = {installed:true};

  version.extensions.tagHelper.getTagRecords = function(options) {

    var settings = $.extend({
      filterTag: null,
      excludeTags: ["systemConfig", "systemTheme", "excludeSearch", "excludeLists"],
      prefix: null
    }, options);

    var tiddlers = settings.filterTag ? store.getTaggedTiddlers(settings.filterTag) : store.getTiddlers();
    var tagRecords = [];
    _.each(tiddlers, function(tiddler) {
      _.each(tiddler.tags, function(tag) {
        if (  (!settings.excludeTags || settings.excludeTags.indexOf(tag)==-1)
           && (!settings.prefix || tag.substr(0,settings.prefix.length)==settings.prefix) ) {
          var tagRecord =
            _.detect(tagRecords, function(tagRecord) { return tagRecord.tag==tag; }) 
            || tagRecords.push({ tag: tag, count: 0 });
          tagRecord.count++;
        }
      });
    });
    if (options.quantity) {
      tagRecords.sort(function(a,b) { return a.count > b.count ? 1 : -1; });
      tagRecords = tagRecords.slice(0,options.quantity);
    }
    tagRecords.sort(function(a,b) { return a.tag > b.tag ? 1 : -1; });
    return tagRecords;
  };

  version.extensions.tagHelper.getTags = function(options) {
    return _.pluck(version.extensions.tagHelper.getTagRecords(options), "tag");
  };

  version.extensions.tagSuggest.enhanceEditTemplate = function() {
    var template = config.shadowTiddlers.EditTemplate;
    if ((/tagSuggest/g).test(template)) return; // already enhanced
    var tagsDiv =       "<div class='editor' macro='edit tags'></div>";
    var tagsDivHidden = "<div class='editor' macro='edit tags' style='display:block;></div>";
    var tagSuggestDiv = "<div class='tagSuggest' macro='tagSuggest'></div>";
    config.shadowTiddlers.EditTemplate = template.replace(tagsDiv,tagSuggestDiv+"\n"+tagsDivHidden);
  };

  var macro = config.macros.tagSuggest = {

    init: function() {
      var stylesheet = store.getTiddlerText("TagSuggest##StyleSheet");
      config.shadowTiddlers["StyleSheetTagSuggest"] = stylesheet;
      store.addNotification("StyleSheetTagSuggest", refreshStyles);
    },

    handler: function(place,macroName,params,wikifier,paramString,tiddler) {
      var $tagsContainer = $("<div/>")
        .appendTo(place)
        .data("tiddler", tiddler)
        .data("params", paramString.parseParams());
      var prefix = getParam(paramString.parseParams(), "prefix") || "";
      var unprefixedTags = _.select(tiddler.tags, function(tag) {
        return tag.indexOf(prefix)===0;
      });
      unprefixedTags = _.map(unprefixedTags, function(tag) {
        return tag.replace(prefix, "");
      });
      $tagsContainer.data("tags", unprefixedTags);
      var $tagInput = $("<input class='tagInput'/>")
        .keydown(function(ev) {
          if (ev.keyCode==13) {
            if ($(".ac_results").css("display")=="none") addTag(ev.target.value, $tagsContainer);
            return false;
          }
        })
        .appendTo($tagsContainer);
      var $add = $("<button>add tag</button>").appendTo($tagsContainer)
        .click(function() {
          addTag($(this).siblings(".tagInput").val(), $tagsContainer);
        });
      $tags = $("<div class='tagsView'/>").appendTo($tagsContainer);
      $("<div class='.clearance'>&nbsp;</div>").appendTo($tagsContainer);
      updateTags($tagsContainer);
      makeAutoComplete($tagsContainer);
    }

  };

  function addTag(tagName, $tagsContainer) {
    var tags = $tagsContainer.data("tags");
    if (tags.indexOf(tagName)==-1) {
      $tagsContainer.data("tags").push(tagName);
      updateTags($tagsContainer);
    }
    $(".tagInput", $tagsContainer).val("");
  }

  function updateTags($tagsContainer) {
    $tagsContainer.data("tags").sort();
    var $tagsView = $(".tagsView", $tagsContainer).empty();
    $.each($tagsContainer.data("tags"), (function(i, tag) {
      var $tag = $("<div class='tagView'>").appendTo($tagsView);
      $("<div class='tagName'/>").text(tag).appendTo($tag);
      $("<div class='tagDelete'/>").text("x").appendTo($tag).click(function() {
        $tagsContainer.data("tags").remove(tag);
        updateTags($tagsContainer);
      });
    }));

    var $realTags = $tagsContainer.parents(".tiddler").find("[edit=tags]");
    console.log("TC", $tagsContainer.length, $tagsContainer, "RT", $realTags.length, "sel", $realTags.selector);
    if (!$realTags.length) return;
    var prefix = getParam($tagsContainer.data("params"), "prefix") || "";
    var unprefixedRealTags = _.select($realTags.val().split(/ +/), function(tag) {
      return tag.indexOf(prefix)!=0;
    });
    chosenPrefixedTags = $tagsContainer.data("tags").map(function(tag) {
      return prefix+tag;
    });
    $realTags.val((unprefixedRealTags.concat(chosenPrefixedTags)).sort().join(" "));
    // makeAutoComplete($tagsContainer);
  };

  function makeAutoComplete($tagsContainer) {
    var params = $tagsContainer.data("params"),
      chosenTags = $tagsContainer.data("tags")
      prefix = getParam(params, "prefix");
    var candidateTags = version.extensions.tagHelper.getTags({
      filterTag: getParam(params, "filterTag"),
      excludeTags: (getParam(params, "excludeTags") || "").split(","),
      prefix: prefix
    });
    console.log("cand 2", candidateTags, "chosen", chosenTags);
    candidateTags = _.select(candidateTags, function(tag) {
      return chosenTags.indexOf(tag)==-1;
    });
    console.log("cand after", candidateTags);
    if (prefix) candidateTags = _.map(candidateTags, function(tag) {
      return tag.replace(prefix, "");
    });
    $(".tagInput", $tagsContainer).autocompleteArray(candidateTags, { 
      onItemSelect: function(li) {
        addTag(li.innerHTML, $tagsContainer);
      }
    });
  }

// http://project.mahemoff.com/autocomplete
// adapted from http://www.pengoworks.com/workshop/jquery/autocomplete.htm 
jQuery.autocomplete = function(input, options) {
	// Create a link to self
	var me = this;

	// Create jQuery object for input element
	var $input = $(input).attr("autocomplete", "off");

	// Apply inputClass if necessary
	if (options.inputClass) $input.addClass(options.inputClass);

	// Create results
	var results = document.createElement("div");
	// Create jQuery object for results
	var $results = $(results);
	$results.hide().addClass(options.resultsClass).css("position", "absolute");
	if( options.width > 0 ) $results.css("width", options.width);

	// Add to body element
	$("body").append(results);

	input.autocompleter = me;

	var timeout = null;
	var prev = "";
	var active = -1;
	var cache = {};
	var keyb = false;
	var hasFocus = false;
	var lastKeyPressCode = null;

	// flush cache
	function flushCache(){
		cache = {};
		cache.data = {};
		cache.length = 0;
	};

	// flush cache
	flushCache();

	// if there is a data array supplied
	if( options.data != null ){
		var sFirstChar = "", stMatchSets = {}, row = [];

		// no url was specified, we need to adjust the cache length to make sure it fits the local data store
		if( typeof options.url != "string" ) options.cacheLength = 1;

		// loop through the array and create a lookup structure
		for( var i=0; i < options.data.length; i++ ){
			// if row is a string, make an array otherwise just reference the array
			row = ((typeof options.data[i] == "string") ? [options.data[i]] : options.data[i]);

			// if the length is zero, don't add to list
			if( row[0].length > 0 ){
				// get the first character
				sFirstChar = row[0].substring(0, 1).toLowerCase();
				// if no lookup array for this character exists, look it up now
				if( !stMatchSets[sFirstChar] ) stMatchSets[sFirstChar] = [];
				// if the match is a string
				stMatchSets[sFirstChar].push(row);
			}
		}

		// add the data items to the cache
		for( var k in stMatchSets ){
			// increase the cache size
			options.cacheLength++;
			// add to the cache
			addToCache(k, stMatchSets[k]);
		}
	}

	$input
	.keydown(function(e) {
		// track last key pressed
		lastKeyPressCode = e.keyCode;
		switch(e.keyCode) {
			case 38: // up
				e.preventDefault();
				moveSelect(-1);
				break;
			case 40: // down
				e.preventDefault();
				moveSelect(1);
				break;
			case 9:  // tab
			case 13: // return
				if( selectCurrent() ){
					e.preventDefault();
				}
				break;
			case 32: // space
				if( selectCurrent() ){
					e.preventDefault();
				}
				break;
			default:
				active = -1;
				if (timeout) clearTimeout(timeout);
				timeout = setTimeout(function(){onChange();}, options.delay);
				break;
		}
	})
	.focus(function(){
		// track whether the field has focus, we shouldn't process any results if the field no longer has focus
		hasFocus = true;
	})
	.blur(function() {
		// track whether the field has focus
		hasFocus = false;
		hideResults();
	});

	hideResultsNow();

	function onChange() {
		// ignore if the following keys are pressed: [del] [shift] [capslock]
		if( lastKeyPressCode == 46 || (lastKeyPressCode > 8 && lastKeyPressCode < 32) ) return $results.hide();
    var v = $input.val().replace(/^.*?(\S+)$/, "$1");
		if (v == prev) return;
		prev = v;
		if (v.length >= options.minChars) {
			$input.addClass(options.loadingClass);
			requestData(v);
		} else {
			$input.removeClass(options.loadingClass);
			$results.hide();
		}
	};

 	function moveSelect(step) {

		var lis = $("li", results);
		if (!lis) return;

		active += step;

		if (active < 0) {
			active = 0;
		} else if (active >= lis.size()) {
			active = lis.size() - 1;
		}

		lis.removeClass("ac_over");

		$(lis[active]).addClass("ac_over");

		// Weird behaviour in IE
		// if (lis[active] && lis[active].scrollIntoView) {
		// 	lis[active].scrollIntoView(false);
		// }

	};

	function selectCurrent() {
		var li = $("li.ac_over", results)[0];
		if (!li) {
			var $li = $("li", results);
			if (options.selectOnly) {
				if ($li.length == 1) li = $li[0];
			} else if (options.selectFirst) {
				li = $li[0];
			}
		}
		if (li) {
			selectItem(li);
			return true;
		} else {
			return false;
		}
	};

	function selectItem(li) {
		if (!li) {
			li = document.createElement("li");
			li.extra = [];
			li.selectValue = "";
		}
		var v = $.trim(li.selectValue ? li.selectValue : li.innerHTML);
		input.lastSelected = v;
		prev = v;
		$results.html("");
    $input.val($input.val().replace(/(\S+)$/, v+" "));
		hideResultsNow();
		if (options.onItemSelect) setTimeout(function() { options.onItemSelect(li); }, 1);
	};

	// selects a portion of the input string
	function createSelection(start, end){
		// get a reference to the input element
		var field = $input.get(0);
		if( field.createTextRange ){
			var selRange = field.createTextRange();
			selRange.collapse(true);
			selRange.moveStart("character", start);
			selRange.moveEnd("character", end);
			selRange.select();
		} else if( field.setSelectionRange ){
			field.setSelectionRange(start, end);
		} else {
			if( field.selectionStart ){
				field.selectionStart = start;
				field.selectionEnd = end;
			}
		}
		field.focus();
	};

	// fills in the input box w/the first match (assumed to be the best match)
	function autoFill(sValue){
		// if the last user key pressed was backspace, don't autofill
		if( lastKeyPressCode != 8 ){
			// fill in the value (keep the case the user has typed)
			$input.val($input.val() + sValue.substring(prev.length));
			// select the portion of the value not typed by the user (so the next character will erase)
			createSelection(prev.length, sValue.length);
		}
	};

	function showResults() {
		// get the position of the input field right now (in case the DOM is shifted)
		var pos = findPos(input);
		// either use the specified width, or autocalculate based on form element
		var iWidth = (options.width > 0) ? options.width : $input.width();
		// reposition
		$results.css({
			width: parseInt(iWidth) + "px",
			top: (pos.y + input.offsetHeight) + "px",
			left: pos.x + "px"
		}).show();
	};

	function hideResults() {
		if (timeout) clearTimeout(timeout);
		timeout = setTimeout(hideResultsNow, 200);
	};

	function hideResultsNow() {
		if (timeout) clearTimeout(timeout);
		$input.removeClass(options.loadingClass);
		if ($results.is(":visible")) {
			$results.hide();
		}
		if (options.mustMatch) {
			var v = $input.val();
			if (v != input.lastSelected) {
				selectItem(null);
			}
		}
	};

	function receiveData(q, data) {
		if (data) {
			$input.removeClass(options.loadingClass);
			results.innerHTML = "";

			// if the field no longer has focus or if there are no matches, do not display the drop down
			if( !hasFocus || data.length == 0 ) return hideResultsNow();

			if ($.browser.msie) {
				// we put a styled iframe behind the calendar so HTML SELECT elements don't show through
				$results.append(document.createElement('iframe'));
			}
			results.appendChild(dataToDom(data));
			// autofill in the complete box w/the first match as long as the user hasn't entered in more data
			if( options.autoFill && ($input.val().toLowerCase() == q.toLowerCase()) ) autoFill(data[0][0]);
			showResults();
		} else {
			hideResultsNow();
		}
	};

	function parseData(data) {
		if (!data) return null;
		var parsed = [];
		var rows = data.split(options.lineSeparator);
		for (var i=0; i < rows.length; i++) {
			var row = $.trim(rows[i]);
			if (row) {
				parsed[parsed.length] = row.split(options.cellSeparator);
			}
		}
		return parsed;
	};

	function dataToDom(data) {
		var ul = document.createElement("ul");
		var num = data.length;

		// limited results to a max number
		if( (options.maxItemsToShow > 0) && (options.maxItemsToShow < num) ) num = options.maxItemsToShow;

		for (var i=0; i < num; i++) {
			var row = data[i];
			if (!row) continue;
			var li = document.createElement("li");
			if (options.formatItem) {
				li.innerHTML = options.formatItem(row, i, num);
				li.selectValue = row[0];
			} else {
				li.innerHTML = row[0];
				li.selectValue = row[0];
			}
			var extra = null;
			if (row.length > 1) {
				extra = [];
				for (var j=1; j < row.length; j++) {
					extra[extra.length] = row[j];
				}
			}
			li.extra = extra;
			ul.appendChild(li);
			$(li).hover(
				function() { $("li", ul).removeClass("ac_over"); $(this).addClass("ac_over"); active = $("li", ul).indexOf($(this).get(0)); },
				function() { $(this).removeClass("ac_over"); }
			).click(function(e) { e.preventDefault(); e.stopPropagation(); selectItem(this) });
		}
		return ul;
	};

	function requestData(q) {
		if (!options.matchCase) q = q.toLowerCase();
		var data = options.cacheLength ? loadFromCache(q) : null;
		// recieve the cached data
		if (data) {
			receiveData(q, data);
		// if an AJAX url has been supplied, try loading the data now
		} else if( (typeof options.url == "string") && (options.url.length > 0) ){
			$.get(makeUrl(q), function(data) {
				data = parseData(data);
				addToCache(q, data);
				receiveData(q, data);
			});
		// if there's been no data found, remove the loading class
		} else {
			$input.removeClass(options.loadingClass);
		}
	};

	function makeUrl(q) {
		var url = options.url + "?q=" + encodeURI(q);
		for (var i in options.extraParams) {
			url += "&" + i + "=" + encodeURI(options.extraParams[i]);
		}
		return url;
	};

	function loadFromCache(q) {
		if (!q) return null;
		if (cache.data[q]) return cache.data[q];
		if (options.matchSubset) {
			for (var i = q.length - 1; i >= options.minChars; i--) {
				var qs = q.substr(0, i);
				var c = cache.data[qs];
				if (c) {
					var csub = [];
					for (var j = 0; j < c.length; j++) {
						var x = c[j];
						var x0 = x[0];
						if (matchSubset(x0, q)) {
							csub[csub.length] = x;
						}
					}
					return csub;
				}
			}
		}
		return null;
	};

	function matchSubset(s, sub) {
		if (!options.matchCase) s = s.toLowerCase();
		var i = s.indexOf(sub);
		if (i == -1) return false;
		return i == 0 || options.matchContains;
	};

	this.flushCache = function() {
		flushCache();
	};

	this.setExtraParams = function(p) {
		options.extraParams = p;
	};

	this.findValue = function(){
		var q = $input.val();

		if (!options.matchCase) q = q.toLowerCase();
		var data = options.cacheLength ? loadFromCache(q) : null;
		if (data) {
			findValueCallback(q, data);
		} else if( (typeof options.url == "string") && (options.url.length > 0) ){
			$.get(makeUrl(q), function(data) {
				data = parseData(data)
				addToCache(q, data);
				findValueCallback(q, data);
			});
		} else {
			// no matches
			findValueCallback(q, null);
		}
	}

	function findValueCallback(q, data){
		if (data) $input.removeClass(options.loadingClass);

		var num = (data) ? data.length : 0;
		var li = null;

		for (var i=0; i < num; i++) {
			var row = data[i];

			if( row[0].toLowerCase() == q.toLowerCase() ){
				li = document.createElement("li");
				if (options.formatItem) {
					li.innerHTML = options.formatItem(row, i, num);
					li.selectValue = row[0];
				} else {
					li.innerHTML = row[0];
					li.selectValue = row[0];
				}
				var extra = null;
				if( row.length > 1 ){
					extra = [];
					for (var j=1; j < row.length; j++) {
						extra[extra.length] = row[j];
					}
				}
				li.extra = extra;
			}
		}

		if( options.onFindValue ) setTimeout(function() { options.onFindValue(li) }, 1);
	}

	function addToCache(q, data) {
		if (!data || !q || !options.cacheLength) return;
		if (!cache.length || cache.length > options.cacheLength) {
			flushCache();
			cache.length++;
		} else if (!cache[q]) {
			cache.length++;
		}
		cache.data[q] = data;
	};

	function findPos(obj) {
		var curleft = obj.offsetLeft || 0;
		var curtop = obj.offsetTop || 0;
		while (obj = obj.offsetParent) {
			curleft += obj.offsetLeft
			curtop += obj.offsetTop
		}
		return {x:curleft,y:curtop};
	}
}

jQuery.fn.autocomplete = function(url, options, data) {
	// Make sure options exists
	options = options || {};
	// Set url as option
	options.url = url;
	// set some bulk local data
	options.data = ((typeof data == "object") && (data.constructor == Array)) ? data : null;

	// Set default values for required options
	options.inputClass = options.inputClass || "ac_input";
	options.resultsClass = options.resultsClass || "ac_results";
	options.lineSeparator = options.lineSeparator || "\n";
	options.cellSeparator = options.cellSeparator || "|";
	options.minChars = options.minChars || 1;
	options.delay = options.delay || 400;
	options.matchCase = options.matchCase || 0;
	options.matchSubset = options.matchSubset || 1;
	options.matchContains = options.matchContains || 0;
	options.cacheLength = options.cacheLength || 1;
	options.mustMatch = options.mustMatch || 0;
	options.extraParams = options.extraParams || {};
	options.loadingClass = options.loadingClass || "ac_loading";
	options.selectFirst = options.selectFirst || false;
	options.selectOnly = options.selectOnly || false;
	options.maxItemsToShow = options.maxItemsToShow || -1;
	options.autoFill = options.autoFill || false;
	options.width = parseInt(options.width, 10) || 0;

	this.each(function() {
		var input = this;
		new jQuery.autocomplete(input, options);
	});

	// Don't break the chain
	return this;
}

jQuery.fn.autocompleteArray = function(data, options) {
	return this.autocomplete(null, options, data);
}

jQuery.fn.indexOf = function(e){
	for( var i=0; i<this.length; i++ ){
		if( this[i] == e ) return i;
	}
	return -1;
};

// underscore
(function(){var j=this,n=j._,i=function(a){this._wrapped=a},m=typeof StopIteration!=="undefined"?StopIteration:"__break__",b=j._=function(a){return new i(a)};if(typeof exports!=="undefined")exports._=b;var k=Array.prototype.slice,o=Array.prototype.unshift,p=Object.prototype.toString,q=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;b.VERSION="0.5.7";b.each=function(a,c,d){try{if(a.forEach)a.forEach(c,d);else if(b.isArray(a)||b.isArguments(a))for(var e=0,f=a.length;e<f;e++)c.call(d,
a[e],e,a);else{var g=b.keys(a);f=g.length;for(e=0;e<f;e++)c.call(d,a[g[e]],g[e],a)}}catch(h){if(h!=m)throw h;}return a};b.map=function(a,c,d){if(a&&b.isFunction(a.map))return a.map(c,d);var e=[];b.each(a,function(f,g,h){e.push(c.call(d,f,g,h))});return e};b.reduce=function(a,c,d,e){if(a&&b.isFunction(a.reduce))return a.reduce(b.bind(d,e),c);b.each(a,function(f,g,h){c=d.call(e,c,f,g,h)});return c};b.reduceRight=function(a,c,d,e){if(a&&b.isFunction(a.reduceRight))return a.reduceRight(b.bind(d,e),c);
var f=b.clone(b.toArray(a)).reverse();b.each(f,function(g,h){c=d.call(e,c,g,h,a)});return c};b.detect=function(a,c,d){var e;b.each(a,function(f,g,h){if(c.call(d,f,g,h)){e=f;b.breakLoop()}});return e};b.select=function(a,c,d){if(a&&b.isFunction(a.filter))return a.filter(c,d);var e=[];b.each(a,function(f,g,h){c.call(d,f,g,h)&&e.push(f)});return e};b.reject=function(a,c,d){var e=[];b.each(a,function(f,g,h){!c.call(d,f,g,h)&&e.push(f)});return e};b.all=function(a,c,d){c=c||b.identity;if(a&&b.isFunction(a.every))return a.every(c,
d);var e=true;b.each(a,function(f,g,h){(e=e&&c.call(d,f,g,h))||b.breakLoop()});return e};b.any=function(a,c,d){c=c||b.identity;if(a&&b.isFunction(a.some))return a.some(c,d);var e=false;b.each(a,function(f,g,h){if(e=c.call(d,f,g,h))b.breakLoop()});return e};b.include=function(a,c){if(b.isArray(a))return b.indexOf(a,c)!=-1;var d=false;b.each(a,function(e){if(d=e===c)b.breakLoop()});return d};b.invoke=function(a,c){var d=b.rest(arguments,2);return b.map(a,function(e){return(c?e[c]:e).apply(e,d)})};b.pluck=
function(a,c){return b.map(a,function(d){return d[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);var e={computed:-Infinity};b.each(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g>=e.computed&&(e={value:f,computed:g})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};b.each(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g<e.computed&&(e={value:f,computed:g})});return e.value};b.sortBy=function(a,c,d){return b.pluck(b.map(a,
function(e,f,g){return{value:e,criteria:c.call(d,e,f,g)}}).sort(function(e,f){e=e.criteria;f=f.criteria;return e<f?-1:e>f?1:0}),"value")};b.sortedIndex=function(a,c,d){d=d||b.identity;for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?(e=g+1):(f=g)}return e};b.toArray=function(a){if(!a)return[];if(a.toArray)return a.toArray();if(b.isArray(a))return a;if(b.isArguments(a))return k.call(a);return b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=function(a,c,d){return c&&!d?k.call(a,
0,c):a[0]};b.rest=function(a,c,d){return k.call(a,b.isUndefined(c)||d?1:c)};b.last=function(a){return a[a.length-1]};b.compact=function(a){return b.select(a,function(c){return!!c})};b.flatten=function(a){return b.reduce(a,[],function(c,d){if(b.isArray(d))return c.concat(b.flatten(d));c.push(d);return c})};b.without=function(a){var c=b.rest(arguments);return b.select(a,function(d){return!b.include(c,d)})};b.uniq=function(a,c){return b.reduce(a,[],function(d,e,f){if(0==f||(c===true?b.last(d)!=e:!b.include(d,
e)))d.push(e);return d})};b.intersect=function(a){var c=b.rest(arguments);return b.select(b.uniq(a),function(d){return b.all(c,function(e){return b.indexOf(e,d)>=0})})};b.zip=function(){for(var a=b.toArray(arguments),c=b.max(b.pluck(a,"length")),d=new Array(c),e=0;e<c;e++)d[e]=b.pluck(a,String(e));return d};b.indexOf=function(a,c){if(a.indexOf)return a.indexOf(c);for(var d=0,e=a.length;d<e;d++)if(a[d]===c)return d;return-1};b.lastIndexOf=function(a,c){if(a.lastIndexOf)return a.lastIndexOf(c);for(var d=
a.length;d--;)if(a[d]===c)return d;return-1};b.range=function(a,c,d){var e=b.toArray(arguments),f=e.length<=1;a=f?0:e[0];c=f?e[0]:e[1];d=e[2]||1;e=Math.ceil((c-a)/d);if(e<=0)return[];e=new Array(e);f=a;for(var g=0;;f+=d){if((d>0?f-c:c-f)>=0)return e;e[g++]=f}};b.bind=function(a,c){var d=b.rest(arguments,2);return function(){return a.apply(c||j,d.concat(b.toArray(arguments)))}};b.bindAll=function(a){var c=b.rest(arguments);if(c.length==0)c=b.functions(a);b.each(c,function(d){a[d]=b.bind(a[d],a)});
return a};b.delay=function(a,c){var d=b.rest(arguments,2);return setTimeout(function(){return a.apply(a,d)},c)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(b.rest(arguments)))};b.wrap=function(a,c){return function(){var d=[a].concat(b.toArray(arguments));return c.apply(c,d)}};b.compose=function(){var a=b.toArray(arguments);return function(){for(var c=b.toArray(arguments),d=a.length-1;d>=0;d--)c=[a[d].apply(this,c)];return c[0]}};b.keys=function(a){if(b.isArray(a))return b.range(0,a.length);
var c=[];for(var d in a)q.call(a,d)&&c.push(d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=function(a){return b.select(b.keys(a),function(c){return b.isFunction(a[c])}).sort()};b.extend=function(a,c){for(var d in c)a[d]=c[d];return a};b.clone=function(a){if(b.isArray(a))return a.slice(0);return b.extend({},a)};b.tap=function(a,c){c(a);return a};b.isEqual=function(a,c){if(a===c)return true;var d=typeof a;if(d!=typeof c)return false;if(a==c)return true;if(!a&&c||a&&!c)return false;
if(a.isEqual)return a.isEqual(c);if(b.isDate(a)&&b.isDate(c))return a.getTime()===c.getTime();if(b.isNaN(a)&&b.isNaN(c))return true;if(b.isRegExp(a)&&b.isRegExp(c))return a.source===c.source&&a.global===c.global&&a.ignoreCase===c.ignoreCase&&a.multiline===c.multiline;if(d!=="object")return false;if(a.length&&a.length!==c.length)return false;d=b.keys(a);var e=b.keys(c);if(d.length!=e.length)return false;for(var f in a)if(!b.isEqual(a[f],c[f]))return false;return true};b.isEmpty=function(a){return b.keys(a).length==
0};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=function(a){return!!(a&&a.concat&&a.unshift)};b.isArguments=function(a){return a&&b.isNumber(a.length)&&!a.concat&&!a.substr&&!a.apply&&!r.call(a,"length")};b.isFunction=function(a){return!!(a&&a.constructor&&a.call&&a.apply)};b.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};b.isNumber=function(a){return a===+a||p.call(a)==="[object Number]"};b.isDate=function(a){return!!(a&&a.getTimezoneOffset&&a.setUTCFullYear)};
b.isRegExp=function(a){return!!(a&&a.test&&a.exec&&(a.ignoreCase||a.ignoreCase===false))};b.isNaN=function(a){return b.isNumber(a)&&isNaN(a)};b.isNull=function(a){return a===null};b.isUndefined=function(a){return typeof a=="undefined"};b.noConflict=function(){j._=n;return this};b.identity=function(a){return a};b.breakLoop=function(){throw m;};var s=0;b.uniqueId=function(a){var c=s++;return a?a+c:c};b.templateSettings={start:"<%",end:"%>",interpolate:/<%=(.+?)%>/g};b.template=function(a,c){var d=b.templateSettings;
a=new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g," ").replace(new RegExp("'(?=[^"+d.end[0]+"]*"+d.end+")","g"),"\t").split("'").join("\\'").split("\t").join("'").replace(d.interpolate,"',$1,'").split(d.start).join("');").split(d.end).join("p.push('")+"');}return p.join('');");return c?a(c):a};b.forEach=b.each;b.foldl=b.inject=b.reduce;b.foldr=b.reduceRight;b.filter=b.select;b.every=b.all;b.some=b.any;b.head=b.first;b.tail=b.rest;
b.methods=b.functions;var l=function(a,c){return c?b(a).chain():a};b.each(b.functions(b),function(a){var c=b[a];i.prototype[a]=function(){var d=b.toArray(arguments);o.call(d,this._wrapped);return l(c.apply(b,d),this._chain)}});b.each(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var c=Array.prototype[a];i.prototype[a]=function(){c.apply(this._wrapped,arguments);return l(this._wrapped,this._chain)}});b.each(["concat","join","slice"],function(a){var c=Array.prototype[a];i.prototype[a]=
function(){return l(c.apply(this._wrapped,arguments),this._chain)}});i.prototype.chain=function(){this._chain=true;return this};i.prototype.value=function(){return this._wrapped}})();

})(jQuery);

/*}}}*/
/*
  TiddlyWiki Comments Plugin - Online demo at http://tiddlyguv.org/CommentsPlugin.html

  TODO:
  - Support Cascade comment delete when the top-level tiddler is deleted
  - Support more than one < <comments> > per tiddler. This will probably entail creating an invisible root tiddler to
    hold all the comments for a macro together. The user will need to provide an ID for this tiddler.
  - Don't use global "macro" var (use "macro" param a la jquery)

*/

/***
|Name|CommentsPlugin|
|Description|Macro for nested comments, where each comment is a separate tiddler.|
|Source|http://tiddlyguv.org/CommentsPlugin.html#CommentsPlugin|
|Documentation|http://tiddlyguv.org/CommentsPlugin.html#CommentsPluginInfo|
|Version|0.1|
|Author|Michael Mahemoff, Osmosoft|
|''License:''|[[BSD open source license]]|
|~CoreVersion|2.2|
***/

/*{{{*/
if(!version.extensions.CommentsPlugin) {

  version.extensions.CommentsPlugin = {installed:true};

  (function(plugin) {

  var cmacro = config.macros.comments = {



init: function() {
  var stylesheet = store.getTiddlerText(tiddler.title + "##StyleSheet");
  if (stylesheet) { // check necessary because it happens more than once for some reason
    config.shadowTiddlers["StyleSheetCommentsPlugin"] = stylesheet;
    store.addNotification("StyleSheetCommentsPlugin", refreshStyles);
  }
  if (!version.extensions.CommentsPlugin.retainViewTemplate) cmacro.enhanceViewTemplate();
},

enhanceViewTemplate: function() {
  var template = config.shadowTiddlers.ViewTemplate;
  if ((/commentBreadcrumb/g).test(template)) return; // already enhanced
  var TITLE_DIV = "<div class='title' macro='view title'></div>";
  var commentsDiv = "<div class='commentBreadcrumb' macro='commentBreadcrumb'></div>";
  config.shadowTiddlers.ViewTemplate = template.replace(TITLE_DIV,commentsDiv+"\n"+TITLE_DIV);
},

handler: function(place,macroName,params,wikifier,paramString,tiddler) {
  var macroParams = paramString.parseParams();
  var tiddlerParam = getParam(macroParams, "tiddler");
  tiddler = tiddlerParam ? store.getTiddler(tiddlerParam) : tiddler;
  if (!tiddler || !store.getTiddler(tiddler.title)) return;
  cmacro.buildCommentsArea(tiddler, place, macroParams);
  // cmacro.refreshCommentsFromRoot(story.getTiddler(tiddler.title).commentsEl, tiddler, macroParams);
  cmacro.refreshCommentsFromRoot(place.commentsEl, tiddler, macroParams);
},


buildCommentsArea: function(rootTiddler, place, macroParams) {
  var commentsArea = createTiddlyElement(place, "div", null, "comments");
  var headingEl, heading = getParam(macroParams, "heading");
  if (heading) heading = heading.replace("%c",
    config.macros.comments.findCommentsFromRoot(rootTiddler).length);
  if (heading) headingEl = createTiddlyElement(commentsArea, "h1", null, null, heading);

  var comments = createTiddlyElement(commentsArea, "div", null, "");
  place.commentsEl = comments;

    var newCommentArea;
  if (cmacro.editable(macroParams)) {
    newCommentArea = createTiddlyElement(commentsArea, "div", null, "newCommentArea", "New comment:");
    cmacro.forceLoginIfRequired(params, newCommentArea, function() {
      var newCommentEl = cmacro.makeTextArea(newCommentArea, macroParams);
      // var addComment = createTiddlyElement(newCommentArea, "button", null, "addComment button", "Add Comment");
      var addComment = createTiddlyButton(newCommentArea, "Add Comment", null, function() {
        var comment = cmacro.createComment(newCommentEl.value, rootTiddler, macroParams); 
        newCommentEl.value = "";
        cmacro.refreshCommentsFromRoot(comments, rootTiddler, macroParams);
      }, "addComment button");
    });
  }

  var expandHeading = getParam(macroParams, "expandHeading");
  if (heading && expandHeading && expandHeading.toLowerCase()=="true") {
    var $expandPrompt, expandableElements;
    var $expandables = jQuery([place.commentsEl,newCommentArea]).hide();
    jQuery(headingEl)
      .append($expandPrompt = "<span id='expandPrompt'> &raquo;</span>")
      .addClass("expander").click(function() {
        $expandables.toggle();
      });
  }

},


makeTextArea: function(container, macroParams) {
  var textArea = createTiddlyElement(container, "textarea");
  textArea.rows = getParam(macroParams, "textRows") || 4;
  textArea.cols = getParam(macroParams, "textCols") || 20;
  textArea.value = getParam(macroParams, "text") || "";
  return textArea;
},

refreshCommentsFromRoot: function(rootCommentsEl, rootTiddler, macroParams) {
  cmacro.treeifyComments(rootTiddler);
  cmacro.refreshComments(rootCommentsEl, rootTiddler, macroParams);
},

refreshComments: function(daddyCommentsEl, tiddler, macroParams) {

  var commentsEl;
  if (tiddler.fields.daddy) {
    var commentEl = cmacro.buildCommentEl(daddyCommentsEl, tiddler, macroParams);
    daddyCommentsEl.appendChild(commentEl);
    commentsEl = commentEl.commentsEl;
  } else { // root element
    removeChildren(daddyCommentsEl);
    // refreshedEl = story.getTiddler(tiddler.title);
    commentsEl = daddyCommentsEl;
  }

  for (var child = tiddler.firstChild; child; child = child.next) {
     cmacro.refreshComments(commentsEl, child, macroParams);
  }

},

// This has become more complex due to "confused comments" - multiple comments
// pointing back to the same daddy (which implies they all think they're the first
// child) or a single "2nd-last" sibling (which implies they all think they're the
// last sibling). This happens in the typical "atomic transaction 101" scenario -
// user A opens wiki, user B opens wiki, one of the users submits a comment, 
// the other user submits a comment.
//
// Normally, each comment says "make my daddy's first child be me", or "make my prev
// sibling's next sibling be me". That's how the tree gets built. But to deal
// with confused comments, we now have to check if daddy/prev is already pointing
// to something. If so, we will have to walk through the list to find the right place
// for the new item.
//
// We begin by sorting by date; if we can assume we are walking through the comments by date, 
// the confused comments will appear in the right order.
treeifyComments: function(rootTiddler) {

  // First, clear the tree data
  // We sort the comments to ensure "confused" comments
  var comments = cmacro.findCommentsFromRoot(rootTiddler).sort(function(a,b) {
    return a.modified > b.modified;
  });
  var nodes=comments.concat(rootTiddler);
  for (var i=0; i<nodes.length; i++) {
    delete nodes[i]["firstChild"];
    delete nodes[i]["next"];
  }

  // Now walk through each comment
  cmacro.forEach(comments, function(comment) {
    var prev = comment.fields.prev;
    var daddy = comment.fields.daddy;
    if (prev) {
      var prevTiddler = store.getTiddler(prev);
      if (prevTiddler.next) {
        for (var lastChild=prevTiddler.next; lastChild.next; lastChild=lastChild.next)
          ;
        lastChild.next = comment;
      // } else {
      } else {
        prevTiddler.next = comment;
      }
    } else {
      var daddyTiddler = store.getTiddler(daddy);
      if (daddyTiddler.firstChild) {
        for (var lastChild=daddyTiddler.firstChild; lastChild.next; lastChild=lastChild.next)
          ;
        lastChild.next = comment;
      } else {
        daddyTiddler.firstChild = comment;
      }
    }
  });
  for (var i=0; i<comments.length; i++) {
    var c=comments.sort()[i];
  }

},

logComments: function(comments) {
  for (var i=0; i<comments.length; i++) {
    var comment = comments[i];
  }
},

findCommentsFromRoot: function(rootTiddler) {
  var comments = [];
  store.forEachTiddler(function(title,tiddler) {
    if (tiddler.fields.root==rootTiddler.title) comments.push(tiddler);
  });
  return comments;
},

findChildren: function(daddyTiddler) {
  var comments = [];
  store.forEachTiddler(function(title,tiddler) {
    if (tiddler.fields.daddy==daddyTiddler.title) comments.push(tiddler);
  });
  return comments;
},

buildCommentEl: function(daddyCommentsEl, comment, macroParams) {

  // COMMENT ELEMENT
  var commentEl = document.createElement("div");
  commentEl.className = "comment";

  // HEADING <- METAINFO AND DELETE
  var headingEl = createTiddlyElement(commentEl, "div", null, "heading");

  var metaInfoEl = createTiddlyElement(headingEl, "div", null, "commentTitle",  comment.modifier + '@' + comment.modified.formatString(getParam(macroParams,"dateFormat") || "DDD, MMM DDth, YYYY hh12:0mm:0ss am"));
  metaInfoEl.onclick = function() { 
    // story.closeAllTiddlers();
    story.displayTiddler("top", comment.title, null, true);
    // document.location.hash = "#" + comment.title;
  };

  var deleteEl = createTiddlyElement(headingEl, "div", null, "deleteComment", "X");
  deleteEl.onclick = function() {
    if (true || confirm("Delete this comment and all of its replies?")) {
      cmacro.deleteTiddlerAndDescendents(comment);
      commentEl.parentNode.removeChild(commentEl);
    }
  };

  // TEXT
  commentEl.text = createTiddlyElement(commentEl, "div", null, "commentText");
  wikify(comment.text, commentEl.text);

  // REPLY LINK
  if (cmacro.editable(macroParams)) {
    var replyLinkZone = createTiddlyElement(commentEl, "div", null, "replyLinkZone");
    var replyLink = createTiddlyElement(replyLinkZone, "span", null, "replyLink", "reply to this comment");
    replyLink.onclick = function() { cmacro.openReplyLink(comment, commentEl, replyLink, macroParams); };
  }

  // var clearance = createTiddlyElement(commentEl, "clearance", null, "clearance");
  // clearance.innerHTML = "&nbsp;";

  // COMMENTS AREA
  commentEl.commentsEl = createTiddlyElement(commentEl, "div", null, "comments");

  // RETURN
  return commentEl;

},

openReplyLink: function(commentTiddler, commentEl, replyLink, macroParams) {
  if (commentEl.replyEl) {
    commentEl.replyEl.style.display = "block";
    return;
  }

  commentEl.replyEl = document.createElement("div");
  commentEl.replyEl.className = "reply";

  replyLink.style.display = "none";
  var newReplyHeading = createTiddlyElement(commentEl.replyEl, "div", null, "newReply");
  createTiddlyElement(newReplyHeading, "div", null, "newReplyLabel", "New Reply:");
  var closeNewReply = createTiddlyElement(newReplyHeading, "div", null, "closeNewReply", "close");
  closeNewReply.onclick = function() {
    commentEl.replyEl.style.display = "none";
    replyLink.style.display = "block";
  };

  cmacro.forceLoginIfRequired(params, commentEl.replyEl, function() {
    var replyText =  cmacro.makeTextArea(commentEl.replyEl, macroParams);
      var submitReply = createTiddlyButton(commentEl.replyEl, "Reply", null, function() {
        var newComment = cmacro.createComment(replyText.value, commentTiddler, macroParams);
        replyText.value = "";
        closeNewReply.onclick();
        cmacro.refreshComments(commentEl.commentsEl, newComment, macroParams);
      });
  });

  commentEl.insertBefore(commentEl.replyEl, commentEl.commentsEl);
},


createComment: function(text, daddy, macroParams) {

  var rootTitle = daddy.fields.root ? daddy.fields.root : daddy.title;
    // second case is the situation where daddy *is* root
  var newComment = cmacro.createCommentTiddler(macroParams, rootTitle);
  var fieldsParam = getParam(macroParams, "fields") || "";
  var fields = fieldsParam.decodeHashMap();
  var inheritedFields = (getParam(macroParams, "inheritedFields") || "").split(",");
  cmacro.forEach(inheritedFields, function(field) {
    if (field!="") fields[field] = daddy.fields[field];
  });
  var tagsParam = getParam(macroParams, "tags") || "comment";
  var now = new Date();
  newComment.set(null, text, config.options.txtUserName, now, tagsParam.split(","), now, fields);

  var youngestSibling = cmacro.findYoungestChild(daddy)
  if (youngestSibling) newComment.fields.prev = youngestSibling.title;
  newComment.fields.daddy = daddy.title;
  newComment.fields.root = rootTitle;

  cmacro.saveTiddler(newComment.title);
  autoSaveChanges(false);
  return newComment;
},

findYoungestChild: function(daddy) {

  var siblingCount = 0;
  var elderSiblings = cmacro.mapize(cmacro.selectTiddlers(function(tiddler) {
    isChild = (tiddler.fields.daddy==daddy.title);
    if (isChild) siblingCount++;
    return isChild;
  }));
  if (!siblingCount) return null;

  // Find the only sibling that doesn't have a prev pointing at it
  var youngestSiblings = cmacro.clone(elderSiblings) // as a starting point
  cmacro.forEachMap(elderSiblings, function(tiddler) {
    delete youngestSiblings[tiddler.fields.prev];
  });

  for (title in youngestSiblings) { return youngestSiblings[title]; }

},

// The recursive delete is run by a separate function (nested inside
// this one, for encapsulation purposes).
deleteTiddlerAndDescendents: function(tiddler) {

  function deleteRecursively(tiddler) {
    for (var child = tiddler.firstChild; child; child = child.next) {
      deleteRecursively(child);
    }
    store.removeTiddler(tiddler.title);
  }

  cmacro.treeifyComments(store.getTiddler(tiddler.fields.root));

  // save some info prior to deleting
  var prev = tiddler.fields.prev;
  var next = tiddler.next;

  deleteRecursively(tiddler);

  // used saved info
  if (next) {
    next.fields.prev = prev;
    cmacro.saveTiddler(next.title);
  }

  autoSaveChanges(false);

},


forEach: function(list, visitor) { for (var i=0; i<list.length; i++) visitor(list[i]); },
forEachMap: function(map, visitor) { for (var key in map) visitor(map[key]); },
select: function(list, selector) { 
  var selection = [];
  cmacro.forEach(list, function(currentItem) {
    if (selector(currentItem)) { selection.push(currentItem); }
  });
  return selection;
},
selectTiddlers: function(selector) { 
  var tiddlers = [];
  store.forEachTiddler(function(title, tiddler) {
    var wanted = selector(tiddler);
    if (wanted) tiddlers.push(tiddler);
  });
  return tiddlers;
},
map: function(list, mapper) { 
  var mapped = [];
  cmacro.forEach(list, function(currentItem) { mapped.push(mapper(currentItem)); });
  return mapped;
},
remove: function(list, unwantedItem) {
  return cmacro.select(list,
        function(currentItem) { return currentItem!=unwantedItem; });
},
mapize: function(tiddlerList) {
  var map = {};
  cmacro.forEach(tiddlerList, function(tiddler) { map[tiddler.title] = tiddler; });
  return map;
},
clone: function(map) { return merge({}, map); },


editable: function(params) {
  var editable = getParam(params, "editable");
  return (!editable || editable!="false");
},

needsLogin: function(params) {
  var loginCheck = getParam(params, "loginCheck");
  return loginCheck && !window[loginCheck]();
},

forceLoginIfRequired: function(params, loginPromptContainer, authenticatedBlock) {
  if (cmacro.needsLogin(params)) wikify("<<"+getParam(macroParams, "loginPrompt")+">>", loginPromptContainer);
  else authenticatedBlock();
},


mergeReadOnly: function(first, second) {
  var merged = {};
  for (var field in first) { merged[field] = first[field]; }
  for (var field in second) { merged[field] = second[field]; }
  return merged;
},

// callers may replace this with their own ID generation algorithm
createCommentTiddler: function(macroParams, rootTitle) {
  // var titleFormat = getParam(macroParams, "titleFormat") || "%root%Comment"; 
  var prefix = rootTitle+"Comment"; // was "_comment"
  if (!store.createGuidTiddler) return store.createTiddler(prefix+((new Date()).getTime()));
  return store.createGuidTiddler(prefix);
},
saveTiddler: function(tiddler) {
  var tiddler = (typeof(tiddler)=="string") ? store.getTiddler(tiddler) : tiddler; 
  store.saveTiddler(tiddler.title, tiddler.title, tiddler.text, tiddler.modifier, tiddler.modified, tiddler.tags, cmacro.mergeReadOnly(config.defaultCustomFields, tiddler.fields), false, tiddler.created)
},
log: function() { if (console && console.firebug) console.log.apply(console, arguments); },
assert: function() { if (console && console.firebug) console.assert.apply(console, arguments); },


copyFields: function(fromTiddler, toTiddler, field1, field2, fieldN) {
  for (var i=2; i<arguments.length; i++) {
    fieldKey = arguments[i];
    if (fromTiddler.fields[fieldKey]) toTiddler.fields[fieldKey] = fromTiddler.fields[fieldKey];
  }
}
}

config.macros.commentsCount = {
  handler: function(place,macroName,params,wikifier,paramString,tiddler) {
    var count = 0;
    if (tiddler && store.getTiddler(tiddler.title)) {
      var rootTiddler = paramString.length ? paramString : tiddler.title;
      count = config.macros.comments.findCommentsFromRoot(store.getTiddler(rootTiddler)).length;
    }
    createTiddlyText(place, count);
  }
},

config.macros.commentBreadcrumb = {
  handler: function(place,macroName,params,wikifier,paramString,tiddler) {
    if (!tiddler.fields.root) return;
    var rootLink = createTiddlyElement(place, "span", null, null);
    createTiddlyLink(rootLink, tiddler.fields.root, true);

    var rootIsParent = tiddler.fields.daddy==tiddler.fields.root;
    var rootIsGrandparent = (store.getTiddler(tiddler.fields.daddy)).fields.daddy==tiddler.fields.root;

    if (!rootIsParent) {
      if (!rootIsGrandparent) createTiddlyElement(place, "span", null, null, " > ... ");
      createTiddlyElement(place, "span", null, null, " > ");
      var daddyLink = createTiddlyElement(place, "span", null, null);
      createTiddlyLink(daddyLink, tiddler.fields.daddy, true);
    }

    createTiddlyElement(place, "span", null, null, " > ");

    // place.appendChild(createTiddlyLink(tiddler.fields.root));
  }
}

config.macros.tiddlyWebComments = {};
config.macros.tiddlyWebComments.handler =
  function(place,macroName,params,wikifier,paramString,tiddler) {
    paramString = "fields:'server.workspace:bags/comments' inheritedFields:'server.host,server.type'";
    config.macros.comments.handler(place,macroName,params,wikifier, paramString,tiddler);
  };

function log() { if (console && console.firebug) console.log.apply(console, arguments); }

})(version.extensions.CommentsPlugin);


/***
!StyleSheet

.comments h1 { margin-bottom: 0; padding-bottom: 0; }
.comments { padding: 0; }
.comment .comments { margin-left: 1em; }

.comment { padding: 0; margin: 1em 0 0; }
.comment .comment { margin 0; }
.comment .toolbar .button { border: 0; color: #9a4; }
.comment .heading { background: [[ColorPalette::PrimaryPale]]; color: [[ColorPalette::PrimaryDark]]; border-bottom: 1px solid [[ColorPalette::PrimaryLight]]; border-right: 1px solid [[ColorPalette::PrimaryLight]]; padding: 0.5em; height: 1.3em; }
.commentTitle { float: left; }
.commentTitle:hover { text-decoration: underline; cursor: pointer; }
.commentText { clear: both; padding: 1em 1em; }
.deleteComment { float: right; cursor: pointer; text-decoration:underline; color:[[ColorPalette::SecondaryDark]]; padding-right: 0.3em; }
.comment .reply { margin-left: 1em; }
.comment .replyLink { color:[[ColorPalette::SecondaryDark]]; font-style: italic; 
                     cursor: pointer; text-decoration: underline; margin: 0 1em; }
.comment .created { }
.comment .newReply { color:[[ColorPalette::SecondaryDark]]; margin-top: 1em; }
.newReplyLabel { float: left; }
.closeNewReply { cursor: pointer; float: right; text-decoration: underline; }
.comments textarea { width: 100%; padding: 0.3em; margin-bottom: 0.6em; }
.newCommentArea { margin-top: 0.5em; }

.clearance { clear: both; }
.expander { cursor: pointer; }

!(end of StyleSheet)

***/

  config.macros.comments.init();

} // end of 'install only once'
/*}}}*/

// function log() { if (console && console.firebug) console.log.apply(console, arguments); }
/***
|Name|TaggedTemplateTweak|
|Source|http://www.TiddlyTools.com/#TaggedTemplateTweak|
|Documentation|http://www.TiddlyTools.com/#TaggedTemplateTweakInfo|
|Version|1.5.1|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides|Story.prototype.chooseTemplateForTiddler()|
|Description|use alternative ViewTemplate/EditTemplate for tiddler's tagged with specific tag values|
This tweak extends story.chooseTemplateForTiddler() so that ''whenever a tiddler is marked with a specific tag value, it can be viewed and/or edited using alternatives to the standard tiddler templates.'' 
!!!!!Documentation
>see [[TaggedTemplateTweakInfo]]
!!!!!Revisions
<<<
2009.01.06 [1.5.1] reversed logic so that title-as-prefix takes precedence over tag-matched prefix
2008.12.18 [1.5.0] added handling for using tiddler //title// as prefix (e.g., {{{SomeTiddlerViewTemplate}}}) 
| please see [[TaggedTemplateTweakInfo]] for previous revision details |
2007.06.11 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.TaggedTemplateTweak= {major: 1, minor: 5, revision: 1, date: new Date(2009,1,6)};

Story.prototype.taggedTemplate_chooseTemplateForTiddler = Story.prototype.chooseTemplateForTiddler
Story.prototype.chooseTemplateForTiddler = function(title,template)
{
	// get default template from core
	var coreTemplate=this.taggedTemplate_chooseTemplateForTiddler.apply(this,arguments);

	// if the tiddler doesn't exist yet, return core result
	var tiddler=store.getTiddler(title); if (!tiddler) return coreTemplate;

	// split core template into theme prefix and template name
	var theme="";
	var template=coreTemplate;
	var parts=template.split(config.textPrimitives.sectionSeparator);
	if (parts[1]) { theme=parts[0]; template=parts[1]; }
	else theme=config.options.txtTheme||""; // fallback if theme is not specified
	theme+=config.textPrimitives.sectionSeparator;

	// look for template whose prefix matches the *title* of this tiddler
	if (!store.getTaggedTiddlers(title).length) { // if tiddler is not a tag
		if (store.getTiddlerText(theme+title+template)) { return theme+title+template; } // theme##TitleTemplate
		if (store.getTiddlerText(title+template)) 	{ return title+template; }	 // TitleTemplate
	}

	// look for template whose prefix matches a *tag* on this tiddler (if any)
	for (i=0; i<tiddler.tags.length; i++) {
		var t=tiddler.tags[i]+template; // add tag prefix to template
		var c=t.substr(0,1).toUpperCase()+t.substr(1); // capitalized for WikiWord title
		if (store.getTiddlerText(theme+t))	{ return theme+t; } // theme##tagTemplate
		if (store.getTiddlerText(theme+c))	{ return theme+c; } // theme##TagTemplate
		if (store.getTiddlerText(t)) 		{ return t; }	    // tagTemplate
		if (store.getTiddlerText(c))		{ return c; }	    // TagTemplate
	}

	// no matching tag OR title prefix... return core result
	return coreTemplate;
}
//}}}
/***
|Name|SinglePageModePlugin|
|Source|http://www.TiddlyTools.com/#SinglePageModePlugin|
|Documentation|http://www.TiddlyTools.com/#SinglePageModePluginInfo|
|Version|2.9.5|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides|Story.prototype.displayTiddler(), Story.prototype.displayTiddlers()|
|Options|##Configuration|
|Description|Show tiddlers one at a time with automatic permalink, or always open tiddlers at top/bottom of page.|
This plugin allows you to configure TiddlyWiki to navigate more like a traditional multipage web site with only one tiddler displayed at a time.
!!!!!Documentation
>see [[SinglePageModePluginInfo]]
!!!!!Configuration
<<<
<<option chkSinglePageMode>> Display one tiddler at a time
><<option chkSinglePagePermalink>> Automatically permalink current tiddler
><<option chkSinglePageKeepFoldedTiddlers>> Don't close tiddlers that are folded
><<option chkSinglePageKeepEditedTiddlers>> Don't close tiddlers that are being edited
<<option chkTopOfPageMode>> Open tiddlers at the top of the page
<<option chkBottomOfPageMode>> Open tiddlers at the bottom of the page
<<option chkSinglePageAutoScroll>> Automatically scroll tiddler into view (if needed)

Notes:
* The "display one tiddler at a time" option can also be //temporarily// set/reset by including a 'paramifier' in the document URL: {{{#SPM:true}}} or {{{#SPM:false}}}.
* If more than one display mode is selected, 'one at a time' display takes precedence over both 'top' and 'bottom' settings, and if 'one at a time' setting is not used, 'top of page' takes precedence over 'bottom of page'.
* When using Apple's Safari browser, automatically setting the permalink causes an error and is disabled.
<<<
!!!!!Revisions
<<<
2008.06.12 [2.9.5] corrected 'scroll to top of page' logic in auto-scroll handling
| Please see [[SinglePageModePluginInfo]] for previous revision details |
2005.08.15 [1.0.0] Initial Release.  Support for BACK/FORWARD buttons adapted from code developed by Clint Checketts.
<<<
!!!!!Code
***/
//{{{
version.extensions.SinglePageModePlugin= {major: 2, minor: 9, revision: 5, date: new Date(2008,6,12)};
//}}}
//{{{
config.paramifiers.SPM = { onstart: function(v) {
	config.options.chkSinglePageMode=eval(v);
	if (config.options.chkSinglePageMode && config.options.chkSinglePagePermalink && !config.browser.isSafari) {
		config.lastURL = window.location.hash;
		if (!config.SPMTimer) config.SPMTimer=window.setInterval(function() {checkLastURL();},1000);
	}
} };
//}}}
//{{{
if (config.options.chkSinglePageMode==undefined)
	config.options.chkSinglePageMode=false;
if (config.options.chkSinglePagePermalink==undefined)
	config.options.chkSinglePagePermalink=true;
if (config.options.chkSinglePageKeepFoldedTiddlers==undefined)
	config.options.chkSinglePageKeepFoldedTiddlers=false;
if (config.options.chkSinglePageKeepEditedTiddlers==undefined)
	config.options.chkSinglePageKeepEditedTiddlers=false;
if (config.options.chkTopOfPageMode==undefined)
	config.options.chkTopOfPageMode=false;
if (config.options.chkBottomOfPageMode==undefined)
	config.options.chkBottomOfPageMode=false;
if (config.options.chkSinglePageAutoScroll==undefined)
	config.options.chkSinglePageAutoScroll=true;
//}}}
//{{{
config.SPMTimer = 0;
config.lastURL = window.location.hash;
function checkLastURL()
{
	if (!config.options.chkSinglePageMode)
		{ window.clearInterval(config.SPMTimer); config.SPMTimer=0; return; }
	if (config.lastURL == window.location.hash) return; // no change in hash
	var tids=decodeURIComponent(window.location.hash.substr(1)).readBracketedList();
	if (tids.length==1) // permalink (single tiddler in URL)
		story.displayTiddler(null,tids[0]);
	else { // restore permaview or default view
		config.lastURL = window.location.hash;
		if (!tids.length) tids=store.getTiddlerText("DefaultTiddlers").readBracketedList();
		story.closeAllTiddlers();
		story.displayTiddlers(null,tids);
	}
}

if (Story.prototype.SPM_coreDisplayTiddler==undefined)
	Story.prototype.SPM_coreDisplayTiddler=Story.prototype.displayTiddler;
Story.prototype.displayTiddler = function(srcElement,tiddler,template,animate,slowly)
{
	var title=(tiddler instanceof Tiddler)?tiddler.title:tiddler;
	var tiddlerElem=document.getElementById(story.idPrefix+title); // ==null unless tiddler is already displayed
	var opt=config.options;
	var single=opt.chkSinglePageMode && !startingUp;
	var top=opt.chkTopOfPageMode && !startingUp;
	var bottom=opt.chkBottomOfPageMode && !startingUp;
	if (single) {
		story.forEachTiddler(function(tid,elem) {
			// skip current tiddler and, optionally, tiddlers that are folded.
			if (	tid==title
				|| (opt.chkSinglePageKeepFoldedTiddlers && elem.getAttribute("folded")=="true"))
				return;
			// if a tiddler is being edited, ask before closing
			if (elem.getAttribute("dirty")=="true") {
				if (opt.chkSinglePageKeepEditedTiddlers) return;
				// if tiddler to be displayed is already shown, then leave active tiddler editor as is
				// (occurs when switching between view and edit modes)
				if (tiddlerElem) return;
				// otherwise, ask for permission
				var msg="'"+tid+"' is currently being edited.\n\n";
				msg+="Press OK to save and close this tiddler\nor press Cancel to leave it opened";
				if (!confirm(msg)) return; else story.saveTiddler(tid);
			}
			story.closeTiddler(tid);
		});
	}
	else if (top)
		arguments[0]=null;
	else if (bottom)
		arguments[0]="bottom";
	if (single && opt.chkSinglePagePermalink && !config.browser.isSafari) {
		window.location.hash = encodeURIComponent(String.encodeTiddlyLink(title));
		config.lastURL = window.location.hash;
		document.title = wikifyPlain("SiteTitle") + " - " + title;
		if (!config.SPMTimer) config.SPMTimer=window.setInterval(function() {checkLastURL();},1000);
	}
	if (tiddlerElem && tiddlerElem.getAttribute("dirty")=="true") { // editing... move tiddler without re-rendering
		var isTopTiddler=(tiddlerElem.previousSibling==null);
		if (!isTopTiddler && (single || top))
			tiddlerElem.parentNode.insertBefore(tiddlerElem,tiddlerElem.parentNode.firstChild);
		else if (bottom)
			tiddlerElem.parentNode.insertBefore(tiddlerElem,null);
		else this.SPM_coreDisplayTiddler.apply(this,arguments); // let CORE render tiddler
	} else
		this.SPM_coreDisplayTiddler.apply(this,arguments); // let CORE render tiddler
	var tiddlerElem=document.getElementById(story.idPrefix+title);
	if (tiddlerElem&&opt.chkSinglePageAutoScroll) {
		// scroll to top of page or top of tiddler
		var isTopTiddler=(tiddlerElem.previousSibling==null);
		var yPos=isTopTiddler?0:ensureVisible(tiddlerElem);
		// if animating, defer scroll until 200ms after animation completes
		var delay=opt.chkAnimate?config.animDuration+200:0;
		setTimeout("window.scrollTo(0,"+yPos+")",delay); 
	}
}

if (Story.prototype.SPM_coreDisplayTiddlers==undefined)
	Story.prototype.SPM_coreDisplayTiddlers=Story.prototype.displayTiddlers;
Story.prototype.displayTiddlers = function() {
	// suspend single/top/bottom modes when showing multiple tiddlers
	var opt=config.options;
	var saveSPM=opt.chkSinglePageMode; opt.chkSinglePageMode=false;
	var saveTPM=opt.chkTopOfPageMode; opt.chkTopOfPageMode=false;
	var saveBPM=opt.chkBottomOfPageMode; opt.chkBottomOfPageMode=false;
	this.SPM_coreDisplayTiddlers.apply(this,arguments);
	opt.chkBottomOfPageMode=saveBPM;
	opt.chkTopOfPageMode=saveTPM;
	opt.chkSinglePageMode=saveSPM;
}
//}}}
!Description
A list of tech news sites for you.

!Bookmarks
[[Slashdot]]
[[Digg]]
[[Reddit]]
[[Hacker News]]
url: http://Slashdot.org

!Description
A Geek's Geek Site
url: http://digg.com
url: http://reddit.com

!Description
Vote up or down.
url: http://news.ycombinator.com
!Description
A list of fun news sites for you.

!Bookmarks
[[YouTube]] Lots of videos.
[[YTMND]] Fun single-serving videos :D
url: http://YouTube.com

!Description
Videos for all!
url: http://ytmnd.com
name: You're The Man Now Dog!