the tuneable player for tiddlywiki is loading ...
requires javascript
LastFM - the <<lastfm>> macro to embed a tunable player in tiddlywiki no longer works
<!--{{{-->
<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>>
Tiddlers related to notetaking are tagging here...   &nbsp;&nbsp;&nbsp;{{light{...inspired by [[QuickNotes|http://tiddlytools.com/#QuickNote]]}}}
/***
|''Name:''|AddTagMacro|
|''Description''|allows to add a tag via clicking a button, optionally removing another tag|
|''Version:''|0.1.2|
|''Type''|macro|
|''Author:''|[[TobiasBeer]]|
|''Info:''|http://lastfm.tiddlyspot.com/#AddTagMacroInfo [[AddTagMacroInfo]]|
|''Source:''|http://lastfm.tiddlyspot.com/#AddTagMacro [[AddTagMacro]]|
|''License''|[[Creative Commons Attribution-Share Alike 3.0|http://creativecommons.org/licenses/by-sa/3.0/]]|
|''Feedback:''|[[here|http://www.google.com]]|
|''~CoreVersion''|2.2|
!Code
***/
//{{{
version.extensions.addTag = {major: 0, minor: 1, revision: 2, date: new Date("Sep 2, 2009")};

config.macros.addTag = {};
config.macros.addTag.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
  var prms = paramString.parseParams(null, null, true);
  var tag = getParam(prms, "tag");
  var tid = getParam(prms, "tiddler");
  var text = getParam(prms, "text");
  var tooltip = getParam(prms, "tooltip");
  var cls= getParam(prms, "class");
  var remove = getParam(prms, "removeTag");
  var rbutton = getParam(prms, "removeButton");
  var hide = getParam(prms, "hide");
  if (tid==undefined) tid=story.findContainingTiddler(place).getAttribute('tiddler');
  if (text==undefined) text="add "+ tag;
  if (tooltip==undefined) tooltip="add "+ tag + " to " + tid;
  var t = store.getTiddler(tid);
  if (t==null) {wikify("@@color:red;invalid tiddler: " + tid +"@@",place);return;}
  if (hide!=undefined && t.tags.containsAny(hide.readBracketedList())) return;
  if (remove != undefined && t.tags.containsAny(remove.readBracketedList()) && rbutton!=undefined) {
  	var btn = createTiddlyButton(place,rbutton,"remove " + remove,function(){tags=this.removeTag.readBracketedList();for (var tag=0; tag<tags.length; tag++) {store.setTiddlerTag(btn.tid,0,tags[tag])}; story.refreshTiddler(story.findContainingTiddler(this).getAttribute('tiddler'),null,true);},cls);
  	btn.removeTag = remove;
  	btn.tid = tid;
  }
  var btn = createTiddlyButton(place,text,tooltip,function() {tags=this.removeTag.readBracketedList();for (var tag=0; tag<tags.length; tag++) {store.setTiddlerTag(btn.tid,0,tags[tag]);};store.setTiddlerTag(btn.tid,1,this.switchTag);story.refreshTiddler(story.findContainingTiddler(this).getAttribute('tiddler'),null,true);},cls);
  btn.switchTag = tag;
  btn.removeTag = remove;
  btn.tid = tid;
};
//}}}
<<showtoc>>
!What for
[[AddTagMacro]] is a simple macro adding a button allowing you to assign a tag to the current tiddler, optionally removing another tag.
!Usage
{{{<<addTag tag:"tagToBeAdded" text:"button text" tooltip:"tooltip to be displayed for button" class:"css class of button" removeTag:"BracketedListOfTagsToBeRemoved" removeButton:"-" hide:"BracketedListOfTagsForHiding" tiddler:"remoteTiddler">>}}}
''Note:'' The order of parameters is of no relevance.
|Parameter|Optional?|Comment|h
|tag|required|the tag you want to set|
|text|optional|the text for the button to set the tag|
|tooltip|optional|the tooltip for the button to set the tag|
|class|optional|the css class for the button to set the tag|
|removeTag|optional|a tag to be removed at the tiddler when adding the other tag|
|removeButton|optional|any string entered here prepends a button named with that string to remove the tag defined for removal|
|hide|optional|list tags for which no button will be displayed if the tiddler is tagged with any of them|
|tiddler|optional|the tiddler for wich you want to set the new tag (and remove the removeTag), by default the current tiddler|
!Examples
{{{<<addTag tag:"tagA" text:"add tagA" tooltip:"click here to add tagA and remove TagB" removeTag:"tagB">>}}}
<<addTag tag:"tagA" text:"add tagA" tooltip:"click here to add tagA and remove TagB" removeTag:"tagB">>

{{{<<addTag tag:"tagB" text:"add tagB" tooltip:"click here to add tagB and remove TagA a b c d"  removeButton:"-" removeTag:"tagA a b c d">>}}}
<<addTag tag:"tagB" text:"add tagB" tooltip:"click here to add tagB and remove tags TagA a b c d"  removeButton:"-" removeTag:"tagA a b c d">>

{{{<<addTag tag:"podcast" text:"make podcast" removeTag:"radio" tiddler:"LastFM">>}}}
<<addTag tag:"podcast" text:"make podcast" removeTag:"radio" tiddler:"LastFM">>      {{{   ...check here for changes:}}} [[LastFM]]

{{{<<addTag tag:"tagB" text:"add tagB" tooltip:"click here to add tagB and remove TagA"  removeTag:"tagA" removeButton:"-" hide:"tagA [[tagB]]">>}}}
<<addTag tag:"tagB" text:"add tagB" tooltip:"click here to add tagB and remove TagA"  removeTag:"tagA" removeButton:"-" hide:"tagA [[tagB]]">>

something invalid: {{{<<addTag tag:"podcast" text:"make podcast" removeTag:"radio" tiddler:"LastFeM">>}}}
<<addTag tag:"podcast" text:"make podcast" removeTag:"radio" tiddler:"LastFeM">>
!Tag manipulation alternatives
In case you are looking for the ability to assign a single tag from a collection of tags while leaving all other tags untouched, 
[[Eric Schulmans|http://www.TiddlyTools.com]] [[ListboxPlugin|http://www.TiddlyTools.com/#ListboxPlugin]] will do the trick like so...
{{{<<select tags  "prompt:set task status..." rows:1 unassigned assigned inprogress testing completed>>}}}
<<select tags "prompt:set task status..." rows:1 unassigned assigned inprogress testing completed>> 

Another powerful alternative is [[TaggerPlugin|http://tw.lewcid.org/#TaggerPlugin]] on [[lewcid.org|http://tw.lewcid.org]] which allows you to see in a popup all assigned tags and assign any tags from either a specified collection or from the list of available tags, while being able to add a tags as well.
!Revision History
|Version|What's New?|h
|0.1.2|- changed removeButton functionality, you can now specify a button name<br>- allowed optional parameter 'hide' which, if specified won't display any button when the tiddler is tagged with a tag from that list|
|0.1.1|- changed order of adding and removing tags so that you can remove and add the same tag (with the optional remove button)<br>- added a refresh to the tiddler, when the button is clicked |
|0.1.0|- made all parameters optional, except the tag<br>- added functionality to set the tag on a remote tiddler |
|0.0.2|- fixed bug caused by using a reserved keyword |
text/plain
.txt .text .js .vbs .asp .cgi .pl
----
text/html
.htm .html .hta .htx .mht
----
text/comma-separated-values
.csv
----
text/javascript
.js
----
text/css
.css
----
text/xml
.xml .xsl .xslt
----
image/gif
.gif
----
image/jpeg
.jpg .jpe .jpeg
----
image/png
.png
----
image/bmp
.bmp
----
image/tiff
.tif .tiff
----
audio/basic
.au .snd
----
audio/wav
.wav
----
audio/x-pn-realaudio
.ra .rm .ram
----
audio/x-midi
.mid .midi
----
audio/mp3
.mp3
----
audio/m3u
.m3u
----
video/x-ms-asf
.asf
----
video/avi
.avi
----
video/mpeg
.mpg .mpeg
----
video/quicktime
.qt .mov .qtvr
----
application/pdf
.pdf
----
application/rtf
.rtf
----
application/postscript
.ai .eps .ps
----
application/wordperfect
.wpd
----
application/mswrite
.wri
----
application/msexcel
.xls .xls3 .xls4 .xls5 .xlw
----
application/msword
.doc
----
application/mspowerpoint
.ppt .pps
----
application/x-director
.swa
----
application/x-shockwave-flash
.swf
----
application/x-zip-compressed
.zip
----
application/x-gzip
.gz
----
application/x-rar-compressed
.rar
----
application/octet-stream
.com .exe .dll .ocx
----
application/java-archive
.jar
/***
|Name|AttachFilePluginFormatters|
|Source|http://www.TiddlyTools.com/#AttachFilePluginFormatters|
|Version|4.0.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1.3|
|Type|plugin|
|Requires||
|Overrides|'image' and 'prettyLink' formatters, TiddlyWiki.prototype.getRecursiveTiddlerText|
|Description|run-time library for displaying attachment tiddlers|
***/
//{{{
version.extensions.AttachFilePluginFormatters= {major: 4, minor: 0, revision: 0, date: new Date(2009,6,4)};
if (config.macros.attach==undefined) config.macros.attach= { };
//}}}
//{{{
if (config.macros.attach.isAttachment==undefined) config.macros.attach.isAttachment=function (title) {
	var tiddler = store.getTiddler(title);
	if (tiddler==undefined || tiddler.tags==undefined) return false;
	return (tiddler.tags.indexOf("attachment")!=-1);
}
//}}}

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

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

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

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

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

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

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

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

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

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

	// find the formatter for "image" and replace the handler
	for (var i=0; i<config.formatters.length && config.formatters[i].name!="image"; i++);
	if (i<config.formatters.length)	config.formatters[i].handler=function(w) {
		this.lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart) // Simple bracketted link
			{
			var e = w.output;
			if(lookaheadMatch[5])
				{
				var link = lookaheadMatch[5];
				// ELS -------------
				var external=config.formatterHelpers.isExternalLink(link);
				if (external)
					{
					if (config.macros.attach.isAttachment(link))
						{
						e = createExternalLink(w.output,link);
						e.href=config.macros.attach.getAttachment(link);
						e.title = config.macros.attach.linkTooltip + link;
						}
					else
						e = createExternalLink(w.output,link);
					}
				else 
					e = createTiddlyLink(w.output,link,false,null,w.isStatic);
				// ELS -------------
				addClass(e,"imageLink");
				}
			var img = createTiddlyElement(e,"img");
			if(lookaheadMatch[1])
				img.align = "left";
			else if(lookaheadMatch[2])
				img.align = "right";
			if(lookaheadMatch[3])
				img.title = lookaheadMatch[3];
			img.src = lookaheadMatch[4];
			// ELS -------------
			if (config.macros.attach.isAttachment(lookaheadMatch[4]))
				img.src=config.macros.attach.getAttachment(lookaheadMatch[4]);
			// ELS -------------
			w.nextMatch = this.lookaheadRegExp.lastIndex;
		}
	}
//}}}
//{{{
	// find the formatter for "prettyLink" and replace the handler
	for (var i=0; i<config.formatters.length && config.formatters[i].name!="prettyLink"; i++);
	if (i<config.formatters.length)	{
		config.formatters[i].handler=function(w) {
			this.lookaheadRegExp.lastIndex = w.matchStart;
			var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
			if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
				var e;
				var text = lookaheadMatch[1];
				if(lookaheadMatch[3]) {
					// Pretty bracketted link
					var link = lookaheadMatch[3];
					if (config.macros.attach.isAttachment(link)) {
						e = createExternalLink(w.output,link);
						e.href=config.macros.attach.getAttachment(link);
						e.title=config.macros.attach.linkTooltip+link;
					}
					else e = (!lookaheadMatch[2] && config.formatterHelpers.isExternalLink(link))
						? createExternalLink(w.output,link)
						: createTiddlyLink(w.output,link,false,null,w.isStatic);
				} else {
					e = createTiddlyLink(w.output,text,false,null,w.isStatic);
				}
				createTiddlyText(e,text);
				w.nextMatch = this.lookaheadRegExp.lastIndex;
			}
		}
	} // if "prettyLink" formatter found
	this.initialized=true;
}
//}}}
//{{{
config.macros.attach.init_formatters(); // load time init
//}}}
//{{{
if (TiddlyWiki.prototype.coreGetRecursiveTiddlerText==undefined) {
	TiddlyWiki.prototype.coreGetRecursiveTiddlerText = TiddlyWiki.prototype.getRecursiveTiddlerText;
	TiddlyWiki.prototype.getRecursiveTiddlerText = function(title,defaultText,depth) {
		return config.macros.attach.isAttachment(title)?
			config.macros.attach.getAttachment(title):this.coreGetRecursiveTiddlerText.apply(this,arguments);
	}
}
//}}}
/***
|Name|CheckboxPlugin|
|Source|http://www.TiddlyTools.com/#CheckboxPlugin|
|Documentation|http://www.TiddlyTools.com/#CheckboxPluginInfo|
|Version|2.4.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Description|Add checkboxes to your tiddler content|
This plugin extends the TiddlyWiki syntax to allow definition of checkboxes that can be embedded directly in tiddler content.  Checkbox states are preserved by:
* by setting/removing tags on specified tiddlers,
* or, by setting custom field values on specified tiddlers,
* or, by saving to a locally-stored cookie ID,
* or, automatically modifying the tiddler content (deprecated)
When an ID is assigned to the checkbox, it enables direct programmatic access to the checkbox DOM element, as well as creating an entry in TiddlyWiki's config.options[ID] internal data.  In addition to tracking the checkbox state, you can also specify custom javascript for programmatic initialization and onClick event handling for any checkbox, so you can provide specialized side-effects in response to state changes.
!!!!!Documentation
>see [[CheckboxPluginInfo]]
!!!!!Revisions
<<<
2008.01.08 [*.*.*] plugin size reduction: documentation moved to [[CheckboxPluginInfo]]
2008.01.05 [2.4.0] set global "window.place" to current checkbox element when processing checkbox clicks.  This allows init/beforeClick/afterClick handlers to reference RELATIVE elements, including using "story.findContainingTiddler(place)".  Also, wrap handlers in "function()" so "return" can be used within handler code.
|please see [[CheckboxPluginInfo]] for additional revision details|
2005.12.07 [0.9.0] initial BETA release
<<<
!!!!!Code
***/
//{{{
version.extensions.CheckboxPlugin = {major: 2, minor: 4, revision:0 , date: new Date(2008,1,5)};
//}}}
//{{{
config.checkbox = { refresh: { tagged:true, tagging:true, container:true } };
config.formatters.push( {
	name: "checkbox",
	match: "\\[[xX_ ][\\]\\=\\(\\{]",
	lookahead: "\\[([xX_ ])(=[^\\s\\(\\]{]+)?(\\([^\\)]*\\))?({[^}]*})?({[^}]*})?({[^}]*})?\\]",
	handler: function(w) {
		var lookaheadRegExp = new RegExp(this.lookahead,"mg");
		lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = lookaheadRegExp.exec(w.source)
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
			// get params
			var checked=(lookaheadMatch[1].toUpperCase()=="X");
			var id=lookaheadMatch[2];
			var target=lookaheadMatch[3];
			if (target) target=target.substr(1,target.length-2).trim(); // trim off parentheses
			var fn_init=lookaheadMatch[4];
			var fn_clickBefore=lookaheadMatch[5];
			var fn_clickAfter=lookaheadMatch[6];
			var tid=story.findContainingTiddler(w.output);  if (tid) tid=tid.getAttribute("tiddler");
			var srctid=w.tiddler?w.tiddler.title:null;
			config.macros.checkbox.create(w.output,tid,srctid,w.matchStart+1,checked,id,target,config.checkbox.refresh,fn_init,fn_clickBefore,fn_clickAfter);
			w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
		}
	}
} );
config.macros.checkbox = {
	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		if(!(tiddler instanceof Tiddler)) { // if no tiddler passed in try to find one
			var here=story.findContainingTiddler(place);
			if (here) tiddler=store.getTiddler(here.getAttribute("tiddler"))
		}
		var srcpos=0; // "inline X" not applicable to macro syntax
		var target=params.shift(); if (!target) target="";
		var defaultState=params[0]=="checked"; if (defaultState) params.shift();
		var id=params.shift(); if (id && !id.length) id=null;
		var fn_init=params.shift(); if (fn_init && !fn_init.length) fn_init=null;
		var fn_clickBefore=params.shift();
		if (fn_clickBefore && !fn_clickBefore.length) fn_clickBefore=null;
		var fn_clickAfter=params.shift();
		if (fn_clickAfter && !fn_clickAfter.length) fn_clickAfter=null;
		var refresh={ tagged:true, tagging:true, container:false };
		this.create(place,tiddler.title,tiddler.title,0,defaultState,id,target,refresh,fn_init,fn_clickBefore,fn_clickAfter);
	},
	create: function(place,tid,srctid,srcpos,defaultState,id,target,refresh,fn_init,fn_clickBefore,fn_clickAfter) {
		// create checkbox element
		var c = document.createElement("input");
		c.setAttribute("type","checkbox");
		c.onclick=this.onClickCheckbox;
		c.srctid=srctid; // remember source tiddler
		c.srcpos=srcpos; // remember location of "X"
		c.container=tid; // containing tiddler (may be null if not in a tiddler)
		c.tiddler=tid; // default target tiddler 
		c.refresh = {};
		c.refresh.container = refresh.container;
		c.refresh.tagged = refresh.tagged;
		c.refresh.tagging = refresh.tagging;
		place.appendChild(c);
		// set default state
		c.checked=defaultState;
		// track state in config.options.ID
		if (id) {
			c.id=id.substr(1); // trim off leading "="
			if (config.options[c.id]!=undefined)
				c.checked=config.options[c.id];
			else
				config.options[c.id]=c.checked;
		}
		// track state in (tiddlername|tagname) or (fieldname@tiddlername)
		if (target) {
			var pos=target.indexOf("@");
			if (pos!=-1) {
				c.field=pos?target.substr(0,pos):"checked"; // get fieldname (or use default "checked")
				c.tiddler=target.substr(pos+1); // get specified tiddler name (if any)
				if (!c.tiddler || !c.tiddler.length) c.tiddler=tid; // if tiddler not specified, default == container
				if (store.getValue(c.tiddler,c.field)!=undefined)
					c.checked=(store.getValue(c.tiddler,c.field)=="true"); // set checkbox from saved state
			} else {
				var pos=target.indexOf("|"); if (pos==-1) var pos=target.indexOf(":");
				c.tag=target;
				if (pos==0) c.tag=target.substr(1); // trim leading "|" or ":"
				if (pos>0) { c.tiddler=target.substr(0,pos); c.tag=target.substr(pos+1); }
				if (!c.tag.length) c.tag="checked";
				var t=store.getTiddler(c.tiddler);
				if (t && t.tags)
					c.checked=t.isTagged(c.tag); // set checkbox from saved state
			}
		}
		// trim off surrounding { and } delimiters from init/click handlers
		if (fn_init) c.fn_init="(function(){"+fn_init.trim().substr(1,fn_init.length-2)+"})()";
		if (fn_clickBefore) c.fn_clickBefore="(function(){"+fn_clickBefore.trim().substr(1,fn_clickBefore.length-2)+"})()";
		if (fn_clickAfter) c.fn_clickAfter="(function(){"+fn_clickAfter.trim().substr(1,fn_clickAfter.length-2)+"})()";
		c.init=true; c.onclick(); c.init=false; // compute initial state and save in tiddler/config/cookie
	},
	onClickCheckbox: function(event) {
		window.place=this;
		if (this.init && this.fn_init) // custom function hook to set initial state (run only once)
			{ try { eval(this.fn_init); } catch(e) { displayMessage("Checkbox init error: "+e.toString()); } }
		if (!this.init && this.fn_clickBefore) // custom function hook to override changes in checkbox state
			{ try { eval(this.fn_clickBefore) } catch(e) { displayMessage("Checkbox onClickBefore error: "+e.toString()); } }
		if (this.id)
			// save state in config AND cookie (only when ID starts with 'chk')
			{ config.options[this.id]=this.checked; if (this.id.substr(0,3)=="chk") saveOptionCookie(this.id); }
		if (this.srctid && this.srcpos>0 && (!this.id || this.id.substr(0,3)!="chk") && !this.tag && !this.field) {
			// save state in tiddler content only if not using cookie, tag or field tracking
			var t=store.getTiddler(this.srctid); // put X in original source tiddler (if any)
			if (t && this.checked!=(t.text.substr(this.srcpos,1).toUpperCase()=="X")) { // if changed
				t.set(null,t.text.substr(0,this.srcpos)+(this.checked?"X":"_")+t.text.substr(this.srcpos+1),null,null,t.tags);
				if (!story.isDirty(t.title)) story.refreshTiddler(t.title,null,true);
				store.setDirty(true);
			}
		}
		if (this.field) {
			if (this.checked && !store.tiddlerExists(this.tiddler))
				store.saveTiddler(this.tiddler,this.tiddler,"",config.options.txtUserName,new Date());
			// set the field value in the target tiddler
			store.setValue(this.tiddler,this.field,this.checked?"true":"false");
			// DEBUG: displayMessage(this.field+"@"+this.tiddler+" is "+this.checked);
		}
		if (this.tag) {
			if (this.checked && !store.tiddlerExists(this.tiddler))
				store.saveTiddler(this.tiddler,this.tiddler,"",config.options.txtUserName,new Date());
			var t=store.getTiddler(this.tiddler);
			if (t) {
				var tagged=(t.tags && t.tags.indexOf(this.tag)!=-1);
				if (this.checked && !tagged) { t.tags.push(this.tag); store.setDirty(true); }
				if (!this.checked && tagged) { t.tags.splice(t.tags.indexOf(this.tag),1); store.setDirty(true); }
			}
			// if tag state has been changed, update display of corresponding tiddlers (unless they are in edit mode...)
			if (this.checked!=tagged) {
				if (this.refresh.tagged) {
					if (!story.isDirty(this.tiddler)) // the TAGGED tiddler in view mode
						story.refreshTiddler(this.tiddler,null,true); 
					else // the TAGGED tiddler in edit mode (with tags field)
						config.macros.checkbox.refreshEditorTagField(this.tiddler,this.tag,this.checked);
				}
				if (this.refresh.tagging)
					if (!story.isDirty(this.tag)) story.refreshTiddler(this.tag,null,true); // the TAGGING tiddler
			}
		}
		if (!this.init && this.fn_clickAfter) // custom function hook to react to changes in checkbox state
			{ try { eval(this.fn_clickAfter) } catch(e) { displayMessage("Checkbox onClickAfter error: "+e.toString()); } }
		// refresh containing tiddler (but not during initial rendering, or we get an infinite loop!) (and not when editing container)
		if (!this.init && this.refresh.container && this.container!=this.tiddler)
			if (!story.isDirty(this.container)) story.refreshTiddler(this.container,null,true); // the tiddler CONTAINING the checkbox
		return true;
	},
	refreshEditorTagField: function(title,tag,set) {
		var tagfield=story.getTiddlerField(title,"tags");
		if (!tagfield||tagfield.getAttribute("edit")!="tags") return; // if no tags field in editor (i.e., custom template)
		var tags=tagfield.value.readBracketedList();
		if (tags.contains(tag)==set) return; // if no change needed
		if (set) tags.push(tag); // add tag
		else tags.splice(tags.indexOf(tag),1); // remove tag
		for (var t=0;t<tags.length;t++) tags[t]=String.encodeTiddlyLink(tags[t]);
		tagfield.value=tags.join(" "); // reassemble tag string (with brackets as needed)
		return;
	}
}
//}}}
Name: Smoke
Background: #fff
Foreground: #000
PrimaryPale: #999
PrimaryLight: #777
PrimaryMid: #111
PrimaryDark: #000
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
/***
<<showtoc>>
|Name|DcTableOfContentsPlugin|
|Author|[[Doug Compton|http://www.zagware.com/tw/plugins.html#DcTableOfContentsPlugin]]|
|Contributors|[[Lewcid|http://lewcid.org]], [[FND|http://devpad.tiddlyspot.com]], [[ELS|http://www.tiddlytools.com]]|
|Source|[[FND's DevPad|http://devpad.tiddlyspot.com#DcTableOfContentsPlugin]]|
|Version|0.4.1|
|~CoreVersion|2.2|
!Description
This macro inserts a table of contents for all headings used in a tiddler and automatically updates on changes. Each ToC entry can be clicked to jump to that heading. A link is placed next to each header allowing to jump to the top of the current tiddler. The appearance can be modified using CSS in the StyleSheet tiddler.
!Usage
!!Only in select tiddlers!
The table of contents at the beginning of this tiddler is an example of how to use this macro in a tiddler. Just insert {{{<<showtoc>>}}} in a tiddler on a line by itself.
It can also display the table of contents of another tiddler by using the macro with a parameter: {{{<<showtoc tiddlerTitle>>}}}
!!On every tiddler
You can also show it on every tiddler by modifying the ViewTemplate as shown below.
//{{{
<div class='toolbar' macro='toolbar -closeTiddler closeOthers +editTiddler permalink references jump'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'>Created <span macro='view created date DD-MM-YY'></span>, updated <span macro='view modified date DD-MM-YY'></span></div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class="toc" macro='showtoc'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
//}}}
!Code
***/
//{{{
version.extensions.DcTableOfContentsPlugin= {
	major: 0, minor: 4, revision: 0,
	type: "macro",
	source: "http://devpad.tiddlyspot.com#DcTableOfContentsPlugin"
};

// Replace heading formatter with our own
for (var n=0; n<config.formatters.length; n++) {
	var format = config.formatters[n];
	if (format.name == 'heading') {
		format.handler = function(w) {
			// following two lines is the default handler
			var e = createTiddlyElement(w.output, "h" + w.matchLength);
			w.subWikifyTerm(e, this.termRegExp); //updated for TW 2.2+

			// Only show [top] if current tiddler is using showtoc
			if (w.tiddler && w.tiddler.isTOCInTiddler == 1) {
				// Create a container for the default CSS values
				var c = createTiddlyElement(e, "div");
				c.setAttribute("style", "color: blue;float:right;margin-top:-1.1em;");
				// Create the link to jump to the top
				createTiddlyButton(c, "^ ToC", "Go to Table of Contents", window.scrollToTop, "dcTOCTop", null, null);
			}
		}
		break;
	}
}

config.macros.showtoc = {
	handler: function(place, macroName, params, wikifier, paramString, tiddler) {
		var text = "";
		var title = "";
		var myTiddler = null;

		// Did they pass in a tiddler?
		if (params.length) {
			title = params[0];
			myTiddler = store.getTiddler(title);
		} else {
			myTiddler = tiddler;
		}

		if (myTiddler == null) {
			wikify("ERROR: Could not find " + title, place);
			return;
		}

		var lines = myTiddler .text.split("\n");
		myTiddler.isTOCInTiddler = 1;

		// Create a parent container so the TOC can be customized using CSS
		var r = createTiddlyElement(place, "div", null, "dcTOC");
		// create toggle button
		createTiddlyButton(r, "Table of Contents...", "show/collapse table of contents",
			function() { config.macros.showtoc.toggleElement(this.nextSibling); },
			"toggleButton")
		// Create a container so the TOC can be customized using CSS
		var c = createTiddlyElement(r, "div");

		if (lines != null) {
			for (var x=0; x<lines.length; x++) {
				var line = lines[x];
				if (line.substr(0,1) == "!") {
					// Find first non ! char
					for (var i=0; i<line.length; i++) {
						if (line.substr(i, 1) != "!") {
							break;
						}
					}
					var desc = line.substring(i);
					// Remove WikiLinks
					desc = desc.replace(/\[\[/g, "");
					desc = desc.replace(/\]\]/g, "");

					text += line.substr(0, i).replace(/[!]/g, '*');
					text += '<html><a href="javascript:;" onClick="window.scrollToHeading(\'' + title + '\', \'' + desc+ '\', event)">' + desc+ '</a></html>\n';
				}
			}
		}
		wikify(text, c);
	}
}

config.macros.showtoc.toggleElement = function(e) {
	if(e) {
		if(e.style.display != "none") {
			e.style.display = "none";
		} else {
			e.style.display = "";
		}
	}
};

window.scrollToTop = function(evt) {
	if (! evt)
		var evt = window.event;

	var target = resolveTarget(evt);
	var tiddler = story.findContainingTiddler(target);

	if (! tiddler)
		return false;

	window.scrollTo(0, ensureVisible(tiddler));

	return false;
};

window.scrollToHeading = function(title, anchorName, evt) {
	var tiddler = null;

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

	if (title) {
		story.displayTiddler(store.getTiddler(title), title, null, false);
		tiddler = document.getElementById(story.idPrefix + title);
	} else {
		var target = resolveTarget(evt);
		tiddler = story.findContainingTiddler(target);
	}

	if (tiddler == null)
		return false;
	
	var children1 = tiddler.getElementsByTagName("h1");
	var children2 = tiddler.getElementsByTagName("h2");
	var children3 = tiddler.getElementsByTagName("h3");
	var children4 = tiddler.getElementsByTagName("h4");
	var children5 = tiddler.getElementsByTagName("h5");

	var children = new Array();
	children = children.concat(children1, children2, children3, children4, children5);

	for (var i = 0; i < children.length; i++) {
		for (var j = 0; j < children[i].length; j++) {
			var heading = children[i][j].innerHTML;

			// Remove all HTML tags
			while (heading.indexOf("<") >= 0) {
				heading = heading.substring(0, heading.indexOf("<")) + heading.substring(heading.indexOf(">") + 1);
			}

			// Cut off the code added in showtoc for TOP
			heading = heading.substr(0, heading.length-5);

			if (heading == anchorName) {
				var y = findPosY(children[i][j]);
				window.scrollTo(0,y);
				return false;
			}
		}
	}
	return false
};
//}}}
[[Home]]
<script label="delete" title="click to delete $1">
  var sTid="$1";
  var update="$2";
  var refresh=true;
  if(sTid=="$"+"1"){
    sTid=story.findContainingTiddler(place).getAttribute('tiddler');
    if (sTid=="DeleteTiddler") return;
    if (!confirm("Do you really want to delete "+sTid+"?")) return;
    else refresh=false;
  }
  var oTid=store.getTiddler(sTid);
  if (oTid==undefined){
    alert("Unable to delete $1.\nTiddler does not exist.");
    return;
  }
  store.removeTiddler(sTid);
  story.closeTiddler(sTid,true);
  if (refresh) story.refreshTiddler(story.findContainingTiddler(place).getAttribute('tiddler'),null,true);
  if (update!="$"+"2") story.refreshTiddler("$2",null,true);
</script>
/***
|''Name:''|DeliciousTaggingPlugin|
|''Version:''|0.1|
|''Source''|http://jackparke.googlepages.com/jtw.html#DeliciousTaggingPlugin ([[del.icio.us|http://del.icio.us/post?url=http://jackparke.googlepages.com/jtw.html%DeliciousTaggingPlugin]])|
|''Author:''|[[Jack]]|
!Description
Allows easy 'del.icio.us'-like tagging in the EditTemplate by showing all tags as a list of link-buttons.
!Usage
Replace your the editorFooter div in your [[EditTemplate]] with the following:
{{{
<div class='editorFooter' macro='deliciousTagging'></div>
}}}
!Code
***/
//{{{
version.extensions.deliciousTagging = {major: 0, minor: 1, revision: 0, date: new Date("June 11, 2007")};

config.macros.deliciousTagging= {};

config.macros.deliciousTagging.onTagClick = function(e)
{
 if(!e) var e = window.event;
 var tag = this.getAttribute("tag");
 var title = this.getAttribute("tiddler");
 if(!readOnly)
  story.setTiddlerTag(title,tag,0);
 return false;
};

config.macros.deliciousTagging.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
 if(tiddler instanceof Tiddler) {
  var title = tiddler.title;
  if(!e) var e = window.event;
  var tags = store.getTags();
  var lingo = config.views.editor.tagChooser;
  for(var t=0; t<tags.length; t++) {
   var theTag = createTiddlyButton(place,tags[t][0],lingo.tagTooltip.format([tags[t][0]]),config.macros.deliciousTagging.onTagClick);
   theTag.setAttribute("tag",tags[t][0]);
   theTag.setAttribute("tiddler",tiddler.title);
   place.appendChild(document.createTextNode(" "));
  }
 }
};
//}}}
/***
|''Name:''|DeprecatedFunctionsPlugin|
|''Description:''|Support for deprecated functions removed from core|
***/
//{{{
if(!version.extensions.DeprecatedFunctionsPlugin) {
version.extensions.DeprecatedFunctionsPlugin = {installed:true};

//--
//-- Deprecated code
//--

// @Deprecated: Use createElementAndWikify and this.termRegExp instead
config.formatterHelpers.charFormatHelper = function(w)
{
	w.subWikify(createTiddlyElement(w.output,this.element),this.terminator);
};

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

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

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

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

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

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

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

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

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

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

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

}
//}}}
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div class="editLabel">Tags</div><div class="editor" macro="edit tags"></div>
<div class='editorFooter' macro='deliciousTagging'></div>
<!--<div class="editorFooter"><span macro="message views.editor.tagPrompt"></span><span macro="tagChooser"></span></div>-->
<div macro='annotations'></div>
<div class="editLabel">Content</div><div class="editor" macro="edit text"></div>
<!--}}}-->
/***
|''Name:''|FiveStarsPluginMod (mod of [[FiveStarsPlugin|http://sinojellyempty.tiddlyspot.com/#FiveStarsPlugin]])|
|''Description''|allows to rate tiddlers using stars as images|
|''Version:''|0.0.1|
|''Type''|macro|
|''Author:''|[[TobiasBeer]]|
|''Info:''|http://lastfm.tiddlyspot.com/#RatingMacroInfo [[RatingMacroInfo]]|
|''Source:''|http://lastfm.tiddlyspot.com/#FiveStarsPluginMod [[FiveStarsPluginMod]]|
|''License''|[[Creative Commons Attribution-Share Alike 3.0|http://creativecommons.org/licenses/by-sa/3.0/]]|
|''Feedback:''|[[here|http://groups.google.com/group/tiddlywiki/browse_frm/thread/35b6e27237f1749b]]|
|''~CoreVersion''|2.2|
|''Requires:''|[[AttachFilePluginFormatters]]|
Inspired by http://sinojellyempty.tiddlyspot.com/#FiveStarsPlugin
!Code
***/

//{{{
config.macros.fiveStars = {
  tooltip: "rating: ",
  cssStyle: "width:15px;float:left;",
  ratings: ["none","poor", "below average", "average", "above average", "excellent"],
  star0: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9gJGxQpCZ4Equ4AAASsSURBVDjLlZVdiF1XFcd/a+9z7p2vOy8d0zY6TSWaxii0NlXQttJqHzSKiFhpoab6oogW+yBFUXxQKCj4Uaoogk+KKRWlfkAfhIIKpgZtTam2xKTJpM183Zl7Z+7H+dp7r+XDnQljlQY3nKdz9m+vs/b//1/y6A9+xGsvgVgi4jDfdmAGGNjkrfNsXjjN6rnTLC/9g5VX+vS64K5IFUc2fvmwqzYWEe+AjL37RP7nxiuAxSRW2UJ46dFr5prPqakBU3vhFsPOT/wfYEMgFAfuvOe+O254y/4PpBBfB8wAOYigiXqwgabwX5W/FtgZxkzcfFdnoZO9/s1HDmfN8LAhU8A0kJmpXLEVJk5M3G4PM8Cban5wsfXJvJ3RzlNrNq2+3/BTwNxOS1q7305u+VVgE6HV9M2PuxqjxpgkBfVBi8H1B48ceqc4odXOOHBNflcMYTaqWFQhqNdoLkaVBOSC5LvgDHFkxdqhg/v9J9qzHd/f7NcIvgmpmW6Pb77q2ps7qOC845Y777qx/7unvmj59Ip3EsQ0+FllebQ93tLweNT0ihk5EDMDLIWWrT17/B33fvo6P51hCTBDEPKWhzSpYn4+yz78sffei05k5hDGvXWeePEPTzd1/VtNoW1GwEiZmPo0t/j8maZzrHPybz+88dZbbp+eb4MKqEGy3UtARGi125NuhobTv3xY//hPTpzdnHpkXK6OY1NPx0A0A3/sgx8SwVrqp7cvbuRPvvTXU3Ld4huOtqcyL8jkGtQmWrUIqaIe9PjN9x5c//0L899fqeYeG2+vV4PechwOxsVoSNXUJAcooA71eTsPW3Nv/c7PHz/1wOr55QIADeBbmG+Dnya5OX72rS+debr39m+OmP3TaGMp29q42IyG/XFVUseAArYrtwRUYKV3FmN74eTm8tIANwMuBwtgEVMFhUGcXy9jurC9dt563YtbW73u9mioo6qkSZEkAg4RMFXTkExjpaab7WbjbW86esfVWDU5NTosCYLgnLA4s3ZD2VvpjEf9wWiwVYwGVhRj6rok7qgXF4sBoRoSq7GFYjvW40Fzdae5rd02QZWyEH78lc8+c+Lr959vwkQpxx76xUIe1g9WdVVUFaEoCNWYlAKKTdydlYMuIoJpshBqTUFm3vP5j9wNiVM/fbB88qR/YtMf+nXT7c1f+sI9xx/49mO3tTOR/bOjm85dan6lisYA4VVZ5NbO/Z3Vs8+ydv456b581qqVZ45a0V1YXloZnXgq/+py3fnJYO3Mdr+79K+/XPRfe+hTH/3uxqULev+XH3kfqZxPiYQhtpPQu4/cfeu1O0c4KcYDu/4qd3zfTPj4i/19D0dxg1Buz5bFoK7K0IAk8y7tm3PvvumAfObPZ5pvXFrldDlkVDUUXLYSyO1vZG9mu8xLx8TnGFNImg2NxaqkrGsCQCunJRnWypkLDWk8YlgXDBulsb3gI3P/mXaqiPdMZZ4pA0mR2NSElIgA3pPlLVpqGIbEhhASjUK4PK+ATHWHKNDUaIg4geDcBGSGqk1MBJCUGANBJrYkGao73txbYXY5p2UiEwNVCFHZNTQGJpenCilBUptEvOydrHvWvwGT9K2B/6ho5wAAAABJRU5ErkJggg==",
  star1: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6AAAdTAAAOpgAAA6lwAAF2+XqZnUAAAF30lEQVR4nGKcPHUGA37AyMDw5zsDIyMTw39mdiYGhv//gYL/wQgky8TM8PbBRYYXdy8yPHt4leH5k/cM714zMAAEEBNBU4EGsnx9rMH0440sAyMzSD0LECP0MTJi1QgQQAQMZvzP+OcHi8jve5N5/71K/vf/H8iZHMiG///zG+oJVAAQQCz4jP0PcvDvb/JqKgw2b779YHr99Y8oEysTyJR/QEv/Mvz7w/Dz8zuGf39/Y7gcIIDwuRhown8Gzt9vLOQ1uDgkJf4Ysvz6rAG0DORiTpCj/v//x4grKAACCG7wf0YmoL+ZYGEIwsz///1jFWF/HconysUgIvBXkPvvC4//DMwgg3mgQcIGU8sAjmUEAAggSDgBbWX79f4/89fX//78AaK/jH9//2P+/e/bJwUJwW/mjCysDELCXAzCbB9s/vz+zf3nH+N/IGYAqvn35z8TUAswWBgYWIHOZ4UZDBBALOBY//ZSjZ/huS8TG9vfX3/eAcUYmf8xMP3lYHxvpKjMLsHwhxHoTjYGNZn3hp/uX837z8b1jPH/v1/AwP3FyPL73xfOjwwf/v1e9eff3yfAeAQZ/gcggFjAifLvbzYBxoe5poac8hzivAx/f/wDxvZ/BhZg6uLm52Vg+AmKLkYGDS1+LmmJ9/H/fr9nYAI6iBWYhl88fsNw6OXnE79+/twMjER2oMG/gVHzFyCAWIA2M//lkb1y9xev95+L19pNjX/4iqsLAe0EhtJPYOT//A9OAwx/QUmalYFPRAiSAj69Z7hw7u6/wzdYl995yzHx6/cXX//8+skJTH1/QKkPIIBYwEHM8Jf9Lxv//ds/jbNf77952/Txy1QNA35eFhZg3PwDxsufP0CDgUnqN9Dpf78wvH/6gmH/sZevzryQm/eZgXvbt0/P/3z7/Iblx4+/f3//Zvj7D+hhgABigaRJhn9MDP/YmNhZf39i05tw4N7jW9+/32o1tRERZvh+l4Hh602G/7//AFPuP4ZvH74xrNvDd+vmL6OZPxh+3Pjy5iHLpw8vfn35/P7rj+8MP4EuBpn3HyCAYBkEFKs/gHwmZiZgZmMXOf7tx/1PDF8uCjN8ugC0Fij9mwUY7iwM/34zMnz6w/fq+5+/Dz69vP//w7tnH758/vjx29f/X4AG//r7BxhowJACCCAmcHgBE+z/f7///v/35weQ8Zb91xsdeeGnsgw/roBt/f6RneH3N1ZgBmdh4OZgZpDleqn+/d1z3q9f3n/68unDty+f/n/79pXh58/vDH+gqZcBIIBY/nz7BIzEf6D8DsxIf4FJmPWvEMMbEwmB2ywMf34y3LzMzrDvstQ5PvZvgq56nxTFRNkYtKT+Ce+9+Ur5x88fl378ANr5jeH3r+/AsP0DNIgRErcAAcTy/dNrIIMR6Oi//3///glMMYxc0nI3zNgYPzIcPsDxbd91xY1vmeU2/nr9ju/py9tx/ia/bRRFmJmkuL8Y3H36ax0wov6ByqHfaGURQACxvLx7AcICpqa//5n/c/3/bMzMc9V6ww6O+4fvyU/5xsR98efnW6zfv399efIPc93jd798g/V/ZqmKMBoevv6d7+9fhreg0uo/vISGAIAAYnn24DI0czMxfPv6iUFBmEnxwNffW2+852/7w/j20+/v97i/f/v088f3379AJdqbN0zTXrxhuqAv9z+dlfEf169fDMAMB/QwWiEEEECMtopIHEYGJhZmRt7/jMysQOs5gOZw//71/w8wtr///AnMUaBSh5WBjRGYQIA0D9Cqv1+/MHz++Y3h869/DL/+Q1IXGAAEoJpKTgAAYVigtbj/muJPpGhRoz8XyB2t5WfiPppIZJUXmXM+PBQmLd/G4QJNBmsb/UYQAzPWA/xEHwHE8g/KZQLGJNCAf8B8AGL+BoYM2CCg6UC7IJkInOD/AfPhb4bfQN+Bi8m/IHmgHFoQMwAEEAu8nIYmE1DJAFQIcgVYJ6hc/Q/O9hAA8u5fkPn/IUU8IwNSzYoEAAIMAOfvwBcmnMG+AAAAAElFTkSuQmCC",
  nostar: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9gJGxQpCZ4Equ4AAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4zNqnn4iUAAAYPSURBVEhLlZUJUFNXFIYDAatWW/cRZd/y8l4WkpAAAZIQdpQtbAoEUEcisgnIjhGxijCVVVABRZChItWKoh0FRYlaUVBxG5wWFQdEpEF2CJDc3sB0YWqd+mbOvHn3nvvdc/93zrk43Bc8hUXHVQuLjql8wZL5rgoMUVFghK+mqKgWfPsNmtGOjJpSc+QkJGOKTAydpJKM4fhCgCGq/3sTBYmoDsFEuDAHWouMTKx4IrB69Uzg2NLNYhz+YEr7YZKMPoTjp2UklDtEoywBJOPPn0JBxhZCsK0cQyoHTSiiw5s3s+PjYgPbm8tnmn/K7ohPSGInJiWz94lEnLt83h4pnVzXT6eIuswYqwGKfhquIBIWQChfjhFOPOJaWe7cudMsLCxMpzQzPlgmvQYGuxtlBdlZvIKi4zpQ51WNmwNWjFKw2jt8bl0Pkx7bbm216l+yKBBj1WkMoc6QiCdbbW044eHhrN3xCSvyCo+q36pOa1SM3gATfVfBicyYQ/lFZQQI1p0mY/VtfF5ymZ+PqZROOdthaR5Yv8F58Ty4AjFaBHX9Xnl8GCUjPDziG6VDTlaWUffjymEw3gTkg9dBY3lC6+G8o/RxBrX2Bc/qYHR0tL7Sb5hOspLSyPX1dlzTUh8v/F/wh77uDiNWzK7z+XsqKgrEuXXlBzJqSzLS6ksSz0/0NQAwehOAkSYw+ObadKe3w9MOD4dHpTmppedL0vfXlYr3H03xS/pIRS/d57AT4hw4yw3VcHN693LMxW+8LEae3qsCEwPXgGygEcikDWBK2jgLBEMQPNQEFAWJYCg1FHS1/whkvQ1gurcRDD6vBqfiuL/8RkNSuhnUmmR3F4KlkQEeB/XCw7TK67Dj5dSVi9vGP1wFYBKCxm/NRQqBs+Dqg2AiWgjePr8AwCCc628Aj4/x5QVR/KqoAEdmC53gNkQmSlK8BdYOLKaaEqw6QyZWtvOsk5OT09inDu3KlXbWT8mHbgAwrATfAoqfC8FUsAfofHQGAq+Aydc14Fy8fl+ykJcZIXS13upqSrtA1bafwJAHqZv8nPzc3dVmpZghISVPeJz9qWliWt6RUq3CTLGo53HV2Cz4ejGYChGAztZaIB+QgBlpMygL03sZv80rNlK4wSXEmWIjsFiDSdB1/EmM0JwSGOACE2AODEs1oZtlejw+IRErLC7RyM0poDy5cqAXtNWC6YCN4NXdKjAthRoPNIOZ/maQs50qiQj2EAQ5oM5upstofIKKZj/RIGCMhFQkhQTbQ/BcZijICGuMgt5OFokY+UXHFhQfEvtPtNcpZnycQGfTSZjDMN1gpABGrJBKQG302g/bXM2CvdgalnxjHIGlgVshwwi57xgmuxO2bWX9A4wuhfVfe9OOH7NlyxaVy9m7Tsh9nMHry0XgY9d1ULwDa6uK1nsl64Nw6W0w1iNR7HDSy9jAWE5ja+P0G/U0jWdQwsV6Fye3SJFID4LnmpMCI+InyKjz73TqxasuzuaTPi5Tby4UAkk+YyI9gFEdGeTpJ/Libs/atE4y3gPl6JOADK/VF+3JX5u4aeINholG+0YoaOze4GA+hC6dV339NPKyfgY1bpSC9j4vSwe/3q8a3SVgxoZ682yDHTEnX44uT8DWsolwWJXz/uFpeVdr9ZgHEW/TiejGDKKGe6tdHG1htIawx/xdecodaph6qm9ZjPUSC8K5NrZhdxXTMNCHq0/yYK00cySpm3ANcCjXQIXAMcYbetPVgwo3Ln7wFNM+9Z5ocOCsNdspKiiIDKHze8WfoYuNluIrGIbGbWYmCX1U7OY7ktGZdkR7xxUDDU6p1hqkQW+d+TNjHWE/apA3hBo1vMQMv8vjWnqG+njT/L29v4XN69OtE12CUxWuXKSWRDZaW8k2dbxnwcx+b0K+NE5CWqYxwgtod0YxpOY1Bcuo5lptivF0dwv09KC48vnL7JjMz98mOjicqi4Ot8Bh/drFkVaWa8ReAjTd35+zVxjonBYU5J4kFDpHwe8QX1/Uw95+tSWBsFALh5uv63/dV9BRRROa8q2MRPlDoKn9aco8dbO3xyvntaF9ivMH9sjgI6ftZl8AAAAASUVORK5CYII=",
  	
  update: function(obj,action){
    //actions --> 0:refresh 1:highlight 2:save
    p=obj.parentNode;
    var r=obj.getAttribute("rel");
    var tid=store.getTiddler(story.findContainingTiddler(obj).getAttribute('tiddler'));
    if (tid==null) return;
    if (action==2) {tid.fields["rating"]=r;store.setDirty(true);}
    var last=parseInt(tid.fields["rating"]||0);
    for (i=1;i<this.ratings.length;i++)
      p.childNodes[i].src=((action>0&&i<=r)||(action==0&&i<=last))?this.star1:this.star0;
  },
  
  handler: function(place, macroName, params, wikifier, paramString, tiddler) {
    imgStar0=new Image();imgStar0.src=this.star0;
    imgStar1=new Image();imgStar1.src=this.star1;
    var sID = tiddler.title+'_fiveStars';
    var sHTML='<html><span id="'+sID+'" title="rating">';
    for (var i=0; i<this.ratings.length; i++)
      sHTML += '<img src="'+this.nostar+'" onmouseover="config.macros.fiveStars.update(this,1)" onclick="config.macros.fiveStars.update(this,2)" onmouseout="config.macros.fiveStars.update(this,0)" title="'+this.tooltip+this.ratings[i]+'" rel="'+i+'" style="'+this.cssStyle+'" />';
    sHTML += '</span></html>';
    wikify(sHTML, place);
    this.update(document.getElementById(sID).childNodes[1],0);
  }
}
//}}}
<<fiveStars>>
This is likely where a future version of this player will end up. Below is a template for the extended player which allows to open, close, minimize, restore and drag a floatable player around. Try it out, copy the code and enter it [[here|http://htmlsandbox.com/]] to see it working.

Still missing is that it maintains its vertical position just like the floating [[HoverMenu|HoverMenuPlugin]] to the right. Also, I haven't yet managed to implement [[dynamic drive's|http://www.dynamicdrive.com]] "Window Widget" as a plugin, though I believe something like this already exists over at [[tiddlytools|http://www.tiddlytools.com]].
{{{
<html>

<style type="text/css" />
#wrapperLastFM {background:none;width:184px;border:none;}
#playerLastFM, #configLastFM {text-align:center;margin:0px;margin-bottom:5px;padding:0px;}
#configTableLastFM {border:none;color:#666;font-size:0.8em;margin:0px;margin-left:10px;padding:0px;}
#configTableLastFM tr {border:none;}
#configTableLastFM td {text-align:right;border:none;width:100%;font-size:12px;}
#configTableLastFM td input {width:130px; color:#666;margin:0px 5px;}


.dhtmlwindow{
position: absolute;
border: 1px solid #999;
visibility: hidden;
background-color:#EEE;
}

.drag-handle{ /*CSS for Drag Handle*/
padding: 0px 2px;
text-indent: 3px;
font: bold 12px;
background-color: #AAA;
color: #333;
cursor: move;
overflow: hidden;
width: auto;
filter:progid:DXImageTransform.Microsoft.alpha(opacity=100);
-moz-opacity: 1;
opacity: 1;
}

.drag-handle .drag-controls{ /*CSS for controls (min, close etc) within Drag Handle*/
position: absolute;
right: 2px;
top: 0px;
cursor: hand;
cursor: pointer;
margin:0px 3px 0px 5px;
}

* html .drag-handle{ /*IE6 and below CSS hack. Width must be set to 100% in order for opaque title bar to render*/
width: 100%;
}


.drag-contentarea{ /*CSS for Content Display Area div*/
border-top: 1px solid #999;
height: 150px;
padding: 0px;
overflow: auto;
}

.drag-statusarea{ /*CSS for Status Bar div (includes resizearea)*/
border-top: 1px solid gray;
background-color: #DDD;
height: 13px; /*height of resize image*/
}


.drag-resizearea{ /*CSS for Resize Area itself*/
float: right;
width: 13px; /*width of resize image*/
height: 13px; /*height of resize image*/
cursor: nw-resize;
font-size: 0;
}
</style>












<script type="text/javascript">

/***********************************************
* DHTML Window Widget- Å  Dynamic Drive (www.dynamicdrive.com)
* This notice must stay intact for legal use.
* Visit http://www.dynamicdrive.com/ for full source code
***********************************************/

// -------------------------------------------------------------------
// DHTML Window Widget- By Dynamic Drive, available at: http://www.dynamicdrive.com
// v1.0: Script created Feb 15th, 07'
// v1.01: Feb 21th, 07' (see changelog.txt)
// v1.02: March 26th, 07' (see changelog.txt)
// v1.03: May 5th, 07' (see changelog.txt)
// v1.1:  Oct 29th, 07' (see changelog.txt)
// -------------------------------------------------------------------

function setLastfmPlayer(e,bArtist)
{
  if(e.keyCode==13) {
  	var sArtist = document.getElementById('txtArtistLastFM').value;
  	var sTag = document.getElementById('txtTagLastFM').value;
  	var sPlay = "";
    if (bArtist && sArtist!="") sPlay="artist%2F"+sArtist+"%2Fsimilarartists&amp;title=artist%2F"+sArtist;
    if (!bArtist && sTag!="") sPlay="globaltags%2F"+sTag+"&amp;title=globaltags%2F"+sTag;
  	var sStart = '<object type="application/x-shockwave-flash" data="http://cdn.last.fm/widgets/radio/22.swf" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="184" height="140"><param name="movie" value="http://cdn.last.fm/widgets/radio/22.swf" /><param name="flashvars" value="lfmMode=radio&amp;radioURL=';
  	var sEnd = '&amp;theme=grey&amp;lang=en" /><param name="allowScriptAccess" value="always" /> <param name="allowNetworking" value="all" /> <param name="allowFullScreen" value="true" /> <param name="quality" value="high" /> <param name="bgcolor" value="999999" /> <param name="wmode" value="transparent" /> <param name="menu" value="true" /> </object>';
    if (sPlay!="") document.getElementById('playerLastFM').innerHTML = sStart + sPlay + sEnd;
  }
  else {
    if (e.keyCode==40 && bArtist) document.forms["configLastFM"].txtTagLastFM.focus();
    if (e.keyCode==38 && !bArtist) document.forms["configLastFM"].txtArtistLastFM.focus();
  }
}

var srcLastFM="http://goto.bplaced.net/x/tiddlyspot/lastfm/"
var dhtmlwindow={
imagefiles:[srcLastFM+'min.gif', srcLastFM+'close.gif', srcLastFM+'restore.gif', srcLastFM+'resize.gif'], //Path to 4 images used by script, in that order
ajaxbustcache: true, //Bust caching when fetching a file via Ajax?
ajaxloadinghtml: '<b>Loading Page. Please wait...</b>', //HTML to show while window fetches Ajax Content?

minimizeorder: 0,
zIndexvalue:100,
tobjects: [], //object to contain references to dhtml window divs, for cleanup purposes
lastactivet: {}, //reference to last active DHTML window

init:function(t){
	var domwindow=document.createElement("div") //create dhtml window div
	domwindow.id=t
	domwindow.className="dhtmlwindow"
	var domwindowdata=''
	domwindowdata='<div class="drag-handle">'
	domwindowdata+='DHTML Window <div class="drag-controls"><span title="minimize"> - </span><span title="Close">x</span></div>'
	domwindowdata+='</div>'
	domwindowdata+='<div class="drag-contentarea"></div>'
	domwindowdata+='<div class="drag-statusarea"><div class="drag-resizearea" style="background: transparent url('+this.imagefiles[3]+') top right no-repeat;">&nbsp;</div></div>'
	domwindowdata+='</div>'
	domwindow.innerHTML=domwindowdata
	document.getElementById("dhtmlwindowholder").appendChild(domwindow)
	//this.zIndexvalue=(this.zIndexvalue)? this.zIndexvalue+1 : 100 //z-index value for DHTML window: starts at 0, increments whenever a window has focus
	var t=document.getElementById(t)
	var divs=t.getElementsByTagName("div")
	for (var i=0; i<divs.length; i++){ //go through divs inside dhtml window and extract all those with class="drag-" prefix
		if (/drag-/.test(divs[i].className))
			t[divs[i].className.replace(/drag-/, "")]=divs[i] //take out the "drag-" prefix for shorter access by name
	}
	//t.style.zIndex=this.zIndexvalue //set z-index of this dhtml window
	t.handle._parent=t //store back reference to dhtml window
	t.resizearea._parent=t //same
	t.controls._parent=t //same
	t.onclose=function(){return true} //custom event handler "onclose"
	t.onmousedown=function(){dhtmlwindow.setfocus(this)} //Increase z-index of window when focus is on it
	t.handle.onmousedown=dhtmlwindow.setupdrag //set up drag behavior when mouse down on handle div
	t.resizearea.onmousedown=dhtmlwindow.setupdrag //set up drag behavior when mouse down on resize div
	t.controls.onclick=dhtmlwindow.enablecontrols
	t.show=function(){dhtmlwindow.show(this)} //public function for showing dhtml window
	t.hide=function(){dhtmlwindow.hide(this)} //public function for hiding dhtml window
	t.close=function(){dhtmlwindow.close(this)} //public function for closing dhtml window (also empties DHTML window content)
	t.setSize=function(w, h){dhtmlwindow.setSize(this, w, h)} //public function for setting window dimensions
	t.moveTo=function(x, y){dhtmlwindow.moveTo(this, x, y)} //public function for moving dhtml window (relative to viewpoint)
	t.isResize=function(bol){dhtmlwindow.isResize(this, bol)} //public function for specifying if window is resizable
	t.isScrolling=function(bol){dhtmlwindow.isScrolling(this, bol)} //public function for specifying if window content contains scrollbars
	t.load=function(contenttype, contentsource, title){dhtmlwindow.load(this, contenttype, contentsource, title)} //public function for loading content into window
	this.tobjects[this.tobjects.length]=t
	return t //return reference to dhtml window div
},

open:function(t, contenttype, contentsource, title, attr, recalonload){
	var d=dhtmlwindow //reference dhtml window object
	function getValue(Name){
		var config=new RegExp(Name+"=([^,]+)", "i") //get name/value config pair (ie: width=400px,)
		return (config.test(attr))? parseInt(RegExp.$1) : 0 //return value portion (int), or 0 (false) if none found
	}
	if (document.getElementById(t)==null) //if window doesn't exist yet, create it
		t=this.init(t) //return reference to dhtml window div
	else
		t=document.getElementById(t)
	this.setfocus(t)
	t.setSize(getValue(("width")), (getValue("height"))) //Set dimensions of window
	var xpos=getValue("center")? "middle" : getValue("left") //Get x coord of window
	dhtmlwindow.rememberattrs(t)
  if (getValue("bottom")) var ypos=dhtmlwindow.scroll_top+dhtmlwindow.docheight-getValue("height")-30+"px"
  else var ypos=getValue("center")? "middle" : getValue("top") //Get y coord of window
	//t.moveTo(xpos, ypos) //Position window
	if (typeof recalonload!="undefined" && recalonload=="recal" && this.scroll_top==0){ //reposition window when page fully loads with updated window viewpoints?
		if (window.attachEvent && !window.opera) //In IE, add another 400 milisecs on page load (viewpoint properties may return 0 b4 then)
			this.addEvent(window, function(){setTimeout(function(){t.moveTo(xpos, ypos)}, 400)}, "load")
		else
			this.addEvent(window, function(){t.moveTo(xpos, ypos)}, "load")
	}
	t.isResize(getValue("resize")) //Set whether window is resizable
	t.isScrolling(getValue("scrolling")) //Set whether window should contain scrollbars
	t.style.visibility="visible"
	t.style.display="block"
	t.contentarea.style.display="block"
	t.moveTo(xpos, ypos) //Position window
	t.load(contenttype, contentsource, title)
	if (t.state=="minimized" && t.controls.firstChild.title=="Restore"){ //If window exists and is currently minimized?
		t.controls.firstChild.innerHTML = "&nbsp;-&nbsp;" //Change "restore" icon within window interface to "minimize" icon
		t.controls.firstChild.setAttribute("title", "Minimize")
		t.state="fullview" //indicate the state of the window as being "fullview"
	}
	return t
},

setSize:function(t, w, h){ //set window size (min is 150px wide by 100px tall)
	t.style.width=Math.max(parseInt(w), 150)+"px"
	t.contentarea.style.height=Math.max(parseInt(h), 100)+"px"
},

moveTo:function(t, x, y){ //move window. Position includes current viewpoint of document
	this.getviewpoint() //Get current viewpoint numbers
	t.style.left=(x=="middle")? this.scroll_left+(this.docwidth-t.offsetWidth)/2+"px" : this.scroll_left+parseInt(x)+"px"
	t.style.top=(y=="middle")? this.scroll_top+(this.docheight-t.offsetHeight)/2+"px" : this.scroll_top+parseInt(y)+"px"
},

isResize:function(t, bol){ //show or hide resize inteface (part of the status bar)
	t.statusarea.style.display=(bol)? "block" : "none"
	t.resizeBool=(bol)? 1 : 0
},

isScrolling:function(t, bol){ //set whether loaded content contains scrollbars
	t.contentarea.style.overflow=(bol)? "auto" : "hidden"
},

load:function(t, contenttype, contentsource, title){ //loads content into window plus set its title (3 content types: "inline", "iframe", or "ajax")
	if (t.isClosed){
		alert("DHTML Window has been closed, so no window to load contents into. Open/Create the window again.")
		return
	}
	var contenttype=contenttype.toLowerCase() //convert string to lower case
	if (typeof title!="undefined")
		t.handle.firstChild.nodeValue=title
	if (contenttype=="inline")
		t.contentarea.innerHTML=contentsource
	else if (contenttype=="div"){
		var inlinedivref=document.getElementById(contentsource)
		t.contentarea.innerHTML=(inlinedivref.defaultHTML || inlinedivref.innerHTML) //Populate window with contents of inline div on page
		if (!inlinedivref.defaultHTML)
			inlinedivref.defaultHTML=inlinedivref.innerHTML //save HTML within inline DIV
		inlinedivref.innerHTML="" //then, remove HTML within inline DIV (to prevent duplicate IDs, NAME attributes etc in contents of DHTML window
		inlinedivref.style.display="none" //hide that div
	}
	else if (contenttype=="iframe"){
		t.contentarea.style.overflow="hidden" //disable window scrollbars, as iframe already contains scrollbars
		if (!t.contentarea.firstChild || t.contentarea.firstChild.tagName!="IFRAME") //If iframe tag doesn't exist already, create it first
			t.contentarea.innerHTML='<iframe src="" style="margin:0; padding:0; width:100%; height: 100%" name="_iframe-'+t.id+'"></iframe>'
		window.frames["_iframe-"+t.id].location.replace(contentsource) //set location of iframe window to specified URL
		}
	else if (contenttype=="ajax"){
		this.ajax_connect(contentsource, t) //populate window with external contents fetched via Ajax
	}
	t.contentarea.datatype=contenttype //store contenttype of current window for future reference
},

setupdrag:function(e){
	var d=dhtmlwindow //reference dhtml window object
	var t=this._parent //reference dhtml window div
	d.etarget=this //remember div mouse is currently held down on ("handle" or "resize" div)
	var e=window.event || e
	d.initmousex=e.clientX //store x position of mouse onmousedown
	d.initmousey=e.clientY
	d.initx=parseInt(t.offsetLeft) //store offset x of window div onmousedown
	d.inity=parseInt(t.offsetTop)
	d.width=parseInt(t.offsetWidth) //store width of window div
	d.contentheight=parseInt(t.contentarea.offsetHeight) //store height of window div's content div
	if (t.contentarea.datatype=="iframe"){ //if content of this window div is "iframe"
		t.style.backgroundColor="#F8F8F8" //colorize and hide content div (while window is being dragged)
		t.contentarea.style.visibility="hidden"
	}
	document.onmousemove=d.getdistance //get distance travelled by mouse as it moves
	document.onmouseup=function(){
		if (t.contentarea.datatype=="iframe"){ //restore color and visibility of content div onmouseup
			t.contentarea.style.backgroundColor="white"
			t.contentarea.style.visibility="visible"
		}
		d.stop()
	}
	return false
},

getdistance:function(e){
	var d=dhtmlwindow
	var etarget=d.etarget
	var e=window.event || e
	d.distancex=e.clientX-d.initmousex //horizontal distance travelled relative to starting point
	d.distancey=e.clientY-d.initmousey
	if (etarget.className=="drag-handle") //if target element is "handle" div
		d.move(etarget._parent, e)
	else if (etarget.className=="drag-resizearea") //if target element is "resize" div
		d.resize(etarget._parent, e)
	return false //cancel default dragging behavior
},

getviewpoint:function(){ //get window viewpoint numbers
	var ie=document.all && !window.opera
	var domclientWidth=document.documentElement && parseInt(document.documentElement.clientWidth) || 100000 //Preliminary doc width in non IE browsers
	this.standardbody=(document.compatMode=="CSS1Compat")? document.documentElement : document.body //create reference to common "body" across doctypes
	this.scroll_top=(ie)? this.standardbody.scrollTop : window.pageYOffset
	this.scroll_left=(ie)? this.standardbody.scrollLeft : window.pageXOffset
	this.docwidth=(ie)? this.standardbody.clientWidth : (/Safari/i.test(navigator.userAgent))? window.innerWidth : Math.min(domclientWidth, window.innerWidth-16)
	this.docheight=(ie)? this.standardbody.clientHeight: window.innerHeight
},

rememberattrs:function(t){ //remember certain attributes of the window when it's minimized or closed, such as dimensions, position on page
	this.getviewpoint() //Get current window viewpoint numbers
	t.lastx=parseInt((t.style.left || t.offsetLeft))-dhtmlwindow.scroll_left //store last known x coord of window just before minimizing
	t.lasty=parseInt((t.style.top || t.offsetTop))-dhtmlwindow.scroll_top
	t.lastwidth=parseInt(t.style.width) //store last known width of window just before minimizing/ closing
	t.contentarea.lastheight=parseInt(t.contentarea.style.height) //store last known width of window just before minimizing/ closing
},

move:function(t, e){
	t.style.left=dhtmlwindow.distancex+dhtmlwindow.initx+"px"
	t.style.top=dhtmlwindow.distancey+dhtmlwindow.inity+"px"
},

resize:function(t, e){
	t.style.width=Math.max(dhtmlwindow.width+dhtmlwindow.distancex, 150)+"px"
	t.contentarea.style.height=Math.max(dhtmlwindow.contentheight+dhtmlwindow.distancey, 100)+"px"
},

enablecontrols:function(e){
	var d=dhtmlwindow
	var sourceobj=window.event? window.event.srcElement : e.target //Get element within "handle" div mouse is currently on (the controls)
	if (/Minimize/i.test(sourceobj.getAttribute("title"))) //if this is the "minimize" control
		d.minimize(sourceobj, this._parent)
	else if (/Restore/i.test(sourceobj.getAttribute("title"))) //if this is the "restore" control
		d.restore(sourceobj, this._parent)
	else if (/Close/i.test(sourceobj.getAttribute("title"))) //if this is the "close" control
		d.close(this._parent)
	return false
},

minimize:function(button, t){
	dhtmlwindow.rememberattrs(t)
	button.innerHTML="&nbsp;+&nbsp;"
	button.setAttribute("title", "Restore")
	t.state="minimized" //indicate the state of the window as being "minimized"
	t.contentarea.style.height="0px"
	t.statusarea.style.height="0px"
	if (typeof t.minimizeorder=="undefined"){ //stack order of minmized window on screen relative to any other minimized windows
		dhtmlwindow.minimizeorder++ //increment order
		t.minimizeorder=dhtmlwindow.minimizeorder
	}
	t.style.left="5px" //left coord of minmized window
	t.style.width="100px"
	var windowspacing=t.minimizeorder*5 //spacing (gap) between each minmized window(s)
	t.style.top=dhtmlwindow.scroll_top+dhtmlwindow.docheight-(t.handle.offsetHeight*t.minimizeorder)-windowspacing+"px"
},

restore:function(button, t){
	dhtmlwindow.getviewpoint()
	button.innerHTML="&nbsp;-&nbsp;"
	button.setAttribute("title", "Minimize")
	t.state="fullview" //indicate the state of the window as being "fullview"
	t.style.display="block"
	t.contentarea.style.height=parseInt(t.contentarea.lastheight)+"px"
	if (t.resizeBool) //if this window is resizable, enable the resize icon
		t.statusarea.style.display="block"
	t.style.left=parseInt(t.lastx)+dhtmlwindow.scroll_left+"px" //position window to last known x coord just before minimizing
	t.style.top=parseInt(t.lasty)+dhtmlwindow.scroll_top+"px"
	t.style.width=parseInt(t.lastwidth)+"px"
},


close:function(t){
	try{
		var closewinbol=t.onclose()
	}
	catch(err){ //In non IE browsers, all errors are caught, so just run the below
		var closewinbol=true
 }
	finally{ //In IE, not all errors are caught, so check if variable isn't defined in IE in those cases
		if (typeof closewinbol=="undefined"){
			alert("An error has occured somwhere inside your \"onclose\" event handler")
			var closewinbol=true
		}
	}
	if (closewinbol){ //if custom event handler function returns true
		if (t.state!="minimized") //if this window isn't currently minimized
			dhtmlwindow.rememberattrs(t) //remember window's dimensions/position on the page before closing
		if (window.frames["_iframe-"+t.id]) //if this is an IFRAME DHTML window
			window.frames["_iframe-"+t.id].location.replace("about:blank")
		else
			t.contentarea.innerHTML=""
		t.style.display="none"
		t.isClosed=true //tell script this window is closed (for detection in t.show())
	}
	return closewinbol
},


setopacity:function(targetobject, value){ //Sets the opacity of targetobject based on the passed in value setting (0 to 1 and in between)
	if (!targetobject)
		return
	if (targetobject.filters && targetobject.filters[0]){ //IE syntax
		if (typeof targetobject.filters[0].opacity=="number") //IE6
			targetobject.filters[0].opacity=value*100
		else //IE 5.5
			targetobject.style.filter="alpha(opacity="+value*100+")"
		}
	else if (typeof targetobject.style.MozOpacity!="undefined") //Old Mozilla syntax
		targetobject.style.MozOpacity=value
	else if (typeof targetobject.style.opacity!="undefined") //Standard opacity syntax
		targetobject.style.opacity=value
},

setfocus:function(t){ //Sets focus to the currently active window
	this.zIndexvalue++
	t.style.zIndex=this.zIndexvalue
	t.isClosed=false //tell script this window isn't closed (for detection in t.show())
	this.setopacity(this.lastactivet.handle, 0.5) //unfocus last active window
	this.setopacity(t.handle, 1) //focus currently active window
	this.lastactivet=t //remember last active window
},


show:function(t){
	if (t.isClosed){
		alert("DHTML Window has been closed, so nothing to show. Open/Create the window again.")
		return
	}
	if (t.lastx) //If there exists previously stored information such as last x position on window attributes (meaning it's been minimized or closed)
		dhtmlwindow.restore(t.controls.firstChild, t) //restore the window using that info
	else
		t.style.display="block"
	this.setfocus(t)
	t.state="fullview" //indicate the state of the window as being "fullview"
},

hide:function(t){
	t.style.display="none"
},

ajax_connect:function(url, t){
	var page_request = false
	var bustcacheparameter=""
	if (window.XMLHttpRequest) // if Mozilla, IE7, Safari etc
		page_request = new XMLHttpRequest()
	else if (window.ActiveXObject){ // if IE6 or below
		try {
		page_request = new ActiveXObject("Msxml2.XMLHTTP")
		} 
		catch (e){
			try{
			page_request = new ActiveXObject("Microsoft.XMLHTTP")
			}
			catch (e){}
		}
	}
	else
		return false
	t.contentarea.innerHTML=this.ajaxloadinghtml
	page_request.onreadystatechange=function(){dhtmlwindow.ajax_loadpage(page_request, t)}
	if (this.ajaxbustcache) //if bust caching of external page
		bustcacheparameter=(url.indexOf("?")!=-1)? "&"+new Date().getTime() : "?"+new Date().getTime()
	page_request.open('GET', url+bustcacheparameter, true)
	page_request.send(null)
},

ajax_loadpage:function(page_request, t){
	if (page_request.readyState == 4 && (page_request.status==200 || window.location.href.indexOf("http")==-1)){
	t.contentarea.innerHTML=page_request.responseText
	}
},


stop:function(){
	dhtmlwindow.etarget=null //clean up
	document.onmousemove=null
	document.onmouseup=null
},

addEvent:function(target, functionref, tasktype){ //assign a function to execute to an event handler (ie: onunload)
	var tasktype=(window.addEventListener)? tasktype : "on"+tasktype
	if (target.addEventListener)
		target.addEventListener(tasktype, functionref, false)
	else if (target.attachEvent)
		target.attachEvent(tasktype, functionref)
},

cleanup:function(){
	for (var i=0; i<dhtmlwindow.tobjects.length; i++){
		dhtmlwindow.tobjects[i].handle._parent=dhtmlwindow.tobjects[i].resizearea._parent=dhtmlwindow.tobjects[i].controls._parent=null
	}
	window.onload=null
}

} //End dhtmlwindow object

document.write('<div id="dhtmlwindowholder"><span style="display:none">.</span></div>') //container that holds all dhtml window divs on page
window.onunload=dhtmlwindow.cleanup
</script>













<body>
<div id="playerWindow" style="display:none">
<div id="wrapperLastFM">
<div id="playerLastFM">
<object type="application/x-shockwave-flash" data="http://cdn.last.fm/widgets/radio/22.swf" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="184" height="140"><param name="movie" value="http://cdn.last.fm/widgets/radio/22.swf" /><param name="flashvars" value="lfmMode=radio&amp;radioURL=globaltags%2Findie&amp;title=click&amp;theme=grey&amp;lang=de" /><param name="allowScriptAccess" value="always" /> <param name="allowNetworking" value="all" /> <param name="allowFullScreen" value="true" /> <param name="quality" value="high" /> <param name="bgcolor" value="999999" /> <param name="wmode" value="transparent" /> <param name="menu" value="true" /> </object>
</div>

<form id="configLastFM">
<table id="configTableLastFM" cellspacing=0 cellborder=0>
<tr>
  <td>artist</td>
  <td><input type='text' id='txtArtistLastFM' value='' onkeyup="setLastfmPlayer(event,true);"/></td>
</tr>
<tr>
  <td>tag</td>
  <td><input type='text' id='txtTagLastFM' value='' onkeyup="setLastfmPlayer(event,false);"/></td>
</tr>
</table>
</form>
</div>
</div>

<a href="#" onClick="divwin=dhtmlwindow.open('divbox', 'div', 'playerWindow', 'player', 'width=184px,height=200px,left=5px,top=400px,resize=0,scrolling=0,bottom=1'); return false"><b>open player</b></a>
<br>
<a href="#" onClick="divwin.show(); return false">restore player</a>

<div style="margin-top:1500px;">&nbsp;</div>
</body>
</html>
}}}
/***
|Name|FullScreenPlugin|
|Created by|SaqImtiaz|
|Location|http://tw.lewcid.org/#FullScreenPlugin|
|Version|1.1|
|Requires|~TW2.x|
!Description:
Toggle between viewing tiddlers fullscreen and normally. Very handy for when you need more viewing space.

!Demo:
Click the ↕ button in the toolbar for this tiddler. Click it again to turn off fullscreen.

!Installation:
Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
Edit the ViewTemplate to add the fullscreen command to the toolbar.

!History:
*25-07-06: ver 1.1
*20-07-06: ver 1.0

!Code
***/
//{{{
var lewcidFullScreen = false;

config.commands.fullscreen =
{
            text:" ↕ ",
            tooltip:"Fullscreen mode"
};

config.commands.fullscreen.handler = function (event,src,title)
{
            if (lewcidFullScreen == false)
               {
                lewcidFullScreen = true;
                setStylesheet('#sidebar, .header, #mainMenu{display:none;} #displayArea{margin:0em 0 0 0 !important;}',"lewcidFullScreenStyle");
               }
            else
               {
                lewcidFullScreen = false;
                setStylesheet(' ',"lewcidFullScreenStyle");
               }
}

config.macros.fullscreen={};
config.macros.fullscreen.handler =  function(place,macroName,params,wikifier,paramString,tiddler)
{
        var label = params[0]||" ↕ ";
        var tooltip = params[1]||"Fullscreen mode";
        createTiddlyButton(place,label,tooltip,config.commands.fullscreen.handler);
}

var lewcid_fullscreen_closeTiddler = Story.prototype.closeTiddler;
Story.prototype.closeTiddler =function(title,animate,slowly)
{
           lewcid_fullscreen_closeTiddler.apply(this,arguments);
           if (story.isEmpty() && lewcidFullScreen == true)
              config.commands.fullscreen.handler();
}


Slider.prototype.lewcidStop = Slider.prototype.stop;
Slider.prototype.stop = function()
{
           this.lewcidStop();
           if (story.isEmpty() && lewcidFullScreen == true)
              config.commands.fullscreen.handler();
}
//}}}
Renamed... substituted with ListRating.
Do not use this tiddler, the script is now called [[RatingSummary]].
/***
|Name:|HideWhenPlugin|
|Description:|Allows conditional inclusion/exclusion in templates|
|Version:|3.1 ($Rev: 3919 $)|
|Date:|$Date: 2008-03-13 02:03:12 +1000 (Thu, 13 Mar 2008) $|
|Source:|http://mptw.tiddlyspot.com/#HideWhenPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
For use in ViewTemplate and EditTemplate. Example usage:
{{{<div macro="showWhenTagged Task">[[TaskToolbar]]</div>}}}
{{{<div macro="showWhen tiddler.modifier == 'BartSimpson'"><img src="bart.gif"/></div>}}}
***/
//{{{

window.hideWhenLastTest = false;

window.removeElementWhen = function(test,place) {
	window.hideWhenLastTest = test;
	if (test) {
		removeChildren(place);
		place.parentNode.removeChild(place);
	}
};


merge(config.macros,{

	hideWhen: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		removeElementWhen( eval(paramString), place);
	}},

	showWhen: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		removeElementWhen( !eval(paramString), place);
	}},

	hideWhenTagged: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
		removeElementWhen( tiddler.tags.containsAll(params), place);
	}},

	showWhenTagged: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
		removeElementWhen( !tiddler.tags.containsAll(params), place);
	}},

	hideWhenTaggedAny: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
		removeElementWhen( tiddler.tags.containsAny(params), place);
	}},

	showWhenTaggedAny: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
		removeElementWhen( !tiddler.tags.containsAny(params), place);
	}},

	hideWhenTaggedAll: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
		removeElementWhen( tiddler.tags.containsAll(params), place);
	}},

	showWhenTaggedAll: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
		removeElementWhen( !tiddler.tags.containsAll(params), place);
	}},

	hideWhenExists: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		removeElementWhen( store.tiddlerExists(params[0]) || store.isShadowTiddler(params[0]), place);
	}},

	showWhenExists: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		removeElementWhen( !(store.tiddlerExists(params[0]) || store.isShadowTiddler(params[0])), place);
	}},

	hideWhenTitleIs: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		removeElementWhen( tiddler.title == params[0], place);
	}},

	showWhenTitleIs: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		removeElementWhen( tiddler.title != params[0], place);
	}},

	'else': { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		removeElementWhen( !window.hideWhenLastTest, place);
	}}

});

//}}}
!Important Note
Access to the lastfm webplayer has changed. In order for it to work you will have to be a registered user and need to login to their services. You can then close their website, yet your login credentials will still be available for this plugin to work. If you're not logged in you will see a message tha reads something like "not enough content to play this station".

@@display:block;float:left;width:500px;background:#EEE;border:1px solid #CCC;padding:0em 1em;
#the [[lastfmMacro]]
#lastfmMacro [[documentation|lastfmMacroInfo]]
**known [[bugs|lastfmMacroBugs]] / (in)compatibilities
#readable [[source|lastfmMacroSource]] code for lastfmMacro
**the [[structure|lastfmMacroStructure]] of lastfmMacro explained
#the initial [[html template|Initial Player]] (non tiddlywiki) for the extended player
**the [[simple way|Simple Player]] of embedding a lastfm player
**the future: a [[floating player|Floating Player]] to open, close, minimize, restore and drag around
#@@color:#090;font-weight:bold;discuss@@ this macro ''[[here|http://groups.google.com/group/TiddlyWiki/browse_frm/thread/959edc01e1989258]]'' at google groups
@@
<<top>>
<<toggleSideBar '' '' show>>
<<fullscreen F>>
<<jump J '' top>>
<<saveChanges>><<renameButton S>>
<<newTiddler>><<renameButton N>>
/***
|Name|HoverMenuPlugin|
|Created by|SaqImtiaz|
|Location|http://tw.lewcid.org/#HoverMenuPlugin|
|Version|1.11|
|Requires|~TW2.x|
!Description:
Provides a hovering menu on the edge of the screen for commonly used commands, that scrolls with the page.

!Demo:
Observe the hovering menu on the right edge of the screen.

!Installation:
Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
To customize your HoverMenu, edit the HoverMenu shadow tiddler.

To customize whether the menu sticks to the right or left edge of the screen, and its start position, edit the HoverMenu configuration settings part of the code below. It's well documented, so don't be scared!

The menu has an id of hoverMenu, in case you want to style the buttons in it using css.

!Notes:
Since the default HoverMenu contains buttons for toggling the side bar and jumping to the top of the screen and to open tiddlers, the ToggleSideBarMacro, JumpMacro and the JumpToTopMacro are included in this tiddler, so you dont need to install them separately. Having them installed separately as well could lead to complications.

If you dont intend to use these three macros at all, feel free to remove those sections of code in this tiddler.

!To Do:
* rework code to allow multiple hovering menus in different positions, horizontal etc.
* incorporate code for keyboard shortcuts that correspond to the buttons in the hovermenu

!History:
*03-08-06, ver 1.1.2: compatibility fix with SelectThemePlugin
*03-08-06,  ver 1.11: fixed error with button tooltips
*27-07-06, ver 1.1 : added JumpMacro to hoverMenu
*23-07-06

!Code
***/

/***
start HoverMenu plugin code
***/
//{{{
config.hoverMenu={};
//}}}

/***
HoverMenu configuration settings
***/
//{{{
config.hoverMenu.settings={
               align: 'right',    //align menu to right or left side of screen, possible values are 'right' and 'left'               
               x: 1,              // horizontal distance of menu from side of screen, increase to your liking.
               y: 220           //vertical distance of menu from top of screen at start, increase or decrease to your liking
               };
//}}}

//{{{
//continue HoverMenu plugin code
config.hoverMenu.handler=function()
{              
               if (!document.getElementById("hoverMenu"))
               {
               var theMenu = createTiddlyElement(document.getElementById("contentWrapper"), "div","hoverMenu");
               theMenu.setAttribute("refresh","content");
               theMenu.setAttribute("tiddler","HoverMenu");
               var menuContent = store.getTiddlerText("HoverMenu");
               wikify(menuContent,theMenu);
              }

	       var Xloc = this.settings.x;
	       Yloc =this.settings.y;
	       var ns = (navigator.appName.indexOf("Netscape") != -1);
	       function SetMenu(id)
                        {
		        var GetElements=document.getElementById?document.getElementById(id):document.all?document.all[id]:document.layers[id];
		        if(document.layers)GetElements.style=GetElements;
		        GetElements.sP=function(x,y){this.style[config.hoverMenu.settings.align]=x +"px";this.style.top=y +"px";};
		        GetElements.x = Xloc;
		        GetElements.y = findScrollY();
		        GetElements.y += Yloc;
		        return GetElements;
	                }
               window.LoCate_XY=function()
                        {
		        var pY =  findScrollY();
                        ftlObj.y += (pY + Yloc - ftlObj.y)/15;
		        ftlObj.sP(ftlObj.x, ftlObj.y);
		        setTimeout("LoCate_XY()", 10);
	                }
               ftlObj = SetMenu("hoverMenu");
	       LoCate_XY();
};

window.hovermenu_restart = refreshAll;
refreshAll = function(){
  window.hovermenu_restart();
  config.hoverMenu.handler();
}

setStylesheet(
"#hoverMenu  .button, #hoverMenu  .tiddlyLink {border:1px solid#AAA; font-weight:bold; background:#CCC; color:#666; padding:0 4px; float:right; margin-bottom:2px;}\n"+
"#hoverMenu .button:hover, #hoverMenu .tiddlyLink:hover {border:1px solid#666; color:#fff; background:#000;}\n"+
"#hoverMenu .button {width:100%; text-align:center}"+
"#hoverMenu { position:absolute; width:7px;}\n"+
"\n","hoverMenuStyles");


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

               if (place.lastChild.tagName!="BR")
                     {
                      place.lastChild.firstChild.data = params[0];
                      if (params[1]) {place.lastChild.title = params[1];}
                     }
};

config.shadowTiddlers["HoverMenu"]="<<top>>\n<<toggleSideBar>><<renameButton '>' >>\n<<jump j '' top>>\n<<saveChanges>><<renameButton s 'Save TiddlyWiki'>>\n<<newTiddler>><<renameButton n>>\n";
//}}}
//end HoverMenu plugin code

//Start ToggleSideBarMacro code
//{{{
config.macros.toggleSideBar={};

config.macros.toggleSideBar.settings={
         styleHide :  "#sidebar { display: none;}\n"+"#contentWrapper #displayArea { margin-right: 1em;}\n"+"",
         styleShow : " ",
         arrow1: "<",
         arrow2: ">"
};

config.macros.toggleSideBar.handler=function (place,macroName,params,wikifier,paramString,tiddler)
{
          var tooltip= params[1]||'toggle sidebar';
          var mode = (params[2] && params[2]=="hide")? "hide":"show";
          var arrow = (mode == "hide")? this.settings.arrow1:this.settings.arrow2;
          var label= (params[0]&&params[0]!='.')?params[0]+" "+arrow:arrow;
          var theBtn = createTiddlyButton(place,label,tooltip,this.onToggleSideBar,"button HideSideBarButton");
          if (mode == "hide")
             { 
             (document.getElementById("sidebar")).setAttribute("toggle","hide");
              setStylesheet(this.settings.styleHide,"ToggleSideBarStyles");
             }
};

config.macros.toggleSideBar.onToggleSideBar = function(){
          var sidebar = document.getElementById("sidebar");
          var settings = config.macros.toggleSideBar.settings;
          if (sidebar.getAttribute("toggle")=='hide')
             {
              setStylesheet(settings.styleShow,"ToggleSideBarStyles");
              sidebar.setAttribute("toggle","show");
              this.firstChild.data= (this.firstChild.data).replace(settings.arrow1,settings.arrow2);
              }
          else
              {    
               setStylesheet(settings.styleHide,"ToggleSideBarStyles");
               sidebar.setAttribute("toggle","hide");
               this.firstChild.data= (this.firstChild.data).replace(settings.arrow2,settings.arrow1);
              }

     return false;
}

setStylesheet(".HideSideBarButton .button {font-weight:bold; padding: 0 5px;}\n","ToggleSideBarButtonStyles");
//}}}
//end ToggleSideBarMacro code

//start JumpToTopMacro code
//{{{
config.macros.top={};
config.macros.top.handler=function(place,macroName)
{
               createTiddlyButton(place,"^","jump to top",this.onclick);
}
config.macros.top.onclick=function()
{
               window.scrollTo(0,0);
};

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

config.commands.top.handler = function(event,src,title)
{
               window.scrollTo(0,0);
}
//}}}
//end JumpToStartMacro code

//start JumpMacro code
//{{{
config.macros.jump= {};
config.macros.jump.handler = function (place,macroName,params,wikifier,paramString,tiddler)
{
        var label = (params[0] && params[0]!=".")? params[0]: 'jump';
        var tooltip = (params[1] && params[1]!=".")? params[1]: 'jump to an open tiddler';
        var top = (params[2] && params[2]=='top') ? true: false;        

        var btn =createTiddlyButton(place,label,tooltip,this.onclick);
        if (top==true)
              btn.setAttribute("top","true")
}

config.macros.jump.onclick = function(e)
{
        if (!e) var e = window.event;
        var theTarget = resolveTarget(e);
        var top = theTarget.getAttribute("top");
	var popup = Popup.create(this);
	if(popup)
		{
                 if(top=="true")
                                {createTiddlyButton(createTiddlyElement(popup,"li"),'Top ↑','Top of TW',config.macros.jump.top);
                                 createTiddlyElement(popup,"hr");}
		
		story.forEachTiddler(function(title,element) {
			createTiddlyLink(createTiddlyElement(popup,"li"),title,true);
			});
                }
	Popup.show(popup,false);
	e.cancelBubble = true;
	if (e.stopPropagation) e.stopPropagation();
	return false;
}

config.macros.jump.top = function()
{
       window.scrollTo(0,0);
}
//}}}
//end JumpMacro code

//utility functions
//{{{
Popup.show = function(unused,slowly)
{
	var curr = Popup.stack[Popup.stack.length-1];
	var rootLeft = findPosX(curr.root);
	var rootTop = findPosY(curr.root);
	var rootHeight = curr.root.offsetHeight;
	var popupLeft = rootLeft;
	var popupTop = rootTop + rootHeight;
	var popupWidth = curr.popup.offsetWidth;
	var winWidth = findWindowWidth();
        if (isChild(curr.root,'hoverMenu'))
              var x = config.hoverMenu.settings.x;
        else
              var x = 0;
	if(popupLeft + popupWidth+x > winWidth)
		popupLeft = winWidth - popupWidth -x;
        if (isChild(curr.root,'hoverMenu'))
  	        {curr.popup.style.right = x + "px";}
        else
                curr.popup.style.left = popupLeft + "px";
	curr.popup.style.top = popupTop + "px";
	curr.popup.style.display = "block";
	addClass(curr.root,"highlight");
	if(config.options.chkAnimate)
		anim.startAnimating(new Scroller(curr.popup,slowly));
	else
		window.scrollTo(0,ensureVisible(curr.popup));
}

window.isChild = function(e,parentId) {
        while (e != null) {
                var parent = document.getElementById(parentId);
                if (parent == e) return true;
                e = e.parentNode;
                }
        return false;
};
//}}}
This was the initial template for the extended player created around the lastfm player. You can copy the code and enter it [[here|http://htmlsandbox.com/]] to see it working.
{{{
<html>
<head>
<title>my lastfm player</title>
<script type="text/javascript" language="JavaScript">
function setLastfmPlayer(e,bArtist)
{
  if(e.keyCode==13) {
  	var sArtist = document.getElementById('txtArtistLastFM').value;
  	var sTag = document.getElementById('txtTagLastFM').value;
  	var sPlay = "";
    if (bArtist && sArtist!="") sPlay="artist%2F"+sArtist+"%2Fsimilarartists&amp;title=artist%2F"+sArtist;
    if (!bArtist && sTag!="") sPlay="globaltags%2F"+sTag+"&amp;title=globaltags%2F"+sTag;
  	var sStart = '<object type="application/x-shockwave-flash" data="http://cdn.last.fm/widgets/radio/22.swf" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="184" height="140"><param name="movie" value="http://cdn.last.fm/widgets/radio/22.swf" /><param name="flashvars" value="lfmMode=radio&amp;radioURL=';
  	var sEnd = '&amp;theme=grey&amp;lang=en" /><param name="allowScriptAccess" value="always" /> <param name="allowNetworking" value="all" /> <param name="allowFullScreen" value="true" /> <param name="quality" value="high" /> <param name="bgcolor" value="999999" /> <param name="wmode" value="transparent" /> <param name="menu" value="true" /> </object>';
    if (sPlay!="") document.getElementById('playerLastFM').innerHTML = sStart + sPlay + sEnd;
  }
  else {
    if (e.keyCode==40 && bArtist) document.forms["configLastFM"].txtTagLastFM.focus();
    if (e.keyCode==38 && !bArtist) document.forms["configLastFM"].txtArtistLastFM.focus();
  }
}
</script>
<style type="text/css">
  html, body {padding:0px; margin:0px;}
  #wrapperLastFM {margin-top:10px;margin-left:10px;background-color:#CCC;width:200px;}
  #playerLastFM, #configLastFM {text-align:center;margin:0px;padding:5px;}
  #configTableLastFM {width:184px;border:1px solid #CCC;color:#666;font-size:0.8em;}
  #configTableLastFM td {padding:0px; text-align:right;}
  #configTableLastFM td input {width:125px; color:#666;}
</style>
</head>
<body>

<div id="wrapperLastFM">
<div id="playerLastFM">
<object type="application/x-shockwave-flash" data="http://cdn.last.fm/widgets/radio/22.swf" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="184" height="140"><param name="movie" value="http://cdn.last.fm/widgets/radio/22.swf" /><param name="flashvars" value="lfmMode=radio&amp;radioURL=globaltags%2Findie&amp;title=click&amp;theme=grey&amp;lang=de" /><param name="allowScriptAccess" value="always" /> <param name="allowNetworking" value="all" /> <param name="allowFullScreen" value="true" /> <param name="quality" value="high" /> <param name="bgcolor" value="999999" /> <param name="wmode" value="transparent" /> <param name="menu" value="true" /> </object>

</div>

<form id="configLastFM">
<table id="configTableLastFM" cellspacing=0 cellborder=0>
<tr>
  <td>artist</td>
  <td><input type='text' id='txtArtistLastFM' value='' onkeyup="setLastfmPlayer(event,true);"/></td>
</tr>
<tr>
  <td>tag</td>
  <td><input type='text' id='txtTagLastFM' value='' onkeyup="setLastfmPlayer(event,false);"/></td>

</tr>
</table>
</form>
</div>

</body>
</html>
}}}
/***
|Name|InlineJavascriptPlugin|
|Source|http://www.TiddlyTools.com/#InlineJavascriptPlugin|
|Documentation|http://www.TiddlyTools.com/#InlineJavascriptPluginInfo|
|Version|1.9.5|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Description|Insert Javascript executable code directly into your tiddler content.|
''Call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
!!!!!Documentation
>see [[InlineJavascriptPluginInfo]]
!!!!!Revisions
<<<
2009.04.11 [1.9.5] pass current tiddler object into wrapper code so it can be referenced from within 'onclick' scripts
2009.02.26 [1.9.4] in $(), handle leading '#' on ID for compatibility with JQuery syntax
|please see [[InlineJavascriptPluginInfo]] for additional revision details|
2005.11.08 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.InlineJavascriptPlugin= {major: 1, minor: 9, revision: 5, date: new Date(2009,4,11)};

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

	handler: function(w) {
		var lookaheadRegExp = new RegExp(this.lookahead,"mg");
		lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = lookaheadRegExp.exec(w.source)
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
			var src=lookaheadMatch[1];
			var label=lookaheadMatch[2];
			var tip=lookaheadMatch[3];
			var key=lookaheadMatch[4];
			var show=lookaheadMatch[5];
			var code=lookaheadMatch[6];
			if (src) { // external script library
				var script = document.createElement("script"); script.src = src;
				document.body.appendChild(script); document.body.removeChild(script);
			}
			if (code) { // inline code
				if (show) // display source in tiddler
					wikify("{{{\n"+lookaheadMatch[0]+"\n}}}\n",w.output);
				if (label) { // create 'onclick' command link
					var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",wikifyPlainText(label));
					var fixup=code.replace(/document.write\s*\(/gi,'place.bufferedHTML+=(');
					link.code="function _out(place,tiddler){"+fixup+"\n};_out(this,this.tiddler);"
					link.tiddler=w.tiddler;
					link.onclick=function(){
						this.bufferedHTML="";
						try{ var r=eval(this.code);
							if(this.bufferedHTML.length || (typeof(r)==="string")&&r.length)
								var s=this.parentNode.insertBefore(document.createElement("span"),this.nextSibling);
							if(this.bufferedHTML.length)
								s.innerHTML=this.bufferedHTML;
							if((typeof(r)==="string")&&r.length) {
								wikify(r,s,null,this.tiddler);
								return false;
							} else return r!==undefined?r:false;
						} catch(e){alert(e.description||e.toString());return false;}
					};
					link.setAttribute("title",tip||"");
					var URIcode='javascript:void(eval(decodeURIComponent(%22(function(){try{';
					URIcode+=encodeURIComponent(encodeURIComponent(code.replace(/\n/g,' ')));
					URIcode+='}catch(e){alert(e.description||e.toString())}})()%22)))';
					link.setAttribute("href",URIcode);
					link.style.cursor="pointer";
					if (key) link.accessKey=key.substr(0,1); // single character only
				}
				else { // run script immediately
					var fixup=code.replace(/document.write\s*\(/gi,'place.innerHTML+=(');
					var c="function _out(place,tiddler){"+fixup+"\n};_out(w.output,w.tiddler);";
					try	 { var out=eval(c); }
					catch(e) { out=e.description?e.description:e.toString(); }
					if (out && out.length) wikify(out,w.output,w.highlightRegExp,w.tiddler);
				}
			}
			w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
		}
	}
} )
//}}}

// // Backward-compatibility for TW2.1.x and earlier
//{{{
if (typeof(wikifyPlainText)=="undefined") window.wikifyPlainText=function(text,limit,tiddler) {
	if(limit > 0) text = text.substr(0,limit);
	var wikifier = new Wikifier(text,formatter,null,tiddler);
	return wikifier.wikifyPlain();
}
//}}}

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

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

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

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

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

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

Notes:
*When writing scripts intended for use as bookmarklets, due to the ~URI-encoding required by the browser, ''you cannot not use ANY double-quotes (") within the bookmarklet script code.''
*All comments embedded in the bookmarklet script must ''use the fully-delimited {{{/* ... */}}} comment syntax,'' rather than the shorter {{{//}}} comment syntax.
*Most importantly, because bookmarklets are invoked directly from the browser interface and are not embedded within the TiddlyWiki document, there is NO containing 'place' DOM element surrounding the script.  As a result, ''you cannot use a bookmarklet to generate dynamic output in your document,''  and using {{{document.write()}}} or returning wiki-syntax text or making reference to the 'place' DOM element will halt the script and report a "Reference Error" when that bookmarklet is invoked.  
Please see [[InstantBookmarklets]] for many examples of 'onclick' scripts that can also be used as bookmarklets.
<<<
!!!!!Special reserved function name
<<<
The plugin 'wraps' all inline javascript code inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler.  To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.
<<<
!!!!!$(...) 'shorthand' function
<<<
As described by Dustin Diaz [[here|http://www.dustindiaz.com/top-ten-javascript/]], the plugin defines a 'shorthand' function that allows you to write:
{{{
$(id)
}}}
in place of the normal standard javascript syntax:
{{{
document.getElementById(id)
}}}
This function is provided merely as a convenience for javascript coders that may be familiar with this abbreviation, in order to allow them to save a few bytes when writing their own inline script code.
<<<
!!!!!Examples
<<<
simple dynamic output:
><script show>
	document.write("The current date/time is: "+(new Date())+"<br>");
	return "link to current user: [["+config.options.txtUserName+"]]\n";
</script>
dynamic output using 'place' to get size information for current tiddler:
><script show>
	if (!window.story) window.story=window;
	var title=story.findContainingTiddler(place).getAttribute("tiddler");
	var size=store.getTiddlerText(title).length;
	return title+" is using "+size+" bytes";
</script>
dynamic output from an 'onclick' script, using {{{document.write()}}} and/or {{{return "..."}}}
><script label="click here" show>
	document.write("<br>The current date/time is: "+(new Date())+"<br>");
	return "link to current user: [["+config.options.txtUserName+"]]\n";
</script>
creating an 'onclick' button/link that accesses the link text AND the containing tiddler:
><script label="click here" title="clicking this link will show an 'alert' box" key="H" show>
	if (!window.story) window.story=window;
	var txt=place.firstChild.data;
	var tid=story.findContainingTiddler(place).getAttribute('tiddler');
	alert('Hello World!\nlinktext='+txt+'\ntiddler='+tid);
</script>
dynamically setting onclick link text based on stateful information:
>{{block{
{{{
<script label="click here">
	/* toggle "txtSomething" value */
	var on=(config.txtSomething=="ON");
	place.innerHTML=on?"enable":"disable";
	config.txtSomething=on?"OFF":"ON";
	return "\nThe current value is: "+config.txtSomething;
</script><script>
	/* initialize onclick link text based on current "txtSomething" value */
	var on=(config.txtSomething=="ON");
	place.lastChild.previousSibling.innerHTML=on?"disable":"enable";
</script>
}}}
<script label="click here">
	/* toggle "txtSomething" value */
	var on=(config.txtSomething=="ON");
	place.innerHTML=on?"enable":"disable";
	config.txtSomething=on?"OFF":"ON";
	return "\nThe current value is: "+config.txtSomething;
</script><script>
	/* initialize onclick link text based on current "txtSomething" value */
	var on=(config.txtSomething=="ON");
	place.lastChild.innerHTML=on?"enable":"disable";
</script>
}}}
loading a script from a source url:
>http://www.TiddlyTools.com/demo.js contains:
>>{{{function inlineJavascriptDemo() { alert('Hello from demo.js!!') } }}}
>>{{{displayMessage('InlineJavascriptPlugin: demo.js has been loaded');}}}
>note: When using this example on your local system, you will need to download the external script file from the above URL and install it into the same directory as your document.
>
><script src="demo.js" show>
	return "inlineJavascriptDemo() function has been defined"
</script>
><script label="click to invoke inlineJavascriptDemo()" key="D" show>
	inlineJavascriptDemo();
</script>
<<<
!!!!!Revisions
<<<
2009.02.26 [1.9.4] in $(), handle leading '#' on ID for compatibility with JQuery syntax
2008.06.11 [1.9.3] added $(...) function as 'shorthand' for document.getElementById()
2008.03.03 [1.9.2] corrected fallback declaration of wikifyPlainText() (fixes Safari "parse error")
2008.02.23 [1.9.1] in onclick function, use string instead of array for 'bufferedHTML' (fixes IE errors)
2008.02.21 [1.9.0] output from 'onclick' scripts (return value or document.write() calls) are now buffered and rendered into into a span following the script.  Also, added default 'return false' handling if no return value provided (prevents HREF from being triggered -- return TRUE to allow HREF to be processed).  Thanks to Xavier Verges for suggestion and preliminary code.
2008.02.14 [1.8.1] added backward-compatibility for use of wikifyPlainText() in TW2.1.3 and earlier
2008.01.08 [*.*.*] plugin size reduction: documentation moved to ...Info tiddler
2007.12.28 [1.8.0] added support for key="X" syntax to specify custom access key definitions
2007.12.15 [1.7.0] autogenerate URI encoded HREF on links for onclick scripts.  Drag links to browser toolbar to create bookmarklets.  IMPORTANT NOTE: place is NOT defined when scripts are used as bookmarklets.  In addition, double-quotes will cause syntax errors.  Thanks to PaulReiber for debugging and brainstorming.
2007.11.26 [1.6.2] when converting "document.write()" function calls in inline code, allow whitespace between "write" and "(" so that "document.write ( foobar )" is properly converted.
2007.11.16 [1.6.1] when rendering "onclick scripts", pass label text through wikifyPlainText() to parse any embedded wiki-syntax to enable use of HTML entities or even TW macros to generate dynamic label text.
2007.02.19 [1.6.0] added support for title="..." to specify mouseover tooltip when using an onclick (label="...") script
2006.10.16 [1.5.2] add newline before closing '}' in 'function out_' wrapper.  Fixes error caused when last line of script is a comment.
2006.06.01 [1.5.1] when calling wikify() on script return value, pass hightlightRegExp and tiddler params so macros that rely on these values can render properly
2006.04.19 [1.5.0] added 'show' parameter to force display of javascript source code in tiddler output
2006.01.05 [1.4.0] added support 'onclick' scripts.  When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked.  'place' value is set to match the clicked button/link element.
2005.12.13 [1.3.1] when catching eval error in IE, e.description contains the error text, instead of e.toString().  Fixed error reporting so IE shows the correct response text.  Based on a suggestion by UdoBorkowski
2005.11.09 [1.3.0] for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content.  Based on a suggestion by BradleyMeck
2005.11.08 [1.2.0] handle loading of javascript from an external URL via src="..." syntax
2005.11.08 [1.1.0] pass 'place' param into scripts to provide direct DOM access 
2005.11.08 [1.0.0] initial release
<<<
Is a [[website and -service|http://www.lastfm.com]] providing information on music artists, genres, listening statstics, interrelations, etc...
/***
|Name:|LessBackupsPlugin|
|Description:|Intelligently limit the number of backup files you create|
|Version:|3.0.1 ($Rev: 2320 $)|
|Date:|$Date: 2007-06-18 22:37:46 +1000 (Mon, 18 Jun 2007) $|
|Source:|http://mptw.tiddlyspot.com/#LessBackupsPlugin|
|Author:|Simon Baird|
|Email:|simon.baird@gmail.com|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
!!Description
You end up with just backup one per year, per month, per weekday, per hour, minute, and second.  So total number won't exceed about 200 or so. Can be reduced by commenting out the seconds/minutes/hours line from modes array
!!Notes
Works in IE and Firefox only.  Algorithm by Daniel Baird. IE specific code by by Saq Imtiaz.
***/
//{{{

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

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

window.getSpecialBackupPath = function(backupPath) {

	var now = new Date();

	var modes = config.lessBackups.modes;

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

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

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

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

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

//}}}
/%
|Name|ListRating|
|Source|http://lastfm.tiddlyspot.com/#ListRating|
|Version|0.0.1|
|Author|TobiasBeer|
|''License''|[[Creative Commons Attribution-Share Alike 3.0|http://creativecommons.org/licenses/by-sa/3.0/]]|
|~CoreVersion|2.1|
|Type|script|
|Requires|InlineJavascriptPlugin|
|Overrides||
|Description|gets a list of tiddlers with a given rating, see RatingMacro|

Usage: <<tiddler ListRating with:rate>>

%/<script>
  var getRate='$1';
  
  var tids=window.store.getTiddlers('modified').reverse();
  var cats=config.macros.rating.ratings;
  var rNum=parseInt(getRate);
  if (getRate=='$'+'1'||isNaN(rNum)||rNum<0||rNum>cats.length-1) getRate=(cats.length-1).toString();
  var out="";
  for (var t=0;t<tids.length; t++) {
    var rate = store.getValue(tids[t],"rating");
    if (rate==undefined) rate="0";
    if (rate==getRate) {
      out+="[["+tids[t].title+"]]<br />";
    }
  }
  return out;
</script>
/***
|Name|ListboxPlugin|
|Source|http://www.TiddlyTools.com/#ListboxPlugin|
|Documentation|http://www.TiddlyTools.com/#ListboxPluginInfo|
|Version|1.4.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Description|set tiddler fields by selecting from a listbox or droplist|
The {{{<<select>>}}} macro allows you to set tiddler field values by selecting pre-configured values from a listbox/droplist control.  
!!!!!Documentation
>see [[ListboxPluginInfo]]
!!!!!Revisions
<<<
2009.09.02 [1.4.0] added 'prompt:...' param
|please see [[ListboxPluginInfo]] for additional revision details|
2007.05.12 [0.5.0] started
<<<
!!!!!Code
***/
//{{{
version.extensions.ListboxPlugin= {major: 1, minor: 4, revision: 0, date: new Date(2009,9,2)};

config.macros.select = {
	tooltip: "select a value for %0@%1",
	blankTooltip: "set %0@%1=[no value]",
	valueTooltip: "set %0@%1=%2",
	otherLabel: "other",
	otherTooltip: "set %0@%1=[enter a value...]",
	otherPrompt: "enter a value for '%0'",
	editLabel: "edit list...",
	editTooltip: "edit '%0' list definition (%1)",
	changeMsg: "setting %0@%1=%2",
	verbose: false,
	hereKeyword: "here",
	defaultTarget: "SiteFields",
	handler:
	function(place,macroName,params,wikifier,paramString,tiddler) {

		// default to containing tiddler or "SiteFields" catch-all
		var here=story.findContainingTiddler(place);
		var targetID=here?here.getAttribute("tiddler"):this.defaultTarget;

		// get field name and non-default target (if any)
		var field=params.shift();
		var pos=field.indexOf("@"); // if non-default target ("field@tiddler" syntax)
		if(pos!=-1) { // split field into field and tiddlername.
			if (field.substr(pos+1)!=this.hereKeyword) // "here" == use default target
				targetID=field.substr(pos+1); // use different target tiddler
			field=field.substr(0,pos);
		}
		if(!field || !field.length) return; // no field name... do nothing
		if (field.substr(0,1)=="=") targetID="(system)"; // internal option value

		var items=[]; var listsrc='';
		var autosave=false; var allowBlank=false; var allowOther=false; var allowEdit=false;
		var allowMultiple=false; var wikifyData=false; var rows=0; var width='';
		var p=params.shift();
		while (p) {
			if (p.toLowerCase()=='autosave')	// autosave on change
				autosave=true;
			else if (p.toLowerCase()=='allowblank')	// add empty item
				var allowBlank=true;
			else if (p.toLowerCase()=='allowother')	// add "other: ____" item
				var allowOther=true;
			else if (p.toLowerCase()=='allowedit')	// add "edit list..." item
				var allowEdit=true;
			else if (p.toLowerCase()=='allowmultiple') // multi-select
				var allowMultiple=true;
			else if (p.startsWith('rows:')) // 0=autosize listbox, 1=droplist, n=listbox
				var rows=p.substr(5);
			else if (p.startsWith('width:')) // CSS width of list
				var width=p.substr(6);
			else if (p.startsWith('prompt:')) // prompt text (1st item in list)
				var ptext=p.substr(7);
			else if (p.substr(0,1)=="+"||p.substr(0,1)=="*") { // read HR-separated tiddler
				var listsrc=p.substr(1);
				var listtxt=store.getTiddlerText(listsrc,'');
				var wikifyData=p.substr(0,1)=="*";
				if (listtxt.length && wikifyData) // wikify source to handle macros/scripts
					listtxt=this.getWikifiedData(listtxt);
				if (listtxt.length)
					items=items.concat(listtxt.split(listtxt.indexOf('\n----\n')!=-1?'\n----\n':'\n'));
			}
			else if (p.substr(0,1)=="=") { // get items from tagged tiddlers
				var tids=store.getTaggedTiddlers(p.substr(1));
				for (var t=0; t<tids.length; t++) items.push(tids[t].title);
			}
			else { // param is item value or 'label=value'
				var parts=p.split("=");
				var label=parts[0]; var v=parts[1]?parts[1]:parts[0];
				items.push(label+"="+v);
			}
			p=params.shift();
		}
		if (rows==1) allowMultiple=false; // droplist cannot do multi-select
		if (tiddler && !story.isDirty(tiddler.title)) autosave=true; // tiddler is in VIEW mode, force autosave

		this.render(createTiddlyElement(place,"span"), null,
			targetID, field, ptext, items, listsrc, wikifyData,
			rows, width, autosave, allowBlank, allowOther, allowEdit, allowMultiple);

		store.addNotification(null,this.refresh); // syncs lists when tiddlers are changed
	},
	getWikifiedData: // wikify tiddler content, then extract text WITH newlines and HRs included
	function(txt) {
		var e=createTiddlyElement(document.body,"div"); wikify(txt,e);
		var breaks=e.getElementsByTagName("br");
		for (var b=0; b<breaks.length; b++) breaks[b].parentNode.insertBefore(document.createTextNode("\n"),breaks[b]);
		var lines=e.getElementsByTagName("hr");
		for (var l=0; l<lines.length; l++) lines[l].parentNode.insertBefore(document.createTextNode("----\n"),lines[l]);
		var items=e.getElementsByTagName("li");
		for (var i=0; i<items.length; i++) items[i].parentNode.insertBefore(document.createTextNode("\n"),items[i]);
		var txt=getPlainText(e); removeNode(e); return txt;
	},
	refresh:
	function (title) { // re-render dependent lists
		var lists=document.getElementsByTagName('select');
		for (var i=0; i<lists.length; i++) { var list=lists[i];
			if (list.getAttribute('listsrc')!=title) continue; // no sync needed
			var listtxt=store.getTiddlerText(list.getAttribute('listsrc')||'','');
			if (listtxt.length && list.getAttribute("wikifyData")=="true")
				listtxt=this.getWikifiedData(listtxt);
			if (listtxt.length)
				var items=listtxt.split(listtxt.indexOf('\n----\n')!=-1?'\n----\n':'\n');
			config.macros.select.render(list.parentNode, list,
				list.getAttribute('tiddler'),
				list.getAttribute('edit'),
				list.getAttribute('ptext'),
				items||[],
				list.getAttribute('listsrc'),
				list.getAttribute("wikifyData")=="true",
				list.getAttribute("rows"),
				list.getAttribute("width"),
				list.getAttribute("autosave")=="true",
				list.getAttribute("allowBlank")=="true",
				list.getAttribute("allowOther")=="true",
				list.getAttribute("allowEdit")=="true",
				list.getAttribute("allowMultiple")=="true");
		}
	},
	render:
	function (place, here, targetID, field, ptext, items, listsrc, wikifyData,
		rows, width, autosave, allowBlank, allowOther, allowEdit, allowMultiple) {

		var values=[]; var opts=[];

		// use current selection(s) (if any) (except for "edit list..." item)
		if (here) for (var i=0; i<here.options.length; i++) {
			var opt=here.options[i];
			if (opt.selected && opt.text!=config.macros.select.editLabel) values.push(opt.value);
		}
		// no listbox or no selections... get value(s) from field (if any)
		if (!values.length) {
			var v=(field.substr(0,1)=='=')?config.options[field.substr(1)]:store.getValue(targetID,field);
			if (v) values=(field=='tags'||allowMultiple)?v.readBracketedList():[v];
		}
		// add prompt item
		if (ptext&&ptext.length)
			opts.push('<option value="_ptext" title="">'+ptext+'</option>');
		// add 'no value' item
		if ((!allowMultiple && !values.length) || allowBlank)
			opts.push('<option value="" title="'+this.blankTooltip.format([field,targetID])+'"></option>');
		// add enumerated items
		var isOther=values.length; // assume no matching value
		for (var opt=0; opt<items.length; opt++) {
			var lines=items[opt].split("\n"); var parts=lines[0].split("=");
			var label=parts[0];
			var v=parts[1]?parts[1]:parts[0];
			var title=lines[1]?lines[1]:this.valueTooltip.format([field,targetID,v]);
			var sel=values.contains(v); if (sel) isOther=false; // found matching value
			opts.push('<option value="'+v+'" '+(sel?'selected':'')+' title="'+title+'">'+label+'</option>');
		}
		// add 'other...'
		if (field=='tags') isOther=false;
		if (isOther||allowOther) {
			var label="other"+(isOther?(": "+values[0]):"...");
			var v=isOther?values[0]:'';
			var t=this.otherTooltip.format([field,targetID]);
			opts.push('<option value="'+v+'" '+(isOther?'selected':'')+' title="'+t+'">'+label+'</option>');
		}
		// add 'edit list...'
		if (listsrc && (!store.getTiddlerText(listsrc) || allowEdit)) {
			var title=this.editTooltip.format([field,listsrc]);
			opts.push('<option value="'+listsrc+'" title="'+title+'">'+this.editLabel+'</option>');
		}
		// render listbox
		var html='<select '+(values[0]?'value="'+values[0]+'" ':' ')
			+' title="'+this.tooltip.format([field,targetID])+'"'
			+' rows="'+rows+'"'+' size="'+(rows!=0?rows:opts.length)+'"'+' style="width:'+width+'"'
			+' tiddler="'+targetID+'"'+' edit="'+field+'"'+' ptext="'+ptext+'"'
			+' listsrc="'+listsrc+'"'+' wikifyData="'+wikifyData+'"'
			+' autosave="'+autosave+'"'+' allowBlank="'+allowBlank+'"'+' allowOther="'+allowOther+'"'
			+' allowEdit="'+allowEdit+'"'+' allowMultiple="'+allowMultiple+'"'+(allowMultiple?' multiple':'')
			+' onclick="return config.macros.select.onClick(this,event)"'
			+' onchange="return config.macros.select.onChange(this,event)"'
			+' ondblclick="return false">'+opts.join('')+'</select>';
		place.innerHTML=html;
	},
	onClick:
	function(here,event) {
		var sel=here.selectedIndex;
		if (sel!=-1 && here.options[sel].text.startsWith(config.macros.select.otherLabel))
			here.onchange.apply(here,arguments);
	},
	onChange:
	function(here,event) {
		var cms=config.macros.select; // abbrev
		var sel=here.selectedIndex;
		if (sel!=-1) {
			if (here.options[sel].text==cms.editLabel) {
				story.displayTiddler(story.findContainingTiddler(here),here.value,DEFAULT_EDIT_TEMPLATE);
				return false;
			}
			if (here.options[sel].text.startsWith(cms.otherLabel)) {
				var newval=prompt(cms.otherPrompt.format([here.getAttribute("edit")]),here.value);
				if (!newval) {// user cancelled
					var v=store.getValue(here.getAttribute("tiddler"),here.getAttribute("edit"));
					{ here.value=v; if (v==undefined) here.selectedIndex=0; return false; }
				};
				here.options[sel].value=newval;
				here.options[sel].text=cms.otherLabel+": "+newval;
				here.value=newval;
			}
			if (here.options[sel].value=='_ptext')
				for (var i=0; i<here.options.length; i++)
					here.options[i].selected=false;
		}
		if (here.getAttribute("autosave")=="true") config.macros.select.setFieldValue(here);
		return false;
	},
	setFieldValue: function(here) {
		var tid=here.getAttribute("tiddler"); if (!tid || !tid.length) return; // no target, do nothing
		var field=here.getAttribute("edit");
		if (field.substr(0,1)=='=') { // option cookie instead of tiddler field
			config.macros.option.propagateOption(field.substr(1),"value",here.value,"input");
			return;
		}
		// ensure tiddler exists
		if (!store.tiddlerExists(tid)) store.saveTiddler(tid,tid,"",config.options.txtUserName,new Date(),[]);
		if (field=='tags') {
			store.suspendNotifications();
			for (var i=0; i<here.options.length; i++) {
				var opt=here.options[i];
				if (opt.text==config.macros.select.editLabel) continue;
				store.setTiddlerTag(tid,opt.selected,opt.value);
			}
			store.resumeNotifications();
		} else {
			// get multi-select items
			var values=[];
			for (var i=0; i<here.options.length; i++) {
				var opt=here.options[i];
				if (opt.text==config.macros.select.editLabel) continue;
				if (opt.selected) values.pushUnique(String.encodeTiddlyLink(opt.value));
			}
			if (values.length==1) values=[here.value]; // remove unneeded brackets around single value
			store.setValue(tid,field,values.length?values.join(' '):null); // if no selections, delete field
		}
		// 'touch' tiddler and report to user
		var t=store.getTiddler(tid);
		var who=config.options.chkForceMinorUpdate?t.modifier:config.options.txtUserName;
		var when=config.options.chkForceMinorUpdate?t.modified:new Date();
		store.saveTiddler(tid,tid,t.body,who,when,t.tags,t.fields);
		if (config.macros.select.verbose)
			{ clearMessage(); displayMessage(config.macros.select.changeMsg.format([field,tid,here.value])); }
	}
}
//}}}
/***
|''Name:''|LoadRemoteFileThroughProxy (previous LoadRemoteFileHijack)|
|''Description:''|When the TiddlyWiki file is located on the web (view over http) the content of [[SiteProxy]] tiddler is added in front of the file url. If [[SiteProxy]] does not exist "/proxy/" is added. |
|''Version:''|1.1.0|
|''Date:''|mar 17, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#LoadRemoteFileHijack|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0|
***/
//{{{
version.extensions.LoadRemoteFileThroughProxy = {
 major: 1, minor: 1, revision: 0, 
 date: new Date("mar 17, 2007"), 
 source: "http://tiddlywiki.bidix.info/#LoadRemoteFileThroughProxy"};

if (!window.bidix) window.bidix = {}; // bidix namespace
if (!bidix.core) bidix.core = {};

bidix.core.loadRemoteFile = loadRemoteFile;
loadRemoteFile = function(url,callback,params)
{
 if ((document.location.toString().substr(0,4) == "http") && (url.substr(0,4) == "http")){ 
 url = store.getTiddlerText("SiteProxy", "/proxy/") + url;
 }
 return bidix.core.loadRemoteFile(url,callback,params);
}
//}}}
![[Notes]]
{{light{[[Archive|NotesArchive]] | }}}[[Recent|NotesRecent]] | [[Write|Notes]]

![[scripts and macros|Home]]
[[cycleTags|cycleTagsInfo]]
[[AddTagMacro|AddTagMacroInfo]]
{{light{[[(Summary)|RatingSummary]]}}} [[RatingMacro|RatingMacroInfo]]

![[more|TobiasBeer]]
[[tbGTD|http://tbgtd.tiddlyspot.com]]
[[widgets|http://widgets.tiddlyspot.com]]
<!--{{{-->
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' />
<!--}}}-->

<style type="text/css">#contentWrapper {display:none;}</style><table id="SplashScreen" style="border: 1px solid #FAA; display:block; text-align:center; width:700px; margin:100px auto; padding:20px; color:white; font-size:24px; font-family:Tahoma; background-color:#CCC;"><tr><td><img src="http://goto.bplaced.net/x/tiddlyspot/lastfm/start.jpg" style="margin:25px;"></td><td>the tuneable player for tiddlywiki is loading<span style="text-decoration:blink;"> ...</span><br style="margin-top:5px;margin-bottom:20px"><span style="font-size: 12px;color:#A66;">requires javascript</span><br style="margin-bottom:20px;"></td></tr></table>
//{{{
config.options.txtFadeTimer = 5000; // 5 seconds 

function displayMessage(text,linkText,fadeTimer) { 
        var e = getMessageDiv(); 
        if(!e) { 
                alert(text); 
                return; 
        } 
        if(linkText) { 
                var link = createTiddlyElement(e,"a",null,null,text); 
                link.href = linkText; 
                link.target = "_blank"; 
        } else { 
                e.appendChild(document.createTextNode(text)); 
        } 
        if(config.options.txtFadeTimer > 0) { 
                setTimeout(clearMessage, config.options.txtFadeTimer); 
        } 
}
//}}}
/***
|Name:|NewHerePlugin|
|Description:|Creates the new here and new journal macros|
|Version:|3.0 ($Rev: 3861 $)|
|Date:|$Date: 2008-03-08 10:53:09 +1000 (Sat, 08 Mar 2008) $|
|Source:|http://mptw.tiddlyspot.com/#NewHerePlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License|http://mptw.tiddlyspot.com/#TheBSDLicense|
***/
//{{{
merge(config.macros, {
	newHere: {
		handler: function(place,macroName,params,wikifier,paramString,tiddler) {
			wikify("<<newTiddler "+paramString+" tag:[["+tiddler.title+"]]>>",place,null,tiddler);
		}
	},
	newJournalHere: {
		handler: function(place,macroName,params,wikifier,paramString,tiddler) {
			wikify("<<newJournal "+paramString+" tag:[["+tiddler.title+"]]>>",place,null,tiddler);
		}
	}
});

//}}}

/***
|Name:|NewMeansNewPlugin|
|Description:|If 'New Tiddler' already exists then create 'New Tiddler (1)' and so on|
|Version:|1.1.1 ($Rev: 2263 $)|
|Date:|$Date: 2007-06-13 04:22:32 +1000 (Wed, 13 Jun 2007) $|
|Source:|http://mptw.tiddlyspot.com/empty.html#NewMeansNewPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License|http://mptw.tiddlyspot.com/#TheBSDLicense|
!!Note: I think this should be in the core
***/
//{{{

// change this or set config.newMeansNewForJournalsToo it in MptwUuserConfigPlugin
if (config.newMeansNewForJournalsToo == undefined) config.newMeansNewForJournalsToo = true;

String.prototype.getNextFreeName = function() {
       var numberRegExp = / \(([0-9]+)\)$/;
       var match = numberRegExp.exec(this);
       if (match) {
               var num = parseInt(match[1]) + 1;
               return this.replace(numberRegExp," ("+num+")");
       }
       else {
               return this + " (1)";
       }
}

config.macros.newTiddler.checkForUnsaved = function(newName) {
	var r = false;
	story.forEachTiddler(function(title,element) {
		if (title == newName)
			r = true;
	});
	return r;
}

config.macros.newTiddler.getName = function(newName) {
       while (store.getTiddler(newName) || config.macros.newTiddler.checkForUnsaved(newName))
               newName = newName.getNextFreeName();
       return newName;
}


config.macros.newTiddler.onClickNewTiddler = function()
{
	var title = this.getAttribute("newTitle");
	if(this.getAttribute("isJournal") == "true") {
		title = new Date().formatString(title.trim());
	}

	// ---- these three lines should be the only difference between this and the core onClickNewTiddler
	if (config.newMeansNewForJournalsToo || this.getAttribute("isJournal") != "true")
		title = config.macros.newTiddler.getName(title);

	var params = this.getAttribute("params");
	var tags = params ? params.split("|") : [];
	var focus = this.getAttribute("newFocus");
	var template = this.getAttribute("newTemplate");
	var customFields = this.getAttribute("customFields");
	if(!customFields && !store.isShadowTiddler(title))
		customFields = String.encodeHashMap(config.defaultCustomFields);
	story.displayTiddler(null,title,template,false,null,null);
	var tiddlerElem = story.getTiddler(title);
	if(customFields)
		story.addCustomFields(tiddlerElem,customFields);
	var text = this.getAttribute("newText");
	if(typeof text == "string")
		story.getTiddlerField(title,"text").value = text.format([title]);
	for(var t=0;t<tags.length;t++)
		story.setTiddlerTag(title,tags[t],+1);
	story.focusTiddler(title,focus);
	return false;
};

//}}}

All notes are tagging here...
<html><nowiki><form id="Notes" style="width:100%;height:90px;">
<input title="message title" name="msgTitle" type="text" cols="60" style="width:45%;height:20px;padding:0px;margin-bottom:1px;margin-right:2px;border:1px solid #CCC;"><input title="tags added to message" name="msgTags" type="text" cols="60" style="width:30%;height:20px;padding:0px;margin-bottom:1px;border:1px solid #CCC;"><br/>
<textarea title="message body" name="msgBody" rows="5" cols="60" style="width:75%;height:77px;padding:0px 2px;margin:0px;float:left;border:1px solid #CCC;"></textarea>
<input class="button" type="button" value="save message" style="font-weight:bold;font-size:1.2em;width:24%;height:102px;float:right;border:1px solid #CCC;margin:-23px 0px 0px 0px;" title="click to save this message" onclick=" 
        var note=this.form.msgBody.value;if (note=='') return;
        var tags=this.form.msgTags.value.readBracketedList();
        tags.push('Note');tags.push('excludeLists');
        var tid=story.findContainingTiddler(place).getAttribute('tiddler');
        var who=config.options.txtUserName; 
        var when=new Date();
        var title=this.form.msgTitle.value;
        if (title=='') title=when.formatString(' YYYY-0MM-0DD 0hh:0mm:0ss');
        var msg=config.messages.overwriteWarning.format([title]);
        if (store.tiddlerExists(title) && !confirm(msg)) return; 
        store.saveTiddler(title,title,note,who,when,tags,{}); 
        refreshDisplay('NotesLast');
        refreshDisplay('NotesRecent');
        story.refreshTiddler('NotesArchive',null,true);
        this.form.msgTitle.value='';
        this.form.msgTags.value='';
        this.form.msgBody.value='';
        this.form.msgBody.focus();">
</form></html>
[[ recent notes |NotesRecent]]|[[ notes archive |NotesArchive]] &nbsp;&nbsp;&nbsp;{{light{...inspired by [[QuickNotes|http://tiddlytools.com/#QuickNote]]}}}<<tiddler NotesLast>>
<<tiddlerList tags:"Note" order:"-modified" itemTemplate:"<<tiddler DeleteTiddler with:'%title'$)): %link (%created)\n">>
<<tiddlerList tags:"Note" top:"1" order:"-modified" dateFormat:"YYYY mmm. 0DD" itemTemplate:"!@@background:#DDD;display:block;width:98%;padding:5px 1%;Last note: %link@@\n<<tiddler DeleteTiddler with: '%title' 'NotesArchive' $)) (%created)\n\n%abstract @@color:red;...@@\n\n">>
<<tiddlerList tags:"Note" top:"10" order:"-modified" dateFormat:"YYYY mmm. 0DD" itemTemplate:"!@@background:#DDD;display:block;width:98%;padding:5px 1%;%link@@\n<<tiddler DeleteTiddler with: '%title' 'NotesArchive' $)) (%created)\n\n%abstract @@color:red;...@@\n\n">>[[Notes Archive|NotesArchive]]
<<tag Site "site">><<permaview>><<newTiddler>><<newJournal "DD MMM YYYY" "journal">><<saveChanges>><<tiddler TspotSidebar>><<slider chkSliderOptionsPanel OptionsPanel "options »" "Change TiddlyWiki advanced options">>
/***
|''Name:''|PasswordOptionPlugin|
|''Description:''|Extends TiddlyWiki options with non encrypted password option.|
|''Version:''|1.0.2|
|''Date:''|Apr 19, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#PasswordOptionPlugin|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0 (Beta 5)|
***/
//{{{
version.extensions.PasswordOptionPlugin = {
	major: 1, minor: 0, revision: 2, 
	date: new Date("Apr 19, 2007"),
	source: 'http://tiddlywiki.bidix.info/#PasswordOptionPlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	license: '[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D]]',
	coreVersion: '2.2.0 (Beta 5)'
};

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

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

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

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

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

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

merge(config.optionsDesc,{
		pasPassword: "Test password"
	});
*/
//}}}
[[on google groups|http://groups.google.com/group/tiddlywiki/browse_frm/thread/8a0506f78361a8c2]]

Based on the discussions in this thread, I've just added a new tiddler to ~TiddlyTools:
   http://www.TiddlyTools.com/#QuickNote

~QuickNote isn't a plugin (no 'systemConfig' tag), nor an inline script.  Instead, it's "transcluded" content using only TW's core
syntax, and doesn't need *any* plugins at all. To use QuickNote, you embed it in another tiddler and pass a 'dateformat' parameter to it by writing something like this:
   {{{<<tiddler QuickNote with: "-YYYY0MM0DD0hh0mm0ss">>}}}

Also, I've improved the interface a bit by adding a text field that the form shows the title (updated automatically) that will be used to
create the next quick note tiddler, plus an input field for entering tags (default tag="quicknote").  When you press 'save', you get a
chance to confirm or change the auto-generated title (or cancel the save).

enjoy,
-e 
Possibly interesting stuff...
http://tbGTD.tiddlyspot.com/#%5B%5Bquick-add%20help%5D%5D
/***
|Name:|QuickOpenTagPlugin|
|Description:|Changes tag links to make it easier to open tags as tiddlers|
|Version:|3.0.1 ($Rev: 3861 $)|
|Date:|$Date: 2008-03-08 10:53:09 +1000 (Sat, 08 Mar 2008) $|
|Source:|http://mptw.tiddlyspot.com/#QuickOpenTagPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
***/
//{{{
config.quickOpenTag = {

	dropdownChar: (document.all ? "\u25bc" : "\u25be"), // the little one doesn't work in IE?

	createTagButton: function(place,tag,excludeTiddler) {
		// little hack so we can do this: <<tag PrettyTagName|RealTagName>>
		var splitTag = tag.split("|");
		var pretty = tag;
		if (splitTag.length == 2) {
			tag = splitTag[1];
			pretty = splitTag[0];
		}
		
		var sp = createTiddlyElement(place,"span",null,"quickopentag");
		createTiddlyText(createTiddlyLink(sp,tag,false),pretty);
		
		var theTag = createTiddlyButton(sp,config.quickOpenTag.dropdownChar,
                        config.views.wikified.tag.tooltip.format([tag]),onClickTag);
		theTag.setAttribute("tag",tag);
		if (excludeTiddler)
			theTag.setAttribute("tiddler",excludeTiddler);
    		return(theTag);
	},

	miniTagHandler: function(place,macroName,params,wikifier,paramString,tiddler) {
		var tagged = store.getTaggedTiddlers(tiddler.title);
		if (tagged.length > 0) {
			var theTag = createTiddlyButton(place,config.quickOpenTag.dropdownChar,
                        	config.views.wikified.tag.tooltip.format([tiddler.title]),onClickTag);
			theTag.setAttribute("tag",tiddler.title);
			theTag.className = "miniTag";
		}
	},

	allTagsHandler: function(place,macroName,params) {
		var tags = store.getTags(params[0]);
		var filter = params[1]; // new feature
		var ul = createTiddlyElement(place,"ul");
		if(tags.length == 0)
			createTiddlyElement(ul,"li",null,"listTitle",this.noTags);
		for(var t=0; t<tags.length; t++) {
			var title = tags[t][0];
			if (!filter || (title.match(new RegExp('^'+filter)))) {
				var info = getTiddlyLinkInfo(title);
				var theListItem =createTiddlyElement(ul,"li");
				var theLink = createTiddlyLink(theListItem,tags[t][0],true);
				var theCount = " (" + tags[t][1] + ")";
				theLink.appendChild(document.createTextNode(theCount));
				var theDropDownBtn = createTiddlyButton(theListItem," " +
					config.quickOpenTag.dropdownChar,this.tooltip.format([tags[t][0]]),onClickTag);
				theDropDownBtn.setAttribute("tag",tags[t][0]);
			}
		}
	},

	// todo fix these up a bit
	styles: [
"/*{{{*/",
"/* created by QuickOpenTagPlugin */",
".tagglyTagged .quickopentag, .tagged .quickopentag { margin-right:1em; border:none; padding:0px 0px 0px 1px; }",
".quickopentag .tiddlyLink { padding:2px; padding-left:3px; }",
".quickopentag a.button { padding:1px; padding-left:2px; padding-right:2px;}",
"/* extra specificity to make it work right */",
"#displayArea .viewer .quickopentag a.button, ",
"#displayArea .viewer .quickopentag a.tiddyLink, ",
"#mainMenu .quickopentag a.tiddyLink, ",
"#mainMenu .quickopentag a.tiddyLink ",
"	{ border:0px solid black; }",
"#displayArea .viewer .quickopentag a.button, ",
"#mainMenu .quickopentag a.button ",
"	{ margin-left:0px; padding-left:2px; }",
"#displayArea .viewer .quickopentag a.tiddlyLink, ",
"#mainMenu .quickopentag a.tiddlyLink ",
"	{ margin-right:0px; padding-right:0px; padding-left:0px; margin-left:0px; }",
"a.miniTag {font-size:150%;} ",
"#mainMenu .quickopentag a.button ",
"	/* looks better in right justified main menus */",
"	{ margin-left:0px; padding-left:2px; margin-right:0px; padding-right:0px; }", 
"#topMenu .quickopentag { padding:0px; margin:0px; border:0px; }",
"#topMenu .quickopentag .tiddlyLink { padding-right:1px; margin-right:0px; }",
"#topMenu .quickopentag .button { padding-left:1px; margin-left:0px; border:0px; }",
"/*}}}*/",
		""].join("\n"),

	init: function() {
		// we fully replace these builtins. can't hijack them easily
		window.createTagButton = this.createTagButton;
		config.macros.allTags.handler = this.allTagsHandler;
		config.macros.miniTag = { handler: this.miniTagHandler };
		config.shadowTiddlers["QuickOpenTagStyles"] = this.styles;
		store.addNotification("QuickOpenTagStyles",refreshStyles);
	}
}

config.quickOpenTag.init();

//}}}
/***
|''Name:''|RatingMacro|
|''Description''|allows to rate tiddlers. e.g. via stars|
|''Version:''|0.1.0|
|''Type''|macro|
|''Author:''|[[TobiasBeer]]|
|''Info:''|http://lastfm.tiddlyspot.com/#RatingMacroInfo [[RatingMacroInfo]]|
|''Source:''|http://lastfm.tiddlyspot.com/#RatingMacro [[RatingMacro]]|
|''License''|[[Creative Commons Attribution-Share Alike 3.0|http://creativecommons.org/licenses/by-sa/3.0/]]|
|''Feedback:''|[[here|http://groups.google.com/group/tiddlywiki/browse_frm/thread/35b6e27237f1749b]]|
|''~CoreVersion''|2.2|
Inspired by http://sinojellyempty.tiddlyspot.com/#FiveStarsPlugin
!Code
***/

//{{{
config.macros.rating = {
icon:'\u2605',
cssClass:'rating',
tooltip:'rating: ',
ratings:['none','poor','below average','average','above average','excellent'],
defaultStyle:['color:#FFD9D9;text-decoration:none;background:none;'],
colorOn:'orange',
colorOff:'#CCC',

update:function(obj,action){//actions --> 0:refresh 1:highlight 2:save
	var last,name,p,r,tid;
	p=obj.parentNode;
	r=obj.getAttribute('rel');
	name=obj.getAttribute('tiddler');
	tid=store.getTiddler(name);
	if(!tid)return;
	if(action==2){
		tid.fields['rating']=r;
		store.setDirty(true);
		tids=obj.getAttribute('update').readBracketedList();
		for(i=0;i<tids.length;i++){
			story.refreshTiddler(tids[i],null,true);
			refreshDisplay(tids[i]);
		}
		story.refreshTiddler(name,null,true);
	}
	last=parseInt(tid.fields['rating']||0);
	for(i=1;i<this.ratings.length;i++){
		p.childNodes[i].style.color=((action>0&&i<=r)||(action==0&&i<=last))?this.colorOn:this.colorOff;
	}
},
handler:function(place,macroName,params,wikifier,paramString,tiddler){
	var i,out,t;
	t=params.shift();
	if(t===undefined||t=='=here')t=story.findContainingTiddler(place).getAttribute('tiddler');
	out='<span title="rating" class="'+this.cssClass+'">';
	for(i=0;i<this.ratings.length;i++){
		out+='<a href="#" onmouseover="config.macros.rating.update(this,1)" '+
			'onclick="config.macros.rating.update(this,2)" '+
			'onmouseout="config.macros.rating.update(this,0)" '+
			'title="'+this.tooltip+this.ratings[i]+'" rel="'+i+
			'" update="[['+params.join(']] [[')+']]" tiddler="'+t+
			'" style="'+this.defaultStyle+'">'+this.icon+'</a>';
	}
	place.innerHTML+=out+'</span>';
	this.update(place.lastChild.firstChild,0);
}
}
//}}}
<<showtoc>>
!What for
[[RatingMacro]] [[(source)|RatingMacro]] is a macro allowing you to add a simple rating mechanism, e.g. via stars.
The rating is stored in a tiddler field called {{{rating}}} as an integer value.
!Usage
|where|scope|how|h
|in a tiddler|current tiddler|{{{<<rating>>}}}|
|in the ViewTemplate|all tiddlers|{{{<span macro="rating" style="margin-left:10px;"></span>}}}|
!!Parameters
You can define parameter using the with statement for the tiddler macro, like so:
{{{<<rating rateThisTiddler refreshThisTiddler refreshThatTiddler ...>>}}}
An example: {{{<<rating Home RatingSummary>>}}}
Rate [[Home]]: <<rating Home RatingSummary>>
|parameter|use|h
|rateThisTiddler |&nbsp;- with the first parameter you can specify which tiddler you want to rate<br/>&nbsp;- using {{{=here}}} will refer to the current tiddler|
|refreshThisTiddler<br/>refreshThatTiddler<br>...|&nbsp;- tiddlers you want to refresh whenever you click to rate|
!!Parameter section in the sourcecode
There are several parameters that allow you to define the rating levels as well as the look and feel of your rating mechanism:
|Parameter|Comment|h
|icon|the symbol used for rating a tiddler, default: {{{★}}} |
|tooltip|the prefix displayed in the tooltip for each symbol, default: {{{rating: }}}  |
|ratings|the array defining allowed ratings;<br />the first entry {{{ratings[0]}}} is reserved for the initial state, meaning: "not rated yet" or "reset rating" |
|defaultStyle|the css style applied to all characters in the rating sequence |
|colorOn|the color used for the highlight {{{(rating-level achieved)}}} |
|colorOff|the color used for the base {{{(rating-level not achieved)}}} |
!List rated tiddlers
!!ListRating
ListRating is a [[script|InlineJavascriptPluginInfo]]-tiddler that allows you to get a list of all tiddlers with a given rating. Invoke it using: {{{<<tiddler ListRating with:rate>>}}}. If you do not specify {{{with:rate}}}, all tiddlers rated with the highest possible rating are returned. {{{with:0}}} returns all unrated tiddlers.

Example to retrieve all tiddlers rated excellent: {{{<<tiddler ListRating with:5>>}}}
<<tiddler ListRating with:5>>
!!RatingSummary
RatingSummary is a [[script|InlineJavascriptPluginInfo]]-tiddler that lists all rated tiddlers in a table.
Invoke it using: {{{<<tiddler RatingSummary with:"icons names count byTitle range[fromRating:toRating]">>}}}

Example: {{{<<tiddler RatingSummary with:"icons names count range[3:-1]">>}}}
<<tiddler RatingSummary with:"icons names count range[3:-1]">>
RatingSummary allows the following parameters which must be enclosed in quotes all together (order irrelevant):
|Parameter|What the parameter does when it is present|h
|icons|shows icons (e.g. ★) in the header|
|names|shows names of rating categories in the header|
|count|shows an item count for each rating category|
|byTitle|lists items by title instead of by last modified|
|range[fromRating:toRating]|displays the rating overview only for categories within the specified range;<br />allowed {{{range[0:numRatings]}}} where {{{numRatings}}} equals the number of categories in [[RatingMacro]]<br />note: {{{toRating=-1 =numRatings =config.macros.rating.ratings.length}}}<br />setting {{{fromRating}}} to {{{0}}} will include unrated tiddlers|
!Revision History
|Version|What's New?|h
|0.1.0|- halfway mature... added css classname of "rating" and new parameters: ''rateTiddler'' and ''refresh Tiddlers''<br />- sorry for the confusion, if I created any by renaming these scripts...<br/>&nbsp;&nbsp;- GetRating is now called ListRating<br/>&nbsp;&nbsp;- GetRatingOverview is now called RatingSummary|
|0.0.1|- initial release inspired by http://sinojellyempty.tiddlyspot.com/#FiveStarsPlugin <br />- [[here|FiveStarsPluginModTest]] is an example based on an alternative [[mod of FiveStarsPlugin|FiveStarsPluginMod]] still using images |
!Revisions RatingSummary
|Version|What's New?|h
|0.2.0|- fixed bug due to a missing variable declaration, overwriting a global one|
|0.1.0|- fixed bug where existing ratings wouldn't work with your new ratings-array|
|0.0.1|- initial release|
/%
|Name|RatingSummary|
|Source|http://lastfm.tiddlyspot.com/#RatingSummary|
|Version|0.2|
|Author|TobiasBeer|
|''License''|[[Creative Commons Attribution-Share Alike 3.0|http://creativecommons.org/licenses/by-sa/3.0/]]|
|~CoreVersion|2.1|
|Type|script|
|Requires|InlineJavascriptPlugin|
|Overrides||
|Description|generates an overview of rated tiddlers, see RatingMacro|

Usage: <<tiddler GetRatingOverview with:"icons names count range[fromRating:toRating]">>
...all parameters optional, order irrelevant

%/<script>
var bIco,bNames,bCount,bByTit,bounds,c,cats,cTids=[],from,num,out,prms,r,rng,s,stars,t,to,upper;
var tiddlerFormat='[[%0]]';//%0=tiddlerName
prms='$1';
bIco=prms.indexOf("icons")>=0;
bNames=prms.indexOf("names")>=0;
bCount=prms.indexOf("count")>=0;
bByTit=prms.indexOf("byTitle");
rng=prms.split("range[");
from=1;to=-1;
if(rng[1]!=undefined){
	bounds=rng[1].split(":");
	if(!isNaN(bounds[0]))from=bounds[0];
	upper=bounds[1].split("]")[0];if(!isNaN(upper))to=upper;
}
num=config.macros.rating.ratings.length-1;
to=to<0?num:Math.min(to,num);
from=Math.min(Math.max(from,0),num,to);
cols=to-from+1;

tids=bByTit?window.store.getTiddlers('title'):window.store.getTiddlers('modified').reverse();
cats=config.macros.rating.ratings;
for(c=0;c<cats.length;c++)cTids[c]=[];
for(t=0;t<tids.length; t++){
	r=parseInt(store.getValue(tids[t],"rating"));
	r=isNaN(r)?0:Math.min(r,num);
	cTids[r].push(tids[t].title);
}

out="|"; var pre="|ratingTable"+cols+"|k\n|";
for(c=0;c<Math.min(cats.length,to+1);c++){
	switch(c){
	case 0: s=cats[0];break;
	case 1: s="";
	default: s+=config.macros.rating.icon; break;
	}
	if(c>=from){
		bIco=bIco||!bNames;
		sep=bNames?'<br>':sep=' ';
		stars=bIco?s+sep:'';
		sName=bNames&&c>0?cats[c]+' ':'';
		sCount=bCount?' ('+cTids[c].length.toString()+') ':sCount=' ';
		hdStyle='text-align:center;width:'+Math.floor(95/cols).toString()+'%;';
		pre+=hdStyle+stars+sName+sCount+"|";
		out+='';
		for(t=0;t<cTids[c].length; t++)out+=tiddlerFormat.format([cTids[c][t]])+'<br />';
		out+="|";
	}
}
setStylesheet('.ratingTable'+cols+'{width:'+Math.floor(90*cols/num)+'%;}'+
'.ratingTable'+cols+' td{padding-left:5px;vertical-align:top;}','RatingTableStyles'+cols);

return(pre+"h\n"+out);
</script>
/***
|Name:|RenameTagsPlugin|
|Description:|Allows you to easily rename or delete tags across multiple tiddlers|
|Version:|3.0 ($Rev: 5501 $)|
|Date:|$Date: 2008-06-10 23:11:55 +1000 (Tue, 10 Jun 2008) $|
|Source:|http://mptw.tiddlyspot.com/#RenameTagsPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License|http://mptw.tiddlyspot.com/#TheBSDLicense|
Rename a tag and you will be prompted to rename it in all its tagged tiddlers.
***/
//{{{
config.renameTags = {

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

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

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

	storeMethods: {

		saveTiddler_orig_renameTags: TiddlyWiki.prototype.saveTiddler,

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

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

		removeTiddler_orig_renameTags: TiddlyWiki.prototype.removeTiddler,

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

	},

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

config.renameTags.init();

//}}}

/%
|Name|ShowObject|
|Source|http://www.TiddlyTools.com/#ShowObject|
|Version|1.4.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements<br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type||
|Requires||
|Overrides||
|Description|display TiddlyWiki runtime objects|

Usage:
	<<tiddler ShowObject with: "function, object or DOM element">>
Examples:
	<<tiddler ShowObject with: "main">>
	<<tiddler ShowObject with: "config.messages">>
	<<tiddler ShowObject with: "mainMenu">>

%/<<tiddler ShowObject##format with: $1 {{
	if (document.getElementById('$1'))
		var out=function(id){
			var t=document.getElementById(id).innerHTML
			var t2=''; var indent=''; var level=0;
			for (var i=0;i<t.length;i++) { var c=t.substr(i,1);
				if (c=='<') {
					if (t.substr(i+1,1)=='/')  indent=indent.substr(0,indent.length-2);
					t2+='\n'+indent;
					if (t.substr(i+1,1)!='/'
						&& t.substr(i+1,3)!='br>'
						&& t.substr(i+1,2)!='p>'
						&& t.substr(i+1,3)!='hr>') indent+='  ';
				}
				t2+=c;
				if (c=='\n') t2+=indent;
				if (c=='>' && t.substr(i+1,1)!='<') t2+='\n'+indent;
			}
			return t2;
		}('$1');
	else if (typeof($1)=='function')
		var out=function(obj){
			return obj.toSource?obj.toSource(true):obj.toString();
		}($1);
	else if (typeof($1)!='undefined') {
		var out=function(obj,indent,level) {
			if (level>10)			return '...';
			if (obj && obj.exec!==undefined) return obj.toString(); 
			if (typeof obj=='function')	return 'function(...){...}';
			if (typeof obj=='string')	return '"'+obj+'"';
			if (typeof obj=='object') {
				var t=[]; for(var p in obj) {
					var v=(p=='innerHTML')?'...':arguments.callee(obj[p],indent+'  ',level+1);
					t.push(indent+'  '+p+': '+v);
				}
				return '{\n'+t.join(',\n')+'\n'+indent+'}';
			}
			return obj.toString();
		}($1,'',0);
	} else var out=' ???';
	out.htmlEncode();
}}>>/%
!format
<html><nowiki><pre>$1 =$2</pre></html>
!end
%/
<<search>><<closeAll>>[[options|Options]]
<<showtoc>>
!General ~HowTo
*simply use the lastfm widget code in html tags (edit this tiddler to see the examples!)
*change settings by looking in the widget code for the parameter {{{radioURL}}}
**change the language by editing {{{lang=xy}}}
**to request a specific station, edit the first part of {{{radioURL}}} ...just before {{{&amp;title}}}
***create an ''artists station'' using:
**** {{{artist%2F<artistName>%2Fsimilarartists}}} ...without actually using the {{{<>}}} characters
***request a ''station for a certain tag (or genre)'' using:
**** {{{globaltags%2F<tagOrGenre>}}} ...without actually using the {{{<>}}} characters

!Example Players
!!English player for tag ...Indie
<html>
<object type="application/x-shockwave-flash" data="http://cdn.last.fm/widgets/radio/22.swf" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="184" height="140"><param name="movie" value="http://cdn.last.fm/widgets/radio/22.swf" /><param name="flashvars" value="lfmMode=radio&amp;radioURL=globaltags%2Findie&amp;title=play&amp;theme=grey&amp;lang=en" /><param name="allowScriptAccess" value="always" /> <param name="allowNetworking" value="all" /> <param name="allowFullScreen" value="true" /> <param name="quality" value="high" /> <param name="bgcolor" value="999999" /> <param name="wmode" value="transparent" /> <param name="menu" value="true" /> </object>
</html>

!!English player for artist ...Pearl Jam
<html>
<object type="application/x-shockwave-flash" data="http://cdn.last.fm/widgets/radio/22.swf" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="184" height="140"><param name="movie" value="http://cdn.last.fm/widgets/radio/22.swf" /><param name="flashvars" value="lfmMode=radio&amp;radioURL=artist%2Fpearljam%2Fsimilarartists&amp;title=play&amp;theme=grey&amp;lang=en" /><param name="allowScriptAccess" value="always" /> <param name="allowNetworking" value="all" /> <param name="allowFullScreen" value="true" /> <param name="quality" value="high" /> <param name="bgcolor" value="999999" /> <param name="wmode" value="transparent" /> <param name="menu" value="true" /> </object>
</html>

!!German player for tag ...Franz&ouml;sisch
<html>
<object type="application/x-shockwave-flash" data="http://cdn.last.fm/widgets/radio/22.swf" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="184" height="140"><param name="movie" value="http://cdn.last.fm/widgets/radio/22.swf" /><param name="flashvars" value="lfmMode=radio&amp;radioURL=globaltags%2Ffranzoesisch&amp;title=play&amp;theme=grey&amp;lang=de" /><param name="allowScriptAccess" value="always" /> <param name="allowNetworking" value="all" /> <param name="allowFullScreen" value="true" /> <param name="quality" value="high" /> <param name="bgcolor" value="999999" /> <param name="wmode" value="transparent" /> <param name="menu" value="true" /> </object>
</html>

!!German player for artist ...Frou Frou
<html>
<object type="application/x-shockwave-flash" data="http://cdn.last.fm/widgets/radio/22.swf" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="184" height="140"><param name="movie" value="http://cdn.last.fm/widgets/radio/22.swf" /><param name="flashvars" value="lfmMode=radio&amp;radioURL=artist%2Ffroufrou%2Fsimilarartists&amp;title=play&amp;theme=grey&amp;lang=de" /><param name="allowScriptAccess" value="always" /> <param name="allowNetworking" value="all" /> <param name="allowFullScreen" value="true" /> <param name="quality" value="high" /> <param name="bgcolor" value="999999" /> <param name="wmode" value="transparent" /> <param name="menu" value="true" /> </object>
</html>
/***
|''Name''|SimpleSearchPlugin|
|''Description''|displays search results as a simple list of matching tiddlers|
|''Authors''|FND|
|''Version''|0.4.1|
|''Status''|stable|
|''Source''|http://devpad.tiddlyspot.com/#SimpleSearchPlugin|
|''CodeRepository''|http://svn.tiddlywiki.org/Trunk/contributors/FND/plugins/SimpleSearchPlugin.js|
|''License''|[[Creative Commons Attribution-ShareAlike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]]|
|''Keywords''|search|
!Code
***/
//{{{
if(!version.extensions.SimpleSearchPlugin) { //# ensure that the plugin is only installed once
version.extensions.SimpleSearchPlugin = { installed: true };

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

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

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

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

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

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

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

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

} //# end of "install only once"
//}}}
Tiddlers related to this tiddlywiki's setup are tagged 'site'.
the {{{<<lastfm>>}}} macro to embed a tunable player in tiddlywiki __no longer works__
[[LastFM|Home]]
/***
|Name|SplashScreenPlugin|
|Created by|SaqImtiaz|
|Location|http://tw.lewcid.org/#SplashScreenPlugin|
|Version|0.21 |
|Requires|~TW2.08+|
!Description:
Provides a simple splash screen that is visible while the TW is loading.
!Code
***/
//{{{
var old_lewcid_splash_restart=restart;

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


var oldText = store.getTiddlerText("MarkupPreHead");
if (oldText.indexOf("SplashScreen")==-1)
   {var siteTitle = store.getTiddlerText("SiteTitle");
   var splasher='\n<style type="text/css">#contentWrapper {display:none;}</style><table id="SplashScreen" style="border: 1px solid #FAA; display:block; text-align:center; width:700px; margin:100px auto; padding:20px; color:white; font-size:24px; font-family:Tahoma; background-color:#CCC;"><tr><td><img src="http://goto.bplaced.net/x/tiddlyspot/lastfm/start.jpg" style="margin:25px;"></td><td>the tuneable player for tiddlywiki is loading<span style="text-decoration:blink;"> ...</span><br style="margin-top:5px;margin-bottom:20px"><span style="font-size: 12px;color:#A66;">requires javascript</span><br style="margin-bottom:20px;"></td></tr></table>';
   if (! store.tiddlerExists("MarkupPreHead"))
       {var myTiddler = store.createTiddler("MarkupPreHead");}
   else
      {var myTiddler = store.getTiddler("MarkupPreHead");}
      myTiddler.set(myTiddler.title,oldText+splasher,config.options.txtUserName,null,null);
      store.setDirty(true);
      var splashScreenInstall = true;
}
//}}}
/***
<<showtoc>>
!General
***/
.headerShadow {padding:1em 0em 0.5em 1em;border-bottom:1px solid #666;}
.headerForeground {padding:1em 0em 0.5em 1em;border-bottom:2px solid #AAA;width:100%;}
#mainMenu {width:190px;}
#mainMenu h1 {margin-top:0px;}
#displayArea {margin-left: 230px;margin-top:0px;}
#sidebar {margin-top:5px;border-bottom:none;}
#sidebarTabs {margin-left:5px;margin-top:2px;}
.wideTable{width:95%}
.wideTable td{padding-left:5px;}
.wideTable thead td{padding-left:15px;}
.light {color:#AAA;}
.light .externalLink, .light .tiddlyLink {color:#AAA;}
.light .externalLink:hover, .light .tiddlyLink:hover {background:#CCC;color:#666;}

/***
!DcTableOfContentsPlugin
***/
.dcTOC {
	width:300px;
	text-align:center;
	margin:0px;
}

.dcTOC div {
	padding:3px 0;
	text-align:left;
	background-color:#EEE;
	border:1px solid #CCF;
	border-top:none;
}

.dcTOC ul {
	margin:0px;
	margin-left:20px;
	color: #AAA;
	list-style-type: lower-roman;
	padding-left:5px;
}
.dcTOC ul ul {
	padding-left:1.5em;
	margin-left:0px;
}
.dcTOC li {
	list-style-position:outside;	
}

.dcTOC li a {
	padding:0px 2px;
	color: #333;
}

.dcTOC li a:hover {
        color:black;
	background: #CCC;
}
.dcTOCTop {
	padding:0px 10px;
	font-size:10px;
}

.toggleButton {
	color:#333;
	width:288px;
	font-size:1.2em;
	font-weight:bold;
	padding:2px 5px;
	background:#EEE;
	display:block;
	border:1px solid #CCF;
}
.toggleButton:hover {
	color:#111;
	background:#CCF;
}
#sidebar{right:10px;}
#hoverMenu{right:10px;}
#displayArea {margin-right:16.5em;}
.twtable td,.twtable tr (vertical-align:top;)
<<timeline "modified" "20" "ddd, YYYY-0MM-0DD">>
[[...complete timeline|Timeline]]
/***
|Name:|TagglyTaggingPlugin|
|Description:|tagglyTagging macro is a replacement for the builtin tagging macro in your ViewTemplate|
|Version:|3.3.1 ($Rev: 9828 $)|
|Date:|$Date: 2009-06-03 21:38:41 +1000 (Wed, 03 Jun 2009) $|
|Source:|http://mptw.tiddlyspot.com/#TagglyTaggingPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
!Notes
See http://mptw.tiddlyspot.com/#TagglyTagging
***/
//{{{

merge(String.prototype,{

	parseTagExpr: function(debug) {

		if (this.trim() == "")
			return "(true)";

		var anyLogicOp = /(!|&&|\|\||\(|\))/g;
		var singleLogicOp = /^(!|&&|\|\||\(|\))$/;

		var spaced = this.
			// because square brackets in templates are no good
			// this means you can use [(With Spaces)] instead of [[With Spaces]]
			replace(/\[\(/g," [[").
			replace(/\)\]/g,"]] "). 
			// space things out so we can use readBracketedList. tricky eh?
			replace(anyLogicOp," $1 ");

		var expr = "";

		var tokens = spaced.readBracketedList(false); // false means don't uniq the list. nice one JR!

		for (var i=0;i<tokens.length;i++)
			if (tokens[i].match(singleLogicOp))
				expr += tokens[i];
			else
				expr += "tiddler.tags.contains('%0')".format([tokens[i].replace(/'/,"\\'")]); // fix single quote bug. still have round bracket bug i think

		if (debug)
			alert(expr);

		return '('+expr+')';
	}

});

merge(TiddlyWiki.prototype,{
	getTiddlersByTagExpr: function(tagExpr,sortField) {

		var result = [];

		var expr = tagExpr.parseTagExpr();

		store.forEachTiddler(function(title,tiddler) {
			if (eval(expr))
				result.push(tiddler);
		});

		if(!sortField)
			sortField = "title";

		result.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
		
		return result;
	}
});

config.taggly = {

	// for translations
	lingo: {
		labels: {
			asc:        "\u2191", // down arrow
			desc:       "\u2193", // up arrow
			title:      "title",
			modified:   "modified",
			created:    "created",
			show:       "+",
			hide:       "-",
			normal:     "normal",
			group:      "group",
			commas:     "commas",
			sitemap:    "sitemap",
			numCols:    "cols\u00b1", // plus minus sign
			label:      "Tagged as '%0':",
			exprLabel:  "Matching tag expression '%0':",
			excerpts:   "excerpts",
			descr:      "descr",
			slices:     "slices",
			contents:   "contents",
			sliders:    "sliders",
			noexcerpts: "title only",
			noneFound:  "(none)"
		},

		tooltips: {
			title:      "Click to sort by title",
			modified:   "Click to sort by modified date",
			created:    "Click to sort by created date",
			show:       "Click to show tagging list",
			hide:       "Click to hide tagging list",
			normal:     "Click to show a normal ungrouped list",
			group:      "Click to show list grouped by tag",
			sitemap:    "Click to show a sitemap style list",
			commas:     "Click to show a comma separated list",
			numCols:    "Click to change number of columns",
			excerpts:   "Click to show excerpts",
			descr:      "Click to show the description slice",
			slices:     "Click to show all slices",
			contents:   "Click to show entire tiddler contents",
			sliders:    "Click to show tiddler contents in sliders",
			noexcerpts: "Click to show entire title only"
		},

		tooDeepMessage: "* //sitemap too deep...//"
	},

	config: {
		showTaggingCounts: true,
		listOpts: {
			// the first one will be the default
			sortBy:     ["title","modified","created"],
			sortOrder:  ["asc","desc"],
			hideState:  ["show","hide"],
			listMode:   ["normal","group","sitemap","commas"],
			numCols:    ["1","2","3","4","5","6"],
			excerpts:   ["noexcerpts","excerpts","descr","slices","contents","sliders"]
		},
		valuePrefix: "taggly.",
		excludeTags: ["excludeTagging"],
		excerptSize: 50,
		excerptMarker: "/%"+"%/",
		siteMapDepthLimit: 25
	},

	getTagglyOpt: function(title,opt) {
		var val = store.getValue(title,this.config.valuePrefix+opt);
		return val ? val : this.config.listOpts[opt][0];
	},

	setTagglyOpt: function(title,opt,value) {
		// create it silently if it doesn't exist
		if (!store.tiddlerExists(title)) {
			store.saveTiddler(title,title,config.views.editor.defaultText.format([title]),config.options.txtUserName,new Date(),"");

			// <<tagglyTagging expr:"...">> creates a tiddler to store its display settings
			// Make those tiddlers less noticeable by tagging as excludeSearch and excludeLists
			// Because we don't want to hide real tags, check that they aren't actually tags before doing so
			// Also tag them as tagglyExpression for manageability
			// (contributed by RA)
			if (!store.getTaggedTiddlers(title).length) {
				store.setTiddlerTag(title,true,"excludeSearch");
				store.setTiddlerTag(title,true,"excludeLists");
				store.setTiddlerTag(title,true,"tagglyExpression");
			}
		}

		// if value is default then remove it to save space
		return store.setValue(title, this.config.valuePrefix+opt, value == this.config.listOpts[opt][0] ? null : value);
	},

	getNextValue: function(title,opt) {
		var current = this.getTagglyOpt(title,opt);
		var pos = this.config.listOpts[opt].indexOf(current);
		// supposed to automagically don't let cols cycle up past the number of items
		// currently broken in some situations, eg when using an expression
		// lets fix it later when we rewrite for jquery
		// the columns thing should be jquery table manipulation probably
		var limit = (opt == "numCols" ? store.getTaggedTiddlers(title).length : this.config.listOpts[opt].length);
		var newPos = (pos + 1) % limit;
		return this.config.listOpts[opt][newPos];
	},

	toggleTagglyOpt: function(title,opt) {
		var newVal = this.getNextValue(title,opt);
		this.setTagglyOpt(title,opt,newVal);
	}, 

	createListControl: function(place,title,type) {
		var lingo = config.taggly.lingo;
		var label;
		var tooltip;
		var onclick;

		if ((type == "title" || type == "modified" || type == "created")) {
			// "special" controls. a little tricky. derived from sortOrder and sortBy
			label = lingo.labels[type];
			tooltip = lingo.tooltips[type];

			if (this.getTagglyOpt(title,"sortBy") == type) {
				label += lingo.labels[this.getTagglyOpt(title,"sortOrder")];
				onclick = function() {
					config.taggly.toggleTagglyOpt(title,"sortOrder");
					return false;
				}
			}
			else {
				onclick = function() {
					config.taggly.setTagglyOpt(title,"sortBy",type);
					config.taggly.setTagglyOpt(title,"sortOrder",config.taggly.config.listOpts.sortOrder[0]);
					return false;
				}
			}
		}
		else {
			// "regular" controls, nice and simple
			label = lingo.labels[type == "numCols" ? type : this.getNextValue(title,type)];
			tooltip = lingo.tooltips[type == "numCols" ? type : this.getNextValue(title,type)];
			onclick = function() {
				config.taggly.toggleTagglyOpt(title,type);
				return false;
			}
		}

		// hide button because commas don't have columns
		if (!(this.getTagglyOpt(title,"listMode") == "commas" && type == "numCols"))
			createTiddlyButton(place,label,tooltip,onclick,type == "hideState" ? "hidebutton" : "button");
	},

	makeColumns: function(orig,numCols) {
		var listSize = orig.length;
		var colSize = listSize/numCols;
		var remainder = listSize % numCols;

		var upperColsize = colSize;
		var lowerColsize = colSize;

		if (colSize != Math.floor(colSize)) {
			// it's not an exact fit so..
			upperColsize = Math.floor(colSize) + 1;
			lowerColsize = Math.floor(colSize);
		}

		var output = [];
		var c = 0;
		for (var j=0;j<numCols;j++) {
			var singleCol = [];
			var thisSize = j < remainder ? upperColsize : lowerColsize;
			for (var i=0;i<thisSize;i++) 
				singleCol.push(orig[c++]);
			output.push(singleCol);
		}

		return output;
	},

	drawTable: function(place,columns,theClass) {
		var newTable = createTiddlyElement(place,"table",null,theClass);
		var newTbody = createTiddlyElement(newTable,"tbody");
		var newTr = createTiddlyElement(newTbody,"tr");
		for (var j=0;j<columns.length;j++) {
			var colOutput = "";
			for (var i=0;i<columns[j].length;i++) 
				colOutput += columns[j][i];
			var newTd = createTiddlyElement(newTr,"td",null,"tagglyTagging"); // todo should not need this class
			wikify(colOutput,newTd);
		}
		return newTable;
	},

	createTagglyList: function(place,title,isTagExpr) {
		switch(this.getTagglyOpt(title,"listMode")) {
			case "group":  return this.createTagglyListGrouped(place,title,isTagExpr); break;
			case "normal": return this.createTagglyListNormal(place,title,false,isTagExpr); break;
			case "commas": return this.createTagglyListNormal(place,title,true,isTagExpr); break;
			case "sitemap":return this.createTagglyListSiteMap(place,title,isTagExpr); break;
		}
	},

	getTaggingCount: function(title,isTagExpr) {
		// thanks to Doug Edmunds
		if (this.config.showTaggingCounts) {
			var tagCount = config.taggly.getTiddlers(title,'title',isTagExpr).length;
			if (tagCount > 0)
				return " ("+tagCount+")";
		}
		return "";
	},

	getTiddlers: function(titleOrExpr,sortBy,isTagExpr) {
		return isTagExpr ? store.getTiddlersByTagExpr(titleOrExpr,sortBy) : store.getTaggedTiddlers(titleOrExpr,sortBy);
	},

	getExcerpt: function(inTiddlerTitle,title,indent) {
		if (!indent)
			indent = 1;

		var displayMode = this.getTagglyOpt(inTiddlerTitle,"excerpts");
		var t = store.getTiddler(title);

		if (t && displayMode == "excerpts") {
			var text = t.text.replace(/\n/," ");
			var marker = text.indexOf(this.config.excerptMarker);
			if (marker != -1) {
				return " {{excerpt{<nowiki>" + text.substr(0,marker) + "</nowiki>}}}";
			}
			else if (text.length < this.config.excerptSize) {
				return " {{excerpt{<nowiki>" + t.text + "</nowiki>}}}";
			}
			else {
				return " {{excerpt{<nowiki>" + t.text.substr(0,this.config.excerptSize) + "..." + "</nowiki>}}}";
			}
		}
		else if (t && displayMode == "contents") {
			return "\n{{contents indent"+indent+"{\n" + t.text + "\n}}}";
		}
		else if (t && displayMode == "sliders") {
			return "<slider slide>\n{{contents{\n" + t.text + "\n}}}\n</slider>";
		}
		else if (t && displayMode == "descr") {
			var descr = store.getTiddlerSlice(title,'Description');
			return descr ? " {{excerpt{" + descr  + "}}}" : "";
		}
		else if (t && displayMode == "slices") {
			var result = "";
			var slices = store.calcAllSlices(title);
			for (var s in slices)
				result += "|%0|<nowiki>%1</nowiki>|\n".format([s,slices[s]]);
			return result ? "\n{{excerpt excerptIndent{\n" + result  + "}}}" : "";
		}
		return "";
	},

	notHidden: function(t,inTiddler) {
		if (typeof t == "string") 
			t = store.getTiddler(t);
		return (!t || !t.tags.containsAny(this.config.excludeTags) ||
				(inTiddler && this.config.excludeTags.contains(inTiddler)));
	},

	// this is for normal and commas mode
	createTagglyListNormal: function(place,title,useCommas,isTagExpr) {

		var list = config.taggly.getTiddlers(title,this.getTagglyOpt(title,"sortBy"),isTagExpr);

		if (this.getTagglyOpt(title,"sortOrder") == "desc")
			list = list.reverse();

		var output = [];
		var first = true;
		for (var i=0;i<list.length;i++) {
			if (this.notHidden(list[i],title)) {
				var countString = this.getTaggingCount(list[i].title);
				var excerpt = this.getExcerpt(title,list[i].title);
				if (useCommas)
					output.push((first ? "" : ", ") + "[[" + list[i].title + "]]" + countString + excerpt);
				else
					output.push("*[[" + list[i].title + "]]" + countString + excerpt + "\n");

				first = false;
			}
		}

		return this.drawTable(place,
			this.makeColumns(output,useCommas ? 1 : parseInt(this.getTagglyOpt(title,"numCols"))),
			useCommas ? "commas" : "normal");
	},

	// this is for the "grouped" mode
	createTagglyListGrouped: function(place,title,isTagExpr) {
		var sortBy = this.getTagglyOpt(title,"sortBy");
		var sortOrder = this.getTagglyOpt(title,"sortOrder");

		var list = config.taggly.getTiddlers(title,sortBy,isTagExpr);

		if (sortOrder == "desc")
			list = list.reverse();

		var leftOvers = []
		for (var i=0;i<list.length;i++)
			leftOvers.push(list[i].title);

		var allTagsHolder = {};
		for (var i=0;i<list.length;i++) {
			for (var j=0;j<list[i].tags.length;j++) {

				if (list[i].tags[j] != title) { // not this tiddler

					if (this.notHidden(list[i].tags[j],title)) {

						if (!allTagsHolder[list[i].tags[j]])
							allTagsHolder[list[i].tags[j]] = "";

						if (this.notHidden(list[i],title)) {
							allTagsHolder[list[i].tags[j]] += "**[["+list[i].title+"]]"
										+ this.getTaggingCount(list[i].title) + this.getExcerpt(title,list[i].title) + "\n";

							leftOvers.setItem(list[i].title,-1); // remove from leftovers. at the end it will contain the leftovers

						}
					}
				}
			}
		}

		var allTags = [];
		for (var t in allTagsHolder)
			allTags.push(t);

		var sortHelper = function(a,b) {
			if (a == b) return 0;
			if (a < b) return -1;
			return 1;
		};

		allTags.sort(function(a,b) {
			var tidA = store.getTiddler(a);
			var tidB = store.getTiddler(b);
			if (sortBy == "title") return sortHelper(a,b);
			else if (!tidA && !tidB) return 0;
			else if (!tidA) return -1;
			else if (!tidB) return +1;
			else return sortHelper(tidA[sortBy],tidB[sortBy]);
		});

		var leftOverOutput = "";
		for (var i=0;i<leftOvers.length;i++)
			if (this.notHidden(leftOvers[i],title))
				leftOverOutput += "*[["+leftOvers[i]+"]]" + this.getTaggingCount(leftOvers[i]) + this.getExcerpt(title,leftOvers[i]) + "\n";

		var output = [];

		if (sortOrder == "desc")
			allTags.reverse();
		else if (leftOverOutput != "")
			// leftovers first...
			output.push(leftOverOutput);

		for (var i=0;i<allTags.length;i++)
			if (allTagsHolder[allTags[i]] != "")
				output.push("*[["+allTags[i]+"]]" + this.getTaggingCount(allTags[i]) + this.getExcerpt(title,allTags[i]) + "\n" + allTagsHolder[allTags[i]]);

		if (sortOrder == "desc" && leftOverOutput != "")
			// leftovers last...
			output.push(leftOverOutput);

		return this.drawTable(place,
				this.makeColumns(output,parseInt(this.getTagglyOpt(title,"numCols"))),
				"grouped");

	},

	// used to build site map
	treeTraverse: function(title,depth,sortBy,sortOrder,isTagExpr) {

		var list = config.taggly.getTiddlers(title,sortBy,isTagExpr);

		if (sortOrder == "desc")
			list.reverse();

		var indent = "";
		for (var j=0;j<depth;j++)
			indent += "*"

		var childOutput = "";

		if (depth > this.config.siteMapDepthLimit)
			childOutput += indent + this.lingo.tooDeepMessage;
		else
			for (var i=0;i<list.length;i++)
				if (list[i].title != title)
					if (this.notHidden(list[i].title,this.config.inTiddler))
						childOutput += this.treeTraverse(list[i].title,depth+1,sortBy,sortOrder,false);

		if (depth == 0)
			return childOutput;
		else
			return indent + "[["+title+"]]" + this.getTaggingCount(title) + this.getExcerpt(this.config.inTiddler,title,depth) + "\n" + childOutput;
	},

	// this if for the site map mode
	createTagglyListSiteMap: function(place,title,isTagExpr) {
		this.config.inTiddler = title; // nasty. should pass it in to traverse probably
		var output = this.treeTraverse(title,0,this.getTagglyOpt(title,"sortBy"),this.getTagglyOpt(title,"sortOrder"),isTagExpr);
		return this.drawTable(place,
				this.makeColumns(output.split(/(?=^\*\[)/m),parseInt(this.getTagglyOpt(title,"numCols"))), // regexp magic
				"sitemap"
				);
	},

	macros: {
		tagglyTagging: {
			handler: function (place,macroName,params,wikifier,paramString,tiddler) {
				var parsedParams = paramString.parseParams("tag",null,true);
				var refreshContainer = createTiddlyElement(place,"div");

				// do some refresh magic to make it keep the list fresh - thanks Saq
				refreshContainer.setAttribute("refresh","macro");
				refreshContainer.setAttribute("macroName",macroName);

				var tag = getParam(parsedParams,"tag");
				var expr = getParam(parsedParams,"expr");

				if (expr) {
					refreshContainer.setAttribute("isTagExpr","true");
					refreshContainer.setAttribute("title",expr);
					refreshContainer.setAttribute("showEmpty","true");
				}
				else {
					refreshContainer.setAttribute("isTagExpr","false");
					if (tag) {
        				refreshContainer.setAttribute("title",tag);
						refreshContainer.setAttribute("showEmpty","true");
					}
					else {
        				refreshContainer.setAttribute("title",tiddler.title);
						refreshContainer.setAttribute("showEmpty","false");
					}
				}
				this.refresh(refreshContainer);
			},

			refresh: function(place) {
				var title = place.getAttribute("title");
				var isTagExpr = place.getAttribute("isTagExpr") == "true";
				var showEmpty = place.getAttribute("showEmpty") == "true";
				removeChildren(place);
				addClass(place,"tagglyTagging");
				var countFound = config.taggly.getTiddlers(title,'title',isTagExpr).length
				if (countFound > 0 || showEmpty) {
					var lingo = config.taggly.lingo;
					config.taggly.createListControl(place,title,"hideState");
					if (config.taggly.getTagglyOpt(title,"hideState") == "show") {
						createTiddlyElement(place,"span",null,"tagglyLabel",
								isTagExpr ? lingo.labels.exprLabel.format([title]) : lingo.labels.label.format([title]));
						config.taggly.createListControl(place,title,"title");
						config.taggly.createListControl(place,title,"modified");
						config.taggly.createListControl(place,title,"created");
						config.taggly.createListControl(place,title,"listMode");
						config.taggly.createListControl(place,title,"excerpts");
						config.taggly.createListControl(place,title,"numCols");
						config.taggly.createTagglyList(place,title,isTagExpr);
						if (countFound == 0 && showEmpty)
							createTiddlyElement(place,"div",null,"tagglyNoneFound",lingo.labels.noneFound);
					}
				}
			}
		}
	},

	// todo fix these up a bit
	styles: [
"/*{{{*/",
"/* created by TagglyTaggingPlugin */",
".tagglyTagging { padding-top:0.5em; }",
".tagglyTagging li.listTitle { display:none; }",
".tagglyTagging ul {",
"	margin-top:0px; padding-top:0.5em; padding-left:2em;",
"	margin-bottom:0px; padding-bottom:0px;",
"}",
".tagglyTagging { vertical-align: top; margin:0px; padding:0px; }",
".tagglyTagging table { margin:0px; padding:0px; }",
".tagglyTagging .button { visibility:hidden; margin-left:3px; margin-right:3px; }",
".tagglyTagging .button, .tagglyTagging .hidebutton {",
"	color:[[ColorPalette::TertiaryLight]]; font-size:90%;",
"	border:0px; padding-left:0.3em;padding-right:0.3em;",
"}",
".tagglyTagging .button:hover, .hidebutton:hover, ",
".tagglyTagging .button:active, .hidebutton:active  {",
"	border:0px; background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]];",
"}",
".selected .tagglyTagging .button { visibility:visible; }",
".tagglyTagging .hidebutton { color:[[ColorPalette::Background]]; }",
".selected .tagglyTagging .hidebutton { color:[[ColorPalette::TertiaryLight]] }",
".tagglyLabel { color:[[ColorPalette::TertiaryMid]]; font-size:90%; }",
".tagglyTagging ul {padding-top:0px; padding-bottom:0.5em; margin-left:1em; }",
".tagglyTagging ul ul {list-style-type:disc; margin-left:-1em;}",
".tagglyTagging ul ul li {margin-left:0.5em; }",
".editLabel { font-size:90%; padding-top:0.5em; }",
".tagglyTagging .commas { padding-left:1.8em; }",
"/* not technically tagglytagging but will put them here anyway */",
".tagglyTagged {margin-bottom:5px;}",
".tagglyTagged li.listTitle { display:none; }",
".tagglyTagged li { display: inline; font-size:90%; }",
".tagglyTagged ul { margin:0px; padding:0px; }",
".excerpt { color:[[ColorPalette::TertiaryDark]]; }",
".excerptIndent { margin-left:4em; }",
"div.tagglyTagging table,",
"div.tagglyTagging table tr,",
"td.tagglyTagging",
" {border-style:none!important; }",
".tagglyTagging .contents { border-bottom:2px solid [[ColorPalette::TertiaryPale]]; padding:0 1em 1em 0.5em;",
"  margin-bottom:0.5em; }",
".tagglyTagging .indent1  { margin-left:3em;  }",
".tagglyTagging .indent2  { margin-left:4em;  }",
".tagglyTagging .indent3  { margin-left:5em;  }",
".tagglyTagging .indent4  { margin-left:6em;  }",
".tagglyTagging .indent5  { margin-left:7em;  }",
".tagglyTagging .indent6  { margin-left:8em;  }",
".tagglyTagging .indent7  { margin-left:9em;  }",
".tagglyTagging .indent8  { margin-left:10em; }",
".tagglyTagging .indent9  { margin-left:11em; }",
".tagglyTagging .indent10 { margin-left:12em; }",
".tagglyNoneFound { margin-left:2em; color:[[ColorPalette::TertiaryMid]]; font-size:90%; font-style:italic; }",
"/*}}}*/",
		""].join("\n"),

	init: function() {
		merge(config.macros,this.macros);
		config.shadowTiddlers["TagglyTaggingStyles"] = this.styles;
		store.addNotification("TagglyTaggingStyles",refreshStyles);
	}
};

config.taggly.init();

//}}}

/***
InlineSlidersPlugin
By Saq Imtiaz
http://tw.lewcid.org/sandbox/#InlineSlidersPlugin

// syntax adjusted to not clash with NestedSlidersPlugin
// added + syntax to start open instead of closed

***/
//{{{
config.formatters.unshift( {
	name: "inlinesliders",
	// match: "\\+\\+\\+\\+|\\<slider",
	match: "\\<slider",
	// lookaheadRegExp: /(?:\+\+\+\+|<slider) (.*?)(?:>?)\n((?:.|\n)*?)\n(?:====|<\/slider>)/mg,
	lookaheadRegExp: /(?:<slider)(\+?) (.*?)(?:>)\n((?:.|\n)*?)\n(?:<\/slider>)/mg,
	handler: function(w) {
		this.lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart ) {
			var btn = createTiddlyButton(w.output,lookaheadMatch[2] + " "+"\u00BB",lookaheadMatch[2],this.onClickSlider,"button sliderButton");
			var panel = createTiddlyElement(w.output,"div",null,"sliderPanel");
			panel.style.display = (lookaheadMatch[1] == '+' ? "block" : "none");
			wikify(lookaheadMatch[3],panel);
			w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
		}
   },
   onClickSlider : function(e) {
		if(!e) var e = window.event;
		var n = this.nextSibling;
		n.style.display = (n.style.display=="none") ? "block" : "none";
		return false;
	}
});

//}}}
[[TidScript]] is the name I give to tiddlers that contain javascript (e.g. invoked via forms) which in their basic functionality work independent of external plugins.
/***
|''Name:''|TiddlerListMacro|
|''Version:''|2.3 (8-Jan-2008)|
|''Source''|http://jackparke.googlepages.com/jtw.html#TiddlerListMacro ([[del.icio.us|http://del.icio.us/post?url=http://jackparke.googlepages.com/jtw.html%23TiddlerListMacro]])|
|''Author:''|[[Jack]]|
|''Type:''|Macro|
|''Documentation:''|[[TiddlerListMacroDocumentation]]|
!Usage
{{{<<tiddlerList parameter1:"value1" parameter2:"value2" ...>>}}}
See TiddlerListMacroDocumentation and TiddlerListMacroExamples
!Code
***/
//{{{
version.extensions.tiddlerList = {major: 2, minor: 3, revision: 0, date: new Date("Jan 08, 2008")};
// template = [header, item, separator, group, footer]
config.macros.tiddlerList={
 formats : {list:true, nlist:true, span:true, stack:true, csv:true, table:true},
 templates : {
 list : [ "%0\n", "* %0\n", "", "%group\n", "%0\n"],
 nlist : [ "%0", "# %0\n", "", "%group\n", "%0\n"],
 span : [ "%0", "%0", " ", "%group", "%0"],
 stack : [ "%0", "%0", "\n", "%group", "%0"],
 csv : [ "%0", "%0", ", ", "%0", "%0\n"],
 table : ["|!%0|\n", "|%0|\n", "", "|%group|\n", "|%0|\n"]
 },
 dateFormat : "DD MMM YYYY"
}

if (typeof gCurrentTiddler == 'undefined')
 var gCurrentTiddler;

config.macros.tiddlerList.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
 // Some globals
 var count=0, groupCount=0, theGroup="", lastGroup="", firstInGroup = false;
 var currentTiddler = tiddler;
 gCurrentTiddler = tiddler;
 var listWikiText="";
 var formats = this.formats;
 
 // SQL-Like parameters
 var parameters = paramString.parseParams("name",null,true);
 var pTags = parameters[0]["tags"]?parameters[0]["tags"][0].split(","):[];
 var pOrder = parameters[0]["order"]?parameters[0]["order"][0]:"title";
 var pTop = parameters[0]["top"]?parameters[0]["top"][0]:-1;
 var pText = parameters[0]["text"]?parameters[0]["text"][0]:"";
 var pTitle = parameters[0]["title"]?parameters[0]["title"][0]:"";
 var pSearch = parameters[0]["search"]?parameters[0]["search"][0]:"";
 var pFilter = parameters[0]["filter"]?parameters[0]["filter"][0]:"";
 var pHeader = parameters[0]["header"]?paramFormat(parameters[0]["header"][0]):"";
 var pFooter = parameters[0]["footer"]?paramFormat(parameters[0]["footer"][0]):"";
 var pGroup = parameters[0]["group"]?parameters[0]["group"][0]:"";
 var pDateFormat = parameters[0]["dateFormat"]?parameters[0]["dateFormat"][0]:this.dateFormat;
 var pCustomParameter = parameters[0]["customParameter"]?parameters[0]["customParameter"][0]:"";
 var pFormat = parameters[0]["format"]?parameters[0]["format"][0]:"list";
 pFormat = formats[pFormat]?pFormat:"list"
 
 // Separator
 var pSeparator = parameters[0]["separator"]?paramFormat(parameters[0]["separator"][0]):(parameters[0]["seperator"]?paramFormat(parameters[0]["seperator"][0]):this.templates[pFormat][2])

 // Template for group
 var pGroupTemplate = this.templates[pFormat][3];
 if (parameters[0]["groupTemplate"])
 pGroupTemplate = paramFormat(parameters[0]["groupTemplate"][0])
 pGroupTemplate = pGroupTemplate.replace("$))", ">>")
 
 // Template for group footer
 var pGroupFooterTemplate = "";
 if (parameters[0]["groupFooterTemplate"])
 pGroupFooterTemplate = paramFormat(parameters[0]["groupFooterTemplate"][0])
 pGroupFooterTemplate = pGroupFooterTemplate.replace("$))", ">>")
 
 // Template for item
 var pItemTemplate = this.templates[pFormat][1];
 if (parameters[0]["itemTemplate"])
 pItemTemplate = paramFormat(parameters[0]["itemTemplate"][0])
 pItemTemplate = pItemTemplate.replace("$))", ">>").replace("%link", "%0").replace("%item", "%1").replace("%abstract", "%2").replace("%text", "%3").replace("%created", "%4").replace("%modified", "%5").replace("%modifier", "%6").replace("%group", "%7").replace("%title", "%8").replace("%tags", "%9").replace("%nolink", "%10").replace("%custom", "%11")
 // Template for footer
 var pFooterTemplate = this.templates[pFormat][4].replace("%count", "%1")

 // Get all tiddlers
 var tiddlers = store.reverseLookup("tags","",false);

 // Sorting
 if(!pOrder)
 pOrder = "title";
 if (pOrder.match(/^\-/i)) {
 pOrder = pOrder.substr(1)
 var sortDesc = true;
 }
 // Sorting on a standard field
 if (pOrder.match(/(title)|(text)|(modifier)|(modified)|(created)|(tags)/))
  if (sortDesc)
   tiddlers.sort(function (a,b) {if(a[pOrder] == b[pOrder]) return(0); else return (a[pOrder] > b[pOrder]) ? -1 : +1; });
  else
  tiddlers.sort(function (a,b) {if(a[pOrder] == b[pOrder]) return(0); else return (a[pOrder] < b[pOrder]) ? -1 : +1; });
 else
  if (sortDesc)
   tiddlers.sort(function (a,b) {if(a.fields[pOrder] == b.fields[pOrder]) return(0); else return (a.fields[pOrder] > b.fields[pOrder]) ? -1 : +1; });
  else
   tiddlers.sort(function (a,b) {if(a.fields[pOrder] == b.fields[pOrder]) return(0); else return (a.fields[pOrder] < b.fields[pOrder]) ? -1 : +1; });

 // Header
 if (pHeader)
 listWikiText += formatItem(this.templates[pFormat][0], [pHeader], pFormat)
 
 for(var t=0; t<tiddlers.length; t++) {
 tiddler = tiddlers[t];
 if (pText!="" && tiddler.text=="") tiddler.text=store.getValue(tiddler, 'text')
 if (pTop==-1 || count<pTop) {
 if (pText=="" || tiddler.text.match(pText)) {
 if (pTitle=="" || tiddler.title.match(pTitle)) {
 if (pSearch=="" || (tiddler.title.match(pSearch) || tiddler.text.match(pSearch))) {
 if (pFilter=="" || eval(pFilter)) {
 if (pTags.length==0 || compareArrays(tiddler.tags, pTags, "all")) {
 count++;
 if (tiddler.text=="") tiddler.text=store.getValue(tiddler, 'text')
 // Grouping
 if (pGroup) {
 theGroup = eval(pGroup);
 if(theGroup != lastGroup) {
 groupCount++;firstInGroup = true;
 if (pGroupFooterTemplate && groupCount>1)
 listWikiText += pGroupFooterTemplate.replace("%group", theGroup)
 listWikiText += pGroupTemplate.replace("%group", theGroup)
 lastGroup = theGroup;
 } else
  firstInGroup = false;
 }
 // Separators
 if (count>1 && !firstInGroup) listWikiText += pSeparator;
 //Plaintext title
 var noLink = tiddler.title.match(config.textPrimitives.wikiLink)?"~" + tiddler.title:tiddler.title;
 // Custom parameter
 if (pCustomParameter)
 var custom="";
 try {
 custom = eval(pCustomParameter)
 } catch (e) {}
 // List individual tiddler
 var strItem = formatItem(pItemTemplate,["[[" + tiddler.title + "]]",count,tiddler.text.substr(0,300),tiddler.text,tiddler.created.formatString(pDateFormat),tiddler.modified.formatString(pDateFormat),tiddler.modifier,theGroup,tiddler.title,tiddler.tags.join(" "),noLink,custom], pFormat)
 for (var fld in tiddler.fields) strItem = strItem.replace('%field.' + fld, tiddler.fields[fld]);
 listWikiText += strItem
 }
 }
 }
 }
 }
 }
 }
 
 // Last group footer
 if (pGroup && pGroupFooterTemplate && count>0)
 listWikiText += pGroupFooterTemplate.replace("%group", theGroup)

 // Footer
 if (pFooter) {
 pFooter = pFooter.replace("%count", count)
 listWikiText += formatItem(pFooterTemplate, [pFooter], pFormat)
 }
 
 // Render result
 if (!parameters[0]["debug"])
 wikify(listWikiText,place, null, currentTiddler)
 else
 place.innerHTML = "<textarea style=\"width:100%;\" rows=30>" + listWikiText + "</textarea>"
 
 
 // Local functions
 
 function paramFormat(param) {
 // Allow "\n" in non evalled parameters
 return param.replace(/\\n/g, "\n");
 }
 
 function formatItem(template, values, format) {
 // Fill template with values (depending on list format)
 if (format.match(/table/) && values[0].match(/\|/))
 return ("%0\n").format(values)
 else
 return template.format(values)
 }
 
 function compareArrays(array, values, logic) {
 // Compare items in array with AND("all") or OR("any") logic
 var matches=0;
 for(var v=0; v<values.length; v++) 
 if(values[v].replace(/^\s+|\s+$/g,"").match(/^\-/) && !array.contains(values[v].replace(/^\s+|\s+$/g,"").substr(1)))
 matches++;
 else if (array.contains(values[v]))
 matches++;
 return ((logic=="all" && matches==values.length) || (logic!="all" && matches>0))
 }
 
}

String.prototype.prettyTrim = function(len,prefix,postfix) {
 var result = this.trim().replace(/\r\n/g,' ').replace(/\n/g,' ');
 if (!prefix) prefix = '';
 if (!postfix) postfix = '';
 if (result.length > len - 3)
 return prefix + result.substr(0,len) + '...' + postfix;
 else if (result.length > 0)
 return prefix + result + postfix;
 else
 return result;
}

//}}}
|wideTable|k
|by modified |by created|h
|<<timeline "modified">>|<<timeline "created">>|
The guy who also designed http://tbGTD.tiddlyspot.com
@@display:block;float:left;width:270px;background:#EEE;border:1px solid #CCC;padding:0em 1em;
Some of my other ''ideas'' hosted on [[tiddlyspot|http:/www.tiddlyspot.com]]:
*[[tbGTD|http://tbgtd.tiddlyspot.com]] [[...there also is a contact-form!|http://tbgtd.tiddlyspot.com]]
*[[widgets|http://widgets.tiddlyspot.com]]

...wouldn't really want to call them projects :o)

*The [[macro code|lastfmMacro]] is compressed using:
**[[javascriptcompressor.com|http://javascriptcompressor.com/]]

@@
/***
Description: Contains the stuff you need to use Tiddlyspot
Note, you also need UploadPlugin, PasswordOptionPlugin and LoadRemoteFileThroughProxy
from http://tiddlywiki.bidix.info for a complete working Tiddlyspot site.
***/
//{{{

// edit this if you are migrating sites or retrofitting an existing TW
config.tiddlyspotSiteId = 'lastfm';

// make it so you can by default see edit controls via http
config.options.chkHttpReadOnly = false;
window.readOnly = false; // make sure of it (for tw 2.2)
window.showBackstage = true; // show backstage too

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

// tweak shadow tiddlers to add upload button, password entry box etc
with (config.shadowTiddlers) {
	SiteUrl = 'http://'+config.tiddlyspotSiteId+'.tiddlyspot.com';
	SideBarOptions = SideBarOptions.replace(/(<<saveChanges>>)/,"$1<<tiddler TspotSidebar>>");
	OptionsPanel = OptionsPanel.replace(/^/,"<<tiddler TspotOptions>>");
	DefaultTiddlers = DefaultTiddlers.replace(/^/,"[[WelcomeToTiddlyspot]] ");
	MainMenu = MainMenu.replace(/^/,"[[WelcomeToTiddlyspot]] ");
}

// create some shadow tiddler content
merge(config.shadowTiddlers,{

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

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

'TspotSidebar':[
 "<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . .  " + config.tiddlyspotSiteId + ">><html><a href='http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download' class='button'>download</a></html>"
].join("\n"),

'TspotOptions':[
 "tiddlyspot password:",
 "<<option pasUploadPassword>>",
 ""
].join("\n")

});
//}}}
| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |
| 07/11/2009 21:30:38 | Tobias | [[/|http://lastfm.tiddlyspot.com/]] | [[store.cgi|http://lastfm.tiddlyspot.com/store.cgi]] | . | [[index.html | http://lastfm.tiddlyspot.com/index.html]] | . |
| 07/11/2009 21:35:17 | Tobias | [[/|http://lastfm.tiddlyspot.com/]] | [[store.cgi|http://lastfm.tiddlyspot.com/store.cgi]] | . | [[index.html | http://lastfm.tiddlyspot.com/index.html]] | . |
| 16/11/2009 13:18:16 | Tobias | [[/|http://lastfm.tiddlyspot.com/]] | [[store.cgi|http://lastfm.tiddlyspot.com/store.cgi]] | . | [[index.html | http://lastfm.tiddlyspot.com/index.html]] | . | ok |
| 16/11/2009 13:20:30 | Tobias | [[/|http://lastfm.tiddlyspot.com/]] | [[store.cgi|http://lastfm.tiddlyspot.com/store.cgi]] | . | [[index.html | http://lastfm.tiddlyspot.com/index.html]] | . | ok |
| 16/11/2009 13:32:43 | Tobias | [[/|http://lastfm.tiddlyspot.com/#]] | [[store.cgi|http://lastfm.tiddlyspot.com/store.cgi]] | . | [[index.html | http://lastfm.tiddlyspot.com/index.html]] | . |
| 11/03/2013 19:22:43 | YourName | [[/|http://lastfm.tiddlyspot.com/#RatingMacro]] | [[store.cgi|http://lastfm.tiddlyspot.com/store.cgi]] | . | [[index.html | http://lastfm.tiddlyspot.com/index.html]] | . |
| 11/03/2013 19:24:48 | YourName | [[/|http://lastfm.tiddlyspot.com/#RatingMacro]] | [[store.cgi|http://lastfm.tiddlyspot.com/store.cgi]] | . | [[index.html | http://lastfm.tiddlyspot.com/index.html]] | . |
| 11/03/2013 19:49:03 | YourName | [[/|http://lastfm.tiddlyspot.com/#RatingMacro]] | [[store.cgi|http://lastfm.tiddlyspot.com/store.cgi]] | . | [[index.html | http://lastfm.tiddlyspot.com/index.html]] | . | ok |
| 11/03/2013 19:49:29 | YourName | [[/|http://lastfm.tiddlyspot.com/#RatingMacro]] | [[store.cgi|http://lastfm.tiddlyspot.com/store.cgi]] | . | [[index.html | http://lastfm.tiddlyspot.com/index.html]] | . | ok |
| 11/03/2013 19:50:19 | YourName | [[/|http://lastfm.tiddlyspot.com/#RatingMacro]] | [[store.cgi|http://lastfm.tiddlyspot.com/store.cgi]] | . | [[index.html | http://lastfm.tiddlyspot.com/index.html]] | . |
/***
|''Name:''|UploadPlugin|
|''Description:''|Save to web a TiddlyWiki|
|''Version:''|4.1.3|
|''Date:''|Feb 24, 2008|
|''Source:''|http://tiddlywiki.bidix.info/#UploadPlugin|
|''Documentation:''|http://tiddlywiki.bidix.info/#UploadPluginDoc|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0|
|''Requires:''|PasswordOptionPlugin|
***/
//{{{
version.extensions.UploadPlugin = {
	major: 4, minor: 1, revision: 3,
	date: new Date("Feb 24, 2008"),
	source: 'http://tiddlywiki.bidix.info/#UploadPlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	coreVersion: '2.2.0'
};

//
// Environment
//

if (!window.bidix) window.bidix = {}; // bidix namespace
bidix.debugMode = false;	// true to activate both in Plugin and UploadService
	
//
// Upload Macro
//

config.macros.upload = {
// default values
	defaultBackupDir: '',	//no backup
	defaultStoreScript: "store.php",
	defaultToFilename: "index.html",
	defaultUploadDir: ".",
	authenticateUser: true	// UploadService Authenticate User
};
	
config.macros.upload.label = {
	promptOption: "Save and Upload this TiddlyWiki with UploadOptions",
	promptParamMacro: "Save and Upload this TiddlyWiki in %0",
	saveLabel: "save to web", 
	saveToDisk: "save to disk",
	uploadLabel: "upload"	
};

config.macros.upload.messages = {
	noStoreUrl: "No store URL in parmeters or options",
	usernameOrPasswordMissing: "Username or password missing"
};

config.macros.upload.handler = function(place,macroName,params) {
	if (readOnly)
		return;
	var label;
	if (document.location.toString().substr(0,4) == "http") 
		label = this.label.saveLabel;
	else
		label = this.label.uploadLabel;
	var prompt;
	if (params[0]) {
		prompt = this.label.promptParamMacro.toString().format([this.destFile(params[0], 
			(params[1] ? params[1]:bidix.basename(window.location.toString())), params[3])]);
	} else {
		prompt = this.label.promptOption;
	}
	createTiddlyButton(place, label, prompt, function() {config.macros.upload.action(params);}, null, null, this.accessKey);
};

config.macros.upload.action = function(params)
{
		// for missing macro parameter set value from options
		if (!params) params = {};
		var storeUrl = params[0] ? params[0] : config.options.txtUploadStoreUrl;
		var toFilename = params[1] ? params[1] : config.options.txtUploadFilename;
		var backupDir = params[2] ? params[2] : config.options.txtUploadBackupDir;
		var uploadDir = params[3] ? params[3] : config.options.txtUploadDir;
		var username = params[4] ? params[4] : config.options.txtUploadUserName;
		var password = config.options.pasUploadPassword; // for security reason no password as macro parameter	
		// for still missing parameter set default value
		if ((!storeUrl) && (document.location.toString().substr(0,4) == "http")) 
			storeUrl = bidix.dirname(document.location.toString())+'/'+config.macros.upload.defaultStoreScript;
		if (storeUrl.substr(0,4) != "http")
			storeUrl = bidix.dirname(document.location.toString()) +'/'+ storeUrl;
		if (!toFilename)
			toFilename = bidix.basename(window.location.toString());
		if (!toFilename)
			toFilename = config.macros.upload.defaultToFilename;
		if (!uploadDir)
			uploadDir = config.macros.upload.defaultUploadDir;
		if (!backupDir)
			backupDir = config.macros.upload.defaultBackupDir;
		// report error if still missing
		if (!storeUrl) {
			alert(config.macros.upload.messages.noStoreUrl);
			clearMessage();
			return false;
		}
		if (config.macros.upload.authenticateUser && (!username || !password)) {
			alert(config.macros.upload.messages.usernameOrPasswordMissing);
			clearMessage();
			return false;
		}
		bidix.upload.uploadChanges(false,null,storeUrl, toFilename, uploadDir, backupDir, username, password); 
		return false; 
};

config.macros.upload.destFile = function(storeUrl, toFilename, uploadDir) 
{
	if (!storeUrl)
		return null;
		var dest = bidix.dirname(storeUrl);
		if (uploadDir && uploadDir != '.')
			dest = dest + '/' + uploadDir;
		dest = dest + '/' + toFilename;
	return dest;
};

//
// uploadOptions Macro
//

config.macros.uploadOptions = {
	handler: function(place,macroName,params) {
		var wizard = new Wizard();
		wizard.createWizard(place,this.wizardTitle);
		wizard.addStep(this.step1Title,this.step1Html);
		var markList = wizard.getElement("markList");
		var listWrapper = document.createElement("div");
		markList.parentNode.insertBefore(listWrapper,markList);
		wizard.setValue("listWrapper",listWrapper);
		this.refreshOptions(listWrapper,false);
		var uploadCaption;
		if (document.location.toString().substr(0,4) == "http") 
			uploadCaption = config.macros.upload.label.saveLabel;
		else
			uploadCaption = config.macros.upload.label.uploadLabel;
		
		wizard.setButtons([
				{caption: uploadCaption, tooltip: config.macros.upload.label.promptOption, 
					onClick: config.macros.upload.action},
				{caption: this.cancelButton, tooltip: this.cancelButtonPrompt, onClick: this.onCancel}
				
			]);
	},
	options: [
		"txtUploadUserName",
		"pasUploadPassword",
		"txtUploadStoreUrl",
		"txtUploadDir",
		"txtUploadFilename",
		"txtUploadBackupDir",
		"chkUploadLog",
		"txtUploadLogMaxLine"		
	],
	refreshOptions: function(listWrapper) {
		var opts = [];
		for(i=0; i<this.options.length; i++) {
			var opt = {};
			opts.push();
			opt.option = "";
			n = this.options[i];
			opt.name = n;
			opt.lowlight = !config.optionsDesc[n];
			opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
			opts.push(opt);
		}
		var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
		for(n=0; n<opts.length; n++) {
			var type = opts[n].name.substr(0,3);
			var h = config.macros.option.types[type];
			if (h && h.create) {
				h.create(opts[n].colElements['option'],type,opts[n].name,opts[n].name,"no");
			}
		}
		
	},
	onCancel: function(e)
	{
		backstage.switchTab(null);
		return false;
	},
	
	wizardTitle: "Upload with options",
	step1Title: "These options are saved in cookies in your browser",
	step1Html: "<input type='hidden' name='markList'></input><br>",
	cancelButton: "Cancel",
	cancelButtonPrompt: "Cancel prompt",
	listViewTemplate: {
		columns: [
			{name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
			{name: 'Option', field: 'option', title: "Option", type: 'String'},
			{name: 'Name', field: 'name', title: "Name", type: 'String'}
			],
		rowClasses: [
			{className: 'lowlight', field: 'lowlight'} 
			]}
};

//
// upload functions
//

if (!bidix.upload) bidix.upload = {};

if (!bidix.upload.messages) bidix.upload.messages = {
	//from saving
	invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
	backupSaved: "Backup saved",
	backupFailed: "Failed to upload backup file",
	rssSaved: "RSS feed uploaded",
	rssFailed: "Failed to upload RSS feed file",
	emptySaved: "Empty template uploaded",
	emptyFailed: "Failed to upload empty template file",
	mainSaved: "Main TiddlyWiki file uploaded",
	mainFailed: "Failed to upload main TiddlyWiki file. Your changes have not been saved",
	//specific upload
	loadOriginalHttpPostError: "Can't get original file",
	aboutToSaveOnHttpPost: 'About to upload on %0 ...',
	storePhpNotFound: "The store script '%0' was not found."
};

bidix.upload.uploadChanges = function(onlyIfDirty,tiddlers,storeUrl,toFilename,uploadDir,backupDir,username,password)
{
	var callback = function(status,uploadParams,original,url,xhr) {
		if (!status) {
			displayMessage(bidix.upload.messages.loadOriginalHttpPostError);
			return;
		}
		if (bidix.debugMode) 
			alert(original.substr(0,500)+"\n...");
		// Locate the storeArea div's 
		var posDiv = locateStoreArea(original);
		if((posDiv[0] == -1) || (posDiv[1] == -1)) {
			alert(config.messages.invalidFileError.format([localPath]));
			return;
		}
		bidix.upload.uploadRss(uploadParams,original,posDiv);
	};
	
	if(onlyIfDirty && !store.isDirty())
		return;
	clearMessage();
	// save on localdisk ?
	if (document.location.toString().substr(0,4) == "file") {
		var path = document.location.toString();
		var localPath = getLocalPath(path);
		saveChanges();
	}
	// get original
	var uploadParams = new Array(storeUrl,toFilename,uploadDir,backupDir,username,password);
	var originalPath = document.location.toString();
	// If url is a directory : add index.html
	if (originalPath.charAt(originalPath.length-1) == "/")
		originalPath = originalPath + "index.html";
	var dest = config.macros.upload.destFile(storeUrl,toFilename,uploadDir);
	var log = new bidix.UploadLog();
	log.startUpload(storeUrl, dest, uploadDir,  backupDir);
	displayMessage(bidix.upload.messages.aboutToSaveOnHttpPost.format([dest]));
	if (bidix.debugMode) 
		alert("about to execute Http - GET on "+originalPath);
	var r = doHttp("GET",originalPath,null,null,username,password,callback,uploadParams,null);
	if (typeof r == "string")
		displayMessage(r);
	return r;
};

bidix.upload.uploadRss = function(uploadParams,original,posDiv) 
{
	var callback = function(status,params,responseText,url,xhr) {
		if(status) {
			var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
			displayMessage(bidix.upload.messages.rssSaved,bidix.dirname(url)+'/'+destfile);
			bidix.upload.uploadMain(params[0],params[1],params[2]);
		} else {
			displayMessage(bidix.upload.messages.rssFailed);			
		}
	};
	// do uploadRss
	if(config.options.chkGenerateAnRssFeed) {
		var rssPath = uploadParams[1].substr(0,uploadParams[1].lastIndexOf(".")) + ".xml";
		var rssUploadParams = new Array(uploadParams[0],rssPath,uploadParams[2],'',uploadParams[4],uploadParams[5]);
		var rssString = generateRss();
		// no UnicodeToUTF8 conversion needed when location is "file" !!!
		if (document.location.toString().substr(0,4) != "file")
			rssString = convertUnicodeToUTF8(rssString);	
		bidix.upload.httpUpload(rssUploadParams,rssString,callback,Array(uploadParams,original,posDiv));
	} else {
		bidix.upload.uploadMain(uploadParams,original,posDiv);
	}
};

bidix.upload.uploadMain = function(uploadParams,original,posDiv) 
{
	var callback = function(status,params,responseText,url,xhr) {
		var log = new bidix.UploadLog();
		if(status) {
			// if backupDir specified
			if ((params[3]) && (responseText.indexOf("backupfile:") > -1))  {
				var backupfile = responseText.substring(responseText.indexOf("backupfile:")+11,responseText.indexOf("\n", responseText.indexOf("backupfile:")));
				displayMessage(bidix.upload.messages.backupSaved,bidix.dirname(url)+'/'+backupfile);
			}
			var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
			displayMessage(bidix.upload.messages.mainSaved,bidix.dirname(url)+'/'+destfile);
			store.setDirty(false);
			log.endUpload("ok");
		} else {
			alert(bidix.upload.messages.mainFailed);
			displayMessage(bidix.upload.messages.mainFailed);
			log.endUpload("failed");			
		}
	};
	// do uploadMain
	var revised = bidix.upload.updateOriginal(original,posDiv);
	bidix.upload.httpUpload(uploadParams,revised,callback,uploadParams);
};

bidix.upload.httpUpload = function(uploadParams,data,callback,params)
{
	var localCallback = function(status,params,responseText,url,xhr) {
		url = (url.indexOf("nocache=") < 0 ? url : url.substring(0,url.indexOf("nocache=")-1));
		if (xhr.status == 404)
			alert(bidix.upload.messages.storePhpNotFound.format([url]));
		if ((bidix.debugMode) || (responseText.indexOf("Debug mode") >= 0 )) {
			alert(responseText);
			if (responseText.indexOf("Debug mode") >= 0 )
				responseText = responseText.substring(responseText.indexOf("\n\n")+2);
		} else if (responseText.charAt(0) != '0') 
			alert(responseText);
		if (responseText.charAt(0) != '0')
			status = null;
		callback(status,params,responseText,url,xhr);
	};
	// do httpUpload
	var boundary = "---------------------------"+"AaB03x";	
	var uploadFormName = "UploadPlugin";
	// compose headers data
	var sheader = "";
	sheader += "--" + boundary + "\r\nContent-disposition: form-data; name=\"";
	sheader += uploadFormName +"\"\r\n\r\n";
	sheader += "backupDir="+uploadParams[3] +
				";user=" + uploadParams[4] +
				";password=" + uploadParams[5] +
				";uploaddir=" + uploadParams[2];
	if (bidix.debugMode)
		sheader += ";debug=1";
	sheader += ";;\r\n"; 
	sheader += "\r\n" + "--" + boundary + "\r\n";
	sheader += "Content-disposition: form-data; name=\"userfile\"; filename=\""+uploadParams[1]+"\"\r\n";
	sheader += "Content-Type: text/html;charset=UTF-8" + "\r\n";
	sheader += "Content-Length: " + data.length + "\r\n\r\n";
	// compose trailer data
	var strailer = new String();
	strailer = "\r\n--" + boundary + "--\r\n";
	data = sheader + data + strailer;
	if (bidix.debugMode) alert("about to execute Http - POST on "+uploadParams[0]+"\n with \n"+data.substr(0,500)+ " ... ");
	var r = doHttp("POST",uploadParams[0],data,"multipart/form-data; ;charset=UTF-8; boundary="+boundary,uploadParams[4],uploadParams[5],localCallback,params,null);
	if (typeof r == "string")
		displayMessage(r);
	return r;
};

// same as Saving's updateOriginal but without convertUnicodeToUTF8 calls
bidix.upload.updateOriginal = function(original, posDiv)
{
	if (!posDiv)
		posDiv = locateStoreArea(original);
	if((posDiv[0] == -1) || (posDiv[1] == -1)) {
		alert(config.messages.invalidFileError.format([localPath]));
		return;
	}
	var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n" +
				store.allTiddlersAsHtml() + "\n" +
				original.substr(posDiv[1]);
	var newSiteTitle = getPageTitle().htmlEncode();
	revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
	revised = updateMarkupBlock(revised,"PRE-HEAD","MarkupPreHead");
	revised = updateMarkupBlock(revised,"POST-HEAD","MarkupPostHead");
	revised = updateMarkupBlock(revised,"PRE-BODY","MarkupPreBody");
	revised = updateMarkupBlock(revised,"POST-SCRIPT","MarkupPostBody");
	return revised;
};

//
// UploadLog
// 
// config.options.chkUploadLog :
//		false : no logging
//		true : logging
// config.options.txtUploadLogMaxLine :
//		-1 : no limit
//      0 :  no Log lines but UploadLog is still in place
//		n :  the last n lines are only kept
//		NaN : no limit (-1)

bidix.UploadLog = function() {
	if (!config.options.chkUploadLog) 
		return; // this.tiddler = null
	this.tiddler = store.getTiddler("UploadLog");
	if (!this.tiddler) {
		this.tiddler = new Tiddler();
		this.tiddler.title = "UploadLog";
		this.tiddler.text = "| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |";
		this.tiddler.created = new Date();
		this.tiddler.modifier = config.options.txtUserName;
		this.tiddler.modified = new Date();
		store.addTiddler(this.tiddler);
	}
	return this;
};

bidix.UploadLog.prototype.addText = function(text) {
	if (!this.tiddler)
		return;
	// retrieve maxLine when we need it
	var maxLine = parseInt(config.options.txtUploadLogMaxLine,10);
	if (isNaN(maxLine))
		maxLine = -1;
	// add text
	if (maxLine != 0) 
		this.tiddler.text = this.tiddler.text + text;
	// Trunck to maxLine
	if (maxLine >= 0) {
		var textArray = this.tiddler.text.split('\n');
		if (textArray.length > maxLine + 1)
			textArray.splice(1,textArray.length-1-maxLine);
			this.tiddler.text = textArray.join('\n');		
	}
	// update tiddler fields
	this.tiddler.modifier = config.options.txtUserName;
	this.tiddler.modified = new Date();
	store.addTiddler(this.tiddler);
	// refresh and notifiy for immediate update
	story.refreshTiddler(this.tiddler.title);
	store.notify(this.tiddler.title, true);
};

bidix.UploadLog.prototype.startUpload = function(storeUrl, toFilename, uploadDir,  backupDir) {
	if (!this.tiddler)
		return;
	var now = new Date();
	var text = "\n| ";
	var filename = bidix.basename(document.location.toString());
	if (!filename) filename = '/';
	text += now.formatString("0DD/0MM/YYYY 0hh:0mm:0ss") +" | ";
	text += config.options.txtUserName + " | ";
	text += "[["+filename+"|"+location + "]] |";
	text += " [[" + bidix.basename(storeUrl) + "|" + storeUrl + "]] | ";
	text += uploadDir + " | ";
	text += "[[" + bidix.basename(toFilename) + " | " +toFilename + "]] | ";
	text += backupDir + " |";
	this.addText(text);
};

bidix.UploadLog.prototype.endUpload = function(status) {
	if (!this.tiddler)
		return;
	this.addText(" "+status+" |");
};

//
// Utilities
// 

bidix.checkPlugin = function(plugin, major, minor, revision) {
	var ext = version.extensions[plugin];
	if (!
		(ext  && 
			((ext.major > major) || 
			((ext.major == major) && (ext.minor > minor))  ||
			((ext.major == major) && (ext.minor == minor) && (ext.revision >= revision))))) {
			// write error in PluginManager
			if (pluginInfo)
				pluginInfo.log.push("Requires " + plugin + " " + major + "." + minor + "." + revision);
			eval(plugin); // generate an error : "Error: ReferenceError: xxxx is not defined"
	}
};

bidix.dirname = function(filePath) {
	if (!filePath) 
		return;
	var lastpos;
	if ((lastpos = filePath.lastIndexOf("/")) != -1) {
		return filePath.substring(0, lastpos);
	} else {
		return filePath.substring(0, filePath.lastIndexOf("\\"));
	}
};

bidix.basename = function(filePath) {
	if (!filePath) 
		return;
	var lastpos;
	if ((lastpos = filePath.lastIndexOf("#")) != -1) 
		filePath = filePath.substring(0, lastpos);
	if ((lastpos = filePath.lastIndexOf("/")) != -1) {
		return filePath.substring(lastpos + 1);
	} else
		return filePath.substring(filePath.lastIndexOf("\\")+1);
};

bidix.initOption = function(name,value) {
	if (!config.options[name])
		config.options[name] = value;
};

//
// Initializations
//

// require PasswordOptionPlugin 1.0.1 or better
bidix.checkPlugin("PasswordOptionPlugin", 1, 0, 1);

// styleSheet
setStylesheet('.txtUploadStoreUrl, .txtUploadBackupDir, .txtUploadDir {width: 22em;}',"uploadPluginStyles");

//optionsDesc
merge(config.optionsDesc,{
	txtUploadStoreUrl: "Url of the UploadService script (default: store.php)",
	txtUploadFilename: "Filename of the uploaded file (default: in index.html)",
	txtUploadDir: "Relative Directory where to store the file (default: . (downloadService directory))",
	txtUploadBackupDir: "Relative Directory where to backup the file. If empty no backup. (default: ''(empty))",
	txtUploadUserName: "Upload Username",
	pasUploadPassword: "Upload Password",
	chkUploadLog: "do Logging in UploadLog (default: true)",
	txtUploadLogMaxLine: "Maximum of lines in UploadLog (default: 10)"
});

// Options Initializations
bidix.initOption('txtUploadStoreUrl','');
bidix.initOption('txtUploadFilename','');
bidix.initOption('txtUploadDir','');
bidix.initOption('txtUploadBackupDir','');
bidix.initOption('txtUploadUserName','');
bidix.initOption('pasUploadPassword','');
bidix.initOption('chkUploadLog',true);
bidix.initOption('txtUploadLogMaxLine','10');


// Backstage
merge(config.tasks,{
	uploadOptions: {text: "upload", tooltip: "Change UploadOptions and Upload", content: '<<uploadOptions>>'}
});
config.backstageTasks.push("uploadOptions");


//}}}
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'>
<span macro="tiddler cycleTags with: tagsTasks" title="task management" style="margin-right:20px;">
</span>
</div>
<div class="tagglyTagged" macro="tags"></div>
<div class='titleContainer'>
	<span class='title' macro='view title'></span>
	<span macro="miniTag"></span>
</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>)<span macro='rating =here RatingSummary' style="margin-left:10px;"></span></div>
<div class='viewer' macro='view text wikified'></div>
<div class="tagglyTagging" macro="tagglyTagging" style="margin-top:5px;"></div>
<div class='tagClear'></div>

<!--}}}-->
This was the basic idea ''cmari'' expressed on the [[tiddlywiki google group|http://groups.google.com/group/tiddlywiki/browse_frm/thread/ac859bd7feb82347]]. It requires [[CheckboxPlugin]] and can be easily implemented in the ViewTemplate(-Toolbar). As I though I was capable of generalising the concept, I set forth to develop [[cycleTags]].

An example:  @@color:blue;<html><span title="task management" style="margin-right:10px;">
<span macro="hideWhenTaggedAny 'queued', 'assigned', 'completed', 'archived'">
<span macro="checkbox [[]] [[]] [[]] [[]] [[{var t=story.findContainingTiddler(place).getAttribute('tiddler'); 
store.setTiddlerTag(t,1,'queued');}]]">
</span>queue as task</span>

<span macro="showWhenTagged 'queued'">
<span macro="checkbox [[]] [[]] [[]] [[]] [[{var t=story.findContainingTiddler(place).getAttribute('tiddler'); 
store.setTiddlerTag(t,0,'queued'); store.setTiddlerTag(t, 1,'assigned');}]]">
</span>assign task</span> 

<span macro="showWhenTagged 'assigned'">
<span macro="checkbox [[]] [[]] [[]] [[]] [[{var t=story.findContainingTiddler(place).getAttribute('tiddler'); 
store.setTiddlerTag(t,0,'assigned'); store.setTiddlerTag(t, 1,'completed');}]]">
</span>complete task</span> 

<span macro="showWhenTagged 'completed'">
<span macro="checkbox [[]] [[]] [[]] [[]] [[{var t=story.findContainingTiddler(place).getAttribute('tiddler'); 
store.setTiddlerTag(t,0,'completed');store.setTiddlerTag(t, 1,'archived');}]]">
</span>archive task</span> 

<span macro="showWhenTagged 'archived'">
<span macro="checkbox [[]] [[]] [[]] [[]] [[{var t=story.findContainingTiddler(place).getAttribute('tiddler'); 
store.setTiddlerTag(t,0,'archived');store.setTiddlerTag(t, 1,'queued');}]]">
</span>requeue task
<span macro="checkbox [[]] [[]] [[]] [[]] [[{var t=story.findContainingTiddler(place).getAttribute('tiddler');
store.setTiddlerTag(t,0,'archived');}]]">
</span>remove from archive</span>
</html>@@
!The wiki code
{{{
<html>
<span title="task management" style="margin-right:10px;">
<span macro="hideWhenTaggedAny 'queued', 'assigned', 'completed', 'archived'">
<span macro="checkbox [[]] [[]] [[]] [[]] [[{var t=story.findContainingTiddler(place).getAttribute('tiddler'); 
store.setTiddlerTag(t,1,'queued');}]]">
</span>queue as task</span>

<span macro="showWhenTagged 'queued'">
<span macro="checkbox [[]] [[]] [[]] [[]] [[{var t=story.findContainingTiddler(place).getAttribute('tiddler'); 
store.setTiddlerTag(t,0,'queued'); store.setTiddlerTag(t, 1,'assigned');}]]">
</span>assign task</span> 

<span macro="showWhenTagged 'assigned'">
<span macro="checkbox [[]] [[]] [[]] [[]] [[{var t=story.findContainingTiddler(place).getAttribute('tiddler'); 
store.setTiddlerTag(t,0,'assigned'); store.setTiddlerTag(t, 1,'completed');}]]">
</span>complete task</span> 

<span macro="showWhenTagged 'completed'">
<span macro="checkbox [[]] [[]] [[]] [[]] [[{var t=story.findContainingTiddler(place).getAttribute('tiddler'); 
store.setTiddlerTag(t,0,'completed');store.setTiddlerTag(t, 1,'archived');}]]">
</span>archive task</span> 

<span macro="showWhenTagged 'archived'">
<span macro="checkbox [[]] [[]] [[]] [[]] [[{var t=story.findContainingTiddler(place).getAttribute('tiddler'); 
store.setTiddlerTag(t,0,'archived');store.setTiddlerTag(t, 1,'queued');}]]">
</span>requeue task
<span macro="checkbox [[]] [[]] [[]] [[]] [[{var t=story.findContainingTiddler(place).getAttribute('tiddler');
store.setTiddlerTag(t,0,'archived');}]]">
</span>remove from archive</span>
</html>
}}}
/%
|Name|cycleTags|
|Source|http://lastfm.tiddlyspot.com/#cycleTags|
|Version|0.1.1|
|Author|Tobias Beer|
|License||''License''|[[Creative Commons Attribution-Share Alike 3.0|http://creativecommons.org/licenses/by-sa/3.0/]]|
|~CoreVersion|2.5|
|Type|script|
|Requires|[[InlineJavascriptPlugin]] [[on tiddlytools|http://www.tiddlytools.com/#InlineJavascriptPlugin]]|
|Overrides||
|Description|allows to cycle through a defined list of tags|
|Discusson|[[tiddlywiki google group|http://groups.google.com/group/tiddlywiki/browse_frm/thread/de78a3e21b1a0b97]].

usage:
	<<tiddler cycleTags with: TagList Tiddler>>

where:
	TagList - (required)
		refers to a tiddler with a defined list of tags see cycleTagsInfo
	Tiddler - (optional) 
		when defined, sets the tags on the specified tiddler instead of the one calling the script

%/<script>
var tid = "$2";
if (tid!="$"+"2") {
  tid = store.getTiddler(tid);
  if (tid==null) return "@@color:red;invalid tiddler reference@@";
}
else {
  tid = store.getTiddler(story.findContainingTiddler(place).getAttribute('tiddler'));
}
if (tid == undefined) return;
var def="$1";
if (store.getTiddler(def)==undefined) return "@@color:red;invalid taglist@@";
var lines=store.getTiddlerText(def).split("\n");
var next=-1;
var i=0;
remove="";
while (next<0 && i<lines.length) {
  var parts=lines[i].split('###');
  if (tid.tags.contains(parts[0])) {
    next=i+1;
    remove=parts[0];
  }
  i++;
}

i>=lines.length?next=0:next=i;
parts=lines[next].split('###');

var tag = parts[0];
parts[1]!=undefined?text = parts[1]:text = "set "+ tag;
parts[2]!=undefined?tooltip = parts[2]:tooltip = "assign '"+ tag + "' to this tiddler";
parts[3]!=undefined?cls = parts[3]:cls = "";
parts[4]!=undefined?rbutton = parts[4]:rbutton = "";

if (remove != "" && rbutton != "") {
  var btn = createTiddlyButton(place,rbutton,"remove tag " + remove,function(){store.setTiddlerTag(this.tid,0,this.removeTag); story.refreshTiddler(story.findContainingTiddler(this).getAttribute('tiddler'),null,true);},cls);
  btn.removeTag = remove;
  btn.tid = tid.title;
}

var btn = createTiddlyButton(place,text,tooltip,function(){store.setTiddlerTag(this.tid,1,this.switchTag);store.setTiddlerTag(this.tid,0,this.removeTag);story.refreshTiddler(story.findContainingTiddler(this).getAttribute('tiddler'),null,true);},cls);
btn.switchTag = parts[0];
btn.removeTag = remove;
btn.tid = tid.title;
</script>
<<showtoc>>
[[Here|cycleTags]] you find a script that I wrote with the help of [[Eric Schulman|http://www.tiddlytools.com]] and his great [[InlineJavascriptPlugin|InlineJavascriptPluginInfo]]. It is called [[cycleTags]] and it allows you to cycle through a list of tags, successively assigning these tags while removing the "last state".
!Installation
If you haven't already, install the almighty [[InlineJavascriptPlugin|InlineJavascriptPluginInfo]] from [[tiddlytools|http://www.tiddlytools.com#InlineJavascriptPlugin]]. Then get the tiddler [[cycleTags]] from this tiddlywiki and, if you need to, [[tagsTasks]] as a template for defining your custom "tag cycle" ...explanations below.
!Configuration
To use it, transclude  [[cycleTags]] (which is the tiddler containing the script) like so:

An example: <<tiddler cycleTags with: tagsTasks cycleTags >>
In general:{{{   <<tiddler cycleTags with: TagDefinitionTiddler NameOfTaggedTiddler>>}}}

where the parameters following the @@color:darkblue;with statement@@ are:
|Parameter|Required?|Comment|h
|@@color:darkblue;~TagDefinitionTiddler@@ | yes |name of the tiddler containing a valid definition list for the tags to be included|
|@@color:darkblue;~NameOfTaggedTiddler@@| no |name of the tiddler for which you want to cycle trough the tags, default is the current tiddler|
!The tag definition tiddler
You need one of those. Set it up the following way, each line representing a state of your @@color:darkblue;tag cycle@@:
{{{
tag1Name###button1Text###button1Tooltip###button1Class###button1Remove
tag2Name###button2Text###button2Tooltip###button2Class###button2Remove
...
}}}

where:
|Parameter|Required?|Comment|h
|tagName| yes |the name of the tag|
|buttonText| no |the text for the button displayed when this tag is to be set|
|buttonTooltip| no |tooltip for the button displayed when this tag is to be set|
|buttonClass| no |css class(es) assigned to the button|
|buttonRemove| no |any text you put here will display an additional @@border:1px solid #db4;padding:0px 3px;-@@ button to remove the current tag|
!An example
You find a simple task status switch in the toolbar at the top of each tiddler that uses the tiddler [[tagsTasks]] as its definition:
@@display:block;padding:7px;margin-left:10px;margin-top:10px;background-color:#FFC;border:1px solid #FE8;<<tiddler tagsTasks>>@@
Also see the corresponding toolbar section in the [[ViewTemplate]]:
{{{
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'>
<span title="task management" style="margin-right:20px;">
<span macro="tiddler cycleTags with: tagsTasks">
</span></span>
</div>
}}}
<<tiddler [[task overview]]>>
!Discusson
Discuss this script [[here|http://groups.google.com/group/tiddlywiki/browse_frm/thread/de78a3e21b1a0b97]].
!Origin
Interested in how I got to write this script? [[Look here!|cycle task status]]
!Revisions
|revision|change|h
|0.1.2|- Fixed error (again in button handler) where [[task overview]] wouldn't update on changes. |
|0.1.1|- Fixed js error on button handler. |
|0.1.0|- Let's call this the first official release. I just inverted the removeButton functionality, so please adopt to it ;o) |
//{{{
version.extensions.favicon = {major: 0, minor: 1, revision: 0, date: new Date("Jul 18, 2005")};
var n = document.createElement("link");
n.rel = "shortcut icon";
n.href = "http://goto.bplaced.net/x/tiddlyspot/lastfm/favicon.ico";
document.getElementsByTagName("head")[0].appendChild(n);
//}}}
/***
|''Name:''|lastfmMacro|
|''Description''|creates a tunable [[LastFM]] radio player|
|''Version:''|0.0.1|
|''Type''|macro|
|''Author:''|[[TobiasBeer]]|
|''Info:''|http://lastfm.tiddlyspot.com/#lastfmMacroInfo [[lastfmMacroInfo]]|
|''Source:''|http://lastfm.tiddlyspot.com/#lastfmMacroSource [[lastfmMacroSource]]|
|''License''|[[Creative Commons Attribution-Share Alike 3.0|http://creativecommons.org/licenses/by-sa/3.0/]]|
|''Feedback:''|[[here|http://groups.google.com/group/TiddlyWiki/browse_frm/thread/959edc01e1989258]]|
|''~CoreVersion''|2.1|
|''Requires''|flash|
!Info
@@color:red; No longer works!@@
<<lastfm>>
!Code
***/
//{{{
version.extensions.lastfmMacro={major:0,minor:1,revision:0,date:new Date("Jun 30, 2009"),type:'macro',source:'http://lastfm.tiddlyspot.com#lastfmMacro'};config.macros.lastfm={sDefaultTag:'indie',sLang:'en',sArtist:'artist',sTag:'tag',handler:function(a,b,c,d,e,f){var g=createTiddlyElement(a,"div","wrapperLastFM");g.title="Enter 'artist' or 'tag' without special characters. Change the initial station by adding the parameters 'tag:myTag' or 'artist:myArtist' to the macro call";g.innerHTML='<div id="playerLastFM"></div>\n'+'<form id="configLastFM">\n'+'<table id="configTableLastFM" cellspacing=0 cellborder=0>\n'+'<tr>\n'+'<td> '+this.sArtist+'</td>\n'+'<td><input type="text" id="txtArtistLastFM" value="" onkeyup="config.macros.lastfm.mKeyListener(event,true);"/></td>\n'+'</tr>\n'+'<tr>\n'+'<td> '+this.sTag+'</td>\n'+'<td><input type="text" id="txtTagLastFM" value="" onkeyup="config.macros.lastfm.mKeyListener(event,false);"/></td>\n'+'</tr>\n'+'</table>\n'+'</form>';var h=e.parseParams(null,null,true);var i=getParam(h,"artist");var j=getParam(h,"tag");if(j==undefined)j=this.sDefaultTag;this.mCreatePlayer(i,j)},mCreatePlayer:function(a,b){var c="";if(a!=undefined&a!="")c="artist%2F"+a+"%2Fsimilarartists&amp;title=artist%2F"+a;if(b!=undefined&b!="")c="globaltags%2F"+b+"&amp;title=globaltags%2F"+b;var d='<object type="application/x-shockwave-flash" data="http://cdn.last.fm/widgets/radio/22.swf" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="180" height="140"><param name="movie" value="http://cdn.last.fm/widgets/radio/22.swf" /><param name="flashvars" value="lfmMode=radio&amp;radioURL=';var e='&amp;theme=grey&amp;lang='+this.sLang+'" /><param name="allowScriptAccess" value="always" /> <param name="allowNetworking" value="all" /> <param name="allowFullScreen" value="true" /> <param name="quality" value="high" /> <param name="bgcolor" value="999999" /> <param name="wmode" value="transparent" /> <param name="menu" value="true" /> </object>';if(c!="")document.getElementById('playerLastFM').innerHTML=d+c+e},mKeyListener:function(e,a){if(e.keyCode==13){var b="";var c="";if(a)b=document.getElementById('txtArtistLastFM').value;else c=document.getElementById('txtTagLastFM').value;this.mCreatePlayer(b,c)}else{if(e.keyCode==40&&a)document.forms["configLastFM"].txtTagLastFM.focus();if(e.keyCode==38&&!a)document.forms["configLastFM"].txtArtistLastFM.focus()}}};setStylesheet("#wrapperLastFM {background:none;width:190px;border:none;background-color:#EEE;border:1px solid #999}\n"+"#playerLastFM, #configLastFM {text-align:center;margin:5px 0px;padding:0px;}\n"+"#configTableLastFM {border:none;color:#666;font-size:0.8em;margin-left:10px;}\n"+"#configTableLastFM tr {border:none;}\n"+"#configTableLastFM td {text-align:right;border:none;width:100%;font-size:12px;}\n"+"#configTableLastFM td input {width:130px; color:#666;margin:0px 5px;}","lastfmStyles");
//}}}
<<showtoc>>
!Bugs
#In certain browsers the whole page reloads instead of the widget when artist/tag is entered.
!Known compatibilities
*Internet Explorer
**>= 6
*Firefox
**>=1.5
*Opera
**>=9.6
!Known Incompatibilities
*Chrome
**<=2 ...bug #1
*Opera
**<=9.2x ...bug #1
*Firefox
**<=1.08 ...many tiddlywiki plugins don't seem to work. Go get an update!
*Safari (on XP!)
**3.1.2 ...bug #1
*Internet Explorer
**<=5.5 ...I don't think tiddlywiki is working for you at all.
<<showtoc>>
!The Idea
To enhance an embeddable lastfm flash widget [[[get one here|http://www.lastfm.com/widgets]]] with textboxes to easily configure playback of music which is:
*related to a certain artist's style
*tagged with certain keywords, such as a genre, instrument or place
!Install
Import lastfmMacro (and make sure it remains tagged with "systemConfig").
!Embedding a Player | Parameters
To create a player type one of the following options into a tiddler of your choice...
#{{{<<lastfm>>}}}
**plays a station for the global tag called ''sDefaultTag'', which can set in this macro code
#{{{<<lastfm artist:listenToThisArtist>>}}}
**plays a given artist which should be defined without any special characters
#{{{<<lastfm tag:listenToThisTagOrGenre>>}}}
**plays a station of a given global tag which should be defined without any special characters
''Note:'' Only one player will work per tiddlywiki. A good place to put the player is a main menu on the left hand side, the right sidebar or any other permanent place outside {{{#displayArea}}} (which is the main div container for tiddlers).

If you don't want to see the player widget all the time, you might want to put it in a [[slider|http://tiddlywiki.org/wiki/Slider]] like so:
{{{<<slider chkSliderLastFM [[TiddlerWithLastfmPlayer]] "webradio" "play lastfm radio">>}}}
!Use it!
*above all, hit play
*the actual player comes as provided by lastfm and I hope it will continue to work for a long time
*to change the station, either type an {{{artist (or band name)}}} or a {{{global tag (e.g. genre)}}} in the provided textboxes and hit {{{enter}}}
To change the look of the player adjust any of the following to your linking and add it in your StyleSheet. To do that, you need a basic understanding of [[CSS|http://www.w3.org/Style/CSS/learning]]. You can inspect and test CSS properties very easily using [[FireBug|https://addons.mozilla.org/de/firefox/addon/1843]].
{{{
#wrapperLastFM {background:none;width:190px;border:none;background-color:#EEE;border:1px solid #999}
#playerLastFM, #configLastFM {text-align:center;margin:5px 0px;padding:0px;}
#configTableLastFM {border:none;color:#666;font-size:0.8em;margin-left:10px;}
#configTableLastFM tr {border:none;}
#configTableLastFM td {text-align:right;border:none;width:100%;font-size:12px;}
#configTableLastFM td input {width:130px; color:#666;margin:0px 5px;}
}}}
There also is a {{{theme=color}}} option in the {{{<object>}}} code of the widget in which you may choose from one of lastfm's predefined color options, which are {{{red}}}, {{{blue}}}, {{{black}}} and ...this one is {{{grey}}}.
!Advanced Setup
!!Default Station
To change the default station, you have to edit __the value__ of {{{sDefaultTag}}} in the macro code declaration.
!!Localisation
For localisation, you have to edit __the values__ of {{{sArtist}}}, {{{sTag}}} and {{{sLang}}} in the macro code declaration
*sLang should represent the browsers short form for your language, such as:
** {{{en}}} for English
** {{{fr}}} for French
** {{{de}}} for German
** etc...
!!Player Size
To change the player size you need to adjust the:
#{{{width}}} parameter in the {{{<object> code}}} of the lastfm widget, as it is declared in the variable {{{sStart}}} in the macro
**this may change what content is displayed and how it is displayed.
#{{{css style definitions}}} which requires a bit finetuning
!!Custom Config
In general, you can also save values in a configuration tiddler tagged {{{systemConfig}}} by assigning values to keys, like:
{{{config.macros.lastfm.sDefaultTag = "raggae";}}}

''Note:'' For this you need to make sure that your configuration tiddler gets loaded __after__ this macro is loaded, e.g. by naming it {{{zzzMyConfig}}}.
!Bugs / Incompatibilities
See [[here|lastfmMacroBugs]]...
/***
|''Name:''|lastfmMacro|
|''Description''|creates a tunable LastFM radio player|
|''Version:''|0.0.1|
|''Type''|macro|
|''Author:''|TobiasBeer|
|''Info:''|http://lastfm.tiddlyspot.com/#lastfmMacroInfo [[lastfmMacroInfo]]|
|''Source:''|http://lastfm.tiddlyspot.com/#lastfmMacroSource [[lastfmMacroSource]]|
|''License''|[[Creative Commons Attribution-Share Alike 3.0|http://creativecommons.org/licenses/by-sa/3.0/]]|
|''Feedback:''|[[here|http://groups.google.com/group/TiddlyWiki/browse_frm/thread/959edc01e1989258]]|
|''~CoreVersion''|2.1|
|''Requires''|flash|
!Code
***/
//{{{
version.extensions.lastfmMacro = {
  major: 0,
  minor: 1,
  revision: 0,
  date: new Date("Jun 30, 2009"),
  type: 'macro',
  source: 'http://lastfm.tiddlyspot.com#lastfmMacro'
};

config.macros.lastfm = {
  sDefaultTag: 'indie',   //default station when no parameter given
  sLang: 'en',            //language for lastfm interface
  sArtist: 'artist',      //string displayed for 'artist'
  sTag: 'tag',            //string displayed for 'tag'
  handler: function (place, macroName, params, wikifier, paramString, tiddler) {
    var player = createTiddlyElement(place,"div","wrapperLastFM");
    player.title = "Enter 'artist' or 'tag' without special characters. Change the initial station by adding the parameters 'tag:myTag' or 'artist:myArtist' to the macro call";
    player.innerHTML = '<div id="playerLastFM"></div>\n' +
  	'<form id="configLastFM">\n' +
      '<table id="configTableLastFM" cellspacing=0 cellborder=0>\n' +
        '<tr>\n' +
          '<td> ' + this.sArtist + '</td>\n' +
          '<td><input type="text" id="txtArtistLastFM" value="" onkeyup="config.macros.lastfm.mKeyListener(event,true);"/></td>\n' +
        '</tr>\n' +
        '<tr>\n' +
          '<td> ' + this.sTag + '</td>\n' +
          '<td><input type="text" id="txtTagLastFM" value="" onkeyup="config.macros.lastfm.mKeyListener(event,false);"/></td>\n' +
        '</tr>\n' +
      '</table>\n' +
    '</form>';
    
    //read params
    var sPrms = paramString.parseParams(null, null, true);
    var sArtist = getParam(sPrms, "artist");
    var sTag = getParam(sPrms, "tag");
    if (sTag==undefined) sTag = this.sDefaultTag;
    //create player
    this.mCreatePlayer(sArtist,sTag);
  },
  mCreatePlayer : function (sArtist,sTag) {//player setup for artist or tag
    var sPlay = "";
    if (sArtist!=undefined & sArtist!="") sPlay="artist%2F" + sArtist + "%2Fsimilarartists&amp;title=artist%2F" + sArtist;
    if (sTag!=undefined & sTag!="") sPlay="globaltags%2F" + sTag + "&amp;title=globaltags%2F" + sTag;
    var sStart = '<object type="application/x-shockwave-flash" data="http://cdn.last.fm/widgets/radio/22.swf" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="180" height="140"><param name="movie" value="http://cdn.last.fm/widgets/radio/22.swf" /><param name="flashvars" value="lfmMode=radio&amp;radioURL=';
    var sEnd = '&amp;theme=grey&amp;lang=' + this.sLang + '" /><param name="allowScriptAccess" value="always" /> <param name="allowNetworking" value="all" /> <param name="allowFullScreen" value="true" /> <param name="quality" value="high" /> <param name="bgcolor" value="999999" /> <param name="wmode" value="transparent" /> <param name="menu" value="true" /> </object>';
    if (sPlay!="") document.getElementById('playerLastFM').innerHTML = sStart + sPlay + sEnd;
  },
  mKeyListener : function (e,bArtist) {//key listener on textfields
    //when enter (return) is pressed
    if(e.keyCode==13) {
      var sArtist = "";
      var sTag = "";
      if (bArtist) sArtist = document.getElementById('txtArtistLastFM').value;
      else sTag = document.getElementById('txtTagLastFM').value;
      this.mCreatePlayer(sArtist,sTag);
    }
    else {
      if (e.keyCode==40 && bArtist) document.forms["configLastFM"].txtTagLastFM.focus();
      if (e.keyCode==38 && !bArtist) document.forms["configLastFM"].txtArtistLastFM.focus();
    } 
  }
};

//Basic stylesheet for player
setStylesheet(
"#wrapperLastFM {background:none;width:190px;border:none;background-color:#EEE;border:1px solid #999}\n"+
"#playerLastFM, #configLastFM {text-align:center;margin:5px 0px;padding:0px;}\n"+
"#configTableLastFM {border:none;color:#666;font-size:0.8em;margin-left:10px;}\n"+
"#configTableLastFM tr {border:none;}\n"+
"#configTableLastFM td {text-align:right;border:none;width:100%;font-size:12px;}\n"+
"#configTableLastFM td input {width:130px; color:#666;margin:0px 5px;}",
"lastfmStyles");
//}}}
!handler
*macro handler to create initial wrappper for player
*accepts two parameters for which the player is initially to be set up (both without any blank spaces or special characters)
**''artist'': artist in lastfm
**''tag'': global tag in lastfm (e.g. genre) 
*calls ''mCreatePlayer'' to create the player object
!mCreatePlayer
*creates player object in placeholder depending on given artist or tag
!mKeyListener
*hooked up to textfields to change player station either for artist or tag
*calls ''mCreatePlayer'' to create (an updated) player object
queued###queue###put tiddler into the task queue######-
active###do###set task status to assigned (as in: "I am doing it")######-
completed###done###set task status to done######-
archived###archive###archive this task######-
!![[task overviews|task overview]] based on the status tags for tasks
!!!A more extended version based on [[TiddlerListMacro]]
example code for the "queued" list:
{{{<<tiddlerList tags:"queued" order:"modified" itemTemplate:"<<tiddler cycleTags with: tagsTasks %0 $))%link\n">>}}}
| !task overview |>|>|>|h
| queued | active | completed | archived |h
|<<tiddlerList tags:"queued" order:"modified" itemTemplate:"<<tiddler cycleTags with: tagsTasks %0 $))%link\n">>|<<tiddlerList tags:"active" order:"modified" itemTemplate:"<<tiddler cycleTags with: tagsTasks %0 $)) %link\n">>|<<tiddlerList tags:"completed" order:"modified" itemTemplate:"<<tiddler cycleTags with: tagsTasks %0 $)) %link\n">>|<<tiddlerList tags:"archived" order:"modified" itemTemplate:"<<tiddler cycleTags with: tagsTasks %0 $)) %link\n">>|
!!!A simple version based on the standard list macro
example code for the "queued" list:
{{{<<list filter [tag[queued]]>>}}}
| !task overview |>|>|>|h
| queued | active | completed | archived |h
|<<list filter [tag[queued]]>>|<<list filter [tag[active]]>>|<<list filter [tag[completed]]>>|<<list filter [tag[archived]]>>|
var co=config.options;
co.chkHttpReadOnly=false;//visitors can edit
co.chkSaveBackups=true;
co.txtBackupFolder='twbackup';//backups in this folder
co.chkAutoSave=(window.location.protocol=="file:");//autosave when local