<!--{{{-->
<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]];}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

hr {height:1px;}

a {text-decoration:none;}

dt {font-weight:bold;}

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

.txtOptionInput {width:11em;}

#contentWrapper .chkOptionInput {border:0;}

.externalLink {text-decoration:underline;}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

----
Also see AdvancedOptions
<<importTiddlers>>
D. Simitopoulos, S.A. Tsaftaris, N.V. Boulgouris and M.G. Strintzis: "Digital watermarking of ~MPEG-1 and ~MPEG-2 multiplexed streams for copyright protection", in //Proceedings of IEEE International Workshop on Digital and Computational Video (DCV 2001)//, Tampa, Florida, USA, pp. 140-147, February 2001.
[ [[PDF|http://www.ece.northwestern.edu/~stsaft/papers/dcv2001.pdf]] ] [ [[WebPDF|http://www.pdfmenot.com/view/http://www.ece.northwestern.edu/~stsaft/papers/dcv2001.pdf]] ] 

''Abstract''
A new technique for watermarking of ~MPEG-1/2 compressed video streams is proposed. The watermarking scheme operates directly in the domain of ~MPEG-1/2 program streams. Perceptual models are used during the embedding process in order to preserve the quality of the video. The detection of the watermark is performed in the compressed domain without requiring the original video. The resulting watermarking system is very fast and reliable and is suitable for copyright protection and real-time content authentication applications.

[[BibTeX or EndNote Entry via CiteULike|http://www.citeulike.org/user/stsaft/article/2475499]]
D. Simitopoulos, S.A. Tsaftaris, N.V. Boulgouris, G.A. Triantafyllidis and M.G. Strintzis: "//Digital Watermarking for the Copyright Protection of Compressed Video//", "Intelligent Integrated Media Communication Techniques", eds. Mohammed Najim, Michael Ansorge, Jurij F. Tasic, Kluwer Academic Pub, December 2003.  [[Find it at AddAll.com|http://www.addall.com/New/BestSeller.cgi?isbn=1402075529&dispCurr=USD]]
D. Simitopoulos, S.A. Tsaftaris, N.V. Boulgouris, A. Briassouli and M.G. Strintzis, "Fast watermarking of ~MPEG-1/2 streams using compressed-domain perceptual embedding and a generalized correlator detector," // EURASIP Journal on Applied Signal Processing//, vol. 8, pp. 1088-1106, July 2004. 
[ [[PDF|http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris_JASP_2004.pdf]] ] [ [[Google PDF Viewer|http://docs.google.com/viewer?url=http://www.ece.northwestern.edu/~stsaft/papersTsaftaris_JASP_2004.pdf]] ]

''Abstract''
A novel technique is proposed for watermarking of ~MPEG-1 and ~MPEG-2 compressed video streams. The proposed scheme is applied directly in the domain of ~MPEG-1 system streams and ~MPEG-2 program streams (multiplexed streams). Perceptual models are used during the embedding process in order to avoid degradation of the video quality. The watermark is detected without the use of the original video sequence. A modified correlation-based detector is introduced that applies nonlinear preprocessing before correlation. Experimental evaluation demonstrates that the proposed scheme is able to withstand several common attacks. The resulting watermarking system is very fast and therefore suitable for copyright protection of compressed video.
In support of the P.A.T.T. event at The Note of WED June 27th 2007.

Track Listing:
1. DJ Pierre - Good Love ( Dennis Ferrer's foreplay Mix )
2. Bob Sinclar ft Ron Carroll - Wonderful World
3. DJ Pierre - Switch 2001
4. Bob Sinclar ft Gary Pine - Love Generation (Ron Carroll bmc black gospel mix)
5. DJ Pierre - Destroy this track filthy rich remix

[[MP3|http://www.ece.northwestern.edu/~stsaft/DJ/pierre_carroll.mp3]]
S. A. Tsaftaris and A. K. Katsaggelos, “DNA sequencing,” in //Wiley Encyclopedia of Medical Devices and Instrumentation//, 2nd ed., J. G. Webster, Ed. Reading, Massachusetts:
John Wiley and Sons, 2006, vol. 2, pp. 427–437. [[link|http://www.wiley.com/WileyCDA/WileyTitle/productCd-0471263583.html]]
[<img[IEEE SP COVER vol 5 2004|http://www.ece.northwestern.edu/~stsaft/media/sig_pro_1.png]] S.A. Tsaftaris, A.K. Katsaggelos, T.N. Pappas and E.T. Papoutsakis, "DNA computing from a signal processing viewpoint", //IEEE Signal Processing Magazine//, vol. 21, no. 5, pp. 100-106, September 2004.
[ [[PDF|http://www.ece.northwestern.edu/~stsaft/papers/SP_mag_1_04.pdf]] ] [ [[Google PDF Viewer|http://docs.google.com/viewer?url=http://www.ece.northwestern.edu/~stsaft/papers/SP_mag_1_04.pdf]] ]


[[BibTeX or EndNote Entry via CiteULike|http://www.citeulike.org/user/stsaft/article/532281]]
D. Simitopoulos, S.A. Tsaftaris, N.V. Boulgouris, and M.G. Strintzis: "Fast compressed domain watermarking of MPEG multiplexed streams", in //Proceedings of Information and Knowledge Management for Integrated Media Communication Workshop//, Madrid, Spain, November 2001.
[ [[PDF|http://www.ece.northwestern.edu/~stsaft/papers/FinalCOST276.pdf]] ] [ [[Google PDF Viewer|http://docs.google.com/viewer?url=http://www.ece.northwestern.edu/~stsaft/papers/FinalCOST276.pdf]] ]
D.Simitopoulos, S.A. Tsaftaris, N.V. Boulgouris and M.G. Strintzis: "Compressed-domain Video Watermarking of MPEG Streams", in //Proceedings of IEEE International Conference on Multimedia and Expo (ICME 2002)//, Lausanne, Switzerland, vol. 1, pp. 569 -572, August 2002.
[ [[PDF|http://www.ece.northwestern.edu/~stsaft/papers/icme2002.pdf]] ] [ [[Google PDF Viewer|http://docs.google.com/viewer?url=http://www.ece.northwestern.edu/~stsaft/papers/icme2002.pdf]] ]

''Abstract''
A new technique for watermarking of MPEG compressed video streams is proposed. The watermarking scheme operates directly in the domain of MPEG program streams. Perceptual models are used during the embedding process in order to preserve the video quality. The watermark is embedded in the compressed domain and is detected without the use of the original video sequence. Experimental evaluation demonstrates that the proposed scheme is able to withstand a variety of attacks. The resulting watermarking system is fast and reliable, and is suitable for copyright protection and real-time content authentication applications.

[[BibTeX or EndNote Entry via CiteULike|http://www.citeulike.org/user/stsaft/article/2475489]]
[<img[IEEE SP COVER vol 6 2004|http://www.ece.northwestern.edu/~stsaft/media/sig_pro_2.png]] S.A. Tsaftaris, A.K. Katsaggelos, T.N. Pappas and E.T. Papoutsakis, "How can ~DNA-Computing be applied in Digital Signal Processing?" // IEEE Signal Processing Magazine//, vol.21, no. 6, pp. 57-61, November 2004.
[ [[PDF|http://www.ece.northwestern.edu/~stsaft/papers/SP_mag_2_04.pdf]] ] [ [[Google PDF Viewer|http://docs.google.com/viewer?url=http://www.ece.northwestern.edu/~stsaft/papers/SP_mag_2_04.pdf]] ]

[[BibTeX or EndNote Entry via CiteULike| http://www.citeulike.org/user/stsaft/article/503213]]
D. Simitopoulos, S.A. Tsaftaris, N.V. Boulgouris and M.G. Strintzis: "Fast MPEG Watermarking for Copyright Protection", in //Proceedings of IEEE International Conference on Electronics, Circuits and Systems (ICECS 2002)//, Dubrovnik, Croatia, vol. 3, pp. 1027-1030, September 2002.
[ [[PDF|http://www.ece.northwestern.edu/~stsaft/papers/icecs2002.pdf]] ] [ [[Google PDF Viewer|http://docs.google.com/viewer?url=http://www.ece.northwestern.edu/~stsaft/papers/icecs2002.pdf]] ]

''Abstract''
In this paper, a new technique for watermarking of MPEG compressed video streams is proposed. The watermarking scheme operates directly in the domain of MPEG multiplexed streams. Perceptual models are used during the embedding process in order to preserve the quality of the video. The watermark is embedded in the compressed domain and is detected without the use of the original video sequence. Experimental evaluation demonstrates that the proposed scheme is able to withstand a variety of attacks. The resulting watermarking system is very fast and reliable, and is suitable for copyright protection and real-time content authentication applications.

[[BibTeX or EndNote Entry via CiteULike|http://www.citeulike.org/user/stsaft/article/2475521]]
S.A. Tsaftaris, V. Hatzimanikatis, and A.K. Katsaggelos, "//In silico// estimation of annealing specificity of query searches in DNA databases", // Journal of Japan Society of Simulation Technology (JSST)// special issue // "Application and Simulation of DNA Computing"//, vol. 24, no. 4, pp. 268-276, December 2005.

''Abstract''
We consider DNA implementations of databases for digital signals with retrieval and mining capabilities. Digital signals are encoded in DNA sequences and retrieval is achieved through annealing between query DNA primers and data carrying DNA target sequences. The hybridization between query and target can be non-specific containing multiple mismatches thus implementing similarity-based searches. In this paper we examine theoretically and by simulation the efficiency of such a system by estimating the concentrations of query-target duplex formations at equilibrium. A coupled kinetic model is used to estimate the concentrations. We offer a derivation that results in an equation that is guaranteed to have a solution and can be easily and accurately solved computationally with bisection root-finding methods. Finally, we also provide an approximate solution at dilute query concentrations that results in a closed form expression. This expression is used to improve the speed of the bi-section algorithm and also to find a closed form expression for the specificity ratios.

[[BibTeX or EndNote Entry via CiteULike|http://www.citeulike.org/user/stsaft/article/524016]]
S.A. Tsaftaris, A.K. Katsaggelos, T.N. Pappas and E.T. Papoutsakis, "DNA Based Matching of Digital Signals", in //Proceedings of IEEE International Conference on Acoustics, Speech, and Signal Processing//, vol. 5, pp. 581-584, Montreal, Quebec, Canada, 17-21 May 2004.
[ [[PDF|http://www.ece.northwestern.edu/~stsaft/papers/ICASSPd.pdf]] ] [ [[Google PDF Viewer|http://docs.google.com/viewer?url=http://www.ece.northwestern.edu/~stsaft/papers/ICASSPd.pdf]] ]

''Abstract''
Adleman with his pioneering work set the stage for the new field of bio-computing research.  His main idea was to use actual chemistry to solve problems that are either unsolvable by conventional computers, or require an enormous amount of computation.  The main focus of our research is to consider the application of molecular computing to the domain of digital signal processing (DSP).  In this paper we consider matching problems that arise in signal processing applications and are amenable to a ~DNA-based solution.  Digital data are encoded in DNA sequences using a sophisticated codeword set that satisfies the Noise Tolerance Constraint (NTC) that we introduce. NTC, one of the main contributions of our work, takes into account the presence of noise in digital signals by exploiting the annealing between non-perfect complementary sequences.  We propose an algorithm to map binary values into DNA codewords by satisfying a number of constraints, including the NTC.  Using that algorithm we retrieved 128 codewords that enables us to use a DNA based approach to digital signal matching.

[[BibTeX or EndNote Entry via CiteULike|http://www.citeulike.org/user/stsaft/article/523917]]
H. Wang, S.A. Tsaftaris and A.K. Katsaggelos, "Joint source-channel coding for wireless object-based video communications utilizing data hiding," //IEEE Trans. Image Processing//, vol. 15, no. 8, pp. 2158-2169, Aug. 2006. [[PDF|http://www.ece.northwestern.edu/~stsaft/papers/WangTsaftKats_TIP06.pdf]] [ [[Google PDF Viewer|http://docs.google.com/viewer?url=http://www.ece.northwestern.edu/~stsaft/papers/WangTsaftKats_TIP06.pdf]] ]

[[BibTeX or EndNote Entry via CiteULike|http://www.citeulike.org/user/stsaft/article/796845]]
S.A. Tsaftaris and A.K. Katsaggelos, "A new codeword design algorithm for DNA based Storage and retrieval of digital signals," in Preproceedings of the //11th International Meeting on ~DNA-based computers DNA 11 //, London, Ontario, Canada, 2005.
[<img[IEEE PROCEEDINGS COVER|http://www.ece.northwestern.edu/~stsaft/media/ieeeproc_cover.png]] S.A. Tsaftaris and A.K. Katsaggelos, "The Not So Digital Future of Digital Signal Processing," in //Proceedings of the IEEE//, vol. 96, no. 3, March 2008.
[ [[PDF|http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris08TheNotSo.pdf]] ] [ [[Google PDF Viewer|http://docs.google.com/viewer?url=http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris08TheNotSo.pdf]] ]

[[BibTeX or EndNote Entry via CiteULike|http://www.citeulike.org/user/stsaft/article/2461515]]
S.A. Tsaftaris and A.K. Katsaggelos, "On Designing DNA Databases for the Storage and Retrieval of Digital Signals", International Conference on Natural Computation, special session on Recent Advances in Biomolecular Computing, Changsha, China, August 26-29, 2005, //Lecture Notes in Computer Science//, vol. 3611, pp. 1192-1201, Jul 2005.
[ [[PDF|http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris_LNCS_china.pdf]] ] [ [[Google PDF Viewer|http://docs.google.com/viewer?url=http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris_LNCS_china.pdf]] ]

''Abstract''
In this paper we propose a procedure for the storage and retrieval of digital signals utilizing DNA. Digital signals are encoded in DNA sequences that satisfy among other constraints the Noise Tolerance Constraint (NTC) that we have previously introduced. NTC takes into account the presence of noise in digital signals by exploiting the annealing between non-perfect complementary sequences. We discuss various issues arising from the development of ~DNA-based database solutions (i) in vitro (in test tubes, or other materials) for short-term storage and (ii) in vivo (inside organisms) for long-term storage. We discuss the benefits and drawbacks of each scheme and its effects on the codeword design problem and performance. We also propose a new way of constructing the database elements such that a short-term database can be converted into a long term one and vice versa without the need for a re-synthesis. The latter improves efficiency and reduces the cost of a long-term database.

[[BibTeX or EndNote Entry via CiteULike|http://www.citeulike.org/user/stsaft/article/523990]]
S.A. Tsaftaris and A.K. Katsaggelos, “Retrieval Efficiency of ~DNA-Based Databases of Digital Signals,” //IEEE Transactions on ~NanoBioscience//, in press.
Preprint: [ [[PDF|http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris_TNB_2009.pdf]] ] [ [[Google PDF Viewer|http://docs.google.com/viewer?url=http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris_TNB_2009.pdf]] ]
S.A. Tsaftaris, V. Hatzimanikatis, and A.K. Katsaggelos, "DNA Hybridization as a Similarity Criterion for Querying Digital Signals Stored in DNA Databases", in //Proceedings of 31st International Conference on Acoustics, Speech, and Signal Processing (ICASSP)//, Toulouse, France, May 14-19, vol. 2, pp. ~II-1084 - ~II-1087, 2006.
[ [[PDF|http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris_ICASSP_2006.pdf]] ] [ [[Google PDF Viewer|http://docs.google.com/viewer?url=http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris_ICASSP_2006.pdf]] ]

''Abstract''
We demonstrate via simulation that hybridization of DNA molecules can be used as a similarity criterion for retrieving digital signals encoded and stored in a synthesized DNA database. After introducing some necessary DNA terminology, we briefly explain how digital signals are transformed to DNA sequences. Since retrieval is achieved through hybridization of query and data carrying DNA molecules, we present a mathematical model to estimate hybridization efficiency (also known as selectivity annealing). We show that selectivity annealing is inversely proportional to the mean squared error (MSE) of the encoded signal values. In addition, we show that the concentration of the molecules plays the same role as the decision threshold employed in digital signal matching algorithms. Finally, similar to the digital domain, we define a DNA signal-to-noise ratio (SNR) measure to assess the performance of the ~DNA-based retrieval scheme. Simulations are presented to validate our arguments.

[[BibTeX or EndNote Entry via CiteULike|http://www.citeulike.org/user/stsaft/article/796846]]
R. Dharmakumar, Z. Zhang, I. Koktzoglou, S.A. Tsaftaris, D. Li, “Dual Contrast Cellular MRI,” //Molecular Imaging//, in press. 
S.A. Tsaftaris, V. Hatzimanikatis, and A.K. Katsaggelos, "DNA as a Medium for Storing Digital Signals", in //Proceedings Artificial Life X//, Bloomington, Indiana USA, June 3-7, 2006, pp. 303-309, Luis Mateus Rocha (Editor), The MIT Press, Aug 1, 2006. [[book@Amazon|http://www.amazon.com/Artificial-Life-Proceedings-International-Conference/dp/0262681625]]
S.A. Tsaftaris, and A.K. Katsaggelos, "Retrieval Accuracy of Very Large ~DNA-Based Databases of Digital Signals," In //Proceedings of European Signal Processing Conference (~EUSIPCO-2007)//, Poznań, Polland, September 3-7, pp. 1561 - 1565, 2007
[ [[PDF|http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris_EUSIPCO_2007.pdf]] ] [ [[Google PDF Viewer|http://docs.google.com/viewer?url=http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris_EUSIPCO_2007.pdf]] ]

''Abstract''
In this paper a simulation of single query searches in very large ~DNA-based databases that are capable of storing and retrieving digital signals is presented. Similarly to the digital domain, a signal-to-noise ratio (SNR) measure to assess the performance of the ~DNA-based retrieval scheme in terms of database size and source statistics is defined. With approximations, it is shown that the SNR of any finite size DNA based database is upper bounded by the SNR of an infinitely large one with the same source distribution. Computer simulations are presented to validate the theoretical outcomes.

[[BibTeX or EndNote Entry via CiteULike|http://www.citeulike.org/user/stsaft/article/1682938]]
S.A. Tsaftaris, R. Ahuja, D. Shiell, and A.K. Katsaggelos, "DNA Microarray Image Intensity Extraction Using Eigenspots," In //Proceedings International Conference on Image Processing (ICIP)//, September 16-19 2007, San Antonio Texas.
[ [[PDF|http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris_ICIP_2007.pdf]] ] [ [[Google PDF Viewer|http://docs.google.com/viewer?url=http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris_ICIP_2007.pdf]] ]

''Abstract''
DNA microarrays are commonly used in the rapid analysis of gene expression in organisms.  Image analysis is used to measure the average intensity of circular image areas (spots), which correspond to the level of expression of the genes. A crucial aspect of image analysis is the estimation of the background noise.  Currently, background subtraction algorithms are used to estimate the local background noise and subtract it from the signal.  In this paper we use Principal Component Analysis (PCA) to de-correlate the signal from the noise, by projecting each spot on the space of eigenvectors, which we term eigenspots. PCA is well suited for such application due to the structural nature of the images. To compare the proposed method with other background estimation methods we use the industry standard signal to noise metric xdev.

[[BibTeX or EndNote Entry via CiteULike|http://www.citeulike.org/user/stsaft/article/1683343]]
I. Koktzoglou, S.A. Tsaftaris, S. Zuelhlsdorff, D. Li, A.K. Katsaggelos, and R. Dharmakumar, "Automated Tracking of a Passive Intramyocardial Needle with ~Off-Resonance MRI A feasibility Study," presented at //Society of Cardiovascular Magnetic Resonance//, January 2008.
I. Koktzoglou, S.A. Tsaftaris, S. Zuelhlsdorff, D. Li, A.K. Katsaggelos, and R. Dharmakumar, "Automated Tracking of a Passive Endomyocardial Stiletto Catheter with Dephased FLAPS MRI: A feasibility Study," to appear // The 16th Meeting of the International Society for Magnetic Resonance in Medicine//, Toronto, Canada, May 2008.
S. A. Tsaftaris, V. Andermatt, A. Schlegel, A. K. Katsaggelos, D. Li, and R. Dharmakumar, "A Dynamic Programming Solution to Tracking and Elastically Matching Left Ventricular Walls in Cardiac CINE MRI," in //Proceedings of International Conference on Image Processing (ICIP)//, 2008.
[ [[PDF|http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris_ICIP_2008_MRI.pdf]] ] [ [[Google PDF Viewer|http://docs.google.com/viewer?url=http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris_ICIP_2008_MRI.pdf]] ]

''Abstract''
In this paper an algorithm to detect and elastically match the contours of the epicardial walls of the left ventricle (LV) in cardiac phase-resolved 2-D Magnetic Resonance (MR) images is presented. For both tasks, dynamic programming (DP) is used. A mask conforming to the six segment model of the LV is fitted on a reference image and propagated utilizing the elastic matching information. At its present form the algorithm requires minimal parameter corrections among different sets of cine MRI images. Future extensions include comparisons with contours hand labeled by imaging experts.

[[BibTeX or EndNote Entry via CiteULike|http://www.citeulike.org/user/stsaft/article/3883789]]
E. Maani, S. A. Tsaftaris, and A. K. Katsaggelos, "Local Feature Extraction for Video Copy Detection in a Database," in //Proceedings of International Conference on Image Processing (ICIP)//, 2008.
[ [[PDF|http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris_ICIP_2008_EHSAN.pdf]] ] [ [[Google PDF Viewer|http://docs.google.com/viewer?url=http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris_ICIP_2008_EHSAN.pdf]] ]

''Abstract''
In this paper a new content-based copy identification method for video sequences is presented that is robust to a number of image transformations and particulary robust to compression artifacts. A scale and rotation invariant local image descriptor for corner points in detected key frames is proposed based on a generalized radon transform. In addition, a distance similarity metric is used that fuses intensity and geometry information to compare key frames extracted using a scene detection algorithm. Furthermore, to achieve low querying computational complexity a DP approach is employed. Experimental results demonstrate the effectiveness of the proposed approach.

[[BibTeX or EndNote Entry via CiteULike|http://www.citeulike.org/user/stsaft/article/3883351]]
S. A. Tsaftaris,  J. Zujovic, and A. K. Katsaggelos, "Automated Line Flattening of Atomic Force Microscopy Images," in //Proceedings of International Conference on Image Processing (ICIP)//, 2008.
[ [[PDF|http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris_ICIP_2008_AFM.pdf]] ] [ [[Google PDF Viewer|http://docs.google.com/viewer?url=http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris_ICIP_2008_AFM.pdf]] ]

''Abstract''
In this paper, an automated algorithm to flatten lines from Atomic Force Microscopy (AFM) images is presented. Due to the mechanics of the AFM, there is a curvature distortion (bowing effect) present in the acquired images. At present, flattening such images requires human intervention to manually segment object data from the background, which is time consuming and highly inaccurate. The proposed method classifies the data into objects and background, and fits convex lines in an iterative fashion. Results on real images from DNA wrapped carbon nanotubes (~DNA-CNTs) and synthetic experiments are presented, demonstrating the effectiveness of the proposed algorithm in increasing the resolution of the surface to-pography.     

[[BibTeX or EndNote Entry via CiteULike|http://www.citeulike.org/user/stsaft/article/4057459]]
S. A. Tsaftaris,  J. Zujovic, and A. K. Katsaggelos, "Restoration of the Cantilever Bowing Distortion in Atomic Force Microscopy," in //Proceedings of 16th  European Signal Processing Conference//, Lausanne, Switzerland, August 2008.
[ [[PDF|http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris_EUSIPCO_2008_AFM.pdf]] ] [ [[Google PDF Viewer|http://docs.google.com/viewer?url=http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris_EUSIPCO_2008_AFM.pdf]] ]

''Abstract''
Due to the mechanics of the Atomic Force Microscope (AFM), there is a curvature distortion (bowing effect) present in the acquired images. At present, flattening such images requires human intervention to manually segment object data from the background, which is time consuming and highly inaccurate. In this paper, an automated algorithm to flatten lines from AFM images is presented. The proposed method classifies the data into objects and background, and fits convex lines in an iterative fashion. Results on real images from DNA wrapped carbon nanotubes (~DNA-CNTs) and synthetic experiments are presented, demonstrating the effectiveness of the proposed algorithm in increasing the resolution of the surface topography.  In addition a link between the flattening problem and MRI inhomogeneity (shading) is given and the proposed method is compared to an entropy based MRI inhomogeniety correction method.   

[[BibTeX or EndNote Entry via CiteULike|http://www.citeulike.org/user/stsaft/article/4057459]]
X. Zhou, R. Tang, R. Klein, S.A. Tsaftaris, D. Li, R. Dharmakumar, “Impact of Temporal Resolution on Cardiac ~Phase-Resolved ~Oxygen-Sensitive Myocardial ~Steady-State Free Precession Imaging,” //Society for Cardiovascular Magnetic Resonance//, Florida, 2009.

[[Full Paper and Citation via JCMR-Online|http://jcmr-online.com/content/11/S1/P178]]
S.A. Tsaftaris, R. Tang, R. Klein, D. Li, R. Dharmakumar, “Visualizing Regional Myocardial Oxygenation Changes with Statistically Optimal Colormaps,” //Society for Cardiovascular Magnetic Resonance//, Florida, 2009.

[[Full Paper and Citation via JCMR-Online|http://jcmr-online.com/content/11/S1/P276]]
S.A. Tsaftaris, X. Zhou, R. Tang, R. Klein, R. Dharmakumar, “An Automated Method for Left Ventricular Localization and Identification of ~End-Systolic and ~End-Diastolic Images from Cine Cardiac MRI,” //Society for Cardiovascular Magnetic Resonance//, Florida, 2009.

[[Full Paper and Citation via JCMR-Online|http://jcmr-online.com/content/11/S1/P222]]
S.A. Tsaftaris, R. Tang, R. Klein, D. Li, R. Dharmakumar, “Visualizing and Quantifying Myocardial Oxygenation Changes with Statistically Optimal Colormaps,” to be presented at //The 17th Meeting of the International Society for Magnetic Resonance in Medicine//, Hawaii, 2009.
S.A. Tsaftaris, X. Zhou, R. Tang, R. Klein, R. Dharmakumar, “An Intensity Based Statistical Approach for Left Ventricular Localization and Identification of ~End-Systolic and ~End-Diastolic Images from Cine Cardiac MRI,” to appear at //The 17th Meeting of the International Society for Magnetic Resonance in Medicine//, Hawaii, 2009.
R. Dharmakumar, I. Koktoglou, S.A. Tsaftaris, S. Zuehlsdorff, R. Tang, W. Graham, D. Li, “Visualization and Tracking of a Conventional Guidewire with Low Flip Angle SSFP Imaging: An Initial Study,” The //17th Meeting of the International Society for Magnetic Resonance in Medicine//, Hawaii, 2009.
S.A. Tsaftaris and C. Noutsos, “Plant Phenotyping with Low Cost Digital Cameras and Image Analytics,” in //Proceedings of the 4th International Symposium on Information Technologies in Environmental Engineering//, May 2009, Thessaloniki, Greece. [ [[PDF|http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris_phenotyping_final.pdf]] ] [ [[Google PDF Viewer|http://docs.google.com/viewer?url=http://www.ece.northwestern.edu/~stsaft/papers/Tsaftaris_phenotyping_final.pdf]] ]

[[BibTeX or EndNote Entry via CiteULike|http://www.citeulike.org/user/stsaft/article/5880470]]
This site runs on a completely self-contained personal wiki called a [[TiddlyWiki|http://www.tiddlywiki.com]] sometimes shorthanded as TW, invented by a fellow named Jeremy Ruston. I use TW version <<version>>.

It is really easy to edit and makes updating a breeze.  All the text, except PDF's, and pictures is included in one HTML file.

It works great with Firefox, good with Internet Explorer but ok with Opera. Please let me know if you have any problems browsing my pages.

!Navigating this site
Navigating this site is a bit tricky and un-conventional.  Each page is not static.  It is created dynamically according to what you have clicked on.  Everything you click on opens in front of you while 'older' stuff are below in the page.  If you open something that its not to your interest you can close it.

You can use the left navigation bar to open major areas.  You can also use the search to find things that you like.  Phrases underlined are external links while phrases in bold blue font are internal links.

!If you came thru Google Search
If you came thru google search due to the uniqueness of TiddlyWiki the web result you saw on google may not appear on this website.  To find it just remember the query items and put on the search line to the upper right of this page. You should be able to find the item you were looking for.

!My Links/Tips on TiddlyWiki
Here you can find information on @@How To@@ start a website based on TiddlyWiki.  Basically its a collection for all the things I had to do to make this site work, with favicon, nice search, automatic lists, etc
<<listTags tips>>


* ([[My CV|http://www.ece.northwestern.edu/~stsaft/media/tsaftaris_CV.pdf]] | PDF)
Dr. Sotirios A. Tsaftaris was born in Thessaloniki Greece in 1978.  He currently is a Research Assistant Professor at Northwestern University, Dept. of Electrical and Computer Engineering.  He received his ~PhD and ~MSc from [[Northwestern University|http://www.northwestern.edu]] in June 2006 and 2003 respectively under the guidance of [[Professor Aggelos Katsaggelos|http://www.eecs.northwestern.edu/%7Eaggk/]]. He received his Diploma in 2000 from the Dept. of Electrical and Computer Engineering of the Aristotle University of Thessaloniki.  His thesis title was "Copyright protection of MPEG 1 and 2 Video Sequences using Digital watermarking techniques" and was supervised by [[Prof. Michael-Gerasimos Strintzis|http://www.iti.gr/db.php/en/people/Michael_Gerasimos_Strintzis.html]].  In 1995 he graduated from the [[Experimental high school of Aristotle University of Thessaloniki|http://web.auth.gr/piramatiko/]].

He was awarded the Murphy Fellowship of Northwestern University and the //Alexander S. Onassis Postgraduate Scholarship// from the [[Alexander S. Onassis Public Benefit Foundation|http://www.onassis.gr/]].  He was also awarded the top class award by the [[Technical Chamber of Greece (TEE)|http://www.tee.gr/]].

After his Bachelor's degree, he worked for a year as a researcher for the [[Informatics and Telematics Institute|http://www.iti.gr/]] of the [[Center for Research and Technology - Hellas|http://www.certh.gr/]].  He has published many papers on DNA computing and digital watermarking.  In parallel, he was working as a technology consultant for Gnosi Anaptyxiaki and he also participated as project leader in the start-up company Multimedia Lab of Thessaloniki.  In the past, he has also worked as a system administrator and designer for [[Antisel S.A|http://www.antisel.gr/]] and ~Makridis-Goras Sport ~Photo-Reportage Studio.

He is a member of the [[International Society for Magnetic Resonance in Medicine|http://www.ismrm.org]], [[International Society of Nanoscale Science, Computation, and Engineering|http://www.isnsce.org/]], the [[IEEE|http://www.ieee.org]], the [[Hellenic Association of Mechanical & Electrical Engineers (HAMEE)|http://www.tee.gr/teeassoc/silogoi/psdmh/index.htm]] and the [[Technical Chamber of Greece (TEE)|http://www.tee.gr/]].

He has consulted for a number of companies and non-profits.

See my [[CV]] and [[NIH Style Biosketch]] for more details.

He has also served for numerous committees for the ECE department and Northwestern University. Some of those are, the Computing Facilities, the Graduate and the Curiculum Committee of the ECE Department and the Graduate Student Advisor Committee of the ~McCormick School of Engineering and Applied Sciences.

He speaks English and French. He has experience in biotechnology, imaging modalities for nanotechnology, MPEG engineering and has an excellent knowledge of programming languages such as C, C++, and Matlab.

His main research interests are DNA computing, bioinformatics and nanotechnology and commercialization of those as well as in MRI, molecular and cellular imaging, and nano-imaging. He is also interested in Digital Watermarking, International Standards, Image and Video Coding, Video Editing, Computer Networking. 
The titles of my book chapters articles are the following. Click on each to retrieve full reference and abstract.
<<listTags BookChapter>>
[[ Six page CV in PDF|http://www.ece.northwestern.edu/~stsaft/media/tsaftaris_CV.pdf]]   
[[Six page CV in Google PDF Viewer|http://docs.google.com/viewer?url=http://www.ece.northwestern.edu/~stsaft/media/tsaftaris_CV.pdf]] 
Codeword Design is the problem of encoding a mathematical problem into DNA sequences such that it can be solved using techniques from DNA Computing.  My work deals with Noise Tolerant Codewords where a new constraint the Noise Tolerance Constraint (NTC) is enforced.  The NTC is necessary such that similarity matches can be achieved.
Rename this tiddler to 'ColorPalette' to enable this color scheme

Background: #ffc
Foreground: #000
PrimaryPale: #fc8
PrimaryLight: #f81
PrimaryMid: #b40
PrimaryDark: #410
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #e88
TertiaryLight: #c66
TertiaryMid: #944
TertiaryDark: #633
The titles of my journal articles are the following. Click on each to retrieve full reference and abstract and an electronic copy.
<<listTags Conference>>
''Sotirios A. Tsaftaris''

''Email'': <<email s-tsaftaris at northwestern dot edu>>

''Phone and address are provided upon email communication for security and privacy reasons.''
Work on Copyright Protection
/***
|''Name:''|CryptoFunctionsPlugin|
|''Description:''|Support for cryptographic functions|
***/
//{{{
if(!version.extensions.CryptoFunctionsPlugin) {
version.extensions.CryptoFunctionsPlugin = {installed:true};

//--
//-- Crypto functions and associated conversion routines
//--

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

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

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

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

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

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

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

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

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

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

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

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


}
//}}}
[img[Three EigenSpots|http://www.ece.northwestern.edu/~stsaft/media/eigen.png]]
DNA microarray images are comprised of thousands of near circular spots arranged in a regular grid structure. The spot intensity is related to the concentration of a gene in a genomic sample. A new method to estimate the spot intensity without resorting to heuristic methods was found. The method relies on the principal component analysis of the image information of spots and the projection of data to the eigenvectors of spots, termed ''~EigenSpots'' (see picture to the left). Using this method, global and local noise can be removed more efficiently.

Relevant Publications:
<<forEachTiddler
 where
        'tiddler.tags.contains("Microarray Imaging") && tiddler.tags.contains("paper")'
write
        '((index == 0) ? "" : ", ")+"[["+tiddler.title+"]]"'
>> 
I am investigating alternative media for storing digital signals.  Currenlty I am interested in DNA.

This a test
Before the digital revolution, image and signal processing was performed using analog circuitry.  Today digital signal processing (DSP) has defined our lives.  Although some mixed-signal designs are of current interest, DSP dominates everything that we own or use everyday. DSP chips exist in many devices such as our cell phones, our iPODs, our wireless router, our new HDTV.

The purpose of my research is to consider possibilities of doing DSP outside the semiconductor or electronic domain.  Organic elements (such as DNA, polymers) that conduct electricity can be used to built organic semiconductors at the molecular level, However, more fundamental questions can be asked. Can DSP be performed in exotic materials, such as chemical substrates, cells, organisms, or even DNA, without the use of electrical currents? Will we be able to built fully blown DSP systems out of these materials? Or will some DSP functions (such as storage and data archiving) be implemented with such materials?

[img[DNA DSP|http://www.ece.northwestern.edu/~stsaft/media/DNAdisp.png]]
These are questions that are worth answering. Our group at Northwestern has been studying the use of DNA for DSP for a number of years.  Due to its unique characteristics, DNA is an excellent medium for storing information, resulting in the creation of a ~DNA-based database of digital signals. Digital samples can be recorded in DNA strands using, instead of their binary representation, the quaternary DNA alphabet.  After they are synthesized, DNA sequences can be either kept in liquid form placed in test tubes, freeze-dried to save volume, or even piggybacked among the genome of organisms (i.e., bacteria).  Once they are stored, DNA sequences can be replicated economically with commonly used laboratory techniques.  This allows for the creation of database replicas with low cost.  The most attractive aspect of this approach is that querying the database can be implemented with a plethora of techniques. With digital databases the query time increases proportionally to the size of the database. However, in DNA databases with appropriate search mechanisms, the querying time is independent of the database size, under certain conditions.

For more information you can look my papers [[06. The Not So Digital Future of Digital Signal Processing]] and [[03. How can DNA-Computing be applied in Digital Signal Processing?]].  If you want an introduction on DNA Computing from a signal processing viewpoint see the similarly titled paper [[02. DNA computing from a signal processing viewpoint]].
I am interesting in alternative methods for storing digital signals.  I am currently investigating DNA as such medium.

See related publications.
<<listTags Simulation>>
[<img[Idea Chart|http://www.ece.northwestern.edu/~stsaft/media/DNAstorage.png]]DNA offers significant advantages when compared to other media for storing digital signals or data.  DNA, especially in its double stranded form, is very stable, compact, and inexpensive.  DNA hybridization is an ideal retrieval mechanism that can even utilize a similarity criterion.  Furthermore, due to the unique properties of the hybridization reaction, the retrieval complexity, as expressed by the search time, does not depend on the size of the database (number of stored records).  In addition, DNA hybridization allows for parallel retrieval.

The first step towards developing a ~DNA-based storage and similarity-based retrieval system is the word design problem (that is the encoding of digital signals into DNA).  This problem is rather well suited to signal processing research because it is closely related to source and channel coding.  I have focused most of my work on finding efficient algorithms that can satisfy certain thermodynamic and kinetic-based constraints.  For more information see [[Codeword Design]].

A critical element of any word design problem is the ability to simulate the behavior of DNA molecular systems.  I have developed models that estimate the thermodynamic and kinetic behavior (see [[Simulations]] of DNA molecules. Therefore, words can be designed more efficiently and the efficiency and performance of a DNA database can be estimated.  This type of work can be readily applied to many biotechnological protocols that rely on DNA to DNA interactions (see [[Molecular Computing for Biotechnology]]).

See <<forEachTiddler
 where
        'tiddler.tags.contains("DNABasedStorageofSignals")'
write
        '((index == 0) ? "" : ", ")+"[["+tiddler.title+"]]"'
>> for more information.

I base my research on [[simulations|Simulations]] but I am interested in experimental verification as well.  If you are interested in this research and you would like to fund our efforts please [[Contact]] me.
[[Welcome]]
/***
|''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");

}
//}}}
A newly funded project to digitally restore a painting from the collection of [[The Art Institute of Chicago |http://www.artic.edu/aic/]] .  Due to confidentiality agreements further information cannot be divulged.
In TiddlyWiki you can define certain operators to format text, paragraphs etc. Below are some links on how you can do that.
* [[Extended Text Formatting|http://www.tiddlywiki.com/#ExtendedFormatting]]
* [[Date Formatting|http://www.tiddlywiki.com/#DateFormatString]]
* [[Bullets and Numbering|http://www.tiddlywiki.com/#BlockQuotes%20BulletPoints%20NumberedBulletPoints]]
* Commenting text enclose it in {{{/%}}} something {{{%/}}}. [[Source|http://www.tiddlywiki.com/#TiddlerComments]]
// //''Name:'' EmailLink
// //''Version:'' <<getversion email>> (<<getversiondate email "DD MMM YYYY">>)
// //''Author:'' AlanHecht
// //''Type:'' [[Macro|Macros]]

// //''Description:'' email lets you list a "email" address without displaying it as readable text.  This helps prevent your email address from being harvested by search engines and other web crawlers that read your page's contents. Using email, you type in the words "at" and "dot" instead of the punctuation symbols and add spaces inbetween words to disguise your address. However, email will display your email address in a web browser so that humans can read it. And email turns the address into a hyperlink that can be clicked to send you an instant email.

// //''Syntax:'' << {{{email SoToS at yourdomain dot com "?optional parameters"}}} >>
// //Example 1: <<email sample at nowhere dot com>> (standard)
// //Example 2: <<email multiple dot sample at somewhere dot nowhere dot com>> (multiple dots)
// //Example 3: <<email sample at nowhere dot com "?subject=Submission&body=Type your message here.">> (with optional parameters)

// //''Directions:'' <<tiddler MacroDirections>>

// //''Notes:'' You can use the optional email parameters to stipulate a subject or message body for the message.  Most (not all) email clients will use this information to construct the email message.

// //''Related Links:'' none

// //''Revision History:''
// // v0.1.0 (20 July 2005): initial release
// // v0.1.1 (22 July 2005): renamed the macro from "mailto" to "email" to further thwart email harvesters.
// // v0.1.2 (15 October 2005): added global replacement of "dots" thanks to a suggestion from Ralph Winter

// //''Code section:''
version.extensions.email = {major: 0, minor: 1, revision: 2, date: new Date("Oct 15, 2005")};
config.macros.email = {}
config.macros.email.handler = function(place,macroName,params)
{
var temp = params.join(" ");
data = temp.split("?");
var recipient = data[0];
recipient = recipient.replace(" at ","@").replace(" dot ",".","g");
recipient = recipient.replace(/\s/g,"");
var optional = data[1] ? "?" + data[1] : "";
var theLink = createExternalLink(place,"ma"+"il"+"to:"+recipient+optional);
theLink.appendChild(document.createTextNode(recipient))
}
Publications on Error Correction with Data Hiding
/***
|''Name:''|ForEachTiddlerPlugin|
|''Version:''|1.0.5 (2006-02-05)|
|''Source:''|http://tiddlywiki.abego-software.de/#ForEachTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license]]|
|''Macros:''|[[ForEachTiddlerMacro]] v1.0.5|
|''TiddlyWiki:''|1.2.38+, 2.0|
|''Browser:''|Firefox 1.0.4+; Firefox 1.5; InternetExplorer 6.0|
!Description

Create customizable lists, tables etc. for your selections of tiddlers. Specify the tiddlers to include and their order through a powerful language.

''Syntax:''
|>|{{{<<}}}''forEachTiddler'' [''in'' //tiddlyWikiPath//] [''where'' //whereCondition//] [''sortBy'' //sortExpression// [''ascending'' //or// ''descending'']] [''script'' //scriptText//] [//action// [//actionParameters//]]{{{>>}}}|
|//tiddlyWikiPath//|The filepath to the TiddlyWiki the macro should work on. When missing the current TiddlyWiki is used.|
|//whereCondition//|(quoted) JavaScript boolean expression. May refer to the build-in variables {{{tiddler}}} and {{{context}}}.|
|//sortExpression//|(quoted) JavaScript expression returning "comparable" objects (using '{{{<}}}','{{{>}}}','{{{==}}}'. May refer to the build-in variables {{{tiddler}}} and {{{context}}}.|
|//scriptText//|(quoted) JavaScript text. Typically defines JavaScript functions that are called by the various JavaScript expressions (whereClause, sortClause, action arguments,...)|
|//action//|The action that should be performed on every selected tiddler, in the given order. By default the actions [[addToList|AddToListAction]] and [[write|WriteAction]] are supported. When no action is specified [[addToList|AddToListAction]] is used.|
|//actionParameters//|(action specific) parameters the action may refer while processing the tiddlers (see action descriptions for details). <<tiddler [[JavaScript in actionParameters]]>>|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|

See details see [[ForEachTiddlerMacro]] and [[ForEachTiddlerExamples]].

!Revision history
* v1.0.5
** Pass tiddler containing the macro with wikify, context object also holds reference to tiddler containing the macro ("inTiddler"). Thanks to SimonBaird.
** Support Firefox 1.5.0.1
** Internal
*** Make "JSLint" conform
*** "Only install once"
* v1.0.4 (2006-01-06)
** Support TiddlyWiki 2.0
* v1.0.3 (2005-12-22)
** Features:
*** Write output to a file supports multi-byte environments (Thanks to Bram Chen)
*** Provide API to access the forEachTiddler functionality directly through JavaScript (see getTiddlers and performMacro)
** Enhancements:
*** Improved error messages on InternetExplorer.
* v1.0.2 (2005-12-10)
** Features:
*** context object also holds reference to store (TiddlyWiki)
** Fixed Bugs:
*** ForEachTiddler 1.0.1 has broken support on win32 Opera 8.51 (Thanks to BrunoSabin for reporting)
* v1.0.1 (2005-12-08)
** Features:
*** Access tiddlers stored in separated TiddlyWikis through the "in" option. I.e. you are no longer limited to only work on the "current TiddlyWiki".
*** Write output to an external file using the "toFile" option of the "write" action. With this option you may write your customized tiddler exports.
*** Use the "script" section to define "helper" JavaScript functions etc. to be used in the various JavaScript expressions (whereClause, sortClause, action arguments,...).
*** Access and store context information for the current forEachTiddler invocation (through the build-in "context" object) .
*** Improved script evaluation (for where/sort clause and write scripts).
* v1.0.0 (2005-11-20)
** initial version

!Code
***/
//{{{


//============================================================================
//============================================================================
// ForEachTiddlerPlugin
//============================================================================
//============================================================================

// Only install once
if (!version.extensions.ForEachTiddlerPlugin) {

version.extensions.ForEachTiddlerPlugin = {major: 1, minor: 0, revision: 5, date: new Date(2006,2,5), source: "http://tiddlywiki.abego-software.de/#ForEachTiddlergPlugin"};

// For backward compatibility with TW 1.2.x
//
if (!TiddlyWiki.prototype.forEachTiddler) {
 TiddlyWiki.prototype.forEachTiddler = function(callback) {
 for(var t in this.tiddlers) {
 callback.call(this,t,this.tiddlers[t]);
 }
 };
}

//============================================================================
// forEachTiddler Macro
//============================================================================

version.extensions.forEachTiddler = {major: 1, minor: 0, revision: 5, date: new Date(2006,2,5), provider: "http://tiddlywiki.abego-software.de"};

// ---------------------------------------------------------------------------
// Configurations and constants
// ---------------------------------------------------------------------------

config.macros.forEachTiddler = {
 // Standard Properties
 label: "forEachTiddler",
 prompt: "Perform actions on a (sorted) selection of tiddlers",

 // actions
 actions: {
 addToList: {},
 write: {}
 }
};

// ---------------------------------------------------------------------------
// The forEachTiddler Macro Handler
// ---------------------------------------------------------------------------

config.macros.forEachTiddler.getContainingTiddler = function(e) {
 while(e && !hasClass(e,"tiddler"))
 e = e.parentNode;
 var title = e ? e.getAttribute("tiddler") : null;
 return title ? store.getTiddler(title) : null;
};

config.macros.forEachTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
 // config.macros.forEachTiddler.traceMacroCall(place,macroName,params,wikifier,paramString,tiddler);

 if (!tiddler) tiddler = config.macros.forEachTiddler.getContainingTiddler(place);
 // --- Parsing ------------------------------------------

 var i = 0; // index running over the params
 // Parse the "in" clause
 var tiddlyWikiPath = undefined;
 if ((i < params.length) && params[i] == "in") {
 i++;
 if (i >= params.length) {
 this.handleError(place, "TiddlyWiki path expected behind 'in'.");
 return;
 }
 tiddlyWikiPath = this.paramEncode((i < params.length) ? params[i] : "");
 i++;
 }

 // Parse the where clause
 var whereClause ="true";
 if ((i < params.length) && params[i] == "where") {
 i++;
 whereClause = this.paramEncode((i < params.length) ? params[i] : "");
 i++;
 }

 // Parse the sort stuff
 var sortClause = null;
 var sortAscending = true;
 if ((i < params.length) && params[i] == "sortBy") {
 i++;
 if (i >= params.length) {
 this.handleError(place, "sortClause missing behind 'sortBy'.");
 return;
 }
 sortClause = this.paramEncode(params[i]);
 i++;

 if ((i < params.length) && (params[i] == "ascending" || params[i] == "descending")) {
 sortAscending = params[i] == "ascending";
 i++;
 }
 }

 // Parse the script
 var scriptText = null;
 if ((i < params.length) && params[i] == "script") {
 i++;
 scriptText = this.paramEncode((i < params.length) ? params[i] : "");
 i++;
 }

 // Parse the action.
 // When we are already at the end use the default action
 var actionName = "addToList";
 if (i < params.length) {
 if (!config.macros.forEachTiddler.actions[params[i]]) {
 this.handleError(place, "Unknown action '"+params[i]+"'.");
 return;
 } else {
 actionName = params[i];
 i++;
 }
 }

 // Get the action parameter
 // (the parsing is done inside the individual action implementation.)
 var actionParameter = params.slice(i);


 // --- Processing ------------------------------------------
 try {
 this.performMacro({
 place: place,
 inTiddler: tiddler,
 whereClause: whereClause,
 sortClause: sortClause,
 sortAscending: sortAscending,
 actionName: actionName,
 actionParameter: actionParameter,
 scriptText: scriptText,
 tiddlyWikiPath: tiddlyWikiPath});

 } catch (e) {
 this.handleError(place, e);
 }
};

// Returns an object with properties "tiddlers" and "context".
// tiddlers holds the (sorted) tiddlers selected by the parameter,
// context the context of the execution of the macro.
//
// The action is not yet performed.
//
// @parameter see performMacro
//
config.macros.forEachTiddler.getTiddlersAndContext = function(parameter) {

 var context = config.macros.forEachTiddler.createContext(parameter.place, parameter.whereClause, parameter.sortClause, parameter.sortAscending, parameter.actionName, parameter.actionParameter, parameter.scriptText, parameter.tiddlyWikiPath, parameter.inTiddler);

 var tiddlyWiki = parameter.tiddlyWikiPath ? this.loadTiddlyWiki(parameter.tiddlyWikiPath) : store;
 context["tiddlyWiki"] = tiddlyWiki;

 // Get the tiddlers, as defined by the whereClause
 var tiddlers = this.findTiddlers(parameter.whereClause, context, tiddlyWiki);
 context["tiddlers"] = tiddlers;

 // Sort the tiddlers, when sorting is required.
 if (parameter.sortClause) {
 this.sortTiddlers(tiddlers, parameter.sortClause, parameter.sortAscending, context);
 }

 return {tiddlers: tiddlers, context: context};
};

// Returns the (sorted) tiddlers selected by the parameter.
//
// The action is not yet performed.
//
// @parameter see performMacro
//
config.macros.forEachTiddler.getTiddlers = function(parameter) {
 return this.getTiddlersAndContext(parameter).tiddlers;
};

// Performs the macros with the given parameter.
//
// @param parameter holds the parameter of the macro as separate properties.
// The following properties are supported:
//
// place
// whereClause
// sortClause
// sortAscending
// actionName
// actionParameter
// scriptText
// tiddlyWikiPath
//
// All properties are optional.
// For most actions the place property must be defined.
//
config.macros.forEachTiddler.performMacro = function(parameter) {
 var tiddlersAndContext = this.getTiddlersAndContext(parameter);

 // Perform the action
 var actionName = parameter.actionName ? parameter.actionName : "addToList";
 var action = config.macros.forEachTiddler.actions[actionName];
 if (!action) {
 this.handleError(parameter.place, "Unknown action '"+actionName+"'.");
 return;
 }

 var actionHandler = action.handler;
 actionHandler(parameter.place, tiddlersAndContext.tiddlers, parameter.actionParameter, tiddlersAndContext.context);
};

// ---------------------------------------------------------------------------
// The actions
// ---------------------------------------------------------------------------

// Internal.
//
// --- The addToList Action -----------------------------------------------
//
config.macros.forEachTiddler.actions.addToList.handler = function(place, tiddlers, parameter, context) {
 // Parse the parameter
 var p = 0;

 // Check for extra parameters
 if (parameter.length > p) {
 config.macros.forEachTiddler.createExtraParameterErrorElement(place, "addToList", parameter, p);
 return;
 }

 // Perform the action.
 var list = document.createElement("ul");
 place.appendChild(list);
 for (var i = 0; i < tiddlers.length; i++) {
 var tiddler = tiddlers[i];
 var listItem = document.createElement("li");
 list.appendChild(listItem);
 createTiddlyLink(listItem, tiddler.title, true);
 }
};

// Internal.
//
// --- The write Action ---------------------------------------------------
//
config.macros.forEachTiddler.actions.write.handler = function(place, tiddlers, parameter, context) {
 // Parse the parameter
 var p = 0;
 if (p >= parameter.length) {
 this.handleError(place, "Missing expression behind 'write'.");
 return;
 }

 var textExpression = config.macros.forEachTiddler.paramEncode(parameter[p]);
 p++;

 // Parse the "toFile" option
 var filename = null;
 var lineSeparator = undefined;
 if ((p < parameter.length) && parameter[p] == "toFile") {
 p++;
 if (p >= parameter.length) {
 this.handleError(place, "Filename expected behind 'toFile' of 'write' action.");
 return;
 }

 filename = config.macros.forEachTiddler.getLocalPath(config.macros.forEachTiddler.paramEncode(parameter[p]));
 p++;
 if ((p < parameter.length) && parameter[p] == "withLineSeparator") {
 p++;
 if (p >= parameter.length) {
 this.handleError(place, "Line separator text expected behind 'withLineSeparator' of 'write' action.");
 return;
 }
 lineSeparator = config.macros.forEachTiddler.paramEncode(parameter[p]);
 p++;
 }
 }

 // Check for extra parameters
 if (parameter.length > p) {
 config.macros.forEachTiddler.createExtraParameterErrorElement(place, "write", parameter, p);
 return;
 }

 // Perform the action.
 var func = config.macros.forEachTiddler.getEvalTiddlerFunction(textExpression, context);
 var count = tiddlers.length;
 var text = "";
 for (var i = 0; i < count; i++) {
 var tiddler = tiddlers[i];
 text += func(tiddler, context, count, i);
 }

 if (filename) {
 if (lineSeparator !== undefined) {
 lineSeparator = lineSeparator.replace(/\\n/mg, "\n").replace(/\\r/mg, "\r");
 text = text.replace(/\n/mg,lineSeparator);
 }
 saveFile(filename, convertUnicodeToUTF8(text));
 } else {
 var wrapper = createTiddlyElement(place, "span");
 wikify(text, wrapper, null/* highlightRegExp */, context.inTiddler);
 }
};


// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

// Internal.
//
config.macros.forEachTiddler.createContext = function(placeParam, whereClauseParam, sortClauseParam, sortAscendingParam, actionNameParam, actionParameterParam, scriptText, tiddlyWikiPathParam, inTiddlerParam) {
 return {
 place : placeParam,
 whereClause : whereClauseParam,
 sortClause : sortClauseParam,
 sortAscending : sortAscendingParam,
 script : scriptText,
 actionName : actionNameParam,
 actionParameter : actionParameterParam,
 tiddlyWikiPath : tiddlyWikiPathParam,
 inTiddler : inTiddlerParam
 };
};

// Internal.
//
// Returns a TiddlyWiki with the tiddlers loaded from the TiddlyWiki of
// the given path.
//
config.macros.forEachTiddler.loadTiddlyWiki = function(path, idPrefix) {
 if (!idPrefix) {
 idPrefix = "store";
 }
 var lenPrefix = idPrefix.length;

 // Read the content of the given file
 var content = loadFile(this.getLocalPath(path));
 if(content === null) {
 throw "TiddlyWiki '"+path+"' not found.";
 }

 // Locate the storeArea div's
 var posOpeningDiv = content.indexOf(startSaveArea);
 var posClosingDiv = content.lastIndexOf(endSaveArea);
 if((posOpeningDiv == -1) || (posClosingDiv == -1)) {
 throw "File '"+path+"' is not a TiddlyWiki.";
 }
 var storageText = content.substr(posOpeningDiv + startSaveArea.length, posClosingDiv);

 // Create a "div" element that contains the storage text
 var myStorageDiv = document.createElement("div");
 myStorageDiv.innerHTML = storageText;
 myStorageDiv.normalize();

 // Create all tiddlers in a new TiddlyWiki
 // (following code is modified copy of TiddlyWiki.prototype.loadFromDiv)
 var tiddlyWiki = new TiddlyWiki();
 var store = myStorageDiv.childNodes;
 for(var t = 0; t < store.length; t++) {
 var e = store[t];
 var title = null;
 if(e.getAttribute)
 title = e.getAttribute("tiddler");
 if(!title && e.id && e.id.substr(0,lenPrefix) == idPrefix)
 title = e.id.substr(lenPrefix);
 if(title && title !== "") {
 var tiddler = tiddlyWiki.createTiddler(title);
 tiddler.loadFromDiv(e,title);
 }
 }
 tiddlyWiki.dirty = false;

 return tiddlyWiki;
};



// Internal.
//
// Returns a function that has a function body returning the given javaScriptExpression.
// The function has the parameters:
//
// (tiddler, context, count, index)
//
config.macros.forEachTiddler.getEvalTiddlerFunction = function (javaScriptExpression, context) {
 var script = context["script"];
 var functionText = "var theFunction = function(tiddler, context, count, index) { return "+javaScriptExpression+"}";
 var fullText = (script ? script+";" : "")+functionText+";theFunction;";
 return eval(fullText);
};

// Internal.
//
config.macros.forEachTiddler.findTiddlers = function(whereClause, context, tiddlyWiki) {
 var result = [];
 var func = config.macros.forEachTiddler.getEvalTiddlerFunction(whereClause, context);
 tiddlyWiki.forEachTiddler(function(title,tiddler) {
 if (func(tiddler, context, undefined, undefined)) {
 result.push(tiddler);
 }
 });
 return result;
};

// Internal.
//
config.macros.forEachTiddler.createExtraParameterErrorElement = function(place, actionName, parameter, firstUnusedIndex) {
 var message = "Extra parameter behind '"+actionName+"':";
 for (var i = firstUnusedIndex; i < parameter.length; i++) {
 message += " "+parameter[i];
 }
 this.handleError(place, message);
};

// Internal.
//
config.macros.forEachTiddler.sortAscending = function(tiddlerA, tiddlerB) {
 var result =
 (tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue)
 ? 0
 : (tiddlerA.forEachTiddlerSortValue < tiddlerB.forEachTiddlerSortValue)
 ? -1
 : +1;
 return result;
};

// Internal.
//
config.macros.forEachTiddler.sortDescending = function(tiddlerA, tiddlerB) {
 var result =
 (tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue)
 ? 0
 : (tiddlerA.forEachTiddlerSortValue < tiddlerB.forEachTiddlerSortValue)
 ? +1
 : -1;
 return result;
};

// Internal.
//
config.macros.forEachTiddler.sortTiddlers = function(tiddlers, sortClause, ascending, context) {
 // To avoid evaluating the sortClause whenever two items are compared
 // we pre-calculate the sortValue for every item in the array and store it in a
 // temporary property ("forEachTiddlerSortValue") of the tiddlers.
 var func = config.macros.forEachTiddler.getEvalTiddlerFunction(sortClause, context);
 var count = tiddlers.length;
 var i;
 for (i = 0; i < count; i++) {
 var tiddler = tiddlers[i];
 tiddler.forEachTiddlerSortValue = func(tiddler,context, undefined, undefined);
 }

 // Do the sorting
 tiddlers.sort(ascending ? this.sortAscending : this.sortDescending);

 // Delete the temporary property that holds the sortValue.
 for (i = 0; i < tiddlers.length; i++) {
 delete tiddlers[i].forEachTiddlerSortValue;
 }
};


// Internal.
//
config.macros.forEachTiddler.trace = function(message) {
 displayMessage(message);
};

// Internal.
//
config.macros.forEachTiddler.traceMacroCall = function(place,macroName,params) {
 var message ="<<"+macroName;
 for (var i = 0; i < params.length; i++) {
 message += " "+params[i];
 }
 message += ">>";
 displayMessage(message);
};


// Internal.
//
// Creates an element that holds an error message
//
config.macros.forEachTiddler.createErrorElement = function(place, exception) {
 var message = (exception.description) ? exception.description : exception.toString();
 return createTiddlyElement(place,"span",null,"forEachTiddlerError","<<forEachTiddler ...>>: "+message);
};

// Internal.
//
// @param place [may be null]
//
config.macros.forEachTiddler.handleError = function(place, exception) {
 if (place) {
 this.createErrorElement(place, exception);
 } else {
 throw exception;
 }
};

// Internal.
//
// Encodes the given string.
//
// Replaces
// "$))" to ">>"
// "$)" to ">"
//
config.macros.forEachTiddler.paramEncode = function(s) {
 var reGTGT = new RegExp("\\$\\)\\)","mg");
 var reGT = new RegExp("\\$\\)","mg");
 return s.replace(reGTGT, ">>").replace(reGT, ">");
};

// Internal.
//
// Returns the given original path (that is a file path, starting with "file:")
// as a path to a local file, in the systems native file format.
//
// Location information in the originalPath (i.e. the "#" and stuff following)
// is stripped.
//
config.macros.forEachTiddler.getLocalPath = function(originalPath) {
 // Remove any location part of the URL
 var hashPos = originalPath.indexOf("#");
 if(hashPos != -1)
 originalPath = originalPath.substr(0,hashPos);
 // Convert to a native file format assuming
 // "file:///x:/path/path/path..." - pc local file --> "x:\path\path\path..."
 // "file://///server/share/path/path/path..." - FireFox pc network file --> "\\server\share\path\path\path..."
 // "file:///path/path/path..." - mac/unix local file --> "/path/path/path..."
 // "file://server/share/path/path/path..." - pc network file --> "\\server\share\path\path\path..."
 var localPath;
 if(originalPath.charAt(9) == ":") // pc local file
 localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\");
 else if(originalPath.indexOf("file://///") === 0) // FireFox pc network file
 localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\");
 else if(originalPath.indexOf("file:///") === 0) // mac/unix local file
 localPath = unescape(originalPath.substr(7));
 else if(originalPath.indexOf("file:/") === 0) // mac/unix local file
 localPath = unescape(originalPath.substr(5));
 else // pc network file
 localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\");
 return localPath;
};

// ---------------------------------------------------------------------------
// Stylesheet Extensions (may be overridden by local StyleSheet)
// ---------------------------------------------------------------------------
//
setStylesheet(
 ".forEachTiddlerError{color: #ffffff;background-color: #880000;}",
 "forEachTiddler");

//============================================================================
// End of forEachTiddler Macro
//============================================================================


//============================================================================
// String.startsWith Function
//============================================================================
//
// Returns true if the string starts with the given prefix, false otherwise.
//
version.extensions["String.startsWith"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.startsWith = function(prefix) {
 var n = prefix.length;
 return (this.length >= n) && (this.slice(0, n) == prefix);
};



//============================================================================
// String.endsWith Function
//============================================================================
//
// Returns true if the string ends with the given suffix, false otherwise.
//
version.extensions["String.endsWith"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.endsWith = function(suffix) {
 var n = suffix.length;
 return (this.length >= n) && (this.right(n) == suffix);
};


//============================================================================
// String.contains Function
//============================================================================
//
// Returns true when the string contains the given substring, false otherwise.
//
version.extensions["String.contains"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.contains = function(substring) {
 return this.indexOf(substring) >= 0;
};

//============================================================================
// Array.indexOf Function
//============================================================================
//
// Returns the index of the first occurance of the given item in the array or
// -1 when no such item exists.
//
// @param item [may be null]
//
version.extensions["Array.indexOf"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.indexOf = function(item) {
 for (var i = 0; i < this.length; i++) {
 if (this[i] == item) {
 return i;
 }
 }
 return -1;
};

//============================================================================
// Array.contains Function
//============================================================================
//
// Returns true when the array contains the given item, otherwise false.
//
// @param item [may be null]
//
version.extensions["Array.contains"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.contains = function(item) {
 return (this.indexOf(item) >= 0);
};

//============================================================================
// Array.containsAny Function
//============================================================================
//
// Returns true when the array contains at least one of the elements
// of the item. Otherwise (or when items contains no elements) false is returned.
//
version.extensions["Array.containsAny"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.containsAny = function(items) {
 for(var i = 0; i < items.length; i++) {
 if (this.contains(items[i])) {
 return true;
 }
 }
 return false;
};


//============================================================================
// Array.containsAll Function
//============================================================================
//
// Returns true when the array contains all the items, otherwise false.
//
// When items is null false is returned (even if the array contains a null).
//
// @param items [may be null]
//
version.extensions["Array.containsAll"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.containsAll = function(items) {
 for(var i = 0; i < items.length; i++) {
 if (!this.contains(items[i])) {
 return false;
 }
 }
 return true;
};


} // of "install only once"

// Used Globals (for JSLint) ==============
// ... DOM
/*global document */
// ... TiddlyWiki Core
/*global convertUnicodeToUTF8, createTiddlyElement, createTiddlyLink,
 displayMessage, endSaveArea, hasClass, loadFile, saveFile,
 startSaveArea, store, wikify */
//}}}


/***
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2005 ([[www.abego-software.de|http://www.abego-software.de]])

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

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

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

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

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
***/

!Favorite Sites
* I frequently visit [[Digg|http://www.digg.com]] for news. I admit I used to read [[Slashdot|http://www.slashdot.org]] but somehow I prefer digg's format.  Slashdot has too many italics.
* To buy stuff I always consult first [[Slickdeals|http://www.slickdeals.net]], then [[dealnews|http://dealnews.com]] and then all the shopping engines and google.
* I always visit [[Gizmodo|http://www.gizmodo.com]] and [[EnGadget|http://www.engadget.com]] for gadget news and [[Lifehacker|http://www.lifehacker.com]].
!~DJing
My biggest hobby is ''~DJing''.  Favorite styles of music are:
* NYC House
* Chicago House
* Afro/Tribal House
* Latin/Miami House
* Tek House
* Goa Trance
* Detroit Techno
''Favorite ~DJs/Producers'': Dennis Ferrer, Stacy Kidd, Ron Carroll, Glenn Underground, Kings of Tomorrow, Masters at Work, Bob Sinclar, Freemasons, Mr V, Africanism, Nicolas Matar, Erick Morillo, Deep Dish, Carl Kennedy, DJ Pierre, DJ Sneak, Moloko, Roger Sanchez, Robbie Rivera, Quentin Harris, Danny Tenaglia, Martin Solveig, Jerome Sydenham, Abicah Soul Project, Joe Claussell, Tiger Stripes, Pete Heller, Ian Pooley, Mark Farina, Justice, Satoshi Tomiie, Hiroshi Watanabe, Juno Reactor, Infected Mushroom, DJ Gregory, ~G-Pal, ~Awa-Klash, Little Louie Vega, and many many others.

My style could be characterized as frequency mixed, with multiple breaks. I rarely scratch. I incorporate a lot of acapellas, filters, and effects.

The ''tools'' I use are:
* Windows Laptop
* Hercules DJ Console: MIDI Controller. Contemplating xponent by ~M-Audio
* Virtual DJ: DJ software.
* Mixed in Key: Music library analyzer, although I dont trust it completely.  Experience always matters more.
* Audacity: Waveform editor.
* A bunch of filters/effects for Virtual DJ.
* Meizu Mini Player: A great portable Mp3 player.
* Creative ~EP-630 in-ear headphones.
* Wireless full ear headphones

Check out some of my MiXes.
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>>
{{{
var GATracker = {
	// save display function
	displayTiddler: story.displayTiddler
};

GATracker.trackAndDisplayTiddler = function(srcElement, titles) {
	// display requested tiddler first.
	GATracker.displayTiddler.apply(this, arguments);

	// now perform tracking as appropriate.
	if(readOnly && !startingUp) {
		try {
			var filename;
			if(typeof(titles) == 'object') { 
				filename = titles.title.htmlEncode();
			} else {
				filename = titles.htmlEncode();
			}
			filename = filename.toLowerCase();
			filename = filename.replace(/ |\/|@/gi, '-');
			filename = filename.replace(/(-)\1+/gi, '$1');
			filename = escape(filename);
			var pageTracker = _gat._getTracker("UA-4175362-1");
			pageTracker._initData();
			pageTracker._trackPageview('/' + filename + '.html');
		} catch (err) {
			// ignore error; we just won't be able to record page views.
		}
	}
};

story.displayTiddler = GATracker.trackAndDisplayTiddler;

}}}
/***
|Name|ImageSizePlugin|
|Source|http://www.TiddlyTools.com/#ImageSizePlugin|
|Version|1.1.0|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin,formatter|
|Requires||
|Overrides|'image' formatter|
|Description|extends image syntax to add optional CSS width/height values|
!!!!!Usage
<<<
Extends standard TiddlyWiki image syntax, ''{{{[img[...]]}}}'', so you can specify CSS width/height values.

The extended syntax is:
>''{{{[img(x,y)[...]]}}}''
>where x and y are the desired width and height of the image, specified using CSS units of measurement (e.g., px, em, cm, in, or %).  Use ''auto'' (or omit the value) for width or height to scale image proportionally (i.e., maintain aspect ratio).  You may also calculate a CSS value on-the-fly by using //evaluated javascript//, enclosed between """{{""" and """}}""", e.g, {{{({{widthFunction()}},{{heightFunction()}})}}}.

Note: this plugin also includes enhancements to support:
*[[AttachFilePluginFormatters]] (embed image files as text-encoded tiddlers)
* [[ImagePathPlugin]] (fallback locations for missing images)
Please refer to those plugins for details...
<<<
!!!!!Examples
<<<
{{{
[<img(34%,auto)[images/meow.gif]]
[<img(21%,auto)[images/meow.gif]]
[<img(13%,auto)[images/meow.gif]]
[<img(8%,auto)[images/meow.gif]]
[<img(5%,auto)[images/meow.gif]]
[<img(3%,auto)[images/meow.gif]]
[<img(2%,auto)[images/meow.gif]]
[img(1%,auto)[images/meow.gif]]
}}}
[<img(34%,auto)[images/meow.gif]]
[<img(21%,auto)[images/meow.gif]]
[<img(13%,auto)[images/meow.gif]]
[<img(8%,auto)[images/meow.gif]]
[<img(5%,auto)[images/meow.gif]]
[<img(3%,auto)[images/meow.gif]]
[<img(2%,auto)[images/meow.gif]]
[img(1%,auto)[images/meow.gif]]
{{clear block{}}}
<<<
!!!!!Revisions
<<<
2008.01.19 [1.1.0] added support for evaluated width/height values!!
2008.01.18 [1.0.1] code cleanup plus improved regexp for matching "(width,height)" by eliminating hard-coded recognition of [px,em,cm,in,%] CSS units.  Syntax now accepts ANY values for width/height, and leaves it to the browser's CSS processing to handle any invalid values.
2008.01.17 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.imageSize = {major: 1, minor: 1, revision: 0, date: new Date(2008,1,19)};

// replace standard handler for image formatter
// note: includes modifications for [[AttachFilePluginFormatters]] AND [[ImagePathPlugin]]
var f=config.formatters.findByField("name","image");
config.formatters[f].match="\\[[<>]?[Ii][Mm][Gg](?:\\([^,]*,[^\\)]*\\))?\\[";
config.formatters[f].lookaheadRegExp=/\[([<]?)(>?)[Ii][Mm][Gg](\([^,]*,[^\)]*\))?\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg;
config.formatters[f].handler=function(w) {
	this.lookaheadRegExp.lastIndex = w.matchStart;
	var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
	if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
		var floatLeft=lookaheadMatch[1];
		var floatRight=lookaheadMatch[2];
		var XY=lookaheadMatch[3];
		var tooltip=lookaheadMatch[4];
		var src=lookaheadMatch[5];
		var link=lookaheadMatch[6];
		// Simple bracketted link
		var e = w.output;
		if(link) { // LINKED IMAGE
			if (config.formatterHelpers.isExternalLink(link)) {
				if (config.macros.attach && config.macros.attach.isAttachment(link)) {
					// see [[AttachFilePluginFormatters]]
					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);
			addClass(e,"imageLink");
		}
		var img = createTiddlyElement(e,"img");
		if(floatLeft) img.align="left"; else if(floatRight) img.align="right"; // FLOAT LEFT/RIGHT
		if(XY) { // CUSTOM SIZE with optional EVAL'ED width/height ({{...}},{{...}})
			var parts=XY.replace(/[\(\)]/g,'').split(","); var x=parts[0]; var y=parts[1];
			if (x.substr(0,2)=="{{") {
				try{img.style.width=eval(x.substr(2,x.length-4));}
				catch(e){displayMessage(e.description||e.toString())}
			} else img.style.width=x;

			if (y.substr(0,2)=="{{") {
				try{img.style.height=eval(y.substr(2,y.length-4));}
				catch(e){displayMessage(e.description||e.toString())}
			} else img.style.height=y;
		}
		if(tooltip) img.title = tooltip; // TOOLTIP
		// GET IMAGE SOURCE (get attachment or resolve fallback path as needed)
		if (config.macros.attach && config.macros.attach.isAttachment(src))
			src=config.macros.attach.getAttachment(src); // see [[AttachFilePluginFormatters]]
		else if (config.formatterHelpers.resolvePath) { // see [[ImagePathPlugin]]
			// Note: IE and Safari use onError to call resolvePath() only if initial lookup fails
			// (avoids security messages for initial filesystem access)... otherwise, attempt to
			// resolve the original path/file before initial rendering
			if (config.browser.isIE || config.browser.isSafari) {
				img.onerror=(function(){
					this.src=config.formatterHelpers.resolvePath(this.src,false);
					return false;
				});
			} else
				src=config.formatterHelpers.resolvePath(lookaheadMatch[5],true);
		}
		img.src=src; // RENDER IMAGE
		w.nextMatch = this.lookaheadRegExp.lastIndex;
	}
}
//}}}
/***
!Description
Use the code below to Insert a favicon.ico.  Change the n.href line to the location of your ico.
tag the tiddler with systemConfig
Source: [[TiddlyWiki Google Groups|http://groups.google.com/group/TiddlyWikiDev/browse_frm/thread/17caafee81eab118/450e723340672f7a?lnk=st&q=%22var+n+%3D+document.createElement(%22link%22)%3B%22&rnum=1&hl=en#450e723340672f7a]]
!Code
***/
//{{{
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://www.ece.northwestern.edu/~stsaft/favicon.ico";
document.getElementsByTagName("head")[0].appendChild(n);
//}}}
The titles of my journal articles are the following. Click on each to retrieve full reference and abstract and an electronic copy.
<<listTags Journal>>
/***
|''Name:''|LegacyStrikeThroughPlugin|
|''Description:''|Support for legacy (pre 2.1) strike through formatting|
|''Version:''|1.0.2|
|''Date:''|Jul 21, 2006|
|''Source:''|http://www.tiddlywiki.com/#LegacyStrikeThroughPlugin|
|''Author:''|MartinBudden (mjbudden (at) gmail (dot) com)|
|''License:''|[[BSD open source license]]|
|''CoreVersion:''|2.1.0|
***/

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

config.formatters.push(
{
    name: "legacyStrikeByChar",
    match: "==",
    termRegExp: /(==)/mg,
    element: "strike",
    handler: config.formatterHelpers.createElementAndWikify
});

} //# end of "install only once"
//}}}
<html>
<table border = "0">
<tr>
<td><div style="text-align:center">
<object type="application/x-shockwave-flash" width="300" height="300" id="FlowPlayer" data="http://www.ece.northwestern.edu/~stsaft/FlowPlayerLight.swf">
  <param name="movie" value="http://www.ece.northwestern.edu/~stsaft/FlowPlayerLight.swf"/>
  <param name="scale" value="noScale"/>
  <param name="wmode" value="transparent"/>
  <param name="allowScriptAccess" value="sameDomain"/>
  <param name="quality" value="high"/>
  <param name="flashvars" value="config={
    loop: false,
    autoPlay:false,
    initialScale: 'fit',
    videoFile: 'http://www.ece.northwestern.edu/~stsaft/media/pig.flv',
    autoBuffering: true,
    bufferLength: 12,
    startingBufferLength: 10,
  }"/>
</object> </div>
<b>An example of Tracking and Elastically Matching Left Ventricle Contours.</b></td>
<td><div style="text-align:center">
<object type="application/x-shockwave-flash" width="300" height="300" id="FlowPlayer" data="http://www.ece.northwestern.edu/~stsaft/FlowPlayerLight.swf">
  <param name="movie" value="http://www.ece.northwestern.edu/~stsaft/FlowPlayerLight.swf"/>
  <param name="scale" value="noScale"/>
  <param name="wmode" value="transparent"/>
  <param name="allowScriptAccess" value="sameDomain"/>
  <param name="quality" value="high"/>
  <param name="flashvars" value="config={
    loop: false,
    autoPlay:false,
    initialScale: 'fit',
    videoFile: 'http://www.ece.northwestern.edu/~stsaft/media/odd.flv',
    autoBuffering: false,
    bufferLength: 12,
    startingBufferLength: 10,
  }"/>
</object> </div>
<b>An example of an MRI image sequence of a needle tip in a phantom.</b></td>
</tr>
</table>
</html>/% ffmpeg -i odd.avi -vcodec h264 -sameq odd.mp4 %/
In collaboration with ''Prof. Dharmakumar'' (NU Radiology) and ''Dr. Koktzoglou'' (Evanston Northwestern Healthcare) I have been working on cardiac MRI for disease detection and interventional myocardial therapies. Specifically, we are working on the detection of myocardial ischemia based on MRI data. I am advising two visiting students that work on the detection and elastic registration of the heart’s left ventricle. This allows us to monitor the "change in blood perfusion" (which might indicate a medical condition) along the cardiac cycle. In another effort, we have developed a real time MRI protocol that can be used to visualize an industry standard radiopaque catheter with signal properties that are similar to those realized with circuit bearing (active) devices (not yet approved by the FDA). I am advising an undergraduate student for creating an object tracking algorithm that is used to track the tip of the catheter (essentially the tip of a needle) in MR sequences, to assist in delivery of therapeutic agents (stem cells, human growth factor) and in other minimally invasive therapies. This task is very challenging, since the object signature is very small, and object occlusions and diffusion with the background are very common.

Recently, we have focused in visualization tools for assessing and quantifying oxygenation deficits resulting from coronary artery stenosis.  Specifically we process the myocardial intensity signals, which are collected with the steady state free precession blood-oxygen-level-dependent (BOLD SSFP) technique that Dr. Dharmakumar pioneered. We are able to define statistical thresholds that are used to visualize and quantify the extent of hypointense regions in the myocardium. The next figure illustrates the outcome of this process.
[img[Three EigenSpots|http://www.ece.northwestern.edu/~stsaft/media/vis.png]]
Rest (RA) and Adenosine (AD) end-systolic images from a canine study that had its LCX artery occluded, are shown in (A) and (B), respectively without any and with windowing (C) and (D). Red arrows on the windowed AD image (D) show the limits of the affected myocardial segments corresponding to the LCX artery. The images of A and B are shown color coded in panels (E) and (F), respectively. Only the myocardium was color coded. 

Relevant Publications:
<<forEachTiddler
 where
        'tiddler.tags.contains("CardiacMRI") && tiddler.tags.contains("paper")'
write
        '((index == 0) ? "" : "; ")+"[["+tiddler.title+"]]"'
>> 
The tiddler 'Main Page' doesn't yet exist. Double-click to create it
[img[none|sotos.jpg]]
[[Welcome]]
[[Biography]]
[[My Research]]
[[Teaching]]
[[Publications]]

[[Contact]]

[[Tools of Trade]]
[[Fun Stuff]]
[[About This Site]]


<html>
<a href="http://www.statcounter.com/" target="_blank"><img src="http://c15.statcounter.com/counter.php?sc_project=1546007&java=0&security=7a48d2b2&invisible=0" alt="site stats" border="0"></a>
</html>
I am super lazy and I dont like photo editing programs like photoshop. The steps I followed to make my icon were:
* Opened Powerpoint 2003 (Yes PowerPoint)
* Made the icon that I wanted in a regular presentation.  Used small fonts like 18.
* Selected the items that made the icon.
* ~Right-Click and select Save as Image
* Choose PNG format
* Surf to [[Favicon From Pics|http://www.chami.com/html-kit/services/favicon/]] and follow the steps.
* See [[Inserting a favicon into TiddlyWiki]] on how to put in TiddlyWiki
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4175362-1");
pageTracker._initData();
pageTracker._trackPageview();
</script>
The list of mixes are here:
<<listTags mix>>
If you dont want to show the Timeline, or tabs or etc then you will want to edit the SideBarTabs.

Search for tiddler SideBarTabs and edit its content.

The content will look like this:
{{{<<tabs txtMainTab Timeline Timeline TabTimeline All 'All tiddlers' TabAll Tags 'All tags' TabTags More 'More lists' TabMore>>}}} and will result in:
<<tabs txtMainTab Timeline Timeline TabTimeline All 'All tiddlers' TabAll Tags 'All tags' TabTags More 'More lists' TabMore>>

If you remove the Timeline Stuff you get this:
{{{<<tabs txtMainTab All 'All tiddlers' TabAll Tags 'All tags' TabTags More 'More lists' TabMore>>}}}
<<tabs txtMainTab All 'All tiddlers' TabAll Tags 'All tags' TabTags More 'More lists' TabMore>>

Some usefull info [[here|http://groups.google.com/group/TiddlyWiki/browse_thread/thread/43112f955cbb16d9/7d15b0059f9f1100?lnk=st&q=tiddlywiki+sidebar+timeline&rnum=2&hl=en#7d15b0059f9f1100]]
This type of work can be readily applied to many biotechnological protocols that rely on DNA to DNA interactions. To this effect I have been investigating an alternative approach to microarray design that maintains all of the advantages of microarrays but simplifies the design optimization task and suppresses the labeling bias found in microarray assays. The basis is a novel linker construction that employs a simple ~DNA-based computational device to reduce mishybridization. The proposed method introduces another level of reliability in the chemical protocols, as well as in the intensity measurements. A patent has been filled.
[>img[Idea Chart|http://www.ece.northwestern.edu/~stsaft/media/logo.png]]Signal processing has become an integral part of our everyday life. It is present in everyday appliances and has even found application in biology (for example, genomic signal processing). On the other hand, biologically inspired ideas such as evolutionary optimization are routinely applied to an increasing number of signal processing problems.

I am motivated to go beyond the above-mentioned interactions. My research is inspired by nature and creates another level of hybrid connection and interaction between biology and signal processing. The main difference is that in my research, I use actual biological entities to do signal processing. Specifically, I use DNA molecules to manage large amounts of digital signals. It is interdisciplinary research in that it requires knowledge of signal processing on one hand, and molecular biology and biotechnology on the other.

I am also very interested in applying signal processing to solve unique imaging and signaling problems in the life sciences, such as cardiac MRI, AFM Imaging, Plant Phenotyping.

Overall, for the past 9 years I have been working on: 
<<listTags Research>>
!Research Sponsoring Agencies
[img[http://www.nih.gov/glmedia/head_main_logo.gif][http://www.nih.gov/]][img[http://www.artic.edu/aic/gfx/header_aic_logo.gif][http://www.artic.edu/aic/]]
[img[none|http://www.sdfoundation.org/images/tsdf-front.gif][http://www.sdfoundation.org/]][img[http://www.northwestern.edu/univ-relations/publications/resource_downloads/web/NU_Logo_purple.jpg][http://www.northwestern.edu]][img[http://www.eecs.northwestern.edu/~stsaft/media/dot.png][http://www.dot.gov/new/index.htm]]
[img[http://www.eecs.northwestern.edu/~stsaft/media/onassis.png][http://www.onassis.gr/index.php]]
[[NIH Style Biosketch in PDF|http://www.ece.northwestern.edu/~stsaft/media/tsaftaris_NIH.pdf]].
[[NIH Style Biosketch in Google PDF Viewer|http://docs.google.com/viewer?url=http://www.ece.northwestern.edu/~stsaft/media/tsaftaris_NIH.pdf]] 
<<tabs txtMainTab Timeline Timeline TabTimeline All 'All tiddlers' TabAll Tags 'All tags' TabTags More 'More lists' TabMore>>

tag it as systemConfig to recover the good old SideBar.
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

See AdvancedOptions
/***
!Description
Creates a list of all tiddlers with a certain tag by invoking {{{<<}}}listTags yourTag{{{>>}}}

Source code [[here|http://tiddlywikitips.com/#%5B%5BPlugin%20listTags%5D%5D]]
More usage info [[here|http://tiddlywikitips.com/#%5B%5BTip%20%2304%3A%20Tag%20Listing%20*%5D%5D]]

!The Code
***/
//{{{
config.macros.listTags = { text: "Hello" };
config.macros.listTags.handler = function(place,macroName,params)
{
 var tagged = store.getTaggedTiddlers(params[0]);
 var ul = createTiddlyElement(place,"ul",null,null,"");
 for(var r=0;r<tagged.length;r++)
 {
 var li = createTiddlyElement(ul,"li",null,null,"");
 createTiddlyLink(li,tagged[r].title,true);
 }
}
//}}}
I have a certain number of plugins installed. So if you want to get a page like mine, you have to install them as well.
On how to install a plugin see [[this|http://www.tiddlywiki.com/#InstallingPlugins]] or better [[this one|http://mnteractive.com/archive/how-to-install-a-tiddlywiki-plugin/]]

The plugins I have installed are:
<<listTags systemConfig>>
Here you can find my publications in chronological order divided in four categories ''Book Chapters'', ''Journals'', ''Conference Papers'', ''Theses & Dissertations''. Click on each of the links to retrieve more information (ie. full citation, abstract, PDF reprint, etc).
/%<<forEachTiddler
where 'tiddler.tags.contains("tips")'
sortBy "tiddler.title.toLowerCase()"
script 'function open(tiddler) {
story.closeTiddler(tiddler.title,false,false);
story.displayTiddler(null,tiddler.title); return ""; }'
write 'open(tiddler);' >> %/
!Book Chapters
<<listTags BookChapter>>
!Journals
<<listTags Journal>>
!Conference Papers
<<listTags Conference>>
!Theses & Dissertations
* S.A. Tsaftaris, "//~DNA-Based Storage and Retrieval of Digital Signals//," ~PhD Dissertation, Northwestern University, Department of Electrical and Computer Engineering, Advisor Dr. Katsaggelos, May 2006.
* S.A. Tsaftaris, "//~DNA-Based Digital Signal Processing//," ~MSc Thesis, Northwestern University, Department of Electrical and Computer Engineering, Advisor Dr. Katsaggelos, May 2003.
* S.A. Tsaftaris, "//Copyright protection of MPEG 1&2 Video Sequences using digital watermarking//,” Diploma Thesis (in Greek), Aristotle University of Thessaloniki, Department of Electrical and Computer Engineering, Advisor Dr. Strintzis, June 2000.
!Purpose / But

I wrote this Search Engine Optimization plugin to improve ~TiddlyWiki website's ranking on Google, Yahoo, etc. Basically, it does two things:
1) For each tiddler and each tag of this one, it creates a html file with the tiddler's content and named according to the tiddler's title. The html file is written in a way that it can be easily crawled by a search engine (short and with html format, not ~TiddlyWiki's format), but if it is opened (which will be the case if it appears in the results of a search engine), it redirects to the ~TiddlyWiki with the corresponding tiddler opened.
2) It creates the sitemap.xml and urllist.txt files for Google and Yahoo which included generated html files.

J'ai écrit ce plugin d'optimisation pour les moteurs de recherche pour améliorer l'indexation d'un ~TiddlyWiki par Google, Yahoo, etc.
Le plugin fait deux choses :
1) Pour chaque tiddler et chaque tag de celui-ci, il créé un fichier html avec le contenu du tiddler et nommé selon le titre du tiddler. Le fichier html est écrit de sorte qu'il soit facilement indexable par un moteur de recherche (court et avec un balisage html et non un balisage ~TiddlyWiki), mais que s'il est ouvert (ce qui sera le cas s'il apparaît dans les résultats d'un moteur de recherche), il redirige vers le ~TiddlyWiki avec le tiddler correspondant ouvert.
2) Il créé les fichiers sitemap.xml et urllist.txt pour Google et Yahoo, qui inclue les fichiers html générés.

!How to install / Comment l'installer

1) Create two tiddlers like these one (same name and content):
[[SEOTiddlyWikiPlugin]] [[SEOTiddlyWikiConfig]]
NB: double-clic on each tiddler to get edit mode and copy-paste their content.
2) Fill in shadow tiddlers SiteUrl, SiteTitle and SiteSubtitle.
3) Edit this tiddler and see the code behind the following link to understand how to launch the process, once the plugin installed: <html><a href="javascript:generateSEOFiles();">Generate SEO files</a></html>.
4) See http://www.google.com/webmasters/sitemaps/ to register your  sitemap.xml file.

Tips
1) Feel free to modify html template [[SEOTiddlyWikiConfig]].
2) Use tag //excludeSearch// to disallow the export of a tiddler.

Limitation
If you remove a tiddler after an export, you have to manually remove the corresponding html file.

1) Créez un tiddler comme ceux-ci (même nom et contenu) :
[[SEOTiddlyWikiPlugin]] [[SEOTiddlyWikiConfig]]
NB: double-cliquez sur chaque tiddler pour le passe en mode d'édition et copier-coller leur contenu.
2) Remplissez les tiddlers cachés SiteUrl, SiteTitle et SiteSubtitle.
3) Editez le contenu de ce tiddler pour voir le code derrière le lien suivant et comprendre comment lancer le processus une fois le plugin installé : <html><a href="javascript:generateSEOFiles();">Générer les fichiers SEO</a></html>.
4) Voyez http://www.google.com/webmasters/sitemaps/ pour enregistrer votre fichier sitemap.xml.

Astuces
1) N'hésitez pas à modifier le modèle html [[SEOTiddlyWikiConfig]].
2) Utilisez le tag //excludeSearch// pour interdire l'export d'un tiddler.

Limitation
Si vous supprimez un tiddler après un export, vous devez manuellement effacer le fichier html correspondant.

!Sample results / Exemple de résultats

http://www.google.com/search?q=site:superphysique.net
http://www.superphysique.net/
http://www.superphysique.net/materials/strength-cycles.htm
http://www.superphysique.net/gallery/eric.htm
http://www.superphysique.net/gallery/jean-louis.htm
http://www.superphysique.net/tiddlywiki/seo-tiddlywiki-plugin.htm
http://www.superphysique.net/gallery/fabrice.htm
http://www.superphysique.net/gallery/alan.htm
http://www.superphysique.net/materials/external-resources.htm
http://www.superphysique.net/gallery/dusan.htm
http://www.superphysique.net/gallery/rudy.htm
http://www.superphysique.net/gallery/fabien-s..htm
http://www.superphysique.net/gallery/fabien-m..htm
http://www.superphysique.net/gallery/franck.htm
http://www.superphysique.net/gallery/benjamin.htm
http://www.superphysique.net/gallery/jerome.htm
http://www.superphysique.net/gallery/pierre.htm
http://www.superphysique.net/super%20physique/updates.htm
http://www.superphysique.net/materials/materials.htm
http://www.superphysique.net/appendix/appendix.htm
http://www.superphysique.net/appendix/quotes.htm
http://www.superphysique.net/appendix/copyright.htm
http://www.superphysique.net/super%20physique/about-a-propos.htm
http://www.superphysique.net/gallery/yann.htm

!Report a bug / Reporter un bug

<<email fabrice.proudhon at yahoo dot com>>

!Copyright

<html><!--Creative Commons License--><a rel="license" href="http://creativecommons.org/licenses/by/2.5/"><img alt="Creative Commons License" style="border-width: 0" src="http://i.creativecommons.org/l/by/2.5/88x31.png"/></a><br/>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/2.5/">Creative Commons Attribution 2.5  License</a>.<!--/Creative Commons License--><!-- <rdf:RDF xmlns="http://web.resource.org/cc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
	<Work rdf:about="">
		<license rdf:resource="http://creativecommons.org/licenses/by/2.5/" />
	</Work>
	<License rdf:about="http://creativecommons.org/licenses/by/2.5/"><permits rdf:resource="http://web.resource.org/cc/Reproduction"/><permits rdf:resource="http://web.resource.org/cc/Distribution"/><requires rdf:resource="http://web.resource.org/cc/Notice"/><requires rdf:resource="http://web.resource.org/cc/Attribution"/><permits rdf:resource="http://web.resource.org/cc/DerivativeWorks"/></License></rdf:RDF> --></html>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>SITE_TITLE: TIDDLER_TITLE</title>
<meta name="description" content="SITE_SUBTITLE">
<meta name="keywords" content="TIDDLER_KEYWORDS">
<script language="JavaScript" type="text/javascript">
location.href = 'TIDDLER_URL';
</script>
<base href="TIDDLER_URL">
</head>

<body>
<noscript>
<a href="TIDDLER_URL">TIDDLER_TITLE</a>
</noscript>
TIDDLER_CONTENT
</body>
</html>
function generateSEOFiles()
{
	// last update: 2007-02-14 by Fabrice Proudhon
	// http://www.superphysique.net#%5B%5BSEO%20TiddlyWiki%20Plugin%5D%5D
	var originalPath = document.location.toString();
	if(originalPath.substr(0,5) != "file:") 
	{
		alert(config.messages.notFileUrlError);
		if(store.tiddlerExists(config.messages.saveInstructions))
			story.displayTiddler(null,config.messages.saveInstructions);
		return;
	}
	var y = [];
	var g = [];
	var localPath = getLocalPath(originalPath);
	var c = store.getTiddlerText("SEOTiddlyWikiConfig");
	var su = store.getTiddlerText("SiteUrl");
	var st = wikifyPlain("SiteTitle");
	var sbt = wikifyPlain("SiteSubtitle");
	var htmlPath = localPath.substr(0,localPath.lastIndexOf("\\"));
	var tiddlers = store.getTiddlers("modified","excludeSearch");
	g.push('<url><loc>' + su.htmlEncode() + '</loc></url>');
	y.push(su.htmlEncode());
	for (var t=0; t<tiddlers.length; t++) {
		var content = c;
		var filename = tiddlers[t].title.htmlEncode();
		filename = filename.toLowerCase();
		filename = filename.replace(/ |\/|@/gi, '-');
		filename = filename.replace(/(-)\1+/gi, '$1');
        	content = content.replace(/SITE_TITLE/gi, st.htmlEncode());
        	content = content.replace(/SITE_SUBTITLE/gi, sbt.htmlEncode());
        	content = content.replace(/TIDDLER_TITLE/gi, tiddlers[t].title.htmlEncode());
		content = content.replace(/TIDDLER_URL/gi, su.htmlEncode() + '#' + String.encodeTiddlyLink(tiddlers[t].title));
        	content = content.replace(/TIDDLER_KEYWORDS/gi, tiddlers[t].tags.join(',').htmlEncode());
        	content = content.replace(/TIDDLER_CONTENT/gi, wikifyStatic(tiddlers[t].text,null,tiddlers[t]).htmlEncode());
        	content = content.replace(/&lt;/gi, '<');
        	content = content.replace(/&gt;/gi, '>');
        	content = content.replace(/&quot;/gi, '"');
		var d = tiddlers[t].modified.getFullYear() + '-';
		if (tiddlers[t].modified.getMonth() + 1 < 10) d = d + '0';
		d = d + (tiddlers[t].modified.getMonth() + 1) + '-';
		if (tiddlers[t].modified.getDate() < 10) d = d + '0';
		d = d + tiddlers[t].modified.getDate();
		for (var ta=0; ta<tiddlers[t].tags.length; ta++) {
			var tag = tiddlers[t].tags[ta].toLowerCase();
			g.push('<url><loc>' + su.htmlEncode() + escape(tag) + '/' + escape(filename) + '.htm' + '</loc><lastmod>' + d + '</lastmod></url>');
			y.push(su.htmlEncode() + escape(tag) + '/' + escape(filename) + '.htm');
			saveFile(htmlPath + '\\' + tag + '\\' + filename + '.htm', convertUnicodeToUTF8(content));
		}
	}
	saveFile(htmlPath + '\\urllist.txt', convertUnicodeToUTF8(y.join('\n')));
	saveFile(htmlPath + '\\sitemap.xml', '<?xml version="1.0" encoding="utf-8"?><urlset xmlns="http://www.google.com/schemas/sitemap/0.84">\n' + convertUnicodeToUTF8(g.join('\n')) + '</urlset>');
	displayMessage('SEO files created', '');
}
<<tabs txtMainTab Tags 'All tags' TabTags>>
These are my publications related to simulating DNA databases.
<<listTags Simulation>>
/***
|Name|SinglePageModePlugin|
|Source|http://www.TiddlyTools.com/#SinglePageModePlugin|
|Documentation|http://www.TiddlyTools.com/#SinglePageModePluginInfo|
|Version|2.9.6|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|Show tiddlers one at a time with automatic permalink, or always open tiddlers at top/bottom of page.|
This plugin allows you to configure TiddlyWiki to navigate more like a traditional multipage web site with only one tiddler displayed at a time.
!!!!!Documentation
>see [[SinglePageModePluginInfo]]
!!!!!Configuration
<<<
<<option chkSinglePageMode>> Display one tiddler at a time
><<option chkSinglePagePermalink>> Automatically permalink current tiddler
><<option chkSinglePageKeepFoldedTiddlers>> Don't close tiddlers that are folded
><<option chkSinglePageKeepEditedTiddlers>> Don't close tiddlers that are being edited
<<option chkTopOfPageMode>> Open tiddlers at the top of the page
<<option chkBottomOfPageMode>> Open tiddlers at the bottom of the page
<<option chkSinglePageAutoScroll>> Automatically scroll tiddler into view (if needed)

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


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

if (Story.prototype.SPM_coreDisplayTiddlers==undefined)
	Story.prototype.SPM_coreDisplayTiddlers=Story.prototype.displayTiddlers;
Story.prototype.displayTiddlers = function() {
	// suspend single/top/bottom modes when showing multiple tiddlers
	var opt=config.options;
	var saveSPM=opt.chkSinglePageMode; opt.chkSinglePageMode=false;
	var saveTPM=opt.chkTopOfPageMode; opt.chkTopOfPageMode=false;
	var saveBPM=opt.chkBottomOfPageMode; opt.chkBottomOfPageMode=false;
	this.SPM_coreDisplayTiddlers.apply(this,arguments);
	opt.chkBottomOfPageMode=saveBPM;
	opt.chkTopOfPageMode=saveTPM;
	opt.chkSinglePageMode=saveSPM;
}
//}}}
Ph.D.
Sotirios ([[SoToS|About Me]]) Tsaftaris
http://www.ece.northwestern.edu/~stsaft
/***
|''Name:''|SparklinePlugin|
|''Description:''|Sparklines macro|
***/
//{{{
if(!version.extensions.SparklinePlugin) {
version.extensions.SparklinePlugin = {installed:true};

//--
//-- Sparklines
//--

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


}
//}}}
.viewer img { padding-left:1em; padding-right:1em; }
/***
!Colors Used
*@@bgcolor(#8cf): #8cf - Background blue@@
*@@bgcolor(#18f): #18f - Top blue@@
*@@bgcolor(#04b): #04b - Mid blue@@
*@@bgcolor(#014):color(#fff): #014 - Bottom blue@@
*@@bgcolor(#ffc): #ffc - Bright yellow@@
*@@bgcolor(#fe8): #fe8 - Highlight yellow@@
*@@bgcolor(#db4): #db4 - Background yellow@@
*@@bgcolor(#841): #841 - Border yellow@@
*@@bgcolor(#703):color(#fff): #703 - Title red@@
*@@bgcolor(#866): #866 - Subtitle grey@@
!Generic Rules /%==============================================%/
***/
/*{{{*/
body {
    background: #fff;
    color: #000;
}

a{
    color: #04b;
}

a:hover{
    background: #04b;
    color: #fff;
}

a img{
    border: 0;
}

h1,h2,h3,h4,h5 {
    color: #703;
    background: #8cf;
}

.button {
    color: #014;
    border: 1px solid #fff;
}

.button:hover {
    color: #014;
    background: #fe8;
    border-color: #db4;
}

.button:active {
    color: #fff;
    background: #db4;
    border: 1px solid #841;
}

/*}}}*/
/***
!Header /%==================================================%/
***/
/*{{{*/
.header {
    background: #04b;
}

.headerShadow {
    color: #000;
}

.headerShadow a {
    font-weight: normal;
    color: #000;
}

.headerForeground {
    color: #fff;
}

.headerForeground a {
    font-weight: normal;
    color: #8cf;
}

/*}}}*/
/***
!General tabs /%=================================================%/
***/
/*{{{*/

.tabSelected{
    color: #014;
    background: #eee;
    border-left: 1px solid #ccc;
    border-top: 1px solid #ccc;
    border-right: 1px solid #ccc;
}

.tabUnselected {
    color: #fff;
    background: #999;
}

.tabContents {
    color: #014;
    background: #eee;
    border: 1px solid #ccc;
}

.tabContents .button {
     border: 0;}

/*}}}*/
/***
!Sidebar options /%=================================================%/
~TiddlyLinks and buttons are treated identically in the sidebar and slider panel
***/
/*{{{*/
#sidebar {
}

#sidebarOptions input {
    border: 1px solid #04b;
}

#sidebarOptions .sliderPanel {
    background: #8cf;
}

#sidebarOptions .sliderPanel a {
    border: none;
    color: #04b;
}

#sidebarOptions .sliderPanel a:hover {
    color: #fff;
    background: #04b;
}

#sidebarOptions .sliderPanel a:active {
    color: #04b;
    background: #fff;
}
/*}}}*/
/***
!Message Area /%=================================================%/
***/
/*{{{*/
#messageArea {
    border: 1px solid #841;
    background: #db4;
    color: #014;
}

#messageArea .button {
    padding: 0.2em 0.2em 0.2em 0.2em;
    color: #014;
    background: #fff;
}

/*}}}*/
/***
!Popup /%=================================================%/
***/
/*{{{*/
.popup {
    background: #18f;
    border: 1px solid #04b;
}

.popup hr {
    color: #014;
    background: #014;
    border-bottom: 1px;
}

.popup li.disabled {
    color: #04b;
}

.popup li a, .popup li a:visited {
    color: #eee;
    border: none;
}

.popup li a:hover {
    background: #014;
    color: #fff;
    border: none;
}
/*}}}*/
/***
!Tiddler Display /%=================================================%/
***/
/*{{{*/
.tiddler .defaultCommand {
 font-weight: bold;
}

.shadow .title {
    color: #866;
}

.title {
    color: #703;
}

.subtitle {
    color: #866;
}

.toolbar {
    color: #04b;
}

.tagging, .tagged {
    border: 1px solid #eee;
    background-color: #eee;
}

.selected .tagging, .selected .tagged {
    background-color: #ddd;
    border: 1px solid #bbb;
}

.tagging .listTitle, .tagged .listTitle {
    color: #014;
}

.tagging .button, .tagged .button {
        border: none;
}

.footer {
    color: #ddd;
}

.selected .footer {
    color: #888;
}

.sparkline {
    background: #8cf;
    border: 0;
}

.sparktick {
    background: #014;
}

.errorButton {
    color: #ff0;
    background: #f00;
}

.cascade {
    background: #eef;
    color: #aac;
    border: 1px solid #aac;
}

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

/*}}}*/
/***
''The viewer is where the tiddler content is displayed'' /%------------------------------------------------%/
***/
/*{{{*/

.viewer .listTitle {list-style-type: none; margin-left: -2em;}

.viewer .button {
    border: 1px solid #db4;
}

.viewer blockquote {
    border-left: 3px solid #666;
}

.viewer table {
    border: 2px solid #333;
}

.viewer th, thead td {
    background: #db4;
    border: 1px solid #666;
    color: #fff;
}

.viewer td, .viewer tr {
    border: 1px solid #666;
}

.viewer pre {
    border: 1px solid #fe8;
    background: #ffc;
}

.viewer code {
    color: #703;
}

.viewer hr {
    border: 0;
    border-top: dashed 1px #666;
    color: #666;
}

.highlight, .marked {
    background: #fe8;
}
/*}}}*/
/***
''The editor replaces the viewer in the tiddler'' /%------------------------------------------------%/
***/
/*{{{*/
.editor input {
    border: 1px solid #04b;
}

.editor textarea {
    border: 1px solid #04b;
    width: 100%;
}

.editorFooter {
    color: #aaa;
}

/*}}}*/
/*{{{*/
#tiddlersBar .button {border:0}
#tiddlersBar .tab {white-space:wrap}
#tiddlersBar {padding : 1em 0.5em 2px 0.5em}
.tabUnselected .tabButton, .tabSelected .tabButton {padding : 0 2px 0 2px; margin: 0 0 0 4px;}
.tiddler, .tabContents {border:1px [[ColorPalette::TertiaryPale]] solid;}
/*}}}*/
<<list all>>
<<tabs txtMoreTab Missing 'Missing tiddlers' TabMoreMissing Orphans 'Orphaned tiddlers' TabMoreOrphans Shadowed 'Shadowed tiddlers' TabMoreShadowed>>
I am teaching [[EECS202 Introduction to Electrical Engineering|http://www.eecs.northwestern.edu/academics/course/eecs_202/]]. Usually four professors co-teach the class. Each professor is responsible for a set of lectures. I am teaching for 11 lectures, material that relates to Signals, Systems, and Communications.  This class is very interesting since it aims to introduce EE to a diverse classroom audience. The audience is comprised from EE, CE, CS sophomores, undecided sophomores/freshmen, and seniors from basically every major our engineering school offers. 

The class also has a mandatory lab, where students are practicing what they learn in class on the innards of a CD player.

This class is a challenge for the professor, since it has to keep the material interesting and attractive without resorting to theory. Lectures are filled by applications and in-class demos.  

I use powerpoint for my lectures and write on the board some examples and mathematical derivations. I also give a student a [[LiveScribe pen|http://www.livescribe.com/]] to take [["livenotes" or "penscasts"| http://eecs202.notlong.com ]] while I am teaching, so the users have an audio-text synced snapshot of the lecture. My teaching style is very interactive, since I keep asking students questions and we jointly develop the lecture. It is very much affected by the [[Socratic Method|http://en.wikipedia.org/wiki/Socratic_method]].
* S.A. Tsaftaris, "//~DNA-Based Storage and Retrieval of Digital Signals//," ~PhD Dissertation, Northwestern University, Department of Electrical and Computer Engineering, Advisor Dr. Katsaggelos, May 2006.
* S.A. Tsaftaris, "//~DNA-Based Digital Signal Processing//," ~MSc Thesis, Northwestern University, Department of Electrical and Computer Engineering, Advisor Dr. Katsaggelos, May 2003.
* S.A. Tsaftaris, "//Copyright protection of MPEG 1&2 Video Sequences using digital watermarking//,” Diploma Thesis (in Greek), Aristotle University of Thessaloniki, Department of Electrical and Computer Engineering, Advisor Dr. Strintzis, June 2000.
/***
|''Name:''|TiddlersBarPlugin|
|''Description:''|A bar to switch between tiddlers through tabs (like browser tabs bar).|
|''Version:''|1.2.5|
|''Date:''|Jan 18,2008|
|''Source:''|http://visualtw.ouvaton.org/VisualTW.html|
|''Author:''|Pascal Collin|
|''License:''|[[BSD open source license|License]]|
|''~CoreVersion:''|2.1.0|
|''Browser:''|Firefox 2.0; InternetExplorer 6.0, others|
!Demos
On [[homepage|http://visualtw.ouvaton.org/VisualTW.html]], open several tiddlers to use the tabs bar.
!Installation
#import this tiddler from [[homepage|http://visualtw.ouvaton.org/VisualTW.html]] (tagged as systemConfig)
#save and reload
#''if you're using a custom [[PageTemplate]]'', add {{{<div id='tiddlersBar' refresh='none' ondblclick='config.macros.tiddlersBar.onTiddlersBarAction(event)'></div>}}} before {{{<div id='tiddlerDisplay'></div>}}}
#optionally, adjust StyleSheetTiddlersBar
!Tips
*Doubleclick on the tiddlers bar (where there is no tab) create a new tiddler.
*Tabs include a button to close {{{x}}} or save {{{!}}} their tiddler.
*By default, click on the current tab close all others tiddlers.
!Configuration options 
<<option chkDisableTabsBar>> Disable the tabs bar (to print, by example).
<<option chkHideTabsBarWhenSingleTab >> Automatically hide the tabs bar when only one tiddler is displayed. 
<<option txtSelectedTiddlerTabButton>> ''selected'' tab command button.
<<option txtPreviousTabKey>> previous tab access key.
<<option txtNextTabKey>> next tab access key.
!Code
***/
//{{{
config.options.chkDisableTabsBar = config.options.chkDisableTabsBar ? config.options.chkDisableTabsBar : false;
config.options.chkHideTabsBarWhenSingleTab  = config.options.chkHideTabsBarWhenSingleTab  ? config.options.chkHideTabsBarWhenSingleTab  : false;
config.options.txtSelectedTiddlerTabButton = config.options.txtSelectedTiddlerTabButton ? config.options.txtSelectedTiddlerTabButton : "closeOthers";
config.options.txtPreviousTabKey = config.options.txtPreviousTabKey ? config.options.txtPreviousTabKey : "";
config.options.txtNextTabKey = config.options.txtNextTabKey ? config.options.txtNextTabKey : "";
config.macros.tiddlersBar = {
	tooltip : "see ",
	tooltipClose : "click here to close this tab",
	tooltipSave : "click here to save this tab",
	promptRename : "Enter tiddler new name",
	currentTiddler : "",
	previousState : false,
	previousKey : config.options.txtPreviousTabKey,
	nextKey : config.options.txtNextTabKey,	
	tabsAnimationSource : null, //use document.getElementById("tiddlerDisplay") if you need animation on tab switching.
	handler: function(place,macroName,params) {
		var previous = null;
		if (config.macros.tiddlersBar.isShown())
			story.forEachTiddler(function(title,e){
				if (title==config.macros.tiddlersBar.currentTiddler){
					var d = createTiddlyElement(null,"span",null,"tab tabSelected");
					config.macros.tiddlersBar.createActiveTabButton(d,title);
					if (previous && config.macros.tiddlersBar.previousKey) previous.setAttribute("accessKey",config.macros.tiddlersBar.nextKey);
					previous = "active";
				}
				else {
					var d = createTiddlyElement(place,"span",null,"tab tabUnselected");
					var btn = createTiddlyButton(d,title,config.macros.tiddlersBar.tooltip + title,config.macros.tiddlersBar.onSelectTab);
					btn.setAttribute("tiddler", title);
					if (previous=="active" && config.macros.tiddlersBar.nextKey) btn.setAttribute("accessKey",config.macros.tiddlersBar.previousKey);
					previous=btn;
				}
				var isDirty =story.isDirty(title);
				var c = createTiddlyButton(d,isDirty ?"!":"x",isDirty?config.macros.tiddlersBar.tooltipSave:config.macros.tiddlersBar.tooltipClose, isDirty ? config.macros.tiddlersBar.onTabSave : config.macros.tiddlersBar.onTabClose,"tabButton");
				c.setAttribute("tiddler", title);
				if (place.childNodes) {
					place.insertBefore(document.createTextNode(" "),place.firstChild); // to allow break line here when many tiddlers are open
					place.insertBefore(d,place.firstChild); 
				}
				else place.appendChild(d);
			})
	}, 
	refresh: function(place,params){
		removeChildren(place);
		config.macros.tiddlersBar.handler(place,"tiddlersBar",params);
		if (config.macros.tiddlersBar.previousState!=config.macros.tiddlersBar.isShown()) {
			story.refreshAllTiddlers();
			if (config.macros.tiddlersBar.previousState) story.forEachTiddler(function(t,e){e.style.display="";});
			config.macros.tiddlersBar.previousState = !config.macros.tiddlersBar.previousState;
		}
	},
	isShown : function(){
		if (config.options.chkDisableTabsBar) return false;
		if (!config.options.chkHideTabsBarWhenSingleTab) return true;
		var cpt=0;
		story.forEachTiddler(function(){cpt++});
		return (cpt>1);
	},
	selectNextTab : function(){  //used when the current tab is closed (to select another tab)
		var previous="";
		story.forEachTiddler(function(title){
			if (!config.macros.tiddlersBar.currentTiddler) {
				story.displayTiddler(null,title);
				return;
			}
			if (title==config.macros.tiddlersBar.currentTiddler) {
				if (previous) {
					story.displayTiddler(null,previous);
					return;
				}
				else config.macros.tiddlersBar.currentTiddler=""; 	// so next tab will be selected
			}
			else previous=title;
			});		
	},
	onSelectTab : function(e){
		var t = this.getAttribute("tiddler");
		if (t) story.displayTiddler(null,t);
		return false;
	},
	onTabClose : function(e){
		var t = this.getAttribute("tiddler");
		if (t) {
			if(story.hasChanges(t) && !readOnly) {
				if(!confirm(config.commands.cancelTiddler.warning.format([t])))
				return false;
			}
			story.closeTiddler(t);
		}
		return false;
	},
	onTabSave : function(e) {
		var t = this.getAttribute("tiddler");
		if (!e) e=window.event;
		if (t) config.commands.saveTiddler.handler(e,null,t);
		return false;
	},
	onSelectedTabButtonClick : function(event,src,title) {
		var t = this.getAttribute("tiddler");
		if (!event) event=window.event;
		if (t && config.options.txtSelectedTiddlerTabButton && config.commands[config.options.txtSelectedTiddlerTabButton])
			config.commands[config.options.txtSelectedTiddlerTabButton].handler(event, src, t);
		return false;
	},
	onTiddlersBarAction: function(event) {
		var source = event.target ? event.target.id : event.srcElement.id; // FF uses target and IE uses srcElement;
		if (source=="tiddlersBar") story.displayTiddler(null,'New Tiddler',DEFAULT_EDIT_TEMPLATE,false,null,null);
	},
	createActiveTabButton : function(place,title) {
		if (config.options.txtSelectedTiddlerTabButton && config.commands[config.options.txtSelectedTiddlerTabButton]) {
			var btn = createTiddlyButton(place, title, config.commands[config.options.txtSelectedTiddlerTabButton].tooltip ,config.macros.tiddlersBar.onSelectedTabButtonClick);
			btn.setAttribute("tiddler", title);
		}
		else
			createTiddlyText(place,title);
	}
}

story.coreCloseTiddler = story.coreCloseTiddler? story.coreCloseTiddler : story.closeTiddler;
story.coreDisplayTiddler = story.coreDisplayTiddler ? story.coreDisplayTiddler : story.displayTiddler;

story.closeTiddler = function(title,animate,unused) {
	if (title==config.macros.tiddlersBar.currentTiddler)
		config.macros.tiddlersBar.selectNextTab();
	story.coreCloseTiddler(title,false,unused); //disable animation to get it closed before calling tiddlersBar.refresh
	var e=document.getElementById("tiddlersBar");
	if (e) config.macros.tiddlersBar.refresh(e,null);
}

story.displayTiddler = function(srcElement,tiddler,template,animate,unused,customFields,toggle){
	story.coreDisplayTiddler(config.macros.tiddlersBar.tabsAnimationSource,tiddler,template,animate,unused,customFields,toggle);
	var title = (tiddler instanceof Tiddler)? tiddler.title : tiddler;  
	if (config.macros.tiddlersBar.isShown()) {
		story.forEachTiddler(function(t,e){
			if (t!=title) e.style.display="none";
			else e.style.display="";
		})
		config.macros.tiddlersBar.currentTiddler=title;
	}
	var e=document.getElementById("tiddlersBar");
	if (e) config.macros.tiddlersBar.refresh(e,null);
}

var coreRefreshPageTemplate = coreRefreshPageTemplate ? coreRefreshPageTemplate : refreshPageTemplate;
refreshPageTemplate = function(title) {
	coreRefreshPageTemplate(title);
	if (config.macros.tiddlersBar) config.macros.tiddlersBar.refresh(document.getElementById("tiddlersBar"));
}

ensureVisible=function (e) {return 0} //disable bottom scrolling (not useful now)

config.shadowTiddlers.StyleSheetTiddlersBar = "/*{{{*/\n";
config.shadowTiddlers.StyleSheetTiddlersBar += "#tiddlersBar .button {border:0}\n";
config.shadowTiddlers.StyleSheetTiddlersBar += "#tiddlersBar .tab {white-space:nowrap}\n";
config.shadowTiddlers.StyleSheetTiddlersBar += "#tiddlersBar {padding : 1em 0.5em 2px 0.5em}\n";
config.shadowTiddlers.StyleSheetTiddlersBar += ".tabUnselected .tabButton, .tabSelected .tabButton {padding : 0 2px 0 2px; margin: 0 0 0 4px;}\n";
config.shadowTiddlers.StyleSheetTiddlersBar += ".tiddler, .tabContents {border:1px [[ColorPalette::TertiaryPale]] solid;}\n";
config.shadowTiddlers.StyleSheetTiddlersBar +="/*}}}*/";
store.addNotification("StyleSheetTiddlersBar", refreshStyles);

config.refreshers.none = function(){return true;}
config.shadowTiddlers.PageTemplate=config.shadowTiddlers.PageTemplate.replace(/<div id='tiddlerDisplay'><\/div>/m,"<div id='tiddlersBar' refresh='none' ondblclick='config.macros.tiddlersBar.onTiddlersBarAction(event)'></div>\n<div id='tiddlerDisplay'></div>");

//}}}
The tiddler 'TiddlyWiki' doesn't yet exist. Double-click to create it
Collection of nice TiddlyWiki pages with either nice templates, plugins, tips, info, etc
* For a nice example of a TiddlyWiki page see [[Frank Dellaert's|http://www-static.cc.gatech.edu/~dellaert/]] web site.
* [[TiddlyWiki Tips|http://tiddlywikitips.com/]]
* [[Mahesh's page|http://www.cse.msu.edu/~arumugam/]]
TiddlyWiki is encoded in ~UTF-8. If another encoding is chosen some errors could arise.  Although the html code is encoded to set the encoding for ~UTF-8 some web servers disregard that.  A possible fix is suggested at the TiddlyWiki site although I find it rather technical.  For a quick fix check the 2nd to last message on [[Google Groups|http://groups.google.com/group/TiddlyWiki/browse_thread/thread/951913058c915f6d/9590409cc07cebda?lnk=st&q=tiddlywiki+encoding+UTF-8&rnum=3#9590409cc07cebda]]. For ease I quote here the content "In the subdirectory where I serve the tiddlywiki file, I created a file called ".htaccess". In this file I placed a single line: ~AddCharset ~UTF-8 .html What this does is force all *.html files in that subdirectory (and child subdirectories too) to be served as ~UTF-8. "
!Research Stuff
* I work on [[MATLAB|http://www.mathworks.com]] and ''C/C++''.
* I use ''UNIX'' and ''Perl'' scripts to get things done.
* I mostly work on ''Windows'' but run code on ''linux''.
* I write my papers/documents on --MS Word-- ~LaTeX.  Shout out to the [[MikTeX|http://www.miktex.org/]] Developing team and [[WinEDT|http://www.winedt.com/]].
* I write grant proposals on MS Word though. I wish latex had good and easy collaboration functionality. I wish [[MonkeyTex|http://monkeytex.bradcater.webfactional.com/]] will get better.
* I write my web pages in --HTML-- ~TiddlyWiki. ~TiddlyWiki allows you to have a non-linear website. See [[About This Site]]
* Love Firefox and --Eudora-- --Thunderbird-- GMAIL.
* Visit [[del.icio.us|http://del.icio.us/sotos]] my ''Favorite Bookmarking Service'' and see my very own bookmarks.
* The best way to keep track of your literature and papers is ''~CiteULike''. See my collection at [[My CiteUlike|http://www.citeulike.org/user/stsaft]]. I also use [[Mendeley|http://www.mendeley.com/profiles/sotirios-tsaftaris/]].
!Favorite Apps
* [[Launchy|http://www.launchy.net]]: The Open Source Keystroke Launcher for Windows
* [[UltraMon|http://ww.realtimesoft.com/ultramon/]]: Best $$ software for management of multiple displays.
* xplorer2 (Pro): Free (or $$) Windows File Manager
* TeamViewer: Remote Desktop application, similar to VNC. Great for admin or helping out a friend from far away.
* Skype
* PSPad editor: Great Free Text Editor
* freeCommander 2006: Free Windows File Manager
* SftpDrive: $$ App that maps an SFTP server as a hard drive in windows.
* TaskSwitchXP: Free alt-tab manager
* My backup solution: [[Mozy|http://www.mozy.com]] to backup to a server the most important files, and an external HD backing up all my data weekly with [[xxcopy|http://www.xxcopy.com/index.htm]].
* MediaCoder: The best and easiest to use media converter.
This as a new effort in collaboration with Professors Ying Wu and Katsaggelos funded by the US Department of Transportation.  Our goal is to use computer vision and tracking to track vehicles and analyzed their trajectories to study congestion propagation patterns.
My name is Sotirios A. Tsaftaris but most people call me @@''Sotos''@@.  I currently hold a joint Research Assistant Professor appointment at [[Northwestern University|http://www.northwestern.edu]] with the Departments of [[Electrical Engineering and Computer Science|http://www.eecs.northwestern.edu/]] and [[Radiology Feinberg School of Medicine|http://www.radiology.northwestern.edu/]].  

I collaborate closely with [[Professor Aggelos Katsaggelos|http://www.ece.northwestern.edu/%7Eaggk/]] at the [[Image and Video Processing Laboratory|http://ivpl.ece.northwestern.edu/]] and [[Professor Rohan Dharmakumar|http://www.cardiacmri.nmh.org/people/rohan-dharmakumar-phd/]] at the [[Cardiovascular MRI Research Laboratory|http://www.cardiacmri.nmh.org/]]. For more information you can check my [[Biography]]. Also visit [[IVPL|http://ivpl.ece.northwestern.edu/Staff/Sotos.html]] and [[Cardiac MRI|http://www.cardiacmri.nmh.org/]] for some information on my colleagues and other projects.

You might be interested in [[My Research]] and [[Publications]].  I also use [[LinkedIn|http://www.linkedin.com/pub/0/92b/291]] to manage my professional contacts.

My website is based on a standalone wiki implementation. To see how to navigate around it or to find something that you found in google but instead you are seeing this layout check [[About This Site]].
/***
|''Name:''|YourSearchPlugin|
|''Version:''|2.0.2 (2006-02-13)|
|''Source:''|http://tiddlywiki.abego-software.de/#YourSearchPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license]]|
|''TiddlyWiki:''|2.0|
|''Browser:''|Firefox 1.0.4+; Firefox 1.5; InternetExplorer 6.0|
<<tiddler [[YourSearch Introduction]]>>
For more information see [[Help|YourSearch Help]].

!Compatibility
This plugin requires TiddlyWiki 2.0.
Use http://tiddlywiki.abego-software.de/#YourSearchPlugin-1.0.1 for older TiddlyWiki versions.

!Revision history
* v2.0.2 (2006-02-13)
** Bugfix for Firefox 1.5.0.1 related to the "Show prefix" checkbox. Thanks to Ted Pavlic for reporting and to BramChen for fixing.
** Internal
*** Make "JSLint" conform
* v2.0.1 (2006-02-05)
** Support "Exact Word Match" (use '=' to prefix word)
** Support default filter settings (when no filter flags are given in search term)
** Rework on the "less than 3 chars search text" feature (thanks to EricShulman)
** Better support SinglePageMode when doing "Open all tiddlers" (thanks to EricShulman)
** Support Firefox 1.5.0.1
** Bug: Fixed a hilite bug in "classic search mode" (thanks to EricShulman)
* v2.0.0 (2006-01-16)
** Add User Interface
* v1.0.1 (2006-01-06)
** Support TiddlyWiki 2.0
* v1.0.0 (2005-12-28)
** initial version
/%
***/
//{{{
//============================================================================
//============================================================================
// YourSearchPlugin
//============================================================================
//============================================================================

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

version.extensions.YourSearchPlugin = {
 major: 2, minor: 0, revision: 2,
 date: new Date(2006, 2, 13),
 type: 'plugin',
 source: "http://tiddlywiki.abego-software.de/#YourSearchPlugin"
};

var alertAndThrow = function(msg) {alert(msg);throw msg;};

if (!window.abego) window.abego = {};
if (abego.YourSearch) alertAndThrow("abego.YourSearch already defined");
abego.YourSearch = {};

if (version.major < 2) alertAndThrow("YourSearchPlugin requires TiddlyWiki 2.0 or newer.\n\nGet YourSearch 1.0.1 to use YourSearch with older versions of TiddlyWiki.\n\nhttp://tiddlywiki.abego-software.de/#YourSearchPlugin-1.0.1");

//----------------------------------------------------------------------------
// The STQ (SimpleTiddlerQuery) Class
//----------------------------------------------------------------------------

// Internal.
//
var STQ = function(queryText, caseSensitive, matchTitleOnly, useRegExp) {
 this.queryText = queryText;
 this.caseSensitive = caseSensitive;

 if (useRegExp) {
 this.regExp = new RegExp(queryText, caseSensitive ? "mg" : "img");
 return;
 }

 this.terms = [];

 // The regular expression that matches a single search term of the form
 // (whitespace handling and grouping omitted for clarity):
 //
 // -?[#!%]*(<doubleQuoteStringLiteral>|<wordWithoutSpace>) (AND|OR)?
 //
 // group 1: '-' (negate, optional)
 // group 2: [!%#]* (may be empty)
 // group 3: String literal "..."
 // group 4: word
 // group 5: AND / OR (optional)
 //
 // (group 3 xor group 4 is defined)
 //
 var re = /\s*(\-)?([#%!=]*)(?:(?:("(?:(?:\\")|[^"])*")|(\S+)))(?:\s+((?:[aA][nN][dD])|(?:[oO][rR]))(?!\S))?/mg;

 var matches = re.exec(queryText);

 while (matches != null && matches.length == 6) {
 var negate = '-' == matches[1];
 var flags = matches[2];
 var inTitle = flags.indexOf('!') >= 0;
 var inText = flags.indexOf('%') >= 0;
 var inTag = flags.indexOf('#') >= 0;
 var wordMatch = flags.indexOf('=') >= 0;
 if (!inTitle && !inText && !inTag) {
 inTitle = config.options.chkSearchInTitle;
 inText = config.options.chkSearchInText;
 inTag = config.options.chkSearchInTags;

 // If all settings are off (i.e. all results would be empty,
 // i.e user error or checkboxes are gone) set all settings
 if (!inTitle && !inText && !inTag) {
 inTitle = inText = inTag = true;
 }
 }
 if (matchTitleOnly) {
 inText = false;
 inTag = false;
 }

 var text;
 if (matches[3]) {
 //Quoted String
 try {
 text = eval(matches[3]);
 } catch (ex) {
 // ignore error. Will be handled right after this.
 }
 } else {
 text = matches[4];
 }
 if (!text) {
 throw "Invalid search expression: %0".format([queryText]);
 }
 var orFollows = matches[5] && matches[5].charAt(0).toLowerCase() == 'o';
 this.terms.push(new STQ.Term(text, inTitle, inText, inTag, negate, orFollows, caseSensitive, wordMatch));

 matches = re.exec(queryText);
 }
};

var me = STQ.prototype;

// Internal.
//
// Returns an array with those tiddlers from the tiddlersMap that
// match the query.
//
me.getMatchingTiddlers = function(tiddlersMap) {
 var result = [];
 for (var i in tiddlersMap) {
 var t = tiddlersMap[i];
 if ((t instanceof Tiddler) && this.matchesTiddler(t)) {
 result.push(t);
 }
 }
 return result;
};


// Internal.
//
// Returns true if the query has a match in the given tiddler.
//
// @param tiddler [may be null]
//
me.matchesTiddler = function(tiddler) {
 if (this.regExp) {
 return this.regExp.test(tiddler.title) || this.regExp.test(tiddler.text);
 }

 var n = this.terms.length;
 if (n == 0) {
 return false;
 }

 var hasMatch = this.terms[0].matchesTiddler(tiddler);
 for (var i = 1; i < this.terms.length; i++) {
 if (this.terms[i-1].orFollows) {
 // the OR case.

 // shortcut: when the first operand of an OR is true
 // we don't need to evaluate the second operand since
 // the result of the OR will always be true.

 // In the other case we actually to the "OR"
 if (!hasMatch) {
 hasMatch |= this.terms[i].matchesTiddler(tiddler);
 }
 } else {
 // the AND case.

 // shortcut: when the first operand of an AND is false
 // we don't need to evaluate the second operand since
 // the result of the AND will always be false.

 // Otherwise we actually to the "AND"
 if (hasMatch) {
 hasMatch &= this.terms[i].matchesTiddler(tiddler);
 }
 }
 }
 return hasMatch;
};

// Internal.
//
me.getOnlyMatchTitleQuery = function() {
 if (!this.onlyMatchTitleQuery) {
 this.onlyMatchTitleQuery = new STQ(this.queryText, this.caseSensitive, true, this.useRegExp);
 }
 return this.onlyMatchTitleQuery;
};


// Returns a regular expression that can be used to marking/hiliting
// matches in the text.
//
// @return [may be null] null when the query does not provide marking information.
//
me.getMarkRegExp = function() {
 if (this.regExp) {
 // Only use the regExp for marking when it does not match the empty string.
 return "".search(this.regExp) >= 0 ? null : this.regExp;
 }

 var stringSet = {};
 var n = this.terms.length;
 for (var i = 0; i < this.terms.length; i++) {
 var term = this.terms[i];
 if (!term.negate) stringSet[term.text] = true;
 }

 var pattern = [];
 for (var t in stringSet) pattern.push("(" + t.escapeRegExp() + ")");

 if (pattern.length == 0) return null;

 var joinedPattern = pattern.join("|");
 return new RegExp(joinedPattern, this.caseSensitive ? "mg" : "img");
};

// Internal.
//
me.toString = function() {
 if (this.regExp) {
 return this.regExp.toString();
 }

 var result = "";
 for (var i = 0; i < this.terms.length; i++) {
 result += this.terms[i].toString();
 }
 return result;
};

//----------------------------------------------------------------------------
// The STQ.Term Class
//----------------------------------------------------------------------------

// Internal.
//
STQ.Term = function(text, inTitle, inText, inTag, negate, orFollows, caseSensitive, wordMatch) {
 this.text = text;
 this.inTitle = inTitle;
 this.inText = inText;
 this.inTag = inTag;
 this.negate = negate;
 this.orFollows = orFollows;
 this.caseSensitive = caseSensitive;
 this.wordMatch = wordMatch;

 var reText = text.escapeRegExp();
 if (this.wordMatch) reText = "\\b"+reText+"\\b";
 this.regExp = new RegExp(reText, "m"+(caseSensitive ? "" : "i"));
};

// Internal.
//
STQ.Term.prototype.toString = function() {
 return (this.negate ? "-" : "")+(this.inTitle ? "!" : "")+(this.inText? "%" : "")+(this.inTag? "#" : "")+(this.wordMatch ? "=" : "")+'"'+this.text+'"'+ (this.orFollows ? " OR " : " AND ");
};

// Internal.
//
// Returns true if the term has a match in the given tiddler.
//
// @param tiddler [may be null]
//
STQ.Term.prototype.matchesTiddler = function(tiddler) {
 if (!tiddler) {
 return false;
 }

 if (this.inTitle && this.regExp.test(tiddler.title)) {
 return !this.negate;
 }
 if (this.inText && this.regExp.test(tiddler.text)) {
 return !this.negate;
 }
 if (this.inTag) {
 var tags = tiddler.tags;
 if (tags) {
 for (var i = 0; i < tags.length; i++) {
 if (this.regExp.test(tags[i])) {
 return !this.negate;
 }
 }
 }
 }

 return this.negate;
};

//----------------------------------------------------------------------------
// Utils
//----------------------------------------------------------------------------

var stringToInt = function(s, defaultValue) {
 if (!s) return defaultValue;
 var n = parseInt(s);
 return (n == NaN) ? defaultValue : n;
};

var getIntAttribute = function(elem, name, defaultValue) {
 return stringToInt(elem.getAttribute(name));
};

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

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

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

var isWordChar = function(c) {
 return (c >= "a" && c <= "z") || (c >= "A" && c <= "Z") || c == "_";
};

// Returns the bounds of the word in s around offset as a {start: , end:} object.
//
// Returns null when the char at offset is not a word char.
//
var getWordBounds = function(s, offset) {
 // Handle the "offset is not in word" case
 if (!isWordChar(s[offset])) return null;

 for (var i = offset-1; i >= 0 && isWordChar(s[i]); i--)
 {/*empty*/}

 var startIndex = i+1;
 var n = s.length;
 for (i = offset+1; i < n && isWordChar(s[i]); i++)
 {/*empty*/}

 return {start: startIndex, end: i};
};


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

var logText = "";
var lastLogTime = null;
var logMessage = function(kind, s) {
 var now = new Date();
 var delta = lastLogTime ? (now-lastLogTime).toString() : "";
 logText += "<tr><td>"+now.convertToYYYYMMDDHHMMSSMMM()+"</td><td align='right'>"+delta+"</td><td>"+kind+"</td><td>"+s.htmlEncode()+"</td></tr>\n";
 lastLogTime = now;
};

function writeLog() {
 var t = " <<JsDoIt 'WriteLog' 'WriteLog' 'javascript:writeLog();story.closeTiddler(\"Log\");story.displayTiddler(null,\"Log\");'>>"+
 "<html><table><tbody><tr><th>Time</th><th>Delta (ms)</th><th>Kind</th><th>Message</th></tr>\n" + logText + "</tbody></table></html>";
 store.saveTiddler("Log", "Log",t,config.options.txtUserName,new Date(),["System", "Log"]);
 logText = "";
 lastLogTime = null;
}

//----------------------------------------------------------------------------
// The Search Core
//----------------------------------------------------------------------------

// Constants

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

// Visual appearance of the result page
var maxCharsInTitle = 80;
var maxCharsInTags = 50;
var maxCharsInText = 250;
var maxPagesInNaviBar = 10; // Maximum number of pages listed in the navigation bar (before or after the current page)

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

// Context Calculation
var minMatchWithContextSize = 40;
var maxMovementForWordCorrection = 4; // When a "match" context starts or end on a word the context borders may be changed to at most this amound to include or exclude the word.

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

// Variables
var resultElement; // The (popup) DOM element containing the search result [may be null]
var lastResults; // Array of tiddlers that matched the last search
var lastQuery; // The last Search query (STQ)
var lastSearchText; // The last search text, as used to create the lastQuery
var searchInputField; // The "search" input field
var searchButton; // The "search" button
var firstIndexOnPage = 0; // The index of the first item of the lastResults list displayed on the search result page

var currentTiddler; // While creating the search result page the tiddler that is currently rendered.
var indexInPage; // The index (in the current page) of the tiddler currently rendered.
var indexInResult; // The index (in the result array) of the tiddler currently rendered.


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

var standardRankFunction = function(tiddler, query) {
 // Count the matches in the title and the tags
 var markRE = query.getMarkRegExp();
 if (!markRE) return 1;

 var matchesInTitle = tiddler.title.match(markRE);
 var nMatchesInTitle = matchesInTitle ? matchesInTitle.length : 0;
 var nMatchesInTags = getMatchCount(tiddler.getTags(), markRE);

 // Calculate the "precision" of the matches in the title as the ratio of
 // the length of the matches to the total length of the title.
 var lengthOfMatchesInTitle = matchesInTitle ? matchesInTitle.join("").length : 0;
 var precisionInTitle = tiddler.title.length > 0 ? lengthOfMatchesInTitle/tiddler.title.length : 0;

 // calculate a weighted score
 var rank= nMatchesInTitle * matchInTitleWeight
 + nMatchesInTags * matchInTagsWeight
 + precisionInTitle * precisionInTitleWeight
 + 1;

 return rank;
};

// @return Tiddler[]
//
var findMatches = function(store, searchText,caseSensitive,useRegExp,sortField,excludeTag) {
 lastSearchText = searchText;

 var candidates = store.reverseLookup("tags",excludeTag,false);
 var query = new STQ(searchText,caseSensitive, false, useRegExp);
 lastQuery = query;

 var results = query.getMatchingTiddlers(candidates);

 // Rank the results
 var rankFunction = abego.YourSearch.getRankFunction();
 for (var i = 0; i < results.length; i++) {
 var tiddler = results[i];
 var rank = rankFunction(tiddler, query);
 // Add the rank information to the tiddler.
 // This is used during the sorting, but it may also
 // be used in the result, e.g. to display some "relevance"
 // information in the result
 tiddler.searchRank = rank;
 }

 // sort the result, taking care of the rank and the sortField
 if(!sortField) {
 sortField = "title";
 }

 var sortFunction = function (a,b) {
 var searchRankDiff = a.searchRank - b.searchRank;
 if (searchRankDiff == 0) {
 if (a[sortField] == b[sortField]) {
 return(0);
 } else {
 return (a[sortField] < b[sortField]) ? -1 : +1;
 }
 } else {
 return (searchRankDiff > 0) ? -1 : +1;
 }
 };
 results.sort(sortFunction);

 lastResults = results;

 return results;
};


//----------------------------------------------------------------------------
// Handling "limited marked text" in the preview
//
// The found/matched texts should be displayed to the user in the preview. To make
// it more useful the matched texts should be shown in their contexts, i.e. with
// some text around them. Since we only have limited space for the preview
// (around two lines for the text preview, less for the tags and title) and
// also don't want to both the user with "too much context" we use some
// heuristics to find the "best context (size)".
//
// On the other hand we want to use as much as possible of the preview area,
// so if there is room left we also display as much text from the beginning
// of the text as possible. This gives the user some kind of "overall context"
// especiallay if the start of the text is introductorily.
//
// Text Ranges
//
// To represent the ranges that should be displayed "Range" object are used.
// This are objects with a "start" and "end" property. In a corresponding
// "Ranges array" these objects are sorted by their start and no range object
// intersects/touches any other of the array.
//
//----------------------------------------------------------------------------

var moveToWordBorder = function(s, offset, isStartOffset) {
 var wordBounds;
 if (isStartOffset) {
 wordBounds = getWordBounds(s, offset);
 } else {
 if (offset <= 0) return offset;
 wordBounds = getWordBounds(s, offset-1);
 }
 if (!wordBounds) return offset;

 if (isStartOffset) {
 if (wordBounds.start >= offset-maxMovementForWordCorrection) return wordBounds.start;
 if (wordBounds.end <= offset+maxMovementForWordCorrection) return wordBounds.end;
 } else {
 if (wordBounds.end <= offset+maxMovementForWordCorrection) return wordBounds.end;
 if (wordBounds.start >= offset-maxMovementForWordCorrection) return wordBounds.start;
 }
 return offset;
};

var getContextRangeAround = function(s, startIndex, endIndex, matchCount, maxLen) {
 // Partition the available space into equal sized areas for each match and one
 // for the text start.
 // But the size should not go below a certain limit
 var size = Math.max(Math.floor(maxLen/(matchCount+1)), minMatchWithContextSize);

 // Substract the size of the range to get the size of the context.
 var contextSize = Math.max(size-(endIndex-startIndex), 0);
 // Two thirds of the context should be before the match, one third after.
 var contextEnd = Math.min(Math.floor(endIndex+contextSize/3), s.length);
 var contextStart = Math.max(contextEnd - size, 0);

 // If the contextStart/End is inside a word and the end of the word is
 // close move the pointers accordingly to make the text more readable.
 contextStart = moveToWordBorder(s, contextStart, true);
 contextEnd = moveToWordBorder(s, contextEnd, false);

 return {start: contextStart, end: contextEnd};
};

// Splits s into a sequence of "matched" and "unmatched" substrings, using the
// matchRegExp to do the matching.
//
// Returns an array of objects with a "text" property containing the substring text.
// Substrings that are "matches" also contain a boolean "isMatch" property set to true.
//
// @param matchRegExp [may be null] when null no matching is performed and the returned
// array just contains one item with s as its text
//
var getTextAndMatchArray = function(s, matchRegExp) {
 var result = [];
 if (matchRegExp) {
 var startIndex = 0;
 var n = s.length;
 var currentLen = 0;
 do {
 matchRegExp.lastIndex = startIndex;
 var match = matchRegExp.exec(s);
 if (match) {
 if (startIndex < match.index) {
 var t = s.substring(startIndex, match.index);
 result.push({text:t});
 }
 result.push({text:match[0], isMatch:true});
 startIndex = match.index + match[0].length;
 } else {
 result.push({text: s.substr(startIndex)});
 break;
 }
 } while (true);
 } else {
 result.push({text: s});
 }
 return result;
};


var simpleCreateLimitedTextWithMarks = function(place, s, maxLen) {
 if (!lastQuery) return;

 var textAndMatches = getTextAndMatchArray(s, lastQuery.getMarkRegExp());
 var currentLen = 0;
 for (var i=0; i < textAndMatches.length && currentLen < maxLen; i++) {
 var t = textAndMatches[i];
 var text = t.text;
 if (t.isMatch) {
 createTiddlyElement(place,"span",null,"marked",text);
 } else {
 var remainingLen = maxLen-currentLen;
 if (remainingLen < text.length) {
 text = text.substring(0, remainingLen)+"...";
 }
 createTiddlyText(place, text);
 }
 currentLen += text.length;
 }
};



var addRange = function(ranges, startIndex, endIndex) {
 var n = ranges.length;

 // When there are no ranges in ranges, just add it.
 if (n == 0) {
 ranges.push({start: startIndex, end: endIndex});
 return;
 }

 var i = 0;
 for (; i < n; i++) {
 var range = ranges[i];

 // find the first range that intersects or "touches" [startIndex, endIndex[
 if (range.start <= endIndex && startIndex <= range.end) {
 // Found.

 var r;
 // find the first range behind the new range that does not interfere
 var rIndex = i+1;
 for (; rIndex < n; rIndex++) {
 r = ranges[rIndex];
 if (r.start > endIndex || startIndex > range.end) {
 break;
 }
 }

 // Replace the ranges i to rIndex-1 with the union of the new range with these ranges.
 var unionStart = startIndex;
 var unionEnd = endIndex;
 for (var j = i; j < rIndex; j++) {
 r = ranges[j];
 unionStart = Math.min(unionStart, r.start);
 unionEnd = Math.max(unionEnd, r.end);
 }
 ranges.splice(i, rIndex-i, {start: unionStart, end: unionEnd});
 return;
 }

 // if we found a range R that is right of the new range there is no
 // intersection and we can insert the new range before R.
 if (range.start > endIndex) {
 break;
 }
 }

 // When we are here the new range does not interfere with any range in ranges and
 // i is the index of the first range right to it (or ranges.length, when the new range
 // becomes the right most range).

 ranges.splice(i, 0, {start: startIndex, end: endIndex});
};

var getTotalRangesSize = function(ranges) {
 var totalRangeSize = 0;
 for (var i=0; i < ranges.length; i++) {
 var range = ranges[i];
 totalRangeSize += range.end-range.start;
 }
 return totalRangeSize;
};

// Processes the text between startIndex and endIndex of the textAndMatches
// "writes" them (as DOM elements) at the given place, possibly as "marked" text.
//
// When endIndex is not the end of the full text an ellisis is appended.
//
var writeTextAndMatchRange = function(place, s, textAndMatches, startIndex, endIndex) {
 var t;
 var text;

 // find the first text item to write
 var pos = 0;
 var i = 0;
 var offset = 0;
 for (;i < textAndMatches.length; i++) {
 t = textAndMatches[i];
 text = t.text;
 if (startIndex < pos+text.length) {
 offset = startIndex - pos;
 break;
 }
 pos += text.length;
 }

 var remainingLen = endIndex - startIndex;
 for (; i < textAndMatches.length && remainingLen > 0; i++) {
 t = textAndMatches[i];
 text = t.text.substr(offset);
 offset = 0;
 if (text.length > remainingLen) text = text.substr(0,remainingLen);

 if (t.isMatch) {
 createTiddlyElement(place,"span",null,"marked",text);
 } else {
 createTiddlyText(place, text);
 }
 remainingLen -= text.length;
 }

 if (endIndex < s.length) {
 createEllipsis(place);
 }
};

var getMatchedTextCount = function(textAndMatches) {
 var result = 0;
 for (var i=0; i < textAndMatches.length; i++) {
 if (textAndMatches[i].isMatch) {
 result++;
 }
 }
 return result;
};

// Get all ranges around matched substrings with their contexts
//
var getMatchedTextWithContextRanges = function(textAndMatches, s, maxLen) {
 var ranges = [];
 var matchCount = getMatchedTextCount(textAndMatches);
 var pos = 0;
 for (var i=0; i < textAndMatches.length; i++) {
 var t = textAndMatches[i];
 var text = t.text;
 if (t.isMatch) {
 var range = getContextRangeAround(s, pos, pos+text.length, matchCount, maxLen);
 addRange(ranges, range.start, range.end);
 }
 pos += text.length;
 }
 return ranges;
};

var fillUpRanges = function(s, ranges, maxLen) {
 var remainingLen = maxLen - getTotalRangesSize(ranges);
 while (remainingLen > 0) {
 if (ranges.length == 0) {
 // No matches added yet. Make one large range.
 addRange(ranges, 0, moveToWordBorder(s, maxLen, false));
 return;
 } else {
 var range = ranges[0];
 var startIndex;
 var maxEndIndex;
 if (range.start == 0) {
 // The first range already starts at the beginning of the string.

 // When there is a second range fill to the next range start or to the maxLen.
 startIndex = range.end;
 if (ranges.length > 1) {
 maxEndIndex = ranges[1].start;
 } else {
 // Only one range. Add a range after that with the complete remaining len
 // (corrected to "beautify" the output)
 addRange(ranges, startIndex, moveToWordBorder(s, startIndex+remainingLen, false));
 return;
 }
 } else {
 // There is unused space between the start of the text and the first range.
 startIndex = 0;
 maxEndIndex = range.start;
 }
 var endIndex = Math.min(maxEndIndex, startIndex+remainingLen);
 addRange(ranges, startIndex, endIndex);
 remainingLen -= (endIndex-startIndex);
 }
 }
};

// Write the given ranges of s, using textAndMatches for marking portions of the text.
//
var writeRanges = function(place, s, textAndMatches, ranges, maxLen) {
 if (ranges.length == 0) return;

 // When the first range is not at the start of the text write an ellipsis("...")
 // (Ellipses between ranges are written in the writeTextAndMatchRange method)
 if (ranges[0].start > 0) createEllipsis(place);

 var remainingLen = maxLen;
 for (var i = 0; i < ranges.length && remainingLen > 0; i++) {
 var range = ranges[i];
 var len = Math.min(range.end - range.start, remainingLen);
 writeTextAndMatchRange(place, s, textAndMatches, range.start, range.start+len);
 remainingLen -= len;
 }
};

var createLimitedTextWithMarksAndContext = function(place, s, maxLen) {
 if (!lastQuery) return;

 if (s.length < maxLen) maxLen = s.length;

 var textAndMatches = getTextAndMatchArray(s, lastQuery.getMarkRegExp());

 var ranges = getMatchedTextWithContextRanges(textAndMatches, s, maxLen);

 // When the maxLen is not yet reached add more ranges
 // starting from the beginning until either maxLen or
 // the end of the string is reached.
 fillUpRanges(s, ranges, maxLen);

 writeRanges(place, s, textAndMatches, ranges, maxLen);
};

var createLimitedTextWithMarks = function(place, s, maxLen) {
// return simpleCreateLimitedTextWithMarks(place, s, maxLen);
 return createLimitedTextWithMarksAndContext(place, s, maxLen);
};


//----------------------------------------------------------------------------
// The Search Result
//----------------------------------------------------------------------------

var myStorySearch = function(text,useCaseSensitive,useRegExp)
{
 highlightHack = new RegExp(useRegExp ? text:text.escapeRegExp(),useCaseSensitive ? "mg" : "img");
 var matches = findMatches(store, text,useCaseSensitive,useRegExp,"title","excludeSearch");

 firstIndexOnPage = 0;
 showResult();

 highlightHack = null;
};


var myMacroSearchHandler = function(place,macroName,params)
{
 var lastSearchText = "";
 var searchTimeout = null;
 var doSearch = function(txt)
 {
 if (config.options.chkUseYourSearch)
 myStorySearch(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch);
 else
 story.search(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch);
 lastSearchText = txt.value;
 };
 var clickHandler = function(e)
 {
 doSearch(searchInputField);
 return false;
 };
 var keyHandler = function(e)
 {
 if (!e) var e = window.event;
 switch(e.keyCode)
 {
 case 13:
 doSearch(this);
 break;
 case 27:
 // When the result is open, close it,
 // otherwise clear the content of the input field
 if (isResultOpen()) {
 closeResult();
 } else {
 this.value = "";
 clearMessage();
 }
 break;
 }
 if (String.fromCharCode(e.keyCode) == this.accessKey || e.altKey)
 {
 reopenResultIfApplicable();
 }

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


 var focusHandler = function(e)
 {
 this.select();
 reopenResultIfApplicable();
 };

 var btn = createTiddlyButton(place,this.label,this.prompt,clickHandler);
 var txt = createTiddlyElement(place,"input",null,null,null);
 if(params[0])
 txt.value = params[0];
 txt.onkeyup = keyHandler;
 txt.onfocus = focusHandler;
 txt.setAttribute("size",this.sizeTextbox);
 txt.setAttribute("accessKey",this.accessKey);
 txt.setAttribute("autocomplete","off");
 if(config.browser.isSafari)
 {
 txt.setAttribute("type","search");
 txt.setAttribute("results","5");
 }
 else
 txt.setAttribute("type","text");

 searchInputField = txt;
 searchButton = btn;
};

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

var closeResult = function() {
 if (isResultOpen()) {
 document.body.removeChild(resultElement);
 }
};


var openAllFoundTiddlers = function() {
 closeResult();
 if (lastResults) {
 var titles=[];
 for(var i = 0; i<lastResults.length; i++)
 titles.push(lastResults[i].title);
 story.displayTiddlers(null,titles);
 }
};

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

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

 // Ensure that the firstIndexOnPage is really a page start.
 // This may have become violated when the ItemsPerPage are changed,
 // e.g. when switching between previewText and simple mode.
 firstIndexOnPage = Math.floor(firstIndexOnPage / getItemsPerPage()) * getItemsPerPage();

 // Expand the template macros etc.
 applyHtmlMacros(resultElement,null);
 refreshElements(resultElement,null);

 // When there are items found add them to the result page (pagewise)
 if (lastResults && lastResults.length > 0) {
 // Load the template how to display the items that represent a found tiddler
 var itemHtml = store.getTiddlerText("YourSearchItemTemplate");
 if (!itemHtml) alertAndThrow("YourSearchItemTemplate not found");

 // Locate the node that shall contain the list of found tiddlers
 var items = document.getElementById(yourSearchResultItemsID);
 if(!items)
 items = createTiddlyElement(resultElement,"div",yourSearchResultItemsID);

 // Add the items of the current page
 var endIndex = Math.min(firstIndexOnPage+getItemsPerPage(), lastResults.length);
 indexInPage = -1;
 for (var i=firstIndexOnPage; i < endIndex; i++) {
 currentTiddler = lastResults[i];
 indexInPage++;
 indexInResult = i;

 var item = createTiddlyElement(items,"div",null, "yourSearchItem");
 item.innerHTML = itemHtml;
 applyHtmlMacros(item,null);
 refreshElements(item,null);
 }
 }

 // The currentTiddler must only be defined while rendering the found tiddlers
 currentTiddler = null;

 ensureResultIsDisplayedNicely();
};

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

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

// Adjusts the resultElement's size and position, relative to the search input field.
//
var adjustResultPositionAndSize = function() {
 if (!searchInputField) return;

 var root = searchInputField;

 // Position the result below the root and resize it if necessary.
 var rootLeft = findPosX(root);
 var rootTop = findPosY(root);
 var rootHeight = root.offsetHeight;
 var popupLeft = rootLeft;
 var popupTop = rootTop + rootHeight;

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

 // Ensure that the left and right of the result are not
 // clipped by the window. Move it to the left or right, if necessary.
 var popupWidth = resultElement.offsetWidth;
 if(popupLeft + popupWidth > winWidth)
 popupLeft = winWidth - popupWidth-30;
 if (popupLeft < 0) popupLeft = 0;

 // Do the actual moving
 resultElement.style.left = popupLeft + "px";
 resultElement.style.top = popupTop + "px";
 resultElement.style.display = "block";
};

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

 refreshResult();
};

var reopenResultIfApplicable = function() {
 if (searchInputField == null || !config.options.chkUseYourSearch) return;

 if ((searchInputField.value == lastSearchText) && lastSearchText && !isResultOpen()) {
 // For speedup we check re-use the previously created resultElement, if possible.
 if (resultElement && (resultElement.parentNode != document.body)) {
 document.body.appendChild(resultElement);
 ensureResultIsDisplayedNicely();
 } else {
 showResult();
 }
 }
};

var setFirstIndexOnPage = function(index) {
 if (!lastResults || lastResults.length == 0) return;

 firstIndexOnPage = Math.min(Math.max(0, index), lastResults.length-1);
 refreshResult();
};


var onDocumentClick = function(e) {
 // Close the search result page when the user clicks on the document
 // (and not into the searchInputField, on the search button or in the result)
 if (e.target == searchInputField) return;
 if (e.target == searchButton) return;
 if (resultElement && isDescendantOrSelf(resultElement, e.target)) return;

 closeResult();
};

var onDocumentKeyup = function(e) {
 // Close the search result page when the user presses "ESC"
 if (e.keyCode == 27) closeResult();
};
addEvent(document,"click",onDocumentClick);
addEvent(document,"keyup",onDocumentKeyup);


//----------------------------------------------------------------------------
// Macros
//----------------------------------------------------------------------------

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

config.macros.yourSearch = {
 // Standard Properties
 label: "yourSearch",
 prompt: "Gives access to the current/last YourSearch result",

 funcs: {},

 tests: {
 "true" : function() {return true;},
 "false" : function() {return false;},
 "found" : function() {return lastResults && lastResults.length > 0;},
 "previewText" : function() {return config.options.chkPreviewText;}
 }
};

config.macros.yourSearch.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
 if (params.length == 0) return;

 var name = params[0];
 var func = config.macros.yourSearch.funcs[name];
 if (func) func(place,macroName,params,wikifier,paramString,tiddler);
};

config.macros.yourSearch.funcs.itemRange = function(place) {
 if (lastResults) {
 var endIndex = Math.min(firstIndexOnPage+getItemsPerPage(), lastResults.length);
 var s = "%0 - %1".format([firstIndexOnPage+1,endIndex]);
 createTiddlyText(place, s);
 }
};

config.macros.yourSearch.funcs.count = function(place) {
 if (lastSearchText) {
 createTiddlyText(place, lastResults.length.toString());
 }
};

config.macros.yourSearch.funcs.query = function(place) {
 if (lastResults) {
 createTiddlyText(place, lastSearchText);
 }
};

config.macros.yourSearch.funcs.version = function(place) {
 var t = "YourSearch %0.%1.%2".format(
 [version.extensions.YourSearchPlugin.major,
 version.extensions.YourSearchPlugin.minor,
 version.extensions.YourSearchPlugin.revision]);
 var e = createTiddlyElement(place, "a");
 e.setAttribute("href", "http://tiddlywiki.abego-software.de/#YourSearchPlugin");
 e.innerHTML = '<font color="black" face="Arial, Helvetica, sans-serif">'+t+'<font>';
};

config.macros.yourSearch.funcs.copyright = function(place) {
 var e = createTiddlyElement(place, "a");
 e.setAttribute("href", "http://tiddlywiki.abego-software.de");
 e.innerHTML = '<font color="black" face="Arial, Helvetica, sans-serif">&copy; 2005-2006 <b><font color="red">abego</font></b> Software<font>';
};


config.macros.yourSearch.funcs.linkButton = function(place,macroName,params,wikifier,paramString,tiddler) {
 if (params < 2) return;

 var tiddlyLink = params[1];
 var text = params < 3 ? tiddlyLink : params[2];
 var tooltip = params < 4 ? text : params[3];
 var accessKey = params < 5 ? null : params[4];

 var btn = createTiddlyButton(place,text,tooltip,closeResultAndDisplayTiddler,null,null, accessKey);
 btn.setAttribute("tiddlyLink",tiddlyLink);
};

config.macros.yourSearch.funcs.closeButton = function(place,macroName,params,wikifier,paramString,tiddler) {
 var button = createTiddlyButton(place, "close", "Close the Search Results (Shortcut: ESC)", closeResult);
};

config.macros.yourSearch.funcs.openAllButton = function(place,macroName,params,wikifier,paramString,tiddler) {
 if (!lastResults) return;
 var n = lastResults.length;
 if (n == 0) return;

 var title = n == 1 ? "open tiddler" : "open all %0 tiddlers".format([n]);
 var button = createTiddlyButton(place, title, "Open all found tiddlers (Shortcut: Alt-O)", openAllFoundTiddlers);
 button.setAttribute("accessKey","O");
};

var onNaviButtonClick = function(e) {
 if (!e) var e = window.event;
 var pageIndex = getIntAttribute(this, "page");
 setFirstIndexOnPage(pageIndex * getItemsPerPage(), 0);
};

config.macros.yourSearch.funcs.naviBar = function(place,macroName,params,wikifier,paramString,tiddler) {
 if (!lastResults || lastResults.length == 0) return;

 var button;
 var currentPageIndex = Math.floor(firstIndexOnPage / getItemsPerPage());
 var lastPageIndex = Math.floor((lastResults.length-1) / getItemsPerPage());
 if (currentPageIndex > 0) {
 button = createTiddlyButton(place, "Previous", "Go to previous page (Shortcut: Alt-'<')", onNaviButtonClick, "prev");
 button.setAttribute("page",(currentPageIndex-1).toString());
 button.setAttribute("accessKey","<");
 }

 for (var i = -maxPagesInNaviBar; i < maxPagesInNaviBar; i++) {
 var pageIndex = currentPageIndex+i;
 if (pageIndex < 0) continue;
 if (pageIndex > lastPageIndex) break;

 var pageNo = (i+currentPageIndex+1).toString();
 var buttonClass = pageIndex == currentPageIndex ? "currentPage" : "otherPage";
 button = createTiddlyButton(place, pageNo, "Go to page %0".format([pageNo]), onNaviButtonClick, buttonClass);
 button.setAttribute("page",(pageIndex).toString());
 }

 if (currentPageIndex < lastPageIndex) {
 button = createTiddlyButton(place, "Next", "Go to next page (Shortcut: Alt-'>')", onNaviButtonClick, "next");
 button.setAttribute("page",(currentPageIndex+1).toString());
 button.setAttribute("accessKey",">");
 }
};


config.macros.yourSearch.funcs["if"] = function(place,macroName,params,wikifier,paramString,tiddler) {
 if (params.length < 2) return;

 var testName = params[1];
 var negate = (testName == "not");
 if (negate) {
 if (params.length < 3) return;
 testName = params[2];
 }

 var test = config.macros.yourSearch.tests[testName];
 var showIt = false;
 try {
 if (test) {
 showIt = test(place,macroName,params,wikifier,paramString,tiddler) != negate;
 } else {
 // When no predefined test is specified try to evaluate it as a JavaScript expression.
 showIt = (!eval(testName)) == negate;
 }
 } catch (ex) {
 }

 if (!showIt) {
 place.style.display="none";
 }
};

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

config.macros.yourSearch.funcs.chkPreviewText = function(place,macroName,params,wikifier,paramString,tiddler) {
 var optionParams = params.slice(1).join(" ");

 var elem = createOptionWithRefresh(place, "chkPreviewText", wikifier,tiddler);
 elem.setAttribute("accessKey", "P");
 elem.title = "Show text preview of found tiddlers (Shortcut: Alt-P)";
 return elem;
};

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

config.macros.foundTiddler = {
 // Standard Properties
 label: "foundTiddler",
 prompt: "Provides information on the tiddler currently processed on the YourSearch result page",

 funcs: {}
};


config.macros.foundTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
 if (!currentTiddler) return;
 var name = params[0];
 var func = config.macros.foundTiddler.funcs[name];
 if (func) func(place,macroName,params,wikifier,paramString,tiddler);
};

// Closes the Search Result window and displays the tiddler
// defined by the "tiddlyLink" attribute of this element
//
var closeResultAndDisplayTiddler = function(e)
{
 closeResult();

 var title = this.getAttribute("tiddlyLink");
 if(title) {
 var withHilite = this.getAttribute("withHilite");
 var oldHighlightHack = highlightHack;
 if (withHilite && withHilite=="true" && lastQuery) {
 highlightHack = lastQuery.getMarkRegExp();
 }
 story.displayTiddler(this,title);
 highlightHack = oldHighlightHack;
 }
 return(false);
};

// Returns the "shortcut number" of the currentTiddler.
// I.e. When the user presses Alt-n the given tiddler is opened/display.
//
// @return 0-9 or -1 when no number is defined
//
var getShortCutNumber = function() {
 if (!currentTiddler) return -1;

 if (indexInPage >= 0 && indexInPage <= 9) {
 return indexInPage < 9 ? (indexInPage+1) : 0;
 } else {
 return -1;
 }
};

config.macros.foundTiddler.funcs.title = function(place,macroName,params,wikifier,paramString,tiddler) {
 if (!currentTiddler) return;

 var shortcutNumber = getShortCutNumber();
 var tooltip = shortcutNumber >= 0
 ? "Open tiddler (Shortcut: Alt-%0)".format([shortcutNumber.toString()])
 : "Open tiddler";

 var btn = createTiddlyButton(place,null,tooltip,closeResultAndDisplayTiddler,null);
 btn.setAttribute("tiddlyLink",currentTiddler.title);
 btn.setAttribute("withHilite","true");

 createLimitedTextWithMarks(btn, currentTiddler.title, maxCharsInTitle);

 if (shortcutNumber >= 0) {
 btn.setAttribute("accessKey",shortcutNumber.toString());
 }
};

config.macros.foundTiddler.funcs.tags = function(place,macroName,params,wikifier,paramString,tiddler) {
 if (!currentTiddler) return;

 createLimitedTextWithMarks(place, currentTiddler.getTags(), maxCharsInTags);
};

config.macros.foundTiddler.funcs.text = function(place,macroName,params,wikifier,paramString,tiddler) {
 if (!currentTiddler) return;

 createLimitedTextWithMarks(place, removeTextDecoration(currentTiddler.text), maxCharsInText);
};


// Renders the "shortcut number" of the current tiddler, to indicate to the user
// what number to "Alt-press" to open the tiddler.
//
config.macros.foundTiddler.funcs.number = function(place,macroName,params,wikifier,paramString,tiddler) {
 var numberToDisplay = getShortCutNumber();
 if (numberToDisplay >= 0) {
 var text = "%0)".format([numberToDisplay.toString()]);
 createTiddlyElement(place,"span",null,"shortcutNumber",text);
 }
};

function scrollToAnchor(name) {
 return false;
}
//----------------------------------------------------------------------------
// Configuration Stuff
//----------------------------------------------------------------------------

if (config.options.chkUseYourSearch == undefined) config.options.chkUseYourSearch = true;
if (config.options.chkPreviewText == undefined) config.options.chkPreviewText = true;
if (config.options.chkSearchAsYouType == undefined) config.options.chkSearchAsYouType=true;
if (config.options.chkSearchInTitle == undefined) config.options.chkSearchInTitle=true;
if (config.options.chkSearchInText == undefined) config.options.chkSearchInText=true;
if (config.options.chkSearchInTags == undefined) config.options.chkSearchInTags=true;
if (config.options.txtItemsPerPage == undefined) config.options.txtItemsPerPage =itemsPerPageDefault;
if (config.options.txtItemsPerPageWithPreview == undefined) config.options.txtItemsPerPageWithPreview=itemsPerPageWithPreviewDefault;

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

//----------------------------------------------------------------------------
// Shadow Tiddlers
//----------------------------------------------------------------------------

config.shadowTiddlers["YourSearch Introduction"] =
 "!About YourSearch\n"+
 "\n"+
 "YourSearch gives you a bunch of new features to simplify and speed up your daily searches in TiddlyWiki. It seamlessly integrates into the standard TiddlyWiki search: just start typing into the 'search' field and explore!\n"+
 "\n"+
 "''May the '~Alt-F' be with you.''\n"+
 "\n"+
 "\n"+
 "!Features\n"+
 "* YourSearch searches for tiddlers that match your query ''as you type'' into the 'search' field. It presents a list of the ''\"Top Ten\"'' tiddlers in a ''popup-like window'': the ''[[YourSearch Result]]''. The tiddlers currently displayed in your TiddlyWiki are not affected.\n"+
 "* Using ''~TiddlerRank technology'' the [[YourSearch Result]] lists the ''most interesting tiddlers first''.\n"+
 "* Through ''Filtered Search'' and ''Boolean Search'' you can easily refining your search, like excluding words or searching for multiple words. This way less tiddlers are displayed in the [[YourSearch Result]] and you can faster scan the result for the tiddler you are looking for.\n"+
 "* The [[YourSearch Result]] lists the found tiddlers ''page-wise'', e.g. 10 per page. Use the ''Result Page Navigation Bar'' to navigate between pages if the result does not fit on one page.\n"+
 "* The [[YourSearch Result]] states the ''total number of found tiddlers''. This way you can quickly decide if you want to browse the result list or if you want to refine your search first to shorten the result list.\n"+
 "* Beside the ''title of the found tiddlers'' the [[YourSearch Result]] also ''displays tags'' and ''tiddler text previews''. The ''tiddler text preview'' is an extract of the tiddler's content, showing the most interesting parts related to your query (e.g. the texts around the words you are looking for).\n"+
 "* The words you are looking for are hilited in the titles, tags and text previews of the [[YourSearch Result]].\n"+
 "* If you are not interested in the tiddler text previews but prefer to get longer lists of tiddlers on one result page you may ''switch of the text preview''.\n"+
 "* If the [[YourSearch Result]] contains the tiddler you are looking for you can just ''click its title to display'' it in your TiddlyWiki. Alternatively you may also ''open all found tiddlers'' at once. \n"+
 "* Use [[YourSearch Options]] to customize YourSearch to your needs. E.g. depending on the size of your screen you may change the number of tiddlers displayed in the [[YourSearch Result]]. In the [[YourSearch Options]] and the AdvancedOptions you may also switch off YourSearch in case you temporarily want to use the standard search.\n"+
 "* For the most frequently actions ''access keys'' are defined so you can perform your search without using the mouse.\n"+
 "\n"
 ;

config.shadowTiddlers["YourSearch Help"] =
// "<html><a name='Top'/>"+
// "<a href='javascript:scrollToAnchor(\"Filtered\");'>[Filtered Search] </a>"+
// "<a href='#Boolean'>[Boolean Search] </a>"+
// "<a href='#Exact'>['Exact Word' Search] </a>"+
// "<a href='#Combined'>[Combined Search] </a>"+
// "<a href='#Case'>[CaseSensitiveSearch and RegExpSearch] </a>"+
// "<a href='#Access'>[Access Keys] </a>"+
// "</html>"+
 "<<tiddler [[YourSearch Introduction]]>>"+
// "<html><sub><a href='#Top'>[Top]</a></sub></html>\n"+
 "\n"+
 "!Filtered Search<html><a name='Filtered'/></html>\n"+
 "Using the Filtered Search you can restrict your search to certain parts of a tiddler, e.g only search the tags or only the titles.\n"+
 "|!What you want|!What you type|!Example|\n"+
 "|Search ''titles only''|start word with ''!''|{{{!jonny}}}|\n"+
 "|Search ''contents only''|start word with ''%''|{{{%football}}}|\n"+
 "|Search ''tags only''|start word with ''#''|{{{#Plugin}}}|\n"+
 "\n"+
 "You may use more than one filter for a word. E.g. {{{!#Plugin}}} finds tiddlers containing \"Plugin\" either in the title or in the tags (but does not look for \"Plugin\" in the content).\n"+
// "<html><sub><a href='#Top'>[Top]</a></sub></html>\n"+
 "\n"+
 "!Boolean Search<html><a name='Boolean'/></html>\n"+
 "The Boolean Search is useful when searching for multiple words.\n"+
 "|!What you want|!What you type|!Example|\n"+
 "|''All words'' must exist|List of words|{{{jonny jeremy}}}|\n"+
 "|''At least one word'' must exist|Separate words by ''or''|{{{jonny or jeremy}}}|\n"+
 "|A word ''must not exist''|Start word with ''-''|{{{-jonny}}}|\n"+
 "\n"+
 "''Note:'' When you specify two words, separated with a space, YourSearch finds all tiddlers that contain both words, but not necessarily next to each other. If you want to find a sequence of word, e.g. '{{{John Brown}}}', you need to put the words into quotes. I.e. you type: {{{\"john brown\"}}}.\n"+
// "<html><sub><a href='#Top'>[Top]</a></sub></html>\n"+
 "\n"+
 "!'Exact Word' Search<html><a name='Exact'/></html>\n"+
 "By default a search result all matches that 'contain' the searched text. \n"+
 " E.g. if you search for 'Task' you will get all tiddlers containing 'Task', but also 'CompletedTask', 'TaskForce' etc.\n"+
 "\n"+
 "If you only want to get the tiddlers that contain 'exactly the word' you need to prefix it with a '='. E.g. typing '=Task' will the tiddlers that contain the word 'Task', ignoring words that just contain 'Task' as a substring.\n"+
// "<html><sub><a href='#Top'>[Top]</a></sub></html>\n"+
 "\n"+
 "!Combined Search<html><a name='Combined'/></html>\n"+
 "You are free to combine the various search options. \n"+
 "\n"+
 "''Examples''\n"+
 "|!What you type|!Result|\n"+
 "|{{{!jonny !jeremy -%football}}}| all tiddlers with both {{{jonny}}} and {{{jeremy}}} in its titles, but no {{{football}}} in content.|\n"+
 "|{{{#=Task}}}|All tiddlers tagged with 'Task' (the exact word). Tags named 'CompletedTask', 'TaskForce' etc. are not considered.|\n"+
// "<html><sub><a href='#Top'>[Top]</a></sub></html>\n"+
 "\n"+
 "!~CaseSensitiveSearch and ~RegExpSearch<html><a name='Case'/></html>\n"+
 "The standard search options ~CaseSensitiveSearch and ~RegExpSearch are fully supported by YourSearch. However when ''~RegExpSearch'' is on Filtered and Boolean Search are disabled.\n"+
// "<html><sub><a href='#Top'>[Top]</a></sub></html>\n"+
 "\n"+
 "!Access Keys<html><a name='Access'/></html>\n"+
 "You are encouraged to use the access keys (also called \"shortcut\" keys) for the most frequently used operations. For quick reference these shortcuts are also mentioned in the tooltip for the various buttons etc.\n"+
 "\n"+
 "|!Key|!Operation|\n"+
 "|{{{Alt-F}}}|''The most important keystroke'': It moves the cursor to the search input field so you can directly start typing your query. Pressing {{{Alt-F}}} will also display the previous search result. This way you can quickly display multiple tiddlers using \"Press {{{Alt-F}}}. Select tiddler.\" sequences.|\n"+
 "|{{{ESC}}}|Closes the [[YourSearch Result]]. When the [[YourSearch Result]] is already closed and the cursor is in the search input field the field's content is cleared so you start a new query.|\n"+
 "|{{{Alt-1}}}, {{{Alt-2}}},... |Pressing these keys opens the first, second etc. tiddler from the result list.|\n"+
 "|{{{Alt-O}}}|Opens all found tiddlers.|\n"+
 "|{{{Alt-P}}}|Toggles the 'Preview Text' mode.|\n"+
 "|{{{Alt-'<'}}}, {{{Alt-'>'}}}|Displays the previous or next page in the [[YourSearch Result]].|\n"+
 "|{{{Return}}}|When you have turned off the 'as you type' search mode pressing the {{{Return}}} key actually starts the search (as does pressing the 'search' button).|\n"+
// "<html><sub><a href='#Top'>[Top]</a></sub></html>\n"+
 "\n"
 ;

config.shadowTiddlers["YourSearch Options"] =
 "|>|!YourSearch Options|\n"+
 "|>|<<option chkUseYourSearch>> Use 'Your Search'|\n"+
 "|!|<<option chkPreviewText>> Show Text Preview|\n"+
 "|!|<<option chkSearchAsYouType>> 'Search As You Type' Mode (No RETURN required to start search)|\n"+
 "|!|Default Search Filter:<<option chkSearchInTitle>>Titles ('!') <<option chkSearchInText>>Texts ('%') <<option chkSearchInTags>>Tags ('#') <html><br><font size=\"-2\">The parts of a tiddlers that are searched when you don't explicitly specify a filter in the search text (using a '!', '%' or '#' prefix).</font></html>|\n"+
 "|!|Number of items on search result page: <<option txtItemsPerPage>>|\n"+
 "|!|Number of items on search result page with preview text: <<option txtItemsPerPageWithPreview>>|\n"
 ;

config.shadowTiddlers["YourSearchStyleSheet"] =
 "/***\n"+
 "!~YourSearchResult Stylesheet\n"+
 "***/\n"+
 "/*{{{*/\n"+
 ".yourSearchResult {\n"+
 "\tposition: absolute;\n"+
 "\twidth: 800px;\n"+
 "\n"+
 "\tpadding: 0.2em;\n"+
 "\tlist-style: none;\n"+
 "\tmargin: 0;\n"+
 "\n"+
 "\tbackground: White;\n"+
 "\tborder: 1px solid DarkGray;\n"+
 "}\n"+
 "\n"+
 "/*}}}*/\n"+
 "/***\n"+
 "!!Summary Section\n"+
 "***/\n"+
 "/*{{{*/\n"+
 ".yourSearchResult .summary {\n"+
 "\tborder-bottom-width: thin;\n"+
 "\tborder-bottom-style: solid;\n"+
 "\tborder-bottom-color: #999999;\n"+
 "\tpadding-bottom: 4px;\n"+
 "}\n"+
 "\n"+
 ".yourSearchRange, .yourSearchCount, .yourSearchQuery {\n"+
 "\tfont-weight: bold;\n"+
 "}\n"+
 "\n"+
 ".yourSearchResult .summary .button {\n"+
 "\tfont-size: 10px;\n"+
 "\n"+
 "\tpadding-left: 0.3em;\n"+
 "\tpadding-right: 0.3em;\n"+
 "}\n"+
 "\n"+
 ".yourSearchResult .summary .chkBoxLabel {\n"+
 "\tfont-size: 10px;\n"+
 "\n"+
 "\tpadding-right: 0.3em;\n"+
 "}\n"+
 "\n"+
 "/*}}}*/\n"+
 "/***\n"+
 "!!Items Area\n"+
 "***/\n"+
 "/*{{{*/\n"+
 ".yourSearchResult .marked {\n"+
 "\tbackground: none;\n"+
 "\tfont-weight: bold;\n"+
 "}\n"+
 "\n"+
 ".yourSearchItem {\n"+
 "\tmargin-top: 2px;\n"+
 "}\n"+
 "\n"+
 ".yourSearchNumber {\n"+
 "\tcolor: #808080;\n"+
 "}\n"+
 "\n"+
 "\n"+
 ".yourSearchTags {\n"+
 "\tcolor: #008000;\n"+
 "}\n"+
 "\n"+
 ".yourSearchText {\n"+
 "\tcolor: #808080;\n"+
 "\tmargin-bottom: 6px;\n"+
 "}\n"+
 "\n"+
 "/*}}}*/\n"+
 "/***\n"+
 "!!Footer\n"+
 "***/\n"+
 "/*{{{*/\n"+
 ".yourSearchFooter {\n"+
 "\tmargin-top: 8px;\n"+
 "\tborder-top-width: thin;\n"+
 "\tborder-top-style: solid;\n"+
 "\tborder-top-color: #999999;\n"+
 "}\n"+
 "\n"+
 ".yourSearchFooter a:hover{\n"+
 "\tbackground: none;\n"+
 "\tcolor: none;\n"+
 "}\n"+
 "/*}}}*/\n"+
 "/***\n"+
 "!!Navigation Bar\n"+
 "***/\n"+
 "/*{{{*/\n"+
 ".yourSearchNaviBar a {\n"+
 "\tfont-size: 16px;\n"+
 "\tmargin-left: 4px;\n"+
 "\tmargin-right: 4px;\n"+
 "\tcolor: black;\n"+
 "\ttext-decoration: underline;\n"+
 "}\n"+
 "\n"+
 ".yourSearchNaviBar a:hover {\n"+
 "\tbackground-color: none;\n"+
 "}\n"+
 "\n"+
 ".yourSearchNaviBar .prev {\n"+
 "\tfont-weight: bold;\n"+
 "\tcolor: blue;\n"+
 "}\n"+
 "\n"+
 ".yourSearchNaviBar .currentPage {\n"+
 "\tcolor: #FF0000;\n"+
 "\tfont-weight: bold;\n"+
 "\ttext-decoration: none;\n"+
 "}\n"+
 "\n"+
 ".yourSearchNaviBar .next {\n"+
 "\tfont-weight: bold;\n"+
 "\tcolor: blue;\n"+
 "}\n"+
 "/*}}}*/\n"
 ;

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

config.shadowTiddlers["YourSearchItemTemplate"] =
 "<!--\n"+
 "{{{\n"+
 "-->\n"+
 "<span class='yourSearchNumber' macro='foundTiddler number'></span>\n"+
 "<span class='yourSearchTitle' macro='foundTiddler title'/></span>&nbsp;-&nbsp;\n"+
 "<span class='yourSearchTags' macro='foundTiddler tags'/></span>\n"+
 "<span macro=\"yourSearch if previewText\"><div class='yourSearchText' macro='foundTiddler text'/></div></span>\n"+
 "<!--\n"+
 "}}}\n"+
 "-->"
 ;
config.shadowTiddlers["YourSearch"] = "<<tiddler [[YourSearch Help]]>>";

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


setStylesheet(
 store.getTiddlerText("YourSearchStyleSheet"),
 "yourSearch");

//----------------------------------------------------------------------------
// Install YourSearch
//----------------------------------------------------------------------------

// Overwrite the TiddlyWiki search handler and verify after a while
// that nobody else has overwritten it.

var origMacros_search_handler = config.macros.search.handler;
config.macros.search.handler = myMacroSearchHandler;


var ownsOverwrittenFunctions = function() {
 var result = (config.macros.search.handler == myMacroSearchHandler);
 return result;
};

var checkForOtherHijacker = function() {
 if (!ownsOverwrittenFunctions()) {
 alert("Message from YourSearchPlugin:\n\n\n"+
 "Another plugin has disabled the 'Your Search' features.\n\n\n"+
 "You may disable the other plugin or change the load order of \n"+
 "the plugins (by changing the names of the tiddlers)\n"+
 "to enable the 'Your Search' features.");
 }
};

setTimeout(checkForOtherHijacker, 5000);


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

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

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

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

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

// ... JavaScript Core
/*global alert,clearTimeout,confirm */
// ... TiddlyWiki Core
/*global Tiddler, applyHtmlMacros, clearMessage, createTiddlyElement, createTiddlyButton, createTiddlyText, ensureVisible ,findPosX, highlightHack, findPosY,findWindowWidth, invokeMacro, saveChanges, refreshElements, story */

/***
%/
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2005-2006 ([[www.abego-software.de|http://www.abego-software.de]])

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

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

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

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

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
***/

config.macros.allTagsC = {tooltip: "Show tiddlers tagged with '%0'", noTags: "There are no tagged tiddlers"},
config.macros.allTagsC.handler = function(place,macroName,params)
{
    var tags = store.getTags();
    var theDateList = createTiddlyElement(place,"ul",null,null,null);
    if(tags.length == 0)
        createTiddlyElement(theDateList,"span",null,"listTitle",this.noTags);
    for(var t=0; t<tags.length; t++)
        {
        var theListItem =createTiddlyElement(theDateList,"span",null,null,null);
        var theTag = createTiddlyButton(theListItem,tags[t][0] + " (" + tags[t][1] + ")",this.tooltip.format([tags[t][0]]),onClickTag);
        theTag.setAttribute("tag",tags[t][0]);
        }
}