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
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='edit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser excludeLists'></span></div>
<!--}}}-->
To get started with this blank [[TiddlyWiki]], you'll need to modify the following tiddlers:
* [[SiteTitle]] & [[SiteSubtitle]]: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* [[MainMenu]]: The menu (usually on the left)
* [[DefaultTiddlers]]: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: <<option txtUserName>>
<<importTiddlers>>
<!--{{{-->
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' />
<!--}}}-->
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]]
<!--{{{-->
<div class='header' role='banner' 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' role='navigation' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' role='navigation' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' role='complementary' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea' role='main'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
/*{{{*/
body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

.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]];}
.readOnly {background:[[ColorPalette::TertiaryPale]];}

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

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

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

hr {height:1px;}

a {text-decoration:none;}

dt {font-weight:bold;}

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

.txtOptionInput {width:11em;}

#contentWrapper .chkOptionInput {border:0;}

.externalLink {text-decoration:underline;}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/
/***
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which need larger font sizes.
***/
/*{{{*/
body {font-size:0.8em;}
#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}
.subtitle {font-size:0.8em;}
.viewer table.listView {font-size:0.95em;}
/*}}}*/
/*{{{*/
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none !important;}
#displayArea {margin: 1em 1em 0em;}
noscript {display:none;} /* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
}
/*}}}*/
<!--{{{-->
<div class='toolbar' role='navigation' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
<<courseAdminPanel>>/%
|Автор			|Александр Евгеньевич Доброчаев|
|Аудитория		|5-6 класс|
|Картинка в шапке	|ard/header.jpg|
|courseId			|ard|
!!!Описание
<p>Второй курс из цикла "Ботаника" посвящен внутреннему строению растений - их анатомии. Из каких клеток и тканей состоят растения, как эти клетки взаимодействуют, где располагаются — это и много другое мы узнаем в этом курсе.</p>
<p><img src="mrd/author.jpg" class="pull-right" width="200px" /><b>Александр Евгеньевич Доброчаев</b></p>
<p>Кандидат биологических наук, преподаватель школы "Интеллектуал" с 2003 года, заведующий кафедрой биологии.</p>
!!!In title
!%/
|visibleName|Клетка растения|
<<lessonAdminPanel>>/%
!!!Описание
Клетки растений отличаются от&nbsp;клеток животных, грибов и&nbsp;других организмов. Чем&nbsp;же они особенны? Все&nbsp;ли клетки растений похожи друг на&nbsp;друга?
!%/
|visibleName|Вещества клеточной стенки|
<<lessonAdminPanel>>/%
!!!Описание
Клеточная стенка — одна из важнейших частей клетки растений. Из каких же веществ она состоит и как эти вещества связаны со свойствами стенок?
!%/
|visibleName|Клетка растения|
|ссылка на контент шага|https://www.youtube.com/watch?v=YRHeyC04MbI|
|тип шага|video|
|visibleName|Клетка растения|
|тип шага|pager|
|ссылка на контент шага|./ard/png/ard_01-1.png|
|visibleName|Клетка растения|
|ссылка на контент шага|./ard/dummy.html|
|тип шага|task|
|visibleName|Клеточная стенка растений|
|ссылка на контент шага|https://www.youtube.com/watch?v=igdio6Ad1cM|
|тип шага|video|
|visibleName|Клеточная стенка растений|
|тип шага|pager|
|ссылка на контент шага|./ard/png/ard_01-2.png|
|visibleName|Клеточная стенка растений|
|ссылка на контент шага|./ard/dummy.html|
|тип шага|task|
|занимает памяти|8,8 Мб|
|visibleName|Конспект лекции|
|ссылка на материал|./ard/pdf/ard_01.pdf|
|тип шага|video|
|ссылка на контент шага|https://www.youtube.com/watch?v=EXwHfP4Lc80|
|visibleName|Клеточные стенки растений|
|тип шага|pager|
|visibleName|Клеточные стенки растений|
|ссылка на контент шага|./ard/png/ard_02-1.png|
|тип шага|video|
|ссылка на контент шага|https://www.youtube.com/watch?v=igdio6Ad1cM|
|visibleName|Пектин|
|тип шага|pager|
|visibleName|Клеточная стенка растений|
|ссылка на контент шага|./ard/png/ard_02-2.png|
|тип шага|video|
|ссылка на контент шага|https://www.youtube.com/watch?v=T5Zj2tVbDes|
|visibleName|Гемицеллюлоза|
|тип шага|pager|
|visibleName|Клеточная стенка растений|
|ссылка на контент шага|./ard/png/ard_02-3.png|
|тип шага|video|
|ссылка на контент шага|https://www.youtube.com/watch?v=1j9WmGuqt2c|
|visibleName|Целлюлоза|
|тип шага|pager|
|visibleName|Целлюлоза|
|ссылка на контент шага|./ard/png/ard_02-4.png|
|тип шага|video|
|ссылка на контент шага|https://www.youtube.com/watch?v=7Zn4-854LZw|
|visibleName|Лигнин|
|тип шага|pager|
|visibleName|Лигнин|
|ссылка на контент шага|./ard/png/ard_02-5.png|
|тип шага|video|
|ссылка на контент шага|https://www.youtube.com/watch?v=S01A6AU6Buc|
|visibleName|Вещества клеточной стенки растений|
|тип шага|task|
|ссылка на контент шага|./ard/dummy.html|
|visibleName|Вещества клеточной стенки растений|
|visibleName|Конспект лекции|
|ссылка на материал|./ard/pdf/ard_02.pdf|
|занимает памяти|18 Мб|
/***
|Version|1.0|
|Short status|<<insertEditable container:"@stateComment" size:max cell>>|
tests: [[BRP 1]], [[BRP 2]], [[BRP 3]]
***/
//{{{
var orig_BRP_changed = Tiddler.prototype.changed;
// this recalcs links according to config.textPrimitives.tiddlerAnyLinkRegExp and
// config.textPrimitives.tiddlerForcedLinkRegExp , so temporarily hijack them
Tiddler.prototype.changed = function()
{
	var brackettedLinkRE = config.textPrimitives.brackettedLink,
//	    titledBrackettedLinkRE = config.textPrimitives.titledBrackettedLink,
	    sectionOrSliceAddition =
		"(?:(?:(?:"+config.textPrimitives.sliceSeparator+"[^\\|\\n\\]]+)|"+ //::
		   "(?:"+config.textPrimitives.sectionSeparator+"[^\\n\\]]+))?)",   //##
	    tiddlerForcedLinkRegExp = config.textPrimitives.tiddlerForcedLinkRegExp,
	    tiddlerAnyLinkRegExp    = config.textPrimitives.tiddlerAnyLinkRegExp;
	
	// hijack REs
	config.textPrimitives.brackettedLink = "\\[\\[([^\\]]+?)"+ // extra "?" is important here
		sectionOrSliceAddition+
		"\\]\\]";
		// core definition: "\\[\\[([^\\]]+)\\]\\]";

//	config.textPrimitives.titledBrackettedLink = "\\[\\[([^\\[\\]\\|]+)\\|([^\\[\\]\\|]+?)"+
//		sectionOrSliceAddition+
//		"\\]\\]";
		// core definition: "\\[\\[([^\\[\\]\\|]+)\\|([^\\[\\]\\|]+)\\]\\]";
	// recalc, as in the core:
	config.textPrimitives.tiddlerForcedLinkRegExp = new RegExp("(?:" +
		config.textPrimitives.titledBrackettedLink + ")|(?:" +
		config.textPrimitives.brackettedLink + ")|(?:" +
		config.textPrimitives.urlPattern + ")","mg");
	config.textPrimitives.tiddlerAnyLinkRegExp = new RegExp("("+
		config.textPrimitives.wikiLink + ")|(?:" +
		config.textPrimitives.titledBrackettedLink + ")|(?:" +
		config.textPrimitives.brackettedLink + ")|(?:" +
		config.textPrimitives.urlPattern + ")","mg");
	
	var result = orig_BRP_changed.apply(this,arguments);
	
	// unhijack REs
	config.textPrimitives.brackettedLink = brackettedLinkRE;
//	config.textPrimitives.titledBrackettedLink = titledBrackettedLinkRE;
	// recalc again
	config.textPrimitives.tiddlerForcedLinkRegExp = new RegExp("(?:" +
		config.textPrimitives.titledBrackettedLink + ")|(?:" +
		config.textPrimitives.brackettedLink + ")|(?:" +
		config.textPrimitives.urlPattern + ")","mg");
	config.textPrimitives.tiddlerAnyLinkRegExp = new RegExp("("+
		config.textPrimitives.wikiLink + ")|(?:" +
		config.textPrimitives.titledBrackettedLink + ")|(?:" +
		config.textPrimitives.brackettedLink + ")|(?:" +
		config.textPrimitives.urlPattern + ")","mg");

	return result; // in fact, there's no result, this is for possible future extensions
};
//}}}
/***
|Description|This plugin fixes a couple of macro params issues:<br>1) fixes macro error in some cases when a named param has {{{{{}}}-containing value;<br>2) makes "escaped" ' and " in macro params be really escaped (examples: {{{"some \"value\""}}} is parsed as {{{some "value"}}} and {{{'that\'s nice'}}} becomes {{{that's nice}}})|
|Version|1.0|
|Short status|<<insertEditable container:"@stateComment" size:max cell>>|
|Author|Yakov Litvin|
***/
//{{{
// fix the error rising for macros with '{{'-containing named param's value
String.prototype.readMacroParams = function(notAllowEval)
{
	// YL tweak: don't use "noNames"
	var p = this.parseParams("_default",null,!notAllowEval);
	var t, n = [];
	for(t = 1; t < p.length; t++)
		n.push(p[t].value);
	return n;
};

// implement escaping of ' and " in macro params
String.prototype.parseParams = function(defaultName,defaultValue,allowEval,noNames,cascadeDefaults)
{
	var parseToken = function(match,p) {
		var n;
		if(match[p]) // Double quoted
			// YL tweak: substitute escaped \" with "
			n = match[p].replace(/\\"/g,'"');
		else if(match[p+1]) // Single quoted
			// YL tweak: substitute escaped \' with '
			n = match[p+1].replace(/\\'/g,"'");
		else if(match[p+2]) // Double-square-bracket quoted
			n = match[p+2];
		else if(match[p+3]) // Double-brace quoted
			try {
				n = match[p+3];
				if(allowEval && config.evaluateMacroParameters != "none") {
					if(config.evaluateMacroParameters == "restricted") {
						if(window.restrictedEval)
							n = window.restrictedEval(n);
					} else
						n = window.eval(n);
				}
			} catch(ex) {
				throw "Unable to evaluate {{" + match[p+3] + "}}: " + exceptionText(ex);
			}
		else if(match[p+4]) // Unquoted
			n = match[p+4];
		else if(match[p+5]) // empty quote
			n = "";
		return n;
	};
	var r = [{}];
	var dblQuote = "(?:\"((?:(?:\\\\\")|[^\"])+)\")";
	var sngQuote = "(?:'((?:(?:\\\\\')|[^'])+)')";
	var dblSquare = "(?:\\[\\[((?:\\s|\\S)*?)\\]\\])";
	var dblBrace = "(?:\\{\\{((?:\\s|\\S)*?)\\}\\})";
	var unQuoted = noNames ? "([^\"'\\s]\\S*)" : "([^\"':\\s][^\\s:]*)";
	var emptyQuote = "((?:\"\")|(?:''))";
	var skipSpace = "(?:\\s*)";
	var token = "(?:" + dblQuote + "|" + sngQuote + "|" + dblSquare + "|" + dblBrace + "|" + unQuoted + "|" + emptyQuote + ")";
	var re = noNames ? new RegExp(token,"mg") : new RegExp(skipSpace + token + skipSpace + "(?:(\\:)" + skipSpace + token + ")?","mg");
	var match;
	do {
		match = re.exec(this);
		if(match) {
			var n = parseToken(match,1);
			if(noNames) {
				r.push({ name:"", value:n });
			} else {
				var v = parseToken(match,8);
				if(v == null && defaultName) {
					v = n;
					n = defaultName;
				} else if(v == null && defaultValue) {
					v = defaultValue;
				}
				r.push({ name:n, value:v });
				if(cascadeDefaults) {
					defaultName = n;
					defaultValue = v;
				}
			}
		}
	} while(match);
	
	// Summarise parameters into first element
	var t;
	for(t = 1; t < r.length; t++)
		if(r[0][r[t].name])
			r[0][r[t].name].push(r[t].value);
		else
			r[0][r[t].name] = [r[t].value];
	return r;
};
//}}}
//{{{
// --- theme switching -------------------------------------
var usedOnline = window.location.protocol != "file:";
var usedWithBackEnd = !!window.saveOnlineChanges;
var visitorThemeName = "WebTheme";
var authorThemeName  = "AuthorTheme";
readOnly = usedOnline && !usedWithBackEnd;

// theme setting helpers
story.setVisitorTheme = function() {
	story.switchTheme(visitorThemeName);
	//# test (online auto-setting, including web config.options.txtTheme is saved)
};
story.setAuthorTheme = function() {
	story.switchTheme(authorThemeName);
	//# test (online auto-setting, including web config.options.txtTheme is saved)
};
story.toggleRoleTheme = function() {
	if(config.options.txtTheme == visitorThemeName)
		story.setAuthorTheme();
	else
		story.setVisitorTheme();
};

// set theme on startup
if(usedOnline && !usedWithBackEnd) {

	var ensureTheme = function(){
		if(config.options.txtTheme != visitorThemeName)
			story.setVisitorTheme();
	}
	setInterval(ensureTheme,100);
} else {
	var ensureTheme = function(){
		if(config.options.txtTheme != authorThemeName)
			story.setAuthorTheme();
	}
	story.setAuthorTheme();
//# dirty hacky way:
	setTimeout(ensureTheme,100);
	setTimeout(ensureTheme,200);
	setTimeout(ensureTheme,300);
	setTimeout(ensureTheme,400);
	setTimeout(ensureTheme,500);
	setTimeout(ensureTheme,600);
}

// enable theme switching by pressing ctrl+shift+t
jQuery(document).on('keydown',function(e){
	if(usedOnline && !usedWithBackEnd) return;
	if((e.which == 222 /*'*/ || e.which == 0) && e.ctrlKey && e.shiftKey) {
		story.toggleRoleTheme();
		if(e.preventDefault)
			e.preventDefault();
		return false;
	}
});

// display different tiddlers for an author and a visitor:
story.orig_displayDefaultTiddlers = story.displayDefaultTiddlers;
story.displayDefaultTiddlers = function()
{
	if(usedOnline && !usedWithBackEnd)
		this.displayTiddlers(null, store.filterTiddlers(store.getTiddlerText("WebDefaultTiddlers")||""));
	else
		this.orig_displayDefaultTiddlers();
};

// disable including for visitors (prevent error message to appear)
config.options.chkUseInclude = !usedOnline;// || usedWithBackEnd;

// --- helpers ---------------------------------------------
function updateQueryParameter(uri, key, value) {
	var re = new RegExp("([?&])" + key + "=.*?(&|#|$)", "i");
	if (uri.match(re))
		return uri.replace(re, '$1' + key + "=" + value + '$2');
	else {
		var hash =  '';
		if( uri.indexOf('#') !== -1 ){
			hash = uri.replace(/.*#/, '#');
			uri = uri.replace(/#.*/, '');
		}
		var separator = uri.indexOf('?') !== -1 ? "&" : "?";    
		return uri + separator + key + "=" + value + hash;
	}
};
Tiddler.prototype.getVisibleName = function(sliceName,anotherDefault)
{
	return this.getSlice(sliceName || "visibleName") || anotherDefault || this.title;
};

// 
if(config.options.txtUnitStep === undefined)
	config.options.txtUnitStep = 0;
if(config.options.txtLessonNumber === undefined)
	config.options.txtLessonNumber = 0;

// --- unit step macros ------------------------------------
config.macros.unitStep =
{
	getUnitSteps: function(tiddler) {
		var tag = tiddler.title,
		    steps = store.filterTiddlers("set: tag["+tag+"] AND tag[unitStep]");
		return config.macros.itemMenu.sortByCounter(steps/*, fieldName*/);
	},
	handler: function(place,macroName,params,wikifier,paramString,tiddler)
	{
		// lesson steps and current step
		var steps = this.getUnitSteps(tiddler);


		// header (navigation)
		var header = createTiddlyElement(place,"div","","lessonNavigation","",{role:'navigation'});

		var hrefToMain = "";
		var linkToMain = hrefToMain ? createExternalLink(header,hrefToMain) :
			jQuery('<a class="noLink"></a>').appendTo(header)[0];
		createTiddlyElement(linkToMain,"div","","logo-sign");

		// link to course contents (if not available, doesn't do anything)
		var i, tag, theCourse;
		for(i = 0; i < tiddler.tags.length; i++) {
			tag = store.fetchTiddler(tiddler.tags[i]);
			if(tag && tag.tags.contains("course"))
				theCourse = tag;
		}
		var title = tiddler.getVisibleName();
		if(theCourse) {
			var linkToContents = createTiddlyLink(header,theCourse.title);
			jQuery(linkToContents).html(title).addClass("linkToContents");
		} else
			jQuery("<a class='noLinkToContents'>"+title+"</a>")
				.appendTo(header);

		var navLink, type;
		if(steps.length > 1)
		  for(let j = 0; j < steps.length; j++) // not compatible with elder browsers
		  {
			navLink = createTiddlyElement(header,"a","","stepLink");
			jQuery(navLink).on('click',function(){
				config.options.txtUnitStep = j;
				refreshStep();
			});
			if(type = steps[j].getSlice("тип шага"))
				createTiddlyElement(navLink,"div","",type);
			else
				createTiddlyText(navLink,"шаг "+j);
		  }


		// wrapper, 3 colomns and buttons (backward/forward)
		var wrapper = createTiddlyElement(place,"div","","unitStep"),
		    leftColomn = createTiddlyElement(wrapper,"div","","goPrevContainer"),
		    contentColomn = createTiddlyElement(wrapper,"div","", "unitStepContentContainer"),
		    rightColomn = createTiddlyElement(wrapper,"div","","goNextContainer");

		if(steps.length == 0)
			return;
			//# display something indicating that the lesson is empty for now

		var backContainer = createTiddlyElement(leftColomn,"div","","backContainer");
		var backButton = createTiddlyElement(backContainer,"div","","backward");
		jQuery(leftColomn).on('click',function(){
			config.options.txtUnitStep--;
			refreshStep();
		});
		var forthContainer = createTiddlyElement(rightColomn,"div","","forthContainer");
		var forwardButton = createTiddlyElement(forthContainer,"div","","forward");
		jQuery(rightColomn).on('click',function(){
			config.options.txtUnitStep++;
			refreshStep();
		});

		var refreshStep = function()
		{
			// refresh content
			jQuery(contentColomn).empty();

			var step = steps[config.options.txtUnitStep];
			var contentUrl = step ? step.getSlice("ссылка на контент шага") : "";
			var specifiedHeight = step.getSlice("contentHeight");
			if(specifiedHeight) specifiedHeight = parseInt(specifiedHeight);

			if(contentUrl) {
				var isImage = /\.(png|jpg|jpeg|gif)(\?.*)?$/i.exec(contentUrl);
				var isVideo = /(?:youtube\.com)|(?:youtu\.be)|(?:^[-\w]+$)/i.exec(contentUrl)
					|| step.getSlice("тип шага") == "video";
				if(isVideo) {
/* deal with the following urls:
 *
 * https://www.youtube.com/watch?v=34c2GNgBpfE
 *   https://www.youtube.com/embed/34c2GNgBpfE <- canonical one, turn them into this form
 *		  https://youtu.be/34c2GNgBpfE
 *				   34c2GNgBpfE
 */
				    var youtubeIdRE = /((?:youtube\.com\/watch\?v=)|(?:youtube\.com\/embed\/)|(?:youtu\.be\/))?([-\w]+)((\?.*)|(\&.*)|$)/,
					youtubeIdMatch = youtubeIdRE.exec(contentUrl),
					youtubeId = youtubeIdMatch ? youtubeIdMatch[2]:"";
				    if(youtubeId)
					contentUrl = "https://www.youtube.com/embed/"+ youtubeId;

				    // disable autoplay, remove "related" from YouTube videos
				    contentUrl = updateQueryParameter(contentUrl, "autoplay", 0);
				    contentUrl = updateQueryParameter(contentUrl,"rel",0);
				}
				
				if(isImage)
					jQuery("<img src='"+contentUrl+"' />")
						.addClass("contentImage")
						.appendTo(contentColomn);
				else {
					var iframe = createTiddlyElement(contentColomn,"iframe","", "contentIframe","",{
						src: contentUrl,
						frameborder: 0,
						scrolling: "no",
						was_reloaded: false
					});

					if(/\.html$/.exec(contentUrl))
						iframe.onload = function(){
							this.height = specifiedHeight || (this.contentWindow.document.body.offsetHeight +50);
							if(!this.was_reloaded) //optional?
							{
								this.was_reloaded = true;
								try {
									this.contentDocument.location.reload();
								}catch(ex){}
							}
						}
						// +50 is a dirty hack; no idea why needed
					else
						// for video mostly
						iframe.height = iframe.offsetWidth * 9/16;
				}
			}
			//contentColomn.heigh = contentColomn.firstChild.offsetHeight; //# set via CSS instead
			//# implement ?? for no-contentUrl case

			// refresh navigation arrows
			if(config.options.txtUnitStep == 0)
				jQuery(backContainer).hide();
			else
				jQuery(backContainer).show();
			if(config.options.txtUnitStep == steps.length-1)
				jQuery(forthContainer).hide();
			else
				jQuery(forthContainer).show();

			//# highlight the link to the opened step in the nav bar
			jQuery(".currentStep").removeClass("currentStep");
			jQuery(".stepLink:eq("+config.options.txtUnitStep+")") .addClass("currentStep");
		}
		refreshStep();
	},
};
config.macros.lesson =
{
	getAttachments: function(tiddler) {
		var tag = tiddler.title,
		    atts = store.filterTiddlers("set: tag["+tag+"] AND tag[attachment]");
		return config.macros.itemMenu.sortByCounter(atts/*, fieldName*/);
	}
};
config.macros.unitStepFooter =
{
	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		//# implement
	}
};
config.macros.lessonAdminPanel =
{
	handler: function(place,macroName,params,wikifier,paramString,tiddler)
	{
		// parametric transclusion is useful for more fluid development
		wikify('<<tiddler [[LessonAdminPanel]] with:[['+tiddler.title+']] noedit>>',place,null,tiddler);
	}
};

// --- course macros ------------------------------------
config.macros.fillCourseHeader = {
	handler: function(place,macroName,params,wikifier,paramString,tiddler)
	{
		var imgUrl = tiddler.getSlice("Картинка в шапке","");
		if(imgUrl)
			jQuery('#course-header').css("background",
				'linear-gradient(to right,rgba(0,0,0,0.4), rgba(0,0,0,0) 80%) no-repeat left, url("'+imgUrl+'") no-repeat center');

		jQuery("#main_content #course-header")
		  .empty()
		  .append(
		   '<h2 id="course-name">'+	tiddler.getVisibleName()	+'</h2>'+
		   '<h4 id="course-author">'+	tiddler.getSlice("Автор","")	+'</h4>'+
		   '<h4 id="course-audience">'+	tiddler.getSlice("Аудитория","")+'</h4>'+
		   (tiddler.getSection("In title",""))
		  );
	}
};
config.macros.fillCourseContents = {
	getCourseContents: function(course)
	{
		if(!course) return null;
		//# may be add getting course tiddler if course is the title or courseId
		var tids = store.filterTiddlers("[tag["+course.title+"]]"),
		    courseId = course.getSlice("courseId","");
		return config.macros.itemMenu.sortByCounter(tids, courseId);
	},
	getCourseLessons: function(course)
	{
		var contents = this.getCourseContents(course), i;
		if(!contents) return null;
		for(i = 0; i < contents.length; i++)
			if(!contents[i].tags.contains("unit"))
				contents.splice(i--,1);
		return contents;
	},
	handler: function(place,macroName,params,wikifier,paramString,tiddler)
	{
		var lessons = this.getCourseLessons(tiddler),
		    contents = this.getCourseContents(tiddler);

		var getIndentIndex = function(itemTiddler) {
			if(itemTiddler.tags.contains("unit")) return 0;
			if(itemTiddler.tags.contains("заголовок 1 надуровня")) return 1;
			if(itemTiddler.tags.contains("заголовок 2 надуровня")) return 2;
		},
		    maxIndentIndex = 0;
		for(n = 0; n < contents.length; n++)
			if(getIndentIndex(contents[n]) > maxIndentIndex)
				maxIndentIndex = getIndentIndex(contents[n]);

		var $courseContentsColomn = jQuery("#lessons-list"),
		    $lessonContentsColomn = jQuery("#lesson-content");

		$courseContentsColomn.empty();
		// add the "about course" link (implement in refresher)
		jQuery('<a class="lesson-name h4">О курсе</a>')
			.on('click',function(){
				config.options.txtLessonNumber = 0;
				selectLesson();
			}).appendTo($courseContentsColomn)
			.wrap('<div class="lesson" />');
		// add per lesson links
		for(let i = 0; i < contents.length; i++) {
			let item = contents[i], isLesson = item.tags.contains("unit");
			let mute = isLesson && !store.getTaggedTiddlers(item.title).length && !item.getSection("Описание");
			levelClass = isLesson ? 'h4' :
				(item.tags.contains("заголовок 1 надуровня")? 'h3' : 'h2');
			indentClass = "indent-"+(maxIndentIndex - getIndentIndex(item));

			jQuery('<a class="lesson-name '+levelClass+' '+indentClass+(mute ? ' mute' : '')+'">'
					+contents[i].getVisibleName()+'</a>')
				.on('click',function(){
					if(!isLesson || mute) return;
					config.options.txtLessonNumber = lessons.indexOf(item)+1;
					selectLesson();
				}).appendTo($courseContentsColomn)
				.wrap('<div class="lesson" />');
		}

		var selectLesson = function() {
			// (re)set selection
			jQuery('.lesson-active').removeClass("lesson-active");
			var lessonNumber = config.options.txtLessonNumber-1,
			    lessonNumberAmongContents = lessonNumber == -1 ? 0 :
				contents.indexOf(lessons[lessonNumber])+1;
			jQuery(".lesson-name").eq(lessonNumberAmongContents).addClass("lesson-active");

			$lessonContentsColomn.empty();
			var $lessonContentsWrapper = jQuery('<div>').appendTo($lessonContentsColomn);
			window.elementToKeepIntoView = $lessonContentsWrapper[0];
			jQuery(window).scroll();
			if(config.options.txtLessonNumber == 0) {
			// show "about course"
				let description = tiddler.getSection("Описание");
				if(description)
					$lessonContentsWrapper.append('<p>'+description+ '</p>');
				/*if(lessons.length)
				  jQuery('<a class="btn btn-primary">Начать курс</a>')
					.on('click',function(){
						config.options.txtUnitStep = 0;
						story.displayTiddler(this,lessons[0]);
					})
					.appendTo($lessonContentsWrapper)
				 */
				//# hr and teacher description
			} else {
			// show lesson
				var lessonNumber = config.options.txtLessonNumber - 1;
				if(lessonNumber >= lessons.length) lessonNumber = 0; // fixes a bug taking place when we jump from a course to another one that has fewer lessons
				var lesson = lessons[lessonNumber],
				    title = lesson.title,
				    visibleTitle = lesson.getVisibleName(),
				    lessonDescription = lesson.getSection("Описание"),
				    goLabel = config.macros.unitStep.getUnitSteps(lesson).length > 1 ? "Пройти занятие" : "Посмотреть";
				$lessonContentsWrapper
					.append('<h2>'+visibleTitle+'</h2>')
					//# number in h2/"Занятие #" as h5
					.append('<p>'+lessonDescription+'</p>')
					.append(config.macros.unitStep.getUnitSteps(lesson).length ?
						jQuery('<a class="btn btn-primary">'+goLabel+'</a>')
						  .on('click',function(){
							config.options.txtUnitStep = 0;
							story.displayTiddler(this,title);
						  })
						: '')
					.append('<ol id="lesson-steps"/>')

				var steps = config.macros.unitStep.getUnitSteps(lesson),
				    ruTypes = {
					pager: "Конспект",
					task:  "Задание",
					test:  "Тест",
					video: "Видео",
				    };
				if(steps.length > 1)
				  for(let i = 0; i < steps.length; i++) {
					let type = steps[i].getSlice("тип шага"),
					    ruType = ruTypes[type];
					jQuery('<a>')
						.appendTo("#lesson-steps")
						.wrap('<li>').wrap('<p>')
						.on('click',function(){
							config.options.txtUnitStep = i;
							story.displayTiddler(this,title);
						})
						//.append('<div class="pull-left '+type+'"/>')
						.append(jQuery('<img/>',{
							class:  "pull-left",
							src:    "images/"+(type=="pager" ? "text" : type)+".svg", // типы, исп-ные до того, отл-ся от тех, что у Виктора
							width:  "24px",
							height: "24px"
						}))
						.append('<span class="lesson-label">'+steps[i].getVisibleName()+'</span><br>'+
							'<small>'+ruType+'</small>')
						//# " - 5:99" (video length)
				  }

				var attachments = config.macros.lesson.getAttachments( lesson ),
				    attName, attUrl, isPdf, attSize;
				for(let i = 0; i < attachments.length; i++)
				{
					attName = attachments[i].getVisibleName();
					attUrl  = attachments[i].getSlice("ссылка на материал");
					isPdf   = /.pdf/i.exec(attUrl);
					attSize = attachments[i].getSlice("занимает памяти");
					jQuery('<a href="'+attUrl+'" target="_blank" />')
						.appendTo($lessonContentsWrapper)
						.wrap('<p>')
						.append('<img src="images/dwnld_solid.svg" class="attachment-icon">'+
							'<span class="attachment-label">'+attName+'</span>' +
							(isPdf ? ' <img src="images/pdf.svg" class="pdf-attachment-icon">' : '') +
							(!attSize ? '' : (isPdf ? ' <span class="file-size">'+attSize+'</span>' : ' (<span class="file-size">'+attSize+'</span>)'))
						);
				}
			}
		};

		// pick the step (by default "about course")
		selectLesson();
	}
};
window.prevScroll = 0;
jQuery(window).scroll(function()
{
	var menuEl = window.elementToKeepIntoView;
	if(!menuEl) return;
	var container = menuEl.parentElement;
	if(!container) return;
	var height = menuEl.offsetHeight,
	    windowHeight = findWindowHeight(),
	    scrollDown = window.prevScroll < findScrollY(),
	    scrollUp   = window.prevScroll > findScrollY();

	menuEl.style.position = "relative";

	var topLimitByContainer = 0, topLimitByWindow = findScrollY() - findPosY(container) -50,
//# get rid of magic value: it's the container's padding-top in px
	    bottomLimitByContainer = container.offsetHeight -80, //jQuery(container).innerHeight() ?
//# get rid of magic value: it's the container's padding-top + padding-bottom in px
	    bottomLimitByWindow = topLimitByWindow + findWindowHeight() -20,
//# get rid of magic value: presumably, it's the header's margin-top in px

	    //re = /(\-?\d+(?:\.\d+)?)\s?px/i,
	    getPixels = function(value) {
		if(!value) return 0;
		//var match = re.exec(value);
		//if(!match) return parseInt(value);
		return parseInt(value);//match[1]);	//test with non-px values (are they stored in .style.top?)
	    },
	    menuElTopCss = getPixels(menuEl.style.top);

	if(!scrollDown && !scrollUp)
		menuEl.style.top = topLimitByWindow + "px"; //attach top to window top
	if(scrollDown) {
		if(height < windowHeight) {
			if(menuElTopCss < topLimitByWindow) // top edge is higher than that of window
				menuEl.style.top = topLimitByWindow + "px"; //top to top
		} else
			if(menuElTopCss + height < bottomLimitByWindow) // bottom edge higher than that of window
				menuEl.style.top = (bottomLimitByWindow - height) + "px"; //bottom to bottom
	}
	if(scrollUp) {
		if(height < windowHeight)
			menuEl.style.top = topLimitByWindow + "px"; //attach top to window top
		else
			if(menuElTopCss > topLimitByWindow) // top edge lower than that of window
				menuEl.style.top = topLimitByWindow + "px"; //attach top to window top
	}

//# deal with selectLesson 46 (ends up below the bottom of the container)
//  somehow in the  menuElTopCss + height > bottomLimitByContainer  check below  height == 0
//  that's true for each selectLesson firing scroll: height == 0

	// ensure we're inside the container
	menuElTopCss = getPixels(menuEl.style.top);
	if(menuElTopCss < topLimitByContainer)
		menuEl.style.top = topLimitByContainer + "px";
console.log("height: "+height);
	if(menuElTopCss + height > bottomLimitByContainer)
		menuEl.style.top = (bottomLimitByContainer - height) + "px";

//# make scrolling smoother

	window.prevScroll = findScrollY();
});

config.macros.courseAdminPanel = {
	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		if(!tiddler) return;

		// parametric transclusion is useful for more fluid development
		wikify('<<tiddler [[CourseAdminPanel]] with:[['+tiddler.title+']] noedit>>',place,null,tiddler);
	}
};

// --- common macros ------------------------------------
// make common topLineMenu and footer? use config.macros.siteTopMenu?
//}}}
/***
|''Name''|DashesFormattersPlugin|
|''Version''|0.1|
|Short status|<<insertEditable container:"@stateComment" size:max cell>>|
test: a -- b, c --- d, --ef--
----
gh &mdash;
***/
//{{{
for(var i = 0; i < config.formatters.length; i++)
	if(config.formatters[i].name == "mdash") {		
		config.formatters[i].name = "ndash";
		config.formatters[i].handler = function(w) {
			createTiddlyElement(w.output,"span").innerHTML = "&ndash;";
		};
	};
/*config.formatters.splice(i,0,{
	name: "mdash",
	match: "---",
	handler: function(w)
	{
		createTiddlyElement(w.output,"span").innerHTML = "&mdash;";
	}
});*/
//}}}
/***
|Version|1.4|
{{PoGc{дописать метаданные!}}} Пока у плагина нет документации и т.п., он базируется в репозитории напару с ~GridPlugin
* в версии 1.2 исправлен тот факт, что ":" включался в slice name
* в версии 1.3 изменено поведение в отношении начальных/конечных пробелов/табуляций вокруг slice name
* в версии 1.4 исправлено поведение, когда пробелы в конце "второго столбца" добавляются в slice value
* когда пробел обязательно заменять на \x20? можно ли тут обойтись без этого?
***/
//{{{
TiddlyWiki.prototype.slicesRE = /(?:^([\'\/]{0,2})~?([\.\w]+)\:\1[\t\x20]*([^\n]+)[\t\x20]*$)|(?:^\|\x20?([\'\/]{0,2})~?([^\|\s\:\~\'\/]|(?:[^\|\s~\'\/][^\|\n\f\r]*[^\|\s\:\'\/]))\:?\4[\x20\t]*\|[\t\x20]*([^\n\t\x20](?:[^\n]*[^\n\t\x20])?)[\t\x20]*\|$)/gm;
store.slices = {};
//}}}
/***
|''Version''|1.4|
|''Requires''|UpToDateFiltersPlugin|
|~|requires http://yakovl.bplaced.net/TW/ExtraFilters.html#UpToDateFiltersPlugin only if TW core version is 2.6.1 or below|
|''Source''|http://yakovl.bplaced.net/TW/ExtraFilters.html#ExtraFiltersPlugin|
|''Documentation''|http://yakovl.bplaced.net/TW/ExtraFilters.html#ExtraFiltersPluginInfo|
|''Author''|Yakov Litvin|
|''Licence''|[[BSD-like open source license|http://yakovl.bplaced.net/TW/ExtraFilters.html#%5B%5BYakov%20Litvin%20Public%20Licence%5D%5D]] |
***/
//{{{
if(!config.filters.all)
	config.filters.all = function(results,match) { // v1.0
		this.forEachTiddler(function(tName,tiddler){
			results.pushUnique(tiddler);
		});
		return results;
	};
config.filters.and = function(results,match) { // v1.0

	// parse the argument as "filterName[filterParam"
	var dividingRE = /([^\[\]]+)\[([^\]]*)/,
	    filterParts = dividingRE.exec(match[3]);
	if(filterParts) {
		var filterName  = filterParts[1],
		    filterParam = filterParts[2];
	} else
		throw("\"and\" filter: wrong syntax");

	// create the set of filtered tiddlers
	var filter = "[" + filterName + "[" + filterParam + "]]",
	    tids = this.filterTiddlers(filter),
	    newResult = [];

	// collect tiddlers present among both "results" and filtered tiddlers
	for(var i = 0; i < results.length; i++)
		for(var j = 0; j < tids.length; j++)
			if(results[i] == tids[j])
				newResult.push(tids[j]);

	return newResult;
};
config.filters.not = function(results,match) { // v1.0

	// parse the argument as "filterName[filterParam"
	var dividingRE = /([^\[\]]*)\[([^\]]*)/,
	    filterParts = dividingRE.exec(match[3]);
	if(filterParts) {
		var filterName  = filterParts[1],
		    filterParam = filterParts[2];
	} else
		throw("\"not\" filter: wrong syntax");

	// create the set of filtered tiddlers
	var filter = "[" + filterName + "[" + filterParam + "]]",
	    tids = this.filterTiddlers(filter);

	// collect tiddlers present among "results", but not among filtered tiddlers
	for(var i = 0; i < results.length; i++)
		for(var j = 0; j < tids.length; j++)
			if(results[i] == tids[j]) {
				results.splice(i,1);
				i--;
				tids.splice(j,1);
				break;
			}

	return results;
};
config.filters.tagTree = function(results, match)
{
	results = results || [];

	var depthRE = /^(\d),(.+)$/, depthMatch = depthRE.exec(match[3]),
	    depth = depthMatch ? parseInt(depthMatch[1]) : -1, currentDepth = 0,
	    root = depthMatch ? depthMatch[2] : match[3], tags = [root], prevLength,
	    i, tagTags, j;

	var rootTid = store.fetchTiddler(root);
	if(rootTid) results.pushUnique(rootTid);

	// get tags, tags of tags etc ;
	// for optimization, push to results inline
	do {
		prevLength = tags.length;
		for(i = 0; i < prevLength; i++) //may be optimized by starting from i = ..
		{
			tagTags = store.getTaggedTiddlers(tags[i]);
			for(j = 0; j < tagTags.length; j++) {
				tags.pushUnique(tagTags[j].title);

				// optimized place to push:
				results.pushUnique(tagTags[j]);
			}
		}
		currentDepth++;
	} while (tags.length > prevLength && currentDepth != depth);

	return results;
};
config.filters.unclassified = function(results,match) { // v1.0

	var category = match[3],
	    instances = this.getTaggedTiddlers(category);

	// filter out tiddlers tagged with instances
	for(var i = 0; i < results.length; i++)
		for(var j = 0; j < instances.length; j++)

			if(results[i].isTagged(instances[j].title)) {
				results.splice(i,1);
				i--;
				break;
			}

	return results;
};
config.filters.taggedOnly =
config.filters.oTag = function(results, match) { // v1.0

	// parse param
	var add = true, arg = match[3], i;
	switch(arg.substr(0,1)) {
		case "+":
			// "add" is "true" already
			arg = arg.substr(1);
			break;
		case "-":
			add = false;
			arg = arg.substr(1);
			break;
	}

	var isTaggedOnly = function(tiddler,arg) {

		return (tiddler.tags.length == 1) && (!arg || arg == tiddler.tags[0])
	};

	if(add) {
		var tiddlers = this.reverseLookup();
		for(i = 0; i < tiddlers.length; i++)
			if(isTaggedOnly(tiddlers[i],arg))
				results.pushUnique(tiddlers[i]);
	} else {
		for(i = 0; i < results.length; i++)
			if(!isTaggedOnly(results[i],arg))
				results.splice(i--,1);
	}

	return results;
};
config.filters.hasPart = function (results, match) { // v1.1

	// parse the argument
	var arg = match[3], reText, re, type, isBad, title;

	switch(arg.substr(0,2)) {
		case "##":
		case "::":
			isBad = function(title) {
				return store.getTiddlerText(title + arg) ? false : true;
			};
			break
		case "@@":
			isBad = function(title) {
				return store.getValue(title, arg.substr(2)) ? false : true;
			};
			break
		case "r@": // regExp (for tiddler.text) mode
		case "R@":
		case "t@": // title mode
		case "T@":
			reText = store.getTiddlerText(arg.substr(2));
			type = arg.substr(0,1);
			if(!reText)
				if(type == "r" || type == "t")
					return results; // "forgiving mode", nothing is filtered out in this case
				else
					throw("RegExp for filtering is not found in " + arg.substr(2));

			// no break here
		case "r[":
		case "R[":
		case "t[":
		case "T[":
			if(!reText) {
				reText = arg.substr(2);
				type = arg.substr(0,1);
			}

			if(type == "r" || type == "t") {
				try {
					re = new RegExp(reText);
				} catch(e) {
					return results; // "forgiving mode"
				}
			} else
				re = new RegExp(reText);

			if(type == "r" || type == "R")
				isBad = function(title) {
					return results[i].text.match(re) ? false : true;
				};
			else
				isBad = function(title) {
					return title.match(re) ? false : true;
				};
			break
		default:
			return results;
	}

	// filter out corresponding tiddlers
	for(var i = 0; i < results.length; i++) {

		title = results[i].title;

		if(isBad(title)) {
			results.splice(i,1);
			i--;
		}
	};
	return results;
};
config.filters.sortByText = function (results, match) { // v1.1

	// parse the argument
	var arg = match[3],
	    ascending = +1;
	switch(arg.substr(0,1)) {
		case "-":
			ascending = -1;
			arg = arg.substr(1);
			break;
		case "+":
			arg = arg.substr(1);
			break;
	}

	// use the rest of the argument to get corresponding section/slice
	var partSuffix = (arg.substr(0,2) == "::" || arg.substr(0,2) == "##") ?
			arg : "";

	var self = this;
	var compareText = function(t1,t2) {
		var text1 = self.getTiddlerText(t1.title + partSuffix),
		    text2 = self.getTiddlerText(t2.title + partSuffix);
		if(text1 && text2)
			return text1.localeCompare(text2);
		if(text1)
			return -1;
		if(text2)
			return 1;
		return 0;
	};

	return results.sort(compareText);
};
orig_sortFilter = config.filters.sort;
config.filters.sort = function(results,match) { // sort: random, v1.0

	if(match[3] === "*random") {

		var auxiliaryArray = [],
		    size = results.length;
		var swap = function(i, j) {
			var tmp1 = auxiliaryArray[i];
			auxiliaryArray[i] = auxiliaryArray[j];
			auxiliaryArray[j] = tmp1;
			var tmp2 = results[i];
			results[i] = results[j];
			results[j] = tmp2;
		};

		for(var i = 0; i < size; i++)
			auxiliaryArray.push(Math.random());

		for(var j = 0; j < size; j++)
			for(var i = 0; i < size - j; i++)
				if(auxiliaryArray[i] < auxiliaryArray[i+1])
					swap(i,i+1);
		return results;
	} else
		return orig_sortFilter.apply(this, arguments);
};
config.filters.from = function(results,match) { // v1.0
	var filter = this.getTiddlerText(match[3],""),
	    tiddlers = this.filterTiddlers(filter);

	for(var i = 0; i < tiddlers.length; i++)
		results.pushUnique(tiddlers[i]);

	return results;
};
//}}}
/***
|Description|Makes ctrl+tab work for tab changing (when focused on an editarea) in ~FireFox (instead of inserting a tab symbol)|
|Version|1.0|
|Short status|<<insertEditable container:"@stateComment" size:max cell>>|
|Author|Yakov Litvin|
keep overwriting in mind! (hope this will move to the core soon)
***/
//{{{
Story.prototype.onTiddlerKeyPress = function(ev)
{
	var e = ev || window.event;
	clearMessage();
	var consume = false;
	var title = this.getAttribute("tiddler");
	var target = resolveTarget(e);
	switch(e.keyCode) {
	case 9: // Tab
		var ed = story.getTiddlerField(title,"text");
		if(target.tagName.toLowerCase() == "input" && ed.value == config.views.editor.defaultText.format([title])) {
			// moving from input field and editor still contains default text, so select it
			ed.focus();
			ed.select();
			consume = true;
		}
		if(config.options.chkInsertTabs && !e.ctrlKey && target.tagName.toLowerCase() == "textarea") {
			replaceSelection(target,String.fromCharCode(9));
			consume = true;
		}
		if(config.isOpera)
			target.onblur = function() {
				this.focus();
				this.onblur = null;
			};
		break;
	case 13: // Ctrl-Enter
	case 10: // Ctrl-Enter on IE PC
	case 77: // Ctrl-Enter is "M" on some platforms
		if(e.ctrlKey) {
			blurElement(this);
			config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
			consume = true;
		}
		break;
	case 27: // Escape
		blurElement(this);
		config.macros.toolbar.invokeCommand(this,"cancelCommand",e);
		consume = true;
		break;
	}
	e.cancelBubble = consume;
	if(consume) {
		if(e.stopPropagation) e.stopPropagation(); // Stop Propagation
		e.returnValue = true; // Cancel The Event in IE
		if(e.preventDefault ) e.preventDefault(); // Cancel The Event in Moz
	}
	return !consume;
};
//}}}
/***
|Version|1.0|
{{DDnc{should be based elsewhere}}}
***/
//{{{
jQuery(window).on("scroll",function(){
	
	var menuElement = document.getElementById("mainMenu");
	if(!menuElement) return;
	var header = document.getElementsByClassName("header")[0],
	    headerBottom = header.offsetTop + header.offsetHeight - findScrollY(),
	    position = findScrollY() + Math.max(headerBottom,0)

	menuElement.style.top = position+"px";
});
//}}}
/***
|Description	|adds toolbar buttons that allow to "fold" tiddlers|
|Version	|1.0.5|
|Author		|Yakov Litvin|
|Forked from	|[[CollapseTiddlersPlugin|http://www.TiddlyTools.com/#CollapseTiddlersPlugin]] by Eric Shulman and previously Bradley Meck|
|Overwrites	|{{{Story.prototype.permaView}}}|
|~CoreVersion?	|{{DDn{??, >=2.1}}}|
***/
//{{{
config.shadowTiddlers.CollapsedTemplate =
	"<!--{{{-->\
	<div class='toolbar' macro='toolbar [[CollapsedToolbarCommands]]'></div>\
	<div class='title' macro='view title'></div>\
	<!--}}}-->";
config.shadowTiddlers.CollapsedToolbarCommands =
	"closeTiddler closeOthers expandTiddler collapseOthers +editTiddler "+
	"jump > fields permalink references deleteTiddler";

// hijack config.macros.toolbar.handler to add collapseTiddler and collapseOthers
config.options.chkAddFoldingButtons = (config.options.chkAddFoldingButtons === undefined) ? true : config.options.chkAddFoldingButtons;
config.foldTiddlers_orig_toolbarHandler = config.macros.toolbar.handler;
if(config.options.chkAddFoldingButtons)
	config.macros.toolbar.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
	// warning: paramString is not changed as /currently/ it's not used by config.macros.toolbar.handler

		var i, doAdd = true,
		    mainCommandRegExp = /closeOthers/,
		    foldCommandRegExp  = /collapseTiddler/,
		    unfoldCommandRegExp = /expandTiddler/;

		// don't add in the folded condition (if expandTiddler is present)
		// or if collapseTiddler is already present (manually added to toolbar)
		for(i = 0; i < params.length; i++)
			if(unfoldCommandRegExp.exec(params[i]) ||
			   foldCommandRegExp.exec(params[i])) {
				doAdd = false;
				break;
			}
		if(!doAdd)
			return config.foldTiddlers_orig_toolbarHandler.apply(this,arguments);
		
		for(i = 0; i < params.length; i++)
			if(mainCommandRegExp.exec(params[i])) { // locate the "edit" command
				params.splice(i+1,0,"collapseTiddler","collapseOthers"); // add after "edit"
				break;
			}
		config.foldTiddlers_orig_toolbarHandler.apply(this,arguments);
	};
//}}}
//{{{
config.commands.collapseTiddler = {

	text: "fold",
	tooltip: "Collapse this tiddler",
	collapsedTemplate: "CollapsedTemplate",
	webCollapsedTemplate: "WebCollapsedTemplate",
	handler: function(event,src,title) {

		var e = story.findContainingTiddler(src); if (!e) return false;
		// don't fold tiddlers that are being edited!
		if(story.isDirty(e.getAttribute("tiddler"))) return false;
		var t = config.commands.collapseTiddler.getCollapsedTemplate();
		config.commands.collapseTiddler.saveTemplate(e);
		config.commands.collapseTiddler.display(title,t);
		e.setAttribute("folded","true");
		return false;
	},
	getCollapsedTemplate: function() {

		return (readOnly && store.tiddlerExists(this.webCollapsedTemplate)) ?
			this.webCollapsedTemplate :
			this.collapsedTemplate
	},
	saveTemplate: function(e) {

		if (e.getAttribute("savedTemplate") == undefined)
			e.setAttribute("savedTemplate", e.getAttribute("template"));
	},
	// fold/unfold tiddler with suspend/resume of single/top/bottom-of-page mode
	display: function(title,t) {

		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;
		story.displayTiddler(null,title,t);
		opt.chkBottomOfPageMode = saveBPM;
		opt.chkTopOfPageMode = saveTPM;
		opt.chkSinglePageMode = saveSPM;
	}
}

config.commands.expandTiddler = {

	text: "unfold",
	tooltip: "Expand this tiddler",
	handler: function(event,src,title)
	{
		var e = story.findContainingTiddler(src); if (!e) return false;
		var t = e.getAttribute("savedTemplate");
		config.commands.collapseTiddler.display(title,t);
		e.setAttribute("folded","false");
		return false;
	}
}

config.macros.collapseAll = {

	text: "collapse all",
	tooltip: "Collapse all tiddlers",
	handler: function(place,macroName,params,wikifier,paramString,tiddler){
		createTiddlyButton(place,this.text,this.tooltip,function(){
			story.forEachTiddler(function(title,tiddler)
			{
				if(story.isDirty(title)) return;
				var t = config.commands.collapseTiddler.getCollapsedTemplate();

				config.commands.collapseTiddler.saveTemplate(tiddler);
				config.commands.collapseTiddler.display(title,t);
				tiddler.folded = true;
			});
			return false;
		})
	}
}

config.macros.expandAll = {

	text: "expand all",
	tooltip: "Expand all tiddlers",
	handler: function(place,macroName,params,wikifier,paramString,tiddler){
		createTiddlyButton(place,this.text,this.tooltip,function(){
			story.forEachTiddler(function(title,tiddler){
				var t = config.commands.collapseTiddler.getCollapsedTemplate();
				if(tiddler.getAttribute("template")!=t) return; // re-display only if collapsed
				var t = tiddler.getAttribute("savedTemplate");
				config.commands.collapseTiddler.display(title,t);
				tiddler.folded = false;
			});
			return false;
		})
	}
}

config.commands.collapseOthers = {

	text: "focus",
	tooltip: "Expand this tiddler and collapse all others",
	handler: function(event,src,title) {
		var e = story.findContainingTiddler(src); if (!e) return false;
		story.forEachTiddler(function(title,tiddler) {
			if(story.isDirty(title)) return;
			var t = config.commands.collapseTiddler.getCollapsedTemplate();
			if (e == tiddler) t = e.getAttribute("savedTemplate");
			config.commands.collapseTiddler.saveTemplate(tiddler);
			config.commands.collapseTiddler.display(title,t);
			tiddler.folded = (e != tiddler);
		})
		return false;
	}
}

// {{{<<foldFirst>>}}} macro forces tiddler to be folded when *initially* displayed.
// Subsequent re-render does NOT re-fold tiddler, but closing/re-opening tiddler DOES cause it to fold first again.
config.macros.foldFirst = {
	handler: function(place,macroName,params,wikifier,paramString,tiddler) {

		var e = story.findContainingTiddler(place);
		if (e.getAttribute("foldedFirst") == "true") return; // already been folded once
		var title = e.getAttribute("tiddler")
		var t = config.commands.collapseTiddler.getCollapsedTemplate();
		config.commands.collapseTiddler.saveTemplate(e);
		config.commands.collapseTiddler.display(title,t);
		e.setAttribute("folded","true");
		e.setAttribute("foldedFirst","true"); // only when tiddler is first rendered
		return false;
	}
}
//}}}
//{{{
// [showFolded[n]] adds .openFolded = true to the last n tiddlers among results
config.filters.showFolded = function(results,match) {
	
	var number = parseInt(match[3]); // if NaN, the loop below just is not executed

	for(var i = 0; (i < number) && (results.length-1-i >= 0); i++)
		results[results.length-1-i].openFolded = true;
	
	return results;
}
// hijack chooseTemplateForTiddler to open tiddlers with .openFolded folded
config.foldTiddlers_orig_chooseTemplateForTiddler = Story.prototype.chooseTemplateForTiddler;
Story.prototype.chooseTemplateForTiddler = function(title,template) {

	var t = store.fetchTiddler(title);
	if(t && t.openFolded)
	{
		t.openFolded = false
		return "CollapsedTemplate";
	}
	
	return config.foldTiddlers_orig_chooseTemplateForTiddler.apply(this,arguments);
}
//# optionally change fold/unfold ~commands so that they change openFolded (the state is remembered during the session)

Story.prototype.isOpenedFolded = function(title) {
	
	var tiddlerElem = story.getTiddler(title);
	if(!tiddlerElem)
		return false;
	return tiddlerElem.getAttribute("template") == "CollapsedTemplate";
}
//# overwrite permaview to save which tiddlers are folded
//  use "filter" paramifier
Story.prototype.permaView = function()
{
	var links = [];
	this.forEachTiddler(function(title,element) {
		links.push(String.encodeTiddlyLink(title) +
			   (story.isOpenedFolded(title) ? " [showFolded[1]]" : ""));
	});
	var t = links.join(" ");
	t = t.replace(/\\/gm,"\\\\").replace(/"/gm,'\\"');
	if(t == "")
		t = "#";
	else
		t = encodeURIComponent('filter:"'+t+'"');
	if(window.location.hash != t)
		window.location.hash = t;
};

// unescape \" and \\ back..
var _unescape = function(value) {
	
	if(typeof value == "string")
		return value.replace(/\\"/gm,'"').replace(/\\\\/,'\\');
	return value;
};
// ..by hijacking invokeParamifier (certain paramifiers may be hijacked instead - probably enough to hijack only the "filter" one)
config.extensions.ftp_orig_invokeParamifier = invokeParamifier;
invokeParamifier = function(params,handler) {
	
	if(!params)
		return config.extensions.ftp_orig_invokeParamifier.apply(this,arguments);
	var paramifiers = params[0], name, i;
	for(name in paramifiers)
		for(i = 0; i < paramifiers[name].length; i++)
			paramifiers[name][i] = _unescape(paramifiers[name][i]);
	for(i = 1; i < params.length; i++)
		params[i].value = _unescape(params[i].value);

	return config.extensions.ftp_orig_invokeParamifier.apply(this,arguments);
};
//}}}
/***
|''Name''|ForEachTiddlerPlugin|
|''Version''|1.3.1|
|''Forked from''|[[abego.ForEachTiddlerPlugin|http://tiddlywiki.abego-software.de/#ForEachTiddlerPlugin]], by Udo Borkowski|
|''Author''|Yakov Litvin|
|''CoreVersion''|2.6.2|
|~|Although 2.6.2 is theoretically minimal TW version required for the correct operation, tests showed that the plugin works in 2.6.0, too.|
***/
//{{{
(function(){

// Only install once
if (version.extensions.ForEachTiddlerPlugin) {
	alert("Warning: more than one copy of ForEachTiddlerPlugin is set to be launched");
	return;
} else
	version.extensions.ForEachTiddlerPlugin = {
		source: "[repository url here]",
		licence: "[licence url here]",
		copyright: "Copyright (c) Yakov Litvin, 2012-2015 [url of the meta page]"
	};

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

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

config.macros.forEachTiddler =
{
	actions: {
		addToList: {},
		write: {}
	}
};

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

config.macros.forEachTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
	// --- Pre-parsing for up-to-date params ----------------

	var preParsedParams = this.getUpToDateParams(paramString);
	// for backward compability, "params" are used as well

	// --- Parsing ------------------------------------------

	var parsedParams = this.parseParams(preParsedParams,params);
	if (parsedParams.errorText) {
		this.handleError(place, parsedParams.errorText);
		return;
	}//else
		parsedParams.place = place;
		parsedParams.inTiddler = tiddler ? tiddler : getContainingTiddler(place);

	// --- "Static" processing ------------------------------

	// Choose the action
	var actionName = parsedParams.actionName;
	var action = this.actions[actionName]; // no this is always a "known" action

	// Create the element
	var element = document.createElement(action.element);
	jQuery(element).attr({ refresh: "macro", macroName: macroName }).data(parsedParams);
	place.appendChild(element);

	// --- "Dynamic" processing -----------------------------

	this.refresh(element);
};

config.macros.forEachTiddler.refresh = function(element)
{
	var parsedParams = jQuery(element).data(),
	    action = this.actions[parsedParams.actionName];

	jQuery(element).empty();
	try {
		var tiddlersAndContext = this.getTiddlersAndContext(parsedParams);

		// Perform the action
		action.handler(element, tiddlersAndContext.tiddlers,
				parsedParams.actionParameter, tiddlersAndContext.context);
	} catch (e) {
		this.handleError(place, e);
	}
};

config.macros.forEachTiddler.oldFashionParams = ["in", "filter", "where", "sortBy",
	"script", "write", "begin", "end", "none", "toFile", "withLineSeparator"
//# add to docs: new actions are to be added here or used in name:param notation only
];

config.macros.forEachTiddler.getUpToDateParams = function(paramString)
// turns stuff like "... where 'tiddler.title.length < 20' ..."
//               to "... where:'tiddler.title.length < 20' ..." and then applies parseParams,
// which allows to use params in an arbitrary order and other goodies of parsed params
{
	var paramPairRegExp = new RegExp("("+this.oldFashionParams.join("|")+")\\s+"+
			"("+ // adapted from String.prototype.parseParams
			'(?:"(?:(?:\\\\")|[^"])+")|'+		// double-quoted param
			"(?:'(?:(?:\\\\')|[^'])+')|"+		// quoted param
			"(?:\\[\\[(?:\\s|\\S)*?\\]\\])|"+	// [[...]]-wrapped
			"(?:\\{\\{(?:\\s|\\S)*?\\}\\})|"+	// {{...}}-wrapped
			"(?:[^\"':\\s][^\\s:]*)|"+		// non-wrapped
			"(?:\"\")|(?:'')"+			// empty '' or ""
			")","g");
	paramString =
		paramString.replace(paramPairRegExp,function($0,$1,$2){ return $1+":"+$2; });

	return paramString.parseParams("filter",null,true,false,true);
	// the first unnamed param is now considered as the 'filter' param
};

// 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.
//
// @param parameter  holds the parameter of the macro as separate properties.
//				  The following properties are supported:
//
//						place
//						filter
//						whereClause
//						sortClause
//						sortAscending
//						actionName
//						actionParameter
//						scriptText
//						tiddlyWikiPath
//
//					All properties are optional.
//					For most actions the place property must be defined.
//
config.macros.forEachTiddler.getTiddlersAndContext = function(parameter)
{
	var context = config.macros.forEachTiddler.createContext(parameter.place, parameter.filter, 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 filter and the whereClause
	var tiddlers = this.findTiddlers(parameter.filter, 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};
};

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

// Internal.
//
// --- The addToList Action -----------------------------------------------
//
config.macros.forEachTiddler.actions.addToList.element = "ul";
config.macros.forEachTiddler.actions.addToList.handler = function(place, tiddlers, parameter, context)
{
	for (var i = 0; i < tiddlers.length; i++)
	{
		var tiddler = tiddlers[i];
		var listItem = document.createElement("li");
		place.appendChild(listItem);
		createTiddlyLink(listItem, tiddler.title, true);
	}
};

// Internal.
//
// --- The write Action ---------------------------------------------------
//
config.macros.forEachTiddler.actions.write.element = "span";
config.macros.forEachTiddler.actions.write.handler = function(place, tiddlers, parameter, context)
{
	var params = parameter[0].nonParsedParams;
	if(!parameter[0]["write"])
		return this.handleError(place, "Missing expression behind 'write'.");
	var textExpression = config.macros.forEachTiddler.paramEncode(getParam(parameter,["write"]));

	var getParamExpression = function(name)
	{
		if(params.contains(name) && !parameter[0][name])
			throw "Missing text behind '%0'".format([name]);
		return config.macros.forEachTiddler.paramEncode(getParam(parameter,name));
	};
	var beginExpression = getParamExpression("begin");
	var   endExpression = getParamExpression("end");
	var  noneExpression = getParamExpression("none");

	var lineSeparator = undefined;
	if(params.contains("toFile") && !parameter[0]["toFile"])
		return this.handleError(place, "Filename expected behind 'toFile' of 'write' action.");
	var filename = getParam(parameter,"toFile");
	filename = config.macros.forEachTiddler.paramEncode(filename);
	if(filename) {
		filename = config.macros.forEachTiddler.getLocalPath(filename);

		if(params.contains("withLineSeparator")&& !parameter[0]["withLineSeparator"])
			return this.handleError(place, "Line separator text expected behind 'withLineSeparator' of 'write' action.")
		lineSeparator = getParamExpression("withLineSeparator");
	}

	// Perform the action.
	var func = config.macros.forEachTiddler.getEvalTiddlerFunction(textExpression, context),
	    count = tiddlers.length,
	    text = "";
	if (count > 0 && beginExpression)
		text += config.macros.forEachTiddler.getEvalTiddlerFunction(beginExpression, context)(undefined, context, count, undefined);

	for (var i = 0; i < count; i++) {
		var tiddler = tiddlers[i];
		text += func(tiddler, context, count, i);
	}

	if (count > 0 && endExpression)
		text += config.macros.forEachTiddler.getEvalTiddlerFunction(endExpression, context)(undefined, context, count, undefined);

	if (count == 0 && noneExpression)
	{
		var beginAddition = beginExpression ? "("+beginExpression+")" : "",
		      endAddition =   endExpression ? "("+  endExpression+")" : "",
		     bothAddition = "("+beginAddition
				+((beginAddition && endAddition) ? "+" : "")+endAddition+")";
		noneExpression = noneExpression
				.replace(/(?=\W|^)begin(?=\W|$)/,beginAddition)
				.replace(/(?=\W|^)end(?=\W|$)/,    endAddition)
				.replace(/(?=\W|^)same(?=\W|$)/,  bothAddition);
		text += config.macros.forEachTiddler.getEvalTiddlerFunction(noneExpression, context)(undefined, context, count, undefined);
	}

	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
		wikify(text, place, null/* highlightRegExp */, context.inTiddler);
};


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

config.macros.forEachTiddler.parseParams = function(preParsedParams,params)
{
	if(params.contains("in") && !preParsedParams[0]["in"])
		return { errorText: "TiddlyWiki path expected behind 'in'." };
	var TWpath = getParam(preParsedParams,"in");

	if(params.contains("filter") && !preParsedParams[0]["filter"])
		return { errorText: "No filter specified." };

	if(params.contains("where") && !preParsedParams[0]["where"])
		return { errorText: "whereClause missing behind 'where'." };
	var where = getParam(preParsedParams,"where");

	var ascending = true;
	if(params.contains("sortBy") && !preParsedParams[0]["sortBy"])
		return { errorText: "sortClause missing behind 'sortBy'." };
	var sortClause = getParam(preParsedParams,"sortBy");
	if(preParsedParams[0]["sortBy"] && preParsedParams[0]["sortBy"].length > 1)
		ascending = !(preParsedParams[0]["sortBy"][1] == "descending");

	if(params.contains("script") && !preParsedParams[0]["script"])
		return { errorText: "scriptText is not specified." };
	var scriptText = getParam(preParsedParams,"script");

	var actionName = "addToList";
	for(var knownActionName in this.actions)
		if(preParsedParams[0][knownActionName]) {
			actionName = knownActionName;
			break;
		}
	// no error handling if there's an unknown action
	// because now the order is not important and actionName can have another position
	preParsedParams[0].nonParsedParams = params; // for parsing inside actions

	return {
		tiddlyWikiPath:	this.paramEncode(TWpath),
		filter:		getParam(preParsedParams,"filter"),
		whereClause:	this.paramEncode(where) || true,
		sortClause:	this.paramEncode(sortClause),
		sortAscending:	ascending,
		scriptText:	this.paramEncode(scriptText),
		actionName:	actionName,
		actionParameter:preParsedParams // not much need to cut out other params
	}
};

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


// Internal.
//
config.macros.forEachTiddler.createContext = function(placeParam, filterParam, whereClauseParam, sortClauseParam, sortAscendingParam, actionNameParam, actionParameterParam, scriptText, tiddlyWikiPathParam, inTiddlerParam) {
	return {
		place		: placeParam,
		filter		: filterParam,
		whereClause	: whereClauseParam,
		sortClause	: sortClauseParam,
		sortAscending	: sortAscendingParam,
		script		: scriptText,
		actionName	: actionNameParam,
		actionParameter	: actionParameterParam,
		tiddlyWikiPath	: tiddlyWikiPathParam,
		inTiddler	: inTiddlerParam, // the tiddler containing the <<forEachTiddler ...>> macro call.
		viewerTiddler	: getContainingTiddler(placeParam) //the tiddler showing the forEachTiddler result
	};
};


// 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.";
	
	var tiddlyWiki = new TiddlyWiki();

	if (!tiddlyWiki.importTiddlyWiki(content))
		throw "File '"+path+"' is not a TiddlyWiki.";
	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 functionText = "var theFunction = function(tiddler, context, count, index) { "+(script ? script+";" : "")+"return "+javaScriptExpression+"}";
//	var fullText = (script ? script+";" : "")+functionText+";theFunction;";
	var fullText = functionText+";theFunction;";
	return eval(fullText);
};


// Internal.
//
config.macros.forEachTiddler.findTiddlers = function(filter, whereClause, context, tiddlyWiki) {
	var result = [];
	var func = config.macros.forEachTiddler.getEvalTiddlerFunction(whereClause, context);
	if(filter) {
		var tids = tiddlyWiki.filterTiddlers(filter);
		for(var i = 0; i < tids.length; i++)
			if(func(tids[i], context, undefined, undefined))
				result.push(tids[i]);
	} else
		tiddlyWiki.forEachTiddler(function(title,tiddler) {
			if(func(tiddler, context, undefined, undefined))
				result.push(tiddler);
		});
	return result;
};


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

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

// 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.
//
// 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)
{
	if(!s) return s;
	var reGTGT = new RegExp("\\$\\)\\)","mg");
	var reGT = new RegExp("\\$\\)","mg");
	return s.replace(reGTGT, ">>").replace(reGT, ">");
};
//# document the .paramEncode transformation of the params; or get rid of it?

// 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.
//
// Handles relative links, too.
//
config.macros.forEachTiddler.getLocalPath = function(originalPath) {
// code adapted from SharedTiddlersPlugin to handle relative paths

	var originalAbsolutePath = originalPath;
	if(originalAbsolutePath.search(/^((http(s)?)|(file)):/) != 0) {
	// no protocol prefix..

		if (originalAbsolutePath.search(/^(.\:\\)|(\\\\)|(\/)/) != 0){// is relative?
		// as Unix filesystem root is "/", urls starting with it are not considered as relative

			var currentUrl  = document.location.toString();
			var currentPath = (currentUrl.lastIndexOf("/") > -1) ?
				currentUrl.substr(0, currentUrl.lastIndexOf("/") + 1) :
				currentUrl + "/";
			originalAbsolutePath = currentPath + originalAbsolutePath;
		} else
		// an "absolute" path to a local file. Prefix it with file://

			originalAbsolutePath = "file://" + originalAbsolutePath;

		// replace every \ by a /, to cover Windows style pathes
		originalAbsolutePath = originalAbsolutePath.replace(/\\/mg,"/");
	}
	return getLocalPath(originalAbsolutePath);
};


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

// ---------------------------------------------------------------------------
// fet alias for the the forEachTiddler Macro
// ---------------------------------------------------------------------------

config.macros.fet = config.macros.forEachTiddler;

//============================================================================
// utilities for String and Tiddler objects useful in fet macros
//============================================================================

// Returns true if the string starts with the given prefix, false otherwise.
//
String.prototype.startsWith = function(prefix) {
	var n =  prefix.length;
	return (this.length >= n) && (this.slice(0, n) == prefix);
};

// Returns true if the string ends with the given suffix, false otherwise.
//
String.prototype.endsWith = function(suffix) {
	var n = suffix.length;
	return (this.length >= n) && (this.right(n) == suffix);
};

// Returns true when the string contains the given substring, false otherwise.
//
String.prototype.contains = function(substring) {
	return this.indexOf(substring) >= 0;
};
})();

// Returns the slice value if it is present or defaultText otherwise
//
Tiddler.prototype.getSlice = function(sliceName,defaultText)
{
	var re = TiddlyWiki.prototype.slicesRE;
	re.lastIndex = 0;
	var m = re.exec(this.text);
	while(m) {
		if(m[2]) {
			if(m[2] == sliceName)
				return m[3];
		} else {
			if(m[5] == sliceName)
				return m[6];
		}
		m = re.exec(this.text);
	}
	return defaultText;
};

// Returns the section value if it is present or defaultText otherwise
//
Tiddler.prototype.getSection = function(sectionName,defaultText)
{
	var beginSectionRegExp = new RegExp("(^!{1,6}[ \t]*" + sectionName.escapeRegExp() + "[ \t]*\n)","mg"),
	    sectionTerminatorRegExp = /^!/mg;

	var match = beginSectionRegExp.exec(this.text), sectionText;
	if(match) {
		sectionText = this.text.substr(match.index+match[1].length);
		match = sectionTerminatorRegExp.exec(sectionText);
		if(match)
			sectionText = sectionText.substr(0,match.index-1); // don't include final \n
		return sectionText;
	}
	return defaultText;
};

var transText = function(tiddlerOrGetTiddlerTextArg, moreArguments)
{
	var title = (tiddlerOrGetTiddlerTextArg instanceof Tiddler) ? tiddlerOrGetTiddlerTextArg.title : tiddlerOrGetTiddlerTextArg;
	return "<<tiddler [[" + title + "]] "+ (moreArguments||"") +">>"
};
//}}}
/***
|''Name''|FromPlaceToPlacePlugin|
|''Description''|allows to substitute current tiddlers or page instead of opening tiddlers/pages in addition|
|''Documentation''|see below|
|''Type''|plugin|
|''Version''|1.2.0|
|''CoreVersion''|2.5.0|
|''Source''|http://yakovl.bplaced.net/TW/FPTP.html#FromPlaceToPlacePlugin|
|''Author''|Yakov Litvin|
|''Contact''|See the [[main plugin discussion thread|https://groups.google.com/forum/#!topic/tiddlywiki/bICRWy8qo8g]] and [[contacts|http://customize.tiddlyspace.com/#%5B%5BYakov%20Litvin%5D%5D]]|
|''Copyright''|Yakov Litvin, 2013|
|''Licence''|[[BSD-like open source license|http://yakovl.bplaced.net/TW/FPTP.html#%5B%5BYakov%20Litvin%20Public%20Licence%5D%5D]]|
!!!Introduction
In ~TiddlyWiki, links work "comulatively": when you click an internal link, you get +1 tiddler opened, external links open pages without closing ~TiddlyWiki (hence +1 browser tab). At times, this causes unnecessary "flooding" with opened things (tiddlers/pages). To solve this, FromPlaceToPlacePlugin was created.

It works in a simple way: it keeps the common functionality of the "click a link" action, but "hold meta key + click a link" causes "close and open" action:
* for internal links, this means "close the tiddler in which the link is placed and open the target tiddler"
* for external links, this means "open the page in the same browser tab"
!!!Installation & usage
Aside the usual import/copy-and-add-{{{systemConfig}}}-tag action, you need to adjust the meta keys for internal and external links (and reload afterwards). To do this, change the "Config" section of this tiddler ({{{txtFromTiddlerToTiddlerKey}}} for internal and {{{txtFromPageToPageKey}}} for external links), if necessary. Note that:
* {{{shift}}} doesn't work well for external links in Opera: on shift+click it opens the link in a new tab, so this will result in two equal tabs opened (with Opera, I recommend {{{alt}}})
* {{{alt}}} doesn't work well with IE, so you probably would prefer {{{shift}}}
* each {{{alt}}}, {{{ctrl}}} and {{{shift}}} work (with the limitations above); any other value of an option deactivates corresponding feature
Once the meta keys are set and TW is reloaded, try to click links..
!!!Demo
* click this [[internal link|Introduction to FromPlaceToPlacePlugin]] while pressing {{{shift}}} (or whatever meta key you've set)
* click this [[external link|http://yakovl.bplaced.net/TW/STP/STP.html]] while holding {{{alt}}} key
!!!Additional notes
* this works even with implicit links (like those in the "references" popup)
* "external links" are links with the {{{externalLink}}} class, so links created with inline-html won't work unless the class is added
!!!Config
***/
//{{{
config.extensions.txtFromPageToPageKey = 'alt';         // each 'alt', 'ctrl' and 'shift' work
config.extensions.txtFromTiddlerToTiddlerKey = 'shift'; // each 'alt', 'ctrl' and 'shift' work
//}}}
/***
!!!Code
***/
//{{{
(function(){
if(version.extensions.FromPlaceToPlacePlugin)
	return;
version.extensions.FromPlaceToPlacePlugin = { major: 1, minor: 2, revision: 0, date: new Date(2013,10,24)};

var firedWhenKeyWasPressed = function(event,key) {

	return (event.shiftKey && key == 'shift') ||
	       (event.ctrlKey && key == 'ctrl') ||
	       (event.altKey && key == 'alt');
}

//------------------------------------------------------------------------------------------------------------
// From tiddler to tiddler

// keep as a global variable for a possibility of introspection
orig_onClickTiddlerLink = onClickTiddlerLink;

// hijack
onClickTiddlerLink = function(ev) {

	var result,
	    sourceTid = story.findContainingTiddler(this),
	    event = ev || window.event,
	    key = config.extensions.txtFromTiddlerToTiddlerKey,
	    close = (firedWhenKeyWasPressed(event,key) && sourceTid) ? true : false;

	// to "correct" page and zoomer position,
	// hide the "source" tiddler before opening the "target" and closing the "source"
	if(close)
		sourceTid.style.display = "none";
	result = orig_onClickTiddlerLink(event);
	if(close) {
		var tName = sourceTid.getAttribute("tiddler");
		story.closeTiddler(tName);
	}
	return result;
}

//------------------------------------------------------------------------------------------------------------
// From page to page
jQuery("body").delegate("a.externalLink", "click", function(ev) {

	var event = ev || window.event,
	    key = config.extensions.txtFromPageToPageKey,
	    close = firedWhenKeyWasPressed(event,key),
	    target = jQuery(this).attr("href");

	if(close) {
		window.location.assign(target);
		return false;
	}
});

})();
//}}}
/***
|Version|custom version forked from 0.4.5|
|Requires|FoldTiddlerPlugin CustomSettingsPlugin|
|~|doesn't require FoldTiddlerPlugin, but if used, that one must be launched first|
***/
//{{{
var permaViewClass = "permaviewButton";
config.options.chkSinglePageMode = readOnly;
config.options.chkKeepPermaview = readOnly;

config.macros.reopenPermaview =
{
	reopen: function()
	{
		var params = getParameters(); // paramifiers in hash
		story.closeAllTiddlers();
		if(!params)
			story.displayDefaultTiddlers();
		else {
			params = params.parseParams("open",null,false);
			invokeParamifier(params,"onstart");
		}
	},
	handler: function(place,macroName,params,wikifier,paramString)
	{
		// parse params
		var pParams = paramString.parseParams("label",null,true,false,true),
		    label = getParam(pParams,"label","⟳"),
		    prompt = getParam(pParams,"prompt","reopen permaview/default tiddlers");

		// create button
		createTiddlyButton(place,label,prompt,this.reopen);
	}
};

// extend the permaview macro:
// when chkAddReopenToPermaview is true, add the reopen button next to the permaview
// also add the permaviewButton class to display the "non-permaview" state
config.macros.permaview.HPVP_orig_handler = config.macros.permaview.handler;
config.macros.permaview.handler = function(place,macroName,params,wikifier,paramString,tid)
{
	if(config.options.chkAddReopenToPermaview) {
		var wrapper = createTiddlyElement(place,"span");
		place = wrapper;
	}
	this.HPVP_orig_handler.apply(this,arguments);
	place.lastChild.classList.add(permaViewClass);
	if(config.options.chkAddReopenToPermaview) {
		place.lastChild.style.display = "inline";
		config.macros.reopenPermaview.handler(place,"",[],wikifier,"");
		place.lastChild.style.display = "inline";
		// inline styling is needed for the sidebar
	}
};

// keep permaview on any tiddler open/close:
story.HPVP_orig_displayTiddler = story.displayTiddler;
story.displayTiddler = function(srcElement,tiddler,template,animate,unused,customFields,toggle,animationSrc)
{
	if(config.options.chkSinglePageMode)
	{
		var savedPermaView = this.permaView;
		this.permaView = function(){}; // prevent intermediate "empty hash" state
		story.closeAllTiddlers();
	}
	var result = this.HPVP_orig_displayTiddler.apply(this,arguments),
	    title = (tiddler instanceof Tiddler) ? tiddler.getVisibleName() : tiddler;
	if(config.options.chkSinglePageMode) {
		this.permaView = savedPermaView;
		document.title = title.replace(/&nbsp;/gm," ") +" - "+ store.getTiddlerText("SiteSubtitle");
	}
	if(config.options.chkKeepPermaview)
		this.permaView();
	if(config.options.chkSinglePageMode) window.scroll(0,0);
//# scroll to the top of #tiddlerDisplay , take animation into account
	return result;
};
story.HPVP_orig_closeTiddler = story.closeTiddler;
story.closeTiddler = function(title,animate,unused)
{
	var result = story.HPVP_orig_closeTiddler.apply(this,arguments);
	if(config.options.chkKeepPermaview) {
		if(config.options.chkAnimate)
		// animated closing keeps tiddler visible for .permaView() for some time
			setTimeout("story.permaView()",config.animDuration+10);
		else
		// delay without animation causes "sometimes fail to close" bug
		// (ensureOpenedByURI fires earlier)
			story.permaView();
	}
	return result;
};

// enable "back" button (both browser and keyboard) by regularly checking permaview
var ensureOpenedByURI = function()
{
	if(!config.options.chkKeepPermaview) return;
	
	var correctTiddlersAreOpen = true,
	    paramifiersString = getParameters(), paramifiers, filter, tiddlers,
	    exoticParamifiersPresent = false, i, currentlyOpenTiddlers = [];
	
	// check if some tiddlers are missing/extra compared to permaview
	if(paramifiersString) {
		paramifiers = paramifiersString.parseParams("open",null,false);
		filter = "";
		// paramifiers[0] is a "summary" object, skip that one
		for(i = 1; i < paramifiers.length; i++)
		   switch(paramifiers[i].name) {
		// since permaview only generates either filter or open we only take them into account
			case "filter": filter += paramifiers[i].value; break;
			case "open": filter += "[["+paramifiers[i].value+"]]"; break;
			default: exoticParamifiersPresent = true;
		   }
	} else
		filter = store.getTiddlerText("DefaultTiddlers");
	if(exoticParamifiersPresent) return;
	
	// compare the open tiddler set and the set suggested by paramifiers
	tiddlers = store.filterTiddlers(filter);
	story.forEachTiddler(function(title,e){currentlyOpenTiddlers.push(title);});
	if(tiddlers.length != currentlyOpenTiddlers.length)
		correctTiddlersAreOpen = false;
	else
		for(i = 0; i < tiddlers.length; i++)
			if(currentlyOpenTiddlers.indexOf(tiddlers[i].title) == -1)
				correctTiddlersAreOpen = false;

	// save scroll position
	var posX = findScrollX();
	var posY = findScrollY();

	// reopen if none are edited
	if(!correctTiddlersAreOpen && !story.areAnyDirty()) {
		config.macros.reopenPermaview.reopen();
		window.scrollTo(posX,posY);
	}
}
setInterval(ensureOpenedByURI,300);
//# see also https://css-tricks.com/using-the-html5-history-api/ , "ajax browser history html5"
//  pragmatics: permalinks well-distinguished by social networks

// apply different styling to the permaview button, depending on permaview/"non-permaview" state
var css = "."+permaViewClass+" { font-weight: bold; color: red !important; }";
//# trace displayed stuff, on story change apply styles to .permaviewButton
//	setStylesheet(css,"highlightSaving");
//# on permaview, change the styles back
//	removeStyleSheet("highlightSaving");
//}}}
/***
|Description|This plugin introduces new formatters that generate sub- and superscripts|
|Version|1.0|
|Short status|<<insertEditable container:"@stateComment" size:max cell>>|
subscript syntax: {{{text,,with subscript,,}}} → text,,with subscript,,
superscript containing float syntaxes:
{{{10^1.1}}} → 10^1.1, {{{10^+1,1}}} → 10^+1,1, {{{10^-0.2}}} → 10^-0.2, {{{2^8}}} → 2^8, {{{n^3}}} → n^3
***/
//{{{
config.formatters.push({
	name: "subscript",
	match: ",,(?!\\s|$)",
	handler: function(w) {
		w.subWikifyTerm(createTiddlyElement(w.output,"sub"),/(,,)/mg);
	}
},{
	name: "power",
	match: "\\^(?:\\+|-|±|\\+-|∓|-\\+)?\\d+(?:(?:\\.|,)\\d+)?",
	lookaheadRegExp: /\^((?:\+|-|±|\+-|∓|-\+)?\d+(?:(?:\.|,)\d+)?)/g,
	handler: function(w) {
		this.lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = this.lookaheadRegExp.exec(w.source),
		    power = lookaheadMatch[1].replace("+-","±").replace("-+","∓");
		
		createTiddlyElement(w.output,'sup',null,null/*class*/,power);
		
		w.nextMatch = this.lookaheadRegExp.lastIndex;
	}
})
//}}}
/***
тестируем в [[test bootstrap]]
***/
//{{{
// using actual link elements in the template works much better as CSS is applied only in that contex
//jQuery("head").append("<link rel='stylesheet' type='text/css' href='css/bootstrap.min.css' />");
//jQuery("head").append("<link rel='stylesheet' type='text/css' href='css/course.css' />");

/*
//# use jQuery.getScript("js/bootstrap.min.js") on "test bootstrap" tiddler open
story.bs_orig_displayTiddler = story.displayTiddler;
story.displayTiddler = function (srcElement,tiddler,template,animate,unused,customFields,toggle,animationSrc)
{
	var res = this.bs_orig_displayTiddler.apply(this,arguments);
	var title = (tiddler instanceof Tiddler) ? tiddler.title : tiddler;
	if(title == "test bootstrap")
		jQuery.getScript("js/bootstrap.min.js");
	return res;
};
*/
//}}}
/***
1. loading bs.js in MarkupPostBody via {{{<script src="js/bootstrap.min.js"></script>}}} doesn't work: looks like it must be launched when the DOM is built already

retest js, notes below may be a misinterpretation (it is CSS which was missing):
2. [[loading|LoadBootstrapPlugin]] bs.js async on tiddler open doesn't work "as expected" as well!
----
3. ???
4. check what happens if bs.js is loaded twice?
5. check if bs.js has to be loaded each time the tiddler is reopened after closing (try to just reopen first)
6. check how 2+ bs.js interact
***/
/***
|Name|NestedSlidersPlugin|
|Source|http://www.TiddlyTools.com/#NestedSlidersPlugin|
|Documentation|http://www.TiddlyTools.com/#NestedSlidersPluginInfo|
|Version|2.4.9|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|show content in nest-able sliding/floating panels, without creating separate tiddlers for each panel's content|
!!!!!Configuration
<<<
<<option chkFloatingSlidersAnimate>> allow floating sliders to animate when opening/closing
Note: for floating slider animation to occur you must also allow animation in general (see [[AdvancedOptions]]).
<<<
!!!!!Code
***/
//{{{
version.extensions.NestedSlidersPlugin= {major: 2, minor: 4, revision: 9, date: new Date(2008,11,15)};

// options for deferred rendering of sliders that are not initially displayed
if (config.options.chkFloatingSlidersAnimate===undefined)
	config.options.chkFloatingSlidersAnimate=false; // avoid clipping problems in IE

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

// if removeCookie() function is not defined by TW core, define it here.
if (window.removeCookie===undefined) {
	window.removeCookie=function(name) {
		document.cookie = name+'=; expires=Thu, 01-Jan-1970 00:00:01 UTC; path=/;'; 
	}
}

config.formatters.push( {
	name: "nestedSliders",
	match: "\\n?\\+{3}",
	terminator: "\\s*\\={3}\\n?",
	lookahead: "\\n?\\+{3}(\\+)?(\\([^\\)]*\\))?(\\!*)?(\\^(?:[^\\^\\*\\@\\[\\>]*\\^)?)?(\\*)?(\\@)?(?:\\{\\{([\\w]+[\\s\\w]*)\\{)?(\\[[^\\]]*\\])?(\\[[^\\]]*\\])?(?:\\}{3})?(\\#[^:]*\\:)?(\\>)?(\\.\\.\\.)?\\s*",
	handler: function(w)
		{
			lookaheadRegExp = new RegExp(this.lookahead,"mg");
			lookaheadRegExp.lastIndex = w.matchStart;
			var lookaheadMatch = lookaheadRegExp.exec(w.source)
			if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
			{
				var defopen=lookaheadMatch[1];
				var cookiename=lookaheadMatch[2];
				var header=lookaheadMatch[3];
				var panelwidth=lookaheadMatch[4];
				var transient=lookaheadMatch[5];
				var hover=lookaheadMatch[6];
				var buttonClass=lookaheadMatch[7];
				var label=lookaheadMatch[8];
				var openlabel=lookaheadMatch[9];
				var panelID=lookaheadMatch[10];
				var blockquote=lookaheadMatch[11];
				var deferred=lookaheadMatch[12];

				// location for rendering button and panel
				var place=w.output;

				// default to closed, no cookie, no accesskey, no alternate text/tip
				var show="none"; var cookie=""; var key="";
				var closedtext=">"; var closedtip="";
				var openedtext="<"; var openedtip="";

				// extra "+", default to open
				if (defopen) show="block";

				// cookie, use saved open/closed state
				if (cookiename) {
					cookie=cookiename.trim().slice(1,-1);
					cookie="chkSlider"+cookie;
					if (config.options[cookie]==undefined)
						{ config.options[cookie] = (show=="block") }
					show=config.options[cookie]?"block":"none";
				}

				// parse label/tooltip/accesskey: [label=X|tooltip]
				if (label) {
					var parts=label.trim().slice(1,-1).split("|");
					closedtext=parts.shift();
					if (closedtext.substr(closedtext.length-2,1)=="=")	
						{ key=closedtext.substr(closedtext.length-1,1); closedtext=closedtext.slice(0,-2); }
					openedtext=closedtext;
					if (parts.length) closedtip=openedtip=parts.join("|");
					else { closedtip="show "+closedtext; openedtip="hide "+closedtext; }
				}

				// parse alternate label/tooltip: [label|tooltip]
				if (openlabel) {
					var parts=openlabel.trim().slice(1,-1).split("|");
					openedtext=parts.shift();
					if (parts.length) openedtip=parts.join("|");
					else openedtip="hide "+openedtext;
				}

				var title=show=='block'?openedtext:closedtext;
				var tooltip=show=='block'?openedtip:closedtip;

				// create the button
				if (header) { // use "Hn" header format instead of button/link
					var lvl=(header.length>5)?5:header.length;
					var btn = createTiddlyElement(createTiddlyElement(place,"h"+lvl,null,null,null),"a",null,buttonClass,title);
					btn.onclick=onClickNestedSlider;
					btn.setAttribute("href","javascript:;");
					btn.setAttribute("title",tooltip);
				}
				else
					var btn = createTiddlyButton(place,title,tooltip,onClickNestedSlider,buttonClass);
				btn.innerHTML=title; // enables use of HTML entities in label

				// set extra button attributes
				btn.setAttribute("closedtext",closedtext);
				btn.setAttribute("closedtip",closedtip);
				btn.setAttribute("openedtext",openedtext);
				btn.setAttribute("openedtip",openedtip);
				btn.sliderCookie = cookie; // save the cookiename (if any) in the button object
				btn.defOpen=defopen!=null; // save default open/closed state (boolean)
				btn.keyparam=key; // save the access key letter ("" if none)
				if (key.length) {
					btn.setAttribute("accessKey",key); // init access key
					btn.onfocus=function(){this.setAttribute("accessKey",this.keyparam);}; // **reclaim** access key on focus
				}
				btn.setAttribute("hover",hover?"true":"false");
				btn.onmouseover=function(ev) {
					// optional 'open on hover' handling
					if (this.getAttribute("hover")=="true" && this.sliderPanel.style.display=='none') {
						document.onclick.call(document,ev); // close transients
						onClickNestedSlider(ev); // open this slider
					}
					// mouseover on button aligns floater position with button
					if (window.adjustSliderPos) window.adjustSliderPos(this.parentNode,this,this.sliderPanel);
				}

				// create slider panel
				var panelClass=panelwidth?"floatingPanel":"sliderPanel";
				if (panelID) panelID=panelID.slice(1,-1); // trim off delimiters
				var panel=createTiddlyElement(place,"div",panelID,panelClass,null);
				panel.button = btn; // so the slider panel know which button it belongs to
				btn.sliderPanel=panel; // so the button knows which slider panel it belongs to
				panel.defaultPanelWidth=(panelwidth && panelwidth.length>2)?panelwidth.slice(1,-1):"";
				panel.setAttribute("transient",transient=="*"?"true":"false");
				panel.style.display = show;
				panel.style.width=panel.defaultPanelWidth;
				panel.onmouseover=function(event) // mouseover on panel aligns floater position with button
					{ if (window.adjustSliderPos) window.adjustSliderPos(this.parentNode,this.button,this); }

				// render slider (or defer until shown) 
				w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
				if ((show=="block")||!deferred) {
					// render now if panel is supposed to be shown or NOT deferred rendering
					w.subWikify(blockquote?createTiddlyElement(panel,"blockquote"):panel,this.terminator);
					// align floater position with button
					if (window.adjustSliderPos) window.adjustSliderPos(place,btn,panel);
				}
				else {
					var src = w.source.substr(w.nextMatch);
					var endpos=findMatchingDelimiter(src,"+++","===");
					panel.setAttribute("raw",src.substr(0,endpos));
					panel.setAttribute("blockquote",blockquote?"true":"false");
					panel.setAttribute("rendered","false");
					w.nextMatch += endpos+3;
					if (w.source.substr(w.nextMatch,1)=="\n") w.nextMatch++;
				}
			}
		}
	}
)

function findMatchingDelimiter(src,starttext,endtext) {
	var startpos = 0;
	var endpos = src.indexOf(endtext);
	// check for nested delimiters
	while (src.substring(startpos,endpos-1).indexOf(starttext)!=-1) {
		// count number of nested 'starts'
		var startcount=0;
		var temp = src.substring(startpos,endpos-1);
		var pos=temp.indexOf(starttext);
		while (pos!=-1)  { startcount++; pos=temp.indexOf(starttext,pos+starttext.length); }
		// set up to check for additional 'starts' after adjusting endpos
		startpos=endpos+endtext.length;
		// find endpos for corresponding number of matching 'ends'
		while (startcount && endpos!=-1) {
			endpos = src.indexOf(endtext,endpos+endtext.length);
			startcount--;
		}
	}
	return (endpos==-1)?src.length:endpos;
}
//}}}
//{{{
window.onClickNestedSlider=function(e)
{
	if (!e) var e = window.event;
	var theTarget = resolveTarget(e);
	while (theTarget && theTarget.sliderPanel==undefined) theTarget=theTarget.parentNode;
	if (!theTarget) return false;
	var theSlider = theTarget.sliderPanel;
	var isOpen = theSlider.style.display!="none";

	// if SHIFT-CLICK, dock panel first (see [[MoveablePanelPlugin]])
	if (e.shiftKey && config.macros.moveablePanel) config.macros.moveablePanel.dock(theSlider,e);

	// toggle label
	theTarget.innerHTML=isOpen?theTarget.getAttribute("closedText"):theTarget.getAttribute("openedText");
	// toggle tooltip
	theTarget.setAttribute("title",isOpen?theTarget.getAttribute("closedTip"):theTarget.getAttribute("openedTip"));

	// deferred rendering (if needed)
	if (theSlider.getAttribute("rendered")=="false") {
		var place=theSlider;
		if (theSlider.getAttribute("blockquote")=="true")
			place=createTiddlyElement(place,"blockquote");
		wikify(theSlider.getAttribute("raw"),place);
		theSlider.setAttribute("rendered","true");
	}

	// show/hide the slider
	if(config.options.chkAnimate && (!hasClass(theSlider,'floatingPanel') || config.options.chkFloatingSlidersAnimate))
		anim.startAnimating(new Slider(theSlider,!isOpen,e.shiftKey || e.altKey,"none"));
	else
		theSlider.style.display = isOpen ? "none" : "block";

	// reset to default width (might have been changed via plugin code)
	theSlider.style.width=theSlider.defaultPanelWidth;

	// align floater panel position with target button
	if (!isOpen && window.adjustSliderPos) window.adjustSliderPos(theSlider.parentNode,theTarget,theSlider);

	// if showing panel, set focus to first 'focus-able' element in panel
	if (theSlider.style.display!="none") {
		var ctrls=theSlider.getElementsByTagName("*");
		for (var c=0; c<ctrls.length; c++) {
			var t=ctrls[c].tagName.toLowerCase();
			if ((t=="input" && ctrls[c].type!="hidden") || t=="textarea" || t=="select")
				{ try{ ctrls[c].focus(); } catch(err){;} break; }
		}
	}
	var cookie=theTarget.sliderCookie;
	if (cookie && cookie.length) {
		config.options[cookie]=!isOpen;
		if (config.options[cookie]!=theTarget.defOpen) window.saveOptionCookie(cookie);
		else window.removeCookie(cookie); // remove cookie if slider is in default display state
	}

	// prevent SHIFT-CLICK from being processed by browser (opens blank window... yuck!)
	// prevent clicks *within* a slider button from being processed by browser
	// but allow plain click to bubble up to page background (to close transients, if any)
	if (e.shiftKey || theTarget!=resolveTarget(e))
		{ e.cancelBubble=true; if (e.stopPropagation) e.stopPropagation(); }
	Popup.remove(); // close open popup (if any)
	return false;
}
//}}}
//{{{
// click in document background closes transient panels 
document.nestedSliders_savedOnClick=document.onclick;
document.onclick=function(ev) { if (!ev) var ev=window.event; var target=resolveTarget(ev);

	if (document.nestedSliders_savedOnClick)
		var retval=document.nestedSliders_savedOnClick.apply(this,arguments);
	// if click was inside a popup... leave transient panels alone
	var p=target; while (p) if (hasClass(p,"popup")) break; else p=p.parentNode;
	if (p) return retval;
	// if click was inside transient panel (or something contained by a transient panel), leave it alone
	var p=target; while (p) {
		if ((hasClass(p,"floatingPanel")||hasClass(p,"sliderPanel"))&&p.getAttribute("transient")=="true") break;
		p=p.parentNode;
	}
	if (p) return retval;
	// otherwise, find and close all transient panels...
	var all=document.all?document.all:document.getElementsByTagName("DIV");
	for (var i=0; i<all.length; i++) {
		 // if it is not a transient panel, or the click was on the button that opened this panel, don't close it.
		if (all[i].getAttribute("transient")!="true" || all[i].button==target) continue;
		// otherwise, if the panel is currently visible, close it by clicking it's button
		if (all[i].style.display!="none") window.onClickNestedSlider({target:all[i].button})
		if (!hasClass(all[i],"floatingPanel")&&!hasClass(all[i],"sliderPanel")) all[i].style.display="none";
	}
	return retval;
};
//}}}
//{{{
// adjust floating panel position based on button position
if (window.adjustSliderPos==undefined) window.adjustSliderPos=function(place,btn,panel) {
	if (hasClass(panel,"floatingPanel") && !hasClass(panel,"undocked")) {
		// see [[MoveablePanelPlugin]] for use of 'undocked'
		var rightEdge=document.body.offsetWidth-1;
		var panelWidth=panel.offsetWidth;
		var left=0;
		var top=btn.offsetHeight; 
		if (place.style.position=="relative" && findPosX(btn)+panelWidth>rightEdge) {
			left-=findPosX(btn)+panelWidth-rightEdge; // shift panel relative to button
			if (findPosX(btn)+left<0) left=-findPosX(btn); // stay within left edge
		}
		if (place.style.position!="relative") {
			var left=findPosX(btn);
			var top=findPosY(btn)+btn.offsetHeight;
			var p=place; while (p && !hasClass(p,'floatingPanel')) p=p.parentNode;
			if (p) { left-=findPosX(p); top-=findPosY(p); }
			if (left+panelWidth>rightEdge) left=rightEdge-panelWidth;
			if (left<0) left=0;
		}
		panel.style.left=left+"px"; panel.style.top=top+"px";
	}
}
//}}}
//{{{
// TW2.1 and earlier:
// hijack Slider stop handler so overflow is visible after animation has completed
Slider.prototype.coreStop = Slider.prototype.stop;
Slider.prototype.stop = function()
	{ this.coreStop.apply(this,arguments); this.element.style.overflow = "visible"; }

// TW2.2+
// hijack Morpher stop handler so sliderPanel/floatingPanel overflow is visible after animation has completed
if (version.major+.1*version.minor+.01*version.revision>=2.2) {
	Morpher.prototype.coreStop = Morpher.prototype.stop;
	Morpher.prototype.stop = function() {
		this.coreStop.apply(this,arguments);
		var e=this.element;
		if (hasClass(e,"sliderPanel")||hasClass(e,"floatingPanel")) {
			// adjust panel overflow and position after animation
			e.style.overflow = "visible";
			if (window.adjustSliderPos) window.adjustSliderPos(e.parentNode,e.button,e);
		}
	};
}
//}}}
/***
|Version|0.7.0|
О плагине и разработке <<tiddler [[to do list template##main]] with:"regarding NestedSwitcherPlugin" with:orderNtpNotes noedit>>
***/
//{{{
// regexp helpers:
var begin = "^",
    someDotsDontRemember = "\\.*",
    someDots = "("+someDotsDontRemember+")",
    mainHead = "%((?: tabs[_><]? )|(?: js )|(?:%+))%\\((\\w*)\\($",
    mainHeadDontRemember = "%(?:(?: tabs[_><]? )|(?: js )|(?:%+))%\\(\\w*\\($",
    terminator = "\\)\\)%%\\n?";

var nestedSwitchersFormatter =
{
	name: "nestedSwitchers",
	match: begin + someDotsDontRemember + mainHeadDontRemember,
	matchAgain: begin + someDots + mainHead
}
nestedSwitchersFormatter.handler = function(w)
{
	// check how many dots go before the %...% (the nesting level)
	var matchAgainRegExp = new RegExp(this.matchAgain,"mg");
	matchAgainRegExp.lastIndex = w.matchStart;
	var dots = matchAgainRegExp.exec(w.source)[1];

	var lookahead = begin + dots + mainHead +"((?:.|\n)*?)"+ begin + dots + terminator;
	lookaheadRegExp = new RegExp(lookahead,"mg");
	lookaheadRegExp.lastIndex = w.matchStart;
	var lookaheadMatch = lookaheadRegExp.exec(w.source);
	if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
	{
		var param = lookaheadMatch[2];
		switch(lookaheadMatch[1])
		{
			case " tabs< ":
			case " tabs> ":
			case " tabs_ ":
//# do extra stuff here, propagate
			case " tabs ":
			case "%":
				if(param.substr(0,7) != "txtTabs")
					param = "txtTabs" + param.substr(0,1).toUpperCase() +
						param.substr(1);
				this.tabsHandler(lookaheadMatch[3],w.output,dots,param,w);
				break;
			case " js ":
			case "%%":
				this.jsHandler(lookaheadMatch[3],w.output,dots,param,w);
				break;
		}
		w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
	}
};
var tabNameAddition = "_text_:";
nestedSwitchersFormatter.tabsHandler = function(text,place,dots,cookieName,wikifier)
{
	// create the place where tabs should be put
	var el = "div", validTab = false,
	    wrapper = createTiddlyElement(null,el,null,"tabsetWrapper " + cookieName),
	    tabset = createTiddlyElement(wrapper,el,null,"tabset",null,{cookie:cookieName});

	// parse the text
	// .%% label 1 % tooltip 1 % or .<% label 1 %
	// content 1
	// ...
	var match, startTextIndex, endTextIndex,
	    tabText, tabLabel, tabTooltip, transclusionArguments, tab, defaultTab,
	    dotsPattern = dots.replace(".","\\."),
	    labelTextPattern = "(?:[^%\\n]|(?:%%))*?", // any of ((not % or linebreak) or %%)
	    labelPattern = "%% +("+labelTextPattern+") +(?:% +("+labelTextPattern+") +)?"+
			   "(?:%(\\[\\["+labelTextPattern+"\\]\\]"+labelTextPattern+"))?%%$",
	    headPattern = begin + dotsPattern + labelPattern,
	    headRegExp = new RegExp(headPattern,"mg");

	match = headRegExp.exec(text);
	defaultTab = match ? match[1] : "";
	while(match)
	{
		startTextIndex = match.index + match[0].length;
		tabLabel		=  match[1]       .replace("%%","%");
		tabTooltip		= (match[2] || "").replace("%%","%");
		transclusionArguments	= (match[3] || "").replace("%%","%");
		if(match = headRegExp.exec(text))
			endTextIndex = match.index;
		else
			endTextIndex = text.length;
		tabText = transclusionArguments ? "<<tiddler "+transclusionArguments+">>" :
				jQuery.trim(text.substring(startTextIndex,endTextIndex));

		tab = createTiddlyElement(tabset,"a",null,"tab tabUnselected",null,{
			tab : tabLabel, content: tabNameAddition + tabText,
			href: "javascript:;"
		})
		if(tabTooltip)
			tab.setAttribute("title",tabTooltip);
		if(tabLabel)
			// tab.appendChild(document.createTextNode(tabLabel));
			wikify(tabLabel,tab,null,wikifier.tiddler)

		tab.onclick = config.macros.tabs.onClickTab;
		tab.title = tabTooltip;
		createTiddlyElement(tab,"span",null,null," ",{style:"font-size:0pt;line-height:0px"});
//# is this necessary? this was taken from config.macros.tabs.handler
		if(config.options[cookieName] == tabLabel)
			validTab = true;
	}
	if(!validTab)
		config.options[cookieName] = defaultTab;

	place.appendChild(wrapper);
	config.macros.tabs.switchTab(tabset,config.options[cookieName]);
};

// hijack config.macros.tabs.switchTab so that is takes into account "text" params
// (aside normal "where text is" params)
config.macros.tabs.bare_switchTab = config.macros.tabs.switchTab;
config.macros.tabs.switchTab = function(tabset,tab)
{
	if(!store.ntp_orig_getTiddlerText)
	{
		store.ntp_orig_getTiddlerText = store.getTiddlerText;
		store.getTiddlerText = function(title,defaultText)
		{
			if(title.substr(0,tabNameAddition.length) == tabNameAddition)
				return title.substr(tabNameAddition.length);
			return this.ntp_orig_getTiddlerText(title,defaultText);
		};
		store.ntp_orig_getTiddler = store.getTiddler;
		store.getTiddler = function(title)
		{
			if(title.substr(0,tabNameAddition.length) == tabNameAddition) {
				var tidElem = story.findContainingTiddler(tabset),
				    tidName = tidElem ? tidElem.getAttribute("tiddler") : "";
				return store.fetchTiddler(tidName);
			}
			return this.ntp_orig_getTiddler(title);
		};
	}
	this.bare_switchTab.apply(this,arguments);
	if(store.ntp_orig_getTiddlerText)
	{
		store.getTiddlerText = store.ntp_orig_getTiddlerText;
		store.getTiddler     = store.ntp_orig_getTiddler;
	}
	store.ntp_orig_getTiddlerText = null;
	// this complicated stuff about .getTiddlerText is needed for nested tabs
	// the stuff about .getTiddler gives the right "tiddler" context for the wikification
};


nestedSwitchersFormatter.jsHandler = function(text,place,dots,param,wikifier)
{
//# for testing:
createTiddlyElement(place,"div",null,null,"js: "+text,{style: "color:green; white-space:pre;"})
	
};

config.formatters.push(nestedSwitchersFormatter);
//}}}
/***
|Name|RearrangeTiddlersPlugin|
|Source|{{DDn{old:}}} http://www.TiddlyTools.com/#RearrangeTiddlersPlugin|
|Version|2.0.2|
|Author|Yakov Litvin|
|Contributor|Eric Shulman|
|OriginalAuthor|Joe Raii|
|License|{{DDn{old:}}} http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.4.0|
|Type|plugin|
|Description|drag tiddlers by title to re-order story column display|
***/
//{{{
if (Story.prototype.rearrangeTiddlersHijack_refreshTiddler === undefined) {
 Story.prototype.rearrangeTiddlersHijack_refreshTiddler = Story.prototype.refreshTiddler;
 Story.prototype.refreshTiddler = function(title,template)
 {
	this.rearrangeTiddlersHijack_refreshTiddler.apply(this,arguments);
	var theTiddler = document.getElementById(this.tiddlerId(title));
	if(!theTiddler) return;
	
	// find title element
	var theHandle = jQuery(theTiddler).children(".title")[0];
	if(!theHandle) return theTiddler;

	Drag.init(theHandle, theTiddler, 0, 0, null, null);
	theHandle.style.cursor = "move";
	theHandle.title = "drag title to re-arrange tiddlers, click for more options...";
	theTiddler.onDrag = function(x,y,myElem)
	{
		if (this.style.position != "relative") {
			this.savedstyle = this.style.position;
			this.style.position = "relative";
		}
		y = myElem.offsetTop;
		var next = myElem.nextSibling;
		var prev = myElem.previousSibling;
		if (next && y + myElem.offsetHeight > next.offsetTop + next.offsetHeight/2) {
			myElem.parentNode.removeChild(myElem);
			next.parentNode.insertBefore(myElem, next.nextSibling);//elems[pos+1]);
			myElem.style["top"] = -next.offsetHeight/2+"px";
		}
		if (prev && y < prev.offsetTop + prev.offsetHeight/2) { 
			myElem.parentNode.removeChild(myElem);
			prev.parentNode.insertBefore(myElem, prev);
			myElem.style["top"] = prev.offsetHeight/2+"px";
		}
	};
	theTiddler.onDragEnd = function(x,y,myElem)
	{
		myElem.style["top"] = "0px";
		if (this.savedstyle != undefined)
			this.style.position = this.savedstyle;
	};
	theHandle.onclick = function(ev)
	{
		ev = ev || window.event;
		var p = Popup.create(this); if (!p) return;
		var b = createTiddlyButton(createTiddlyElement(p,"li"),
			"\u25B2 move to top of column ","move this tiddler to the top of the story column",
			function() {
				var t = story.getTiddler(this.getAttribute("tid"));
				t.parentNode.insertBefore(t,t.parentNode.firstChild); // move to top of column
				window.scrollTo(0,ensureVisible(t));
				return false;
			});
		b.setAttribute("tid",title);
		var b = createTiddlyButton(createTiddlyElement(p,"li"),
			"\u25BC move to bottom of column ","move this tiddler to the bottom of the story column",
			function() {
				var t = story.getTiddler(this.getAttribute("tid"));
				t.parentNode.insertBefore(t,null); // move to bottom of column
				window.scrollTo(0,ensureVisible(t));
				return false;
			});
		b.setAttribute("tid",title);
		Popup.show();
		ev.cancelBubble = true;
		if (ev.stopPropagation) ev.stopPropagation();
		return false;
	};
	return theTiddler;
 }
}

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

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

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

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

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

	fixE: function(e)
	{
		if (typeof e == 'undefined') e = window.event;
		if (typeof e.layerX == 'undefined') e.layerX = e.offsetX;
		if (typeof e.layerY == 'undefined') e.layerY = e.offsetY;
		return e;
	}
};
//}}}
/***
***/
//{{{
// tagging macro
config.macros.tagging.label = "записи с этим тэгом:";
config.macros.tagging.labelNotTag = "нет записей с этим тэгом";
config.macros.tagging.tooltip = "Список записей с тэгом '%0'";

// tags macro
config.views.wikified.tag.labelNoTags = "нет тэгов";
config.views.wikified.tag.labelTags = "тэги:";
config.views.wikified.tag.openTag = "Открыть тэг '%0'";
config.views.wikified.tag.tooltip = "Показать записи с тэгом '%0'";
config.views.wikified.tag.openAllText = "Открыть все";
config.views.wikified.tag.openAllTooltip = "Открыть все эти записи";
config.views.wikified.tag.popupNone = "Других записей с тэгом '%0' нет";

// closeAll macro
config.macros.closeAll.label = "закрыть все";
config.macros.closeAll.prompt = readOnly ? "закрыть все открытые записи" : "закрыть все открытые записи (кроме редактируемых)";

// search macro
config.macros.search.label = "искать";
config.macros.search.prompt = "Искать на этом сайте (в этой TiddlyWiki)";

// toolbar commands (some)
config.commands.closeTiddler.text = "закрыть";
config.commands.closeTiddler.tooltip = "закрыть эту запись";

// simple translation of view->date (uses numerical representation of months)
//# to do: convert to "13 июля 2016"
config.macros.view.views.date = function(value,place,params,wikifier,paramString,tiddler) {
	value = Date.convertFromYYYYMMDDHHMM(value);
	dateString = params[2] ? value.formatString(params[2]) : value.toLocaleDateString(); // "13.07.2016"
	createTiddlyText(place,dateString);
}

// "created" above tiddler's title
config.views.wikified.createdPrompt = "создано";
//}}}
/***
|Name|SaveAsPlugin|
|Source||
|Documentation||
|Version|2.10.1|
|Author|Eric Shulman|
|Tweaked by|Yakov Litvin (after v2.7.1)|
|License||
|~CoreVersion|2.1|
|Type|plugin|
|Description|Save current document to another path/filename|
***/
//{{{
version.extensions.SaveAsPlugin = { major: 2, minor: 10, revision: 1, date: new Date(2016,5,6) };

/* YL tweak+ */ // code adapted from SharedTiddlersPlugin to handle relative paths
var getPathFromURL = function(url) {

	return (url.lastIndexOf("/") > -1) ?
		url.substr(0, url.lastIndexOf("/") + 1) : "";
};

// limitedly turns URI (URL) reference into an absolute URI (URL) and windows paths into URL
var resolveURL = function(url) {

	if (url.search(/^((http(s)?)|(file)):/) != 0) {
	// no protocol prefix..

		if (url.search(/^(.\:\\)|(\\\\)|(\/)/) != 0) // is relative?
		// as Unix filesystem root is "/", urls starting with it are not considered as relative

			url = getPathFromURL(document.location.toString()) + url;
		else
		// "url" is an "absolute" path to a local file. Prefix it with file://

			url = "file://" + url;

		// replace every \ by a /, to cover Windows style pathes
		url = url.replace(/\\/mg,"/");
	}
	return url;
};
/* YL tweak= */

config.macros.saveAs = {
	label: 'save as...',
	labelparam: 'label:',
	prompt: 'Save current document to a different path/file',
	promptparam: 'prompt:',
	filePrompt: 'Please select or enter a target path/filename',
	targetparam: 'target:',
	defaultFilename: 'new.html',
	filenameparam: 'filename:',
	currfilekeyword: 'here',
	typeparam: 'type:',
	type_TW: 'tw', type_PS: 'ps', type_TX: 'tx', type_CS: 'cs', type_NF: 'nf', // file type tokens
	type_map: {
		tiddlywiki:'tw', tw:'tw', wiki: 'tw',
		purestore: 'ps', ps:'ps', store:'ps',
		plaintext: 'tx', tx:'tx', text: 'tx',
		comma:     'cs', cs:'cs', csv:  'cs',
		newsfeed:  'nf', nf:'nf', xml:  'nf', rss:'nf'
	},
/* YL tweak */	filterExpression: 'filter:',
	limitparam: 'limit:',
/* YL tweak */	scriptparam: 'process:',
	replaceparam: 'replace',
	mergeparam: 'merge',
	quietparam: 'quiet',
	openparam: 'open',
	askParam: 'ask',
	hereParam: 'here',
	askMsg: "Enter a tag filter (use * for all tiddlers, 'none' for blank document)",
	hereMsg: 'Enter a tiddler title',
	emptyParam: 'none',
	confirmmsg: "Found %0 tiddlers matching\n\n'%1'\n\nPress OK to proceed",
	mergeprompt: '%0\nalready contains tiddler definitions.\n'
		+'\nPress OK to add new/revised tiddlers to current file contents.'
		+'\nPress Cancel to completely replace file contents',
	mergestatus: 'Merged %0 new/revised tiddlers and %1 existing tiddlers',
	okmsg: '%0 tiddlers written to %1',
	failmsg: 'An error occurred while creating %1',
	filter: '',
	handler: function(place,macroName,params) {
		if ((params[0]||'').startsWith(this.labelparam))
			var label=params.shift().substr(this.labelparam.length);
		if ((params[0]||'').startsWith(this.promptparam))
			var prompt=params.shift().substr(this.promptparam.length);
		if ((params[0]||'').startsWith(this.targetparam))
			var target=params.shift().substr(this.targetparam.length);
		if ((params[0]||'').startsWith(this.filenameparam))
			var filename=params.shift().substr(this.filenameparam.length);
		if ((params[0]||'').startsWith(this.typeparam))
			var filetype=this.type_map[params.shift().substr(this.typeparam.length).toLowerCase()];
/* YL tweak */	if ((params[0]||'').startsWith(this.filterExpression))
			var filterExpression=params.shift().substr(this.filterExpression.length);
		if ((params[0]||'').startsWith(this.limitparam))
			var limit=params.shift().substr(this.limitparam.length);
/* YL tweak */	if ((params[0]||'').startsWith(this.scriptparam))
			var script=params.shift().substr(this.scriptparam.length);
		var q=((params[0]||'')==this.quietparam);   if (q) params.shift();
		var o=((params[0]||'')==this.replaceparam); if (o) params.shift();
		var m=((params[0]||'')==this.mergeparam);   if (m) params.shift();
		var a=((params[0]||'')==this.openparam);    if (a) params.shift();
		var btn=createTiddlyButton(place,label||this.label,prompt||this.prompt,
			function(){ config.macros.saveAs.go( this.getAttribute('target'),
				this.getAttribute('filename'), this.getAttribute('filetype'),
				this.getAttribute('filter'),
/* YL tweak */			this.getAttribute('filterExpression'),
				this.getAttribute('limit'),
/* YL tweak */			this.getAttribute('modScript'),
				this.getAttribute('quiet')=='true',
				this.getAttribute('overwrite')=='true',
				this.getAttribute('merge')=='true',
				this.getAttribute('autoopen')=='true',
				this);
				return false;
			});
		if (target) btn.setAttribute('target',target);
		if (filename) btn.setAttribute('filename',filename);
		btn.setAttribute('filetype',filetype||this.type_TW);
/* YL tweak */	btn.setAttribute('filterExpression',filterExpression||'');
		btn.setAttribute('filter',params.join(' '));
/* YL tweak */	btn.setAttribute('modScript',script||'');
		btn.setAttribute('limit',limit||0);
		btn.setAttribute('quiet',q?'true':'false');
		btn.setAttribute('overwrite',o?'true':'false');
		btn.setAttribute('merge',m?'true':'false');
		btn.setAttribute('autoopen',a?'true':'false');
	},
	go: function(target,filename,filetype,filter,filterExpr,limit,script,quiet,overwrite,merge,autoopen,here){
/* YL tweak */	// added the "filterExpr" and "script" params
		var cm=config.messages; // abbreviation
		var cms=config.macros.saveAs; // abbreviation
		if (window.location.protocol!='file:') // make sure we are local
			{ displayMessage(cm.notFileUrlError); return; }

		// get tidders, confirm filtered results
/* YL tweak+ */	filter = filter.length ? "[tag["+filter+"]]" : null;
/* YL tweak= */	filterExpr = filterExpr.length ? filterExpr + (filter ? " "+filter : "") : (filter ? filter : "");
		var tids=cms.selectTiddlers(filterExpr,here);
		if (tids===false) return; // cancelled by user
		if (cms.filter!=cms.emptyParam && cms.filter.length && !quiet)
			if (!confirm(cms.confirmmsg.format([tids.length,cms.filter]))) return;

/* YL tweak+ */	// switch tiddlers from store to their copies, launch the script
		for(var i = 0; i < tids.length; i++) {
			tids[i] = jQuery.extend(true, new Tiddler(), tids[i]);
			// keep the original titles for correct markup blocks updating and other things
			tids[i].orig_title = tids[i].title;
		}
		// wrap the script so that the "tids" var can be used even if the code is minified
		script = "config.macros.saveAs.f = function(tids) {" + script + "};"
		eval(script);
/* YL tweak= */	this.f(tids);

		// get target path/filename
		if (!filetype) filetype=this.type_TW;
		target=target||cms.getTarget(filename,filetype==this.type_TX?'txt':filetype==this.type_CS?'csv':'html');
		if (!target) return; // cancelled by user

/* YL tweak+ */	var link = resolveURL(target);
/* YL tweak= */	target = getLocalPath(link);
		var samefile=link==decodeURIComponent(window.location.href);
		var p=getLocalPath(document.location.href);
		if (samefile) {
			if (config.options.chkSaveBackups)
				{ var t=loadOriginal(p);if(t)saveBackup(p,t); }
			if (config.options.chkGenerateAnRssFeed && saveRss instanceof Function)
				saveRss(p);
		}
		var notes='';
		var total={val:0};
		var out=this.assembleFile(target,filetype,tids,limit||0,notes,quiet,overwrite,merge,total);
		var ok=saveFile(target,out);
		if (ok && autoopen) {
			if (!samefile) window.open(link).focus();
			else { store.setDirty(false); window.location.reload(); }
		}
		if (!quiet || !(ok && autoopen))
			displayMessage((ok?this.okmsg:this.failmsg).format([total.val,target]),link);
	},
	selectTiddlers: function(filter,here) {
		var cms=config.macros.saveAs; // abbreviation
		var tids=[]; cms.filter=filter||'';
		if (filter==cms.emptyParam)
			return tids;
		if (filter==config.macros.saveAs.hereParam) {
			var here=story.findContainingTiddler(here);
			if (here) var tid=here.getAttribute('tiddler');
			else var tid=prompt(config.macros.saveAs.hereMsg,'');
			while (tid && !store.tiddlerExists(tid)) {
				var err='"'+tid+'" not found.\nPlease try again.\n\n';
				var tid=prompt(err+config.macros.saveAs.hereMsg,tid);
			}
			if (!tid) return false;  // cancelled by user
			return [store.getTiddler(tid)];
		}
		if (filter==config.macros.saveAs.askParam) {
			filter=prompt(config.macros.saveAs.askMsg,'');
			if (!filter) return false;  // cancelled by user
			cms.filter=filter=='*'?'':filter;
		}
		if (!filter||!filter.length||filter=='*') tids=store.getTiddlers('title');
/* YL tweak */	else tids=store.filterTiddlers(filter);
		return tids;
	},
	getTarget: function(defName,defExt) {
		var cms=config.macros.saveAs; // abbreviation
		// get new target path/filename
		var newPath=getLocalPath(window.location.href);
		var slashpos=newPath.lastIndexOf('/'); if (slashpos==-1) slashpos=newPath.lastIndexOf('\\'); 
		if (slashpos!=-1) newPath=newPath.substr(0,slashpos+1); // trim filename
		if (!defName||!defName.length) { // use current filename as default
			var p=getLocalPath(window.location.href);
			var s=p.lastIndexOf('/'); if (s==-1) s=p.lastIndexOf('\\'); 
			if (s!=-1) defName=p.substr(s+1);
		}
		var defFilename=(defName||cms.defaultFilename).replace(/.html$/,'.'+defExt);
		var target=cms.askForFilename(cms.filePrompt,newPath,defFilename,defExt);
		if (!target) return; // cancelled by user
		// if specified file does not include a path, assemble fully qualified path and filename
		var slashpos=target.lastIndexOf('/'); if (slashpos==-1) slashpos=target.lastIndexOf('\\');
		if (slashpos==-1) target=target+(defName||cms.defaultFilename).replace(/.html$/,'.'+defExt);
		return target;
	},
	askForFilename: function(msg,path,file,defExt) {
/*		if(window.Components) { // moz
			try {
				netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
				var nsIFilePicker = window.Components.interfaces.nsIFilePicker;
				var picker = Components.classes['@mozilla.org/filepicker;1'].createInstance(nsIFilePicker);
				picker.init(window, msg, nsIFilePicker.modeSave);
				var thispath = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
				thispath.initWithPath(path);
				picker.displayDirectory=thispath;
				picker.defaultExtension=defExt||'html';
				picker.defaultString=file;
				picker.appendFilters(nsIFilePicker.filterAll|nsIFilePicker.filterText|nsIFilePicker.filterHTML);
				if (picker.show()!=nsIFilePicker.returnCancel) var result=picker.file.path;
			}
			catch(e) { alert('error during local file access: '+e.toString()) }
		} else {
*/			try { // IE, XP/Vista only
				var s = new ActiveXObject('UserAccounts.CommonDialog');
				s.Filter='All files|*.*|Text files|*.txt|HTML files|*.htm;*.html|';
				s.FilterIndex=(defExt=='txt')?2:3; // default to HTML files;
				s.InitialDir=path;
				s.FileName=file;
				if (s.showOpen()) var result=s.FileName;
			}
			catch(e) { var result=prompt(msg,path+file); } // fallback for other browsers
//		}
		return result;
	},
	plainTextHeader:
		 'Source:\n\t%0\n'
		+'Title:\n\t%1\n'
		+'Subtitle:\n\t%2\n'
		+'Created:\n\t%3 by %4\n'
		+'Application:\n\tTiddlyWiki %5 / %6 %7\n\n',
	plainTextTiddler:
		'- - - - - - - - - - - - - - -\n'
		+'|     title: %0\n'
		+'|   created: %1\n'
		+'|  modified: %2\n'
		+'| edited by: %3\n'
		+'|      tags: %4\n'
		+'- - - - - - - - - - - - - - -\n'
		+'%5\n',
	plainTextFooter:
		'',
	newsFeedHeader:
		 '<'+'?xml version="1.0"?'+'>\n'
		+'<rss version="2.0">\n'
		+'<channel>\n'
		+'<title>%1</title>\n'
		+'<link>%0</link>\n'
		+'<description>%2</description>\n'
		+'<language>en-us</language>\n'
		+'<copyright>Copyright '+(new Date().getFullYear())+' %4</copyright>\n'
		+'<pubDate>%3</pubDate>\n'
		+'<lastBuildDate>%3</lastBuildDate>\n'
		+'<docs>http://blogs.law.harvard.edu/tech/rss</docs>\n'
		+'<generator>TiddlyWiki %5 / %6 %7</generator>\n',
	newsFeedTiddler:
		'\n%0\n',
	newsFeedFooter:
		'</channel></rss>',
	pureStoreHeader:
		 '<html><body>'
		+'<style type="text/css">'
		+'	#storeArea {display:block;margin:1em;}'
		+'	#storeArea div {padding:0.5em;margin:1em;border:2px solid black;height:10em;overflow:auto;}'
		+'	#pureStoreHeading {width:100%;text-align:left;background-color:#eeeeee;padding:1em;}'
		+'</style>'
		+'<div id="pureStoreHeading">'
		+'	TiddlyWiki "PureStore" export file<br>'
		+'	Source'+': <b>%0</b><br>'
		+'	Title: <b>%1</b><br>'
		+'	Subtitle: <b>%2</b><br>'
		+'	Created: <b>%3</b> by <b>%4</b><br>'
		+'	TiddlyWiki %5 / %6 %7<br>'
		+'	Notes:<hr><pre>%8</pre>'
		+'</div>'
		+'<div id="storeArea">',
	pureStoreTiddler:
		'%0\n%1',
	pureStoreFooter:
		'</div><!--POST-BODY-START-->\n<!--POST-BODY-END--></body></html>',
	assembleFile: function(target,filetype,tids,limit,notes,quiet,overwrite,merge,total) {
		var revised='';
		var now = new Date().toLocaleString();
		var src=convertUnicodeToUTF8(document.location.href);
		var title = convertUnicodeToUTF8(wikifyPlain('SiteTitle').htmlEncode());
		var subtitle = convertUnicodeToUTF8(wikifyPlain('SiteSubtitle').htmlEncode());
		var user = convertUnicodeToUTF8(config.options.txtUserName.htmlEncode());
		var twver = version.major+'.'+version.minor+'.'+version.revision;
		var v=version.extensions.SaveAsPlugin; var pver = v.major+'.'+v.minor+'.'+v.revision;
		var headerargs=[src,title,subtitle,now,user,twver,'SaveAsPlugin',pver,notes];
		switch (filetype) {
			case this.type_TX: // plain text
				var header=this.plainTextHeader.format(headerargs);
				var footer=this.plainTextFooter;
				break;
			case this.type_CS: // comma-separated
				var fields={};
				for (var i=0; i<tids.length; i++) for (var f in tids[i].fields) fields[f]=f;
				var names=['title','created','modified','modifier','tags','text'];
				for (var f in fields) names.push(f);
				var header=names.join(',')+'\n';
				var footer='';
				break;
			case this.type_NF: // news feed (XML)
				headerargs[0]=store.getTiddlerText('SiteUrl','');
				var header=this.newsFeedHeader.format(headerargs);
				var footer=this.newsFeedFooter;
				tids=store.sortTiddlers(tids,'-modified');
				break;
			case this.type_PS: // PureStore (no code)
				var header=this.pureStoreHeader.format(headerargs);
				var footer=this.pureStoreFooter;
				break;
			case this.type_TW: // full TiddlyWiki
			default:
				var currPath=getLocalPath(window.location.href);
				var original=loadFile(currPath);
				if (!original) { alert(config.messages.cantSaveError); return; }
				var posDiv = locateStoreArea(original);
				if (!posDiv) { alert(config.messages.invalidFileError.format([currPath])); return; }
				var header = original.substr(0,posDiv[0]+startSaveArea.length)+'\n';
				var footer = '\n'+original.substr(posDiv[1]);
				break;
		}
		if (parseInt(limit)!=0) tids=tids.slice(0,limit);
		var out=this.getData(target,filetype,tids,quiet,overwrite,merge,fields);
		var revised = header+convertUnicodeToUTF8(out.join('\n'))+footer;
		// if full TW, insert page title and language attr, and reset MARKUP blocks as needed...

/* YL tweak+ */	// for that, temporary change the store so that already processed tiddlers are used
		var createContextStore = function(tids) {

			var contextStore = new TiddlyWiki();

			// copy all the tiddlers:
			store.forEachTiddler(function(title,tiddler){ contextStore.addTiddler(tiddler); });
			// substitute original tiddlers with their processed "images":
			for(var i = 0; i < tids.length; i++) {
				contextStore.deleteTiddler(tids[i].orig_title);
				contextStore.addTiddler(tids[i]);
			}
			return contextStore;
/* YL tweak= */	}

		if (filetype==this.type_TW) {
/* YL tweak+ */		// change the store so that in this context already processed tiddlers are used:
			var mainStore = store;
/* YL tweak= */		store = createContextStore(tids);
			var newSiteTitle=convertUnicodeToUTF8(getPageTitle()).htmlEncode();
			revised=revised.replaceChunk('<title'+'>','</title'+'>',' ' + newSiteTitle + ' ');
			revised=updateLanguageAttribute(revised);
			var titles=[]; for (var i=0; i<tids.length; i++) titles.push(tids[i].title);
			revised=updateMarkupBlock(revised,'PRE-HEAD',
				titles.contains('MarkupPreHead')? 'MarkupPreHead' :null);
			revised=updateMarkupBlock(revised,'POST-HEAD',
				titles.contains('MarkupPostHead')?'MarkupPostHead':null);
			revised=updateMarkupBlock(revised,'PRE-BODY',
				titles.contains('MarkupPreBody')? 'MarkupPreBody' :null);
			revised=updateMarkupBlock(revised,'POST-SCRIPT',
				titles.contains('MarkupPostBody')?'MarkupPostBody':null);
/* YL tweak */		// return the original store:
/* YL tweak */		store = mainStore;
		}
		total.val=out.length;
		return revised;
	},
	getData: function(target,filetype,tids,quiet,overwrite,merge,fields) {
		// output selected tiddlers and gather list of titles (for use with merge)
		var out=[]; var titles=[];
		var url=store.getTiddlerText('SiteUrl','');
		for (var i=0; i<tids.length; i++) {
			out.push(this.formatItem(store,filetype,tids[i],url,fields));
			titles.push(tids[i].title);
		}
		// if TW or PureStore format, ask to merge with existing tiddlers (if any)
		if (filetype==this.type_TW || filetype==this.type_PS) {
			if (overwrite) return out; // skip merge... forced overwrite
			var txt=loadFile(target);
			if (txt && txt.length) {
				var remoteStore=new TiddlyWiki();
				if (version.major+version.minor*.1+version.revision*.01<2.52) txt=convertUTF8ToUnicode(txt);
				if (remoteStore.importTiddlyWiki(txt) && (merge||confirm(this.mergeprompt.format([target])))) {
					var existing=remoteStore.getTiddlers('title');
					for (var i=0; i<existing.length; i++)
						if (!titles.contains(existing[i].title))
							out.push(this.formatItem(remoteStore,filetype,existing[i],url));
					if (!quiet) displayMessage(this.mergestatus.format([tids.length,out.length-tids.length]));
				}
			}
		}
		return out;
	},
	formatItem: function(s,f,t,u,fields) {
		if (f==this.type_TW)
			var r=s.getSaver().externalizeTiddler(s,t);
		if (f==this.type_PS)
			var r=this.pureStoreTiddler.format([t.title,s.getSaver().externalizeTiddler(s,t)]);
		if (f==this.type_NF)
			var r=this.newsFeedTiddler.format([t.saveToRss(u)]);
		if (f==this.type_TX)
			var r=this.plainTextTiddler.format([t.title, t.created.toLocaleString(), t.modified.toLocaleString(),
				t.modifier, String.encodeTiddlyLinkList(t.tags), t.text]);
		if (f==this.type_CS) {
			function toCSV(t) { return '"'+t.replace(/"/g,'""')+'"'; } // always encode CSV
			var out=[ toCSV(t.title), toCSV(t.created.toLocaleString()), toCSV(t.modified.toLocaleString()),
				toCSV(t.modifier), toCSV(String.encodeTiddlyLinkList(t.tags)), toCSV(t.text) ];
			for (var f in fields) out.push(toCSV(t.fields[f]||''));
			var r=out.join(',');
		}
		return r||'';
	}
};
//}}}
//{{{
// automatically add saveAs to backstage
config.tasks.saveAs = {
	text: 'saveAs',
	tooltip: config.macros.saveAs.prompt,
	action: function(){ clearMessage(); config.macros.saveAs.go(); }
}
config.backstageTasks.splice(config.backstageTasks.indexOf('save')+1,0,'saveAs');
//}}}
/***
|''Version''|0.1.1|
|''Author''|Yakov Litvin|
|''Description''|This plugin fixes [[issue 146|https://github.com/TiddlyWiki/tiddlywiki/issues/146]] and ... [{{{TiddlyWiki.prototype.saveTiddler}}}]|
|''Overwriting notice''|This plugin overwrites the {{{Story.prototype.saveTiddler}}} and {{{TiddlyWiki.prototype.saveTiddler}}} methods. So any plugin that hijack any of these should use the {{{Requires}}} slice mentioning this plugin (if it is installed); plugins that overwrite these functions will likely cause conflicts/won't work.|
***/
//{{{
Story.prototype.saveTiddler = function(title,minorUpdate)
{
	var tiddlerElem = this.getTiddler(title);
	if(!tiddlerElem)
		return null;

	var fields = {};
	this.gatherSaveFields(tiddlerElem,fields);
	var newTitle = fields.title || title;
	if(!store.tiddlerExists(newTitle)) {
		newTitle = newTitle.trim();
		var creator = config.options.txtUserName;
	}
	if(store.tiddlerExists(newTitle) && newTitle != title) {
		if(!confirm(config.messages.overwriteWarning.format([newTitle.toString()])))
			return null;
// was:			title = newTitle;
//		title = newTitle; // this line causes the bug
// this line is to be deleted; learn when and why this was introduced
// the next 4 usages of "title" marked with "+>" should work fine without this
	}
/*+>*/	if(newTitle != title)
		this.closeTiddler(newTitle,false);
	tiddlerElem.id = this.tiddlerId(newTitle);
	tiddlerElem.setAttribute("tiddler",newTitle);
	tiddlerElem.setAttribute("template",DEFAULT_VIEW_TEMPLATE);
	tiddlerElem.setAttribute("dirty","false");
	if(config.options.chkForceMinorUpdate)
		minorUpdate = !minorUpdate;
	if(!store.tiddlerExists(newTitle))
		minorUpdate = false;
	var newDate = new Date();
/*+>*/	if(store.tiddlerExists(title)) {
/*+>*/		var t = store.fetchTiddler(title);
		var extendedFields = t.fields;
		creator = t.creator;
	} else
		extendedFields = merge({},config.defaultCustomFields);
	
	for(var n in fields)
		if(!TiddlyWiki.isStandardField(n))
			extendedFields[n] = fields[n];

/*+>*/	var tiddler = store.saveTiddler(title,newTitle,fields.text,minorUpdate ? undefined : config.options.txtUserName,minorUpdate ? undefined : newDate,fields.tags,extendedFields,null,null,creator);
	autoSaveChanges(null,[tiddler]);
	return newTitle;
};
//}}}

// // Ещё один, более мелкий фикс (делает более ясным код {{{TiddlyWiki.prototype.saveTiddler}}}, исправляет назначение custom fields в случае, когда ...); важен для renaming в SMP!
//{{{
TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created,creator)
{
	var tiddler;
	if(title instanceof Tiddler) {
		tiddler = title;
		title = tiddler.title;
	} else
		tiddler = this.fetchTiddler(title);

	if(tiddler) {
		created = created || tiddler.created; // Preserve created date
		creator = creator || tiddler.creator;
		this.deleteTiddler(title);
	} else {
		created = created || modified;
		tiddler = new Tiddler();
	}
	fields = merge(merge(tiddler ? tiddler.fields : {},fields), config.defaultCustomFields,true);
	tiddler.set(newTitle,newBody,modifier,modified,tags,created,fields,creator);

	this.addTiddler(tiddler);
	if(clearChangeCount)
		tiddler.clearChangeCount();
	else
		tiddler.incChangeCount();

	if(title != newTitle)
		this.notify(title,true);
	this.notify(newTitle,true);
	if(window.location.protocol == "file:")
		this.setDirty(true);
	return tiddler;
};
//}}}
/***
|''Version''|1.6.5|
***/
//{{{
// styling helpers
var getStylesFromSection = function(sectionName) {
	var css = store.getTiddlerText("SetCommonStylesPlugin##"+sectionName,"");
	return css.replace("{{{","/"+"*{{{*"+"/").replace("}}}","/"+"*}}}*"+"/");
};
var setCssShadow = function(sectionName,shadowName) {
	config.shadowTiddlers[shadowName] = getStylesFromSection(sectionName);
	store.addNotification(shadowName, refreshStyles);

	store.addNotification("ColorPalette",function(smth,doc) {
		refreshStyles(shadowName,doc);
	});
};

// set styles
setCssShadow("Semantics",		"CommonSemanticsStyleSheet");
setCssShadow("Representation",		"CommonRepresenationStyleSheet");
setCssShadow("Representation tools",	"CommonRepresToolsStyleSheet");
//if(jQuery.browser.mozilla) // now done via CSS
	setCssShadow("FireFox CSS",	"FireFoxFixesStyleSheet");

csp_old_wikify = wikify;
wikify = function(source,output,highlightRegExp,tiddler) {
	csp_old_wikify.apply(this,arguments);
	jQuery(".NTerm").each(function(){
		// avoid multiple wrapping because of ~ wikifications:
		if(!jQuery(this.parentNode).hasClass("NTermWrap"))
			jQuery(this).wrap("<span class='NTermWrap'></span>");
	});
	jQuery(".LimitGeneral").each(function(){
		// avoid multiple wrapping because of ~ wikifications:
		if(!jQuery(this.parentNode).hasClass("LimitGeneralWrap"))
			jQuery(this).wrap("<span class='LimitGeneralWrap'></span>");
	});
};
// to get green underlined, add a wrapper with the NTermWrap class; apply the styling to both wrappers (.NTermWrap) and the element itself (.NTerm)

// hide message area on click elsewhere (useful for touchscreen devices
var positiveClearMessage = function() {
	clearMessage();
	return true;
};
jQuery("body").on("click",positiveClearMessage);
if(!config.extensions.postponeMsg) {
	config.extensions.postponeMsg = true;
	config.extensions.orig_displayMessage = displayMessage;
	displayMessage = function(a,b) {
		var doDisplay = function() { config.extensions.orig_displayMessage(a,b); };
		setTimeout(doDisplay,100);
	};
}
//}}}
/***
!!!Semantics
{{{
div[tags~="code"] .editor, #tiddlerStyleSheet .editor,
div[tags~="systemConfig"] .editor
		{ font-family: monospace; }

.PoG, .PoGc	{ color: green; }

.DDn, .DDnc	{ color: purple; }

.FpG, .FpGc	{ color: blue; }

.PoGc:before, .DDnc:before, .FpGc:before,
.c:before	{ content: "["; color: [[ColorPalette::Foreground]]; }

.PoGc:after, .DDnc:after, .FpGc:after,
.c:after	{ content: "]"; color: [[ColorPalette::Foreground]]; }

.NTerm		  { color: [[ColorPalette::Foreground]]; font-weight: normal; }
.NTermWrap	  { color: #00aa00; text-decoration: underline; font-weight: bold; }
.LimitGeneral	  { color: [[ColorPalette::Foreground]]; font-weight: normal; }
.LimitGeneralWrap { color: #0000ff; text-decoration: underline; font-weight: bold; }
}}}
!!!Semantics details
* possibilities of growth (including comments): {{{.PoG}}}, {{{.PoGc}}}
* done [written] to be done, but not ideally: {{{.DDn}}}, {{{.DDnc}}}
* frozen possibilities of growth or things done/written not ideally: {{{.FpG}}}, {{{.FpGc}}}
* just comments or implied things: {{{.c}}}
* {{{code}}} tag is to be used in tiddlers with code (CSS, ~JavaScript etc) which changes representation when editing
* {{PoGc{лучше перевести все описания на русский -- слишком много неопределённости}}}
* {{PoGc{стоит что-то придумать с {{{[[ColorPalette::Foreground]]}}} на случай night mode}}}
!!!Representation
{{{
#sidebarOptions .sliderPanel	{ background-color:[[ColorPalette::Background]] !important;
				  font-size: 95% !important; }
#tiddlerStyleSheet .viewer pre	{ clear: both !important; }

.headerForeground,
.headerShadow			{ padding-top: 1em !important; }

.viewer .tabContents		{ background-color: inherit; }
}}}
!!!Representation details
* ...
* {{{margin-bottom}}} of {{{#displayArea}}} -- to be able to keep the content on the eye level
!!!Representation tools
{{{
table.borderless,
table.borderless tr,
table.borderless td,
table.borderless th	{ border: 0 !important; }

div[tags~="hideTagged"]
.tagging		{ display: none; }
div[tags~="hideTags"]
.tagged			{ display: none; }
}}}
!!!Representation tools details
* ...
!!!FireFox CSS
{{{
@-moz-document url-prefix() {
  pre, code, div[tags~="code"] .editor,
  div[tags~="systemConfig"] .editor,
  #tiddlerStyleSheet .editor
	{ font-family: 'Droid Sans Mono', Consolas !important; font-size: 100% !important; }
}
}}}
!!!!
***/
/***
|Version|0.1|
|Short status|<<insertEditable container:"@stateComment" size:max cell>>|
***/
//{{{
// adapted from the 2.7.1 core:

// Sets the value of the given field of the tiddler to the value.
// Setting an ExtendedField's value to null or undefined removes the field.
// Setting a namespace to undefined removes all fields of that namespace.
// The fieldName is case-insensitive.
// All values will be converted to a string value.
TiddlyWiki.prototype.setValue = function(tiddler,fieldName,value,noNotify)
{
	TiddlyWiki.checkFieldName(fieldName);
	var t = this.resolveTiddler(tiddler);
	if(!t)
		return;
	fieldName = fieldName.toLowerCase();
	var isRemove = (value === undefined) || (value === null);
	var accessor = TiddlyWiki.standardFieldAccess[fieldName];
	if(accessor) {
		if(isRemove)
			// don't remove StandardFields
			return;
		var h = TiddlyWiki.standardFieldAccess[fieldName];
		if(!h.set(t,value))
			return;
	} else {
		var oldValue = t.fields[fieldName];
		if(isRemove) {
			if(oldValue !== undefined)
				// deletes a single field
				delete t.fields[fieldName];
			else {
				// no concrete value is defined for the fieldName
				// so we guess this is a namespace path.
				// delete all fields in a namespace
				var re = new RegExp("^"+fieldName+"\\.");
				var dirty = false;
				var n;
				for(n in t.fields)
					if(n.match(re)) {
						delete t.fields[n];
						dirty = true;
					}
				if(!dirty)
					return;
			}
		} else {
			// the "normal" set case. value is defined (not null/undefined)
			// For convenience provide a nicer conversion Date->String
			value = value instanceof Date ? value.convertToYYYYMMDDHHMMSSMMM() : String(value);
			if(oldValue == value)
				return;
			t.fields[fieldName] = value;
		}
	}
	// When we are here the tiddler/store really was changed.
	if(!noNotify)
		this.notify(t.title,true);
	if(!fieldName.match(/^temp\./))
		this.setDirty(true);
};
//}}}
/***
|Version|0.11.1|
|Requires|ForEachTiddlerPlugin SetFieldPlugin|
|~|ForEachTiddlerPlugin is not required for the operation of the plugin, but if FETP is present, it should be evaluated before this plugin; also, there's no other simple way to use this plugin aside with FETP; SetFieldPlugin is required for quality (removes extra refreshing which quickens some actions and removes some representation bugs|
|Documentation|[[SetManagerPluginInfo]]|
|Author|Yakov Litvin|
***/

//{{{
// for performance improving/debugging
var timeTester = {
	startTime: null,
	init: function() {
		this.startTime = new Date();
	},
	timePoints: [],
	calcElapsedTime: function() {
		var time = new Date() - this.startTime;
		this.timePoints.push(time);
		return time;
	}
};

// --------------------------------------------------------------------------------
// sort counters methods ("S" stands for "sort")
Tiddler.prototype.getSCounterPlace = function(storageName)
{
	storageName = storageName || config.macros.itemMenu.defaultField
	var withFieldNameRegExp = /(.*?)@@(.*)/,
	    withFieldNameMatch  = withFieldNameRegExp.exec(storageName);
	return {
		storageName: withFieldNameMatch ? withFieldNameMatch[1] : null,
		fieldName: withFieldNameMatch ? withFieldNameMatch[2] : storageName
	}
};
Tiddler.prototype.getRawSData = function(fieldName)
{
	fieldName = fieldName || config.macros.itemMenu.defaultField;

	if(!this.getIncludeURL || !this.getIncludeURL())
		return store.getValue(this,fieldName);
};
Tiddler.prototype.getSCounter = function(fieldName)
{
	var storageData  = this.getSCounterPlace(fieldName),
	    storageName  = storageData.storageName,
	    storageField = storageData.fieldName,
	    indexText;

	if(!this.getIncludeURL || !this.getIncludeURL())
		return parseInt(this.getRawSData(storageField));

	// for included tiddlers use a separate tiddler as a stored index
	if(storageName) {
		var storageTiddler = store.fetchTiddler(storageName);
		indexText = storageTiddler ?
				storageTiddler.getRawSData(storageField) : "";
	} else
		indexText = store.getTiddlerText(storageField);

	// find the line in the index which describes the tiddler, if present
	var indexLineRegExp = config.macros.itemMenu.getIndexLineRegExp(this.title),
	    indexMatch      = indexLineRegExp.exec(indexText);

	return indexMatch ? parseInt(indexMatch[1]) : undefined;
};
Tiddler.prototype.setRawSData = function(fieldName,value)
{
	fieldName = fieldName || config.macros.itemMenu.defaultField;

	// reduced version of store.setValue(this,fieldName,value) for SData
	TiddlyWiki.checkFieldName(fieldName);
	fieldName = fieldName.toLowerCase();
	if(TiddlyWiki.standardFieldAccess[fieldName])
		return;

	if(this.fields[fieldName] == value)
		return;
	this.fields[fieldName] = ""+value; // as a String, only string values are stored

	store.setDirty(true);
};
Tiddler.prototype.updateSIndex = function(indexText,value)
{
	// find the line in the index which describes the tiddler, if present
	var indexLineRegExp = config.macros.itemMenu.getIndexLineRegExp(this.title),
	    indexMatch      = indexLineRegExp.exec(indexText);

	var newIndexLine = config.macros.itemMenu.createIndexLine(this.title,value);
	if(indexMatch)
		indexText = indexText.replace(indexLineRegExp,newIndexLine);
	else
		indexText += (newIndexLine+"\n");
	return indexText;
};
Tiddler.prototype.setSCounter = function(fieldName,value)
{
	var storageData  = this.getSCounterPlace(fieldName),
	    storageName  = storageData.storageName,
	    storageField = storageData.fieldName,
	    indexText;

	if(!this.getIncludeURL || !this.getIncludeURL()) {
		this.setRawSData(storageField,value);
		return;
	};

	// for included tiddlers use a separate tiddler as a stored index
	// for orderField@@tiddlerName syntax, use the storageName tiddler
	//  for storage, otherwise use the fieldName tiddler
	var indexTid = store.fetchTiddler(storageName || fieldName);
	if(!indexTid)
		indexTid = store.createTiddler(storageName || fieldName);

	if(storageName) {
		indexText = indexTid.getRawSData(storageField) || "";
		indexText = this.updateSIndex(indexText,value);
		indexTid.setRawSData(storageField,indexText);
	} else
		indexTid.text = this.updateSIndex(indexTid.text,value);
};
Tiddler.prototype.deleteSCounter = function(fieldName)
{
	fieldName = fieldName.toLowerCase();
	if(TiddlyWiki.standardFieldAccess[fieldName])
		return; // use of StandardFields is unlikely, but don't remove them anyway

	if(this.getSCounter(fieldName) !== undefined)
		delete this.fields[fieldName];
};
// --------------------------------------------------------------------------------

TiddlyWiki.prototype.saveModifiedTiddler = function(title, newTitle, newBody, tags, fields, clearChangeCount, created, creator) {
	var tidBeingChanged = (title instanceof Tiddler) ? title : this.fetchTiddler(title);
	title = tidBeingChanged.title;
	var conflictingTiddler = this.fetchTiddler(newTitle) || this.fetchTiddler(title);
	if(conflictingTiddler && conflictingTiddler != tidBeingChanged)
		if(!confirm("A tiddler named \""+title+"\" already exists. Do you want to overwrite it?"))
			return;

	return this.saveTiddler(title,newTitle,newBody,
		config.options.txtUserName,
		new Date(),
	  tags,fields,clearChangeCount,created,creator)
};

var preventOtherHandling = window.preventOtherHandling = function(e)
{
	// prevent propagation
	if (e.stopPropagation)
		e.stopPropagation();
	e.cancelBubble = true;

	// prevent browser action from firing
	if(e.preventDefault)
		e.preventDefault();
	e.returnValue = false;
	// see https://learn.javascript.ru/default-browser-action
}

// helps to avoid popup closing on event
var wrapNoClose = window.wrapNoClose = function(func)
{
	return function(ev)
	{
		if(func) func.apply(this,arguments);

		var e = ev || window.event; // support old IE
		if(!e)
			return false;

		preventOtherHandling(e);
	};
};

// helps to make the height of textareas appropriate (a working prototype)
//#  defaultHeight should be calced as a height of one line;
//#  maxHeight - not more than 1/2 or 3/4 of the screen
//#  also, better to get rid of shrinking scrollbar..
var adjustHeightToContent = function()
{
	var defaultHeight = 30;
	var maxHeight = 400;
	jQuery(this).height(defaultHeight);
	jQuery(this).height(Math.min(this.scrollHeight , maxHeight));
};

// --------------------------------------------------------------------------------
// constants for using with jQuery .which()
var $tab =	9,
    $enter =	13,
    $esc =	27,
    $delete =	46,
    $home =	36,
    $end =	35,
    $up =	38,
    $down =	40,
    $pgUp =     33,
    $pgDn =     34,
    $plus =	61,
    $r =	82,
    $e =	69,
    $t =	84,
    $i =	73,
    $x =	88;

// --------------------------------------------------------------------------------
// detecting touch-screens (see http://stackoverflow.com/a/4819886/3995261)
window.isOpenedOnTouchScreen = function()
{
	return !!('ontouchstart' in this);
}
//}}}
//{{{
config.macros.itemMenu =
{
	getIndexLineRegExp: function(tiddlerName) {
		return new RegExp(tiddlerName.escapeRegExp() + ": ([0-9]+)");
	},
	createIndexLine: function(tiddlerName, value) {
		return tiddlerName+": "+value;
	},
	defaultField: "orderCounter",
	itemMenuClass: "listMenuButton",
	sortByCounter: function(tiddlerArray, fieldName)
	{
		var defaultValue = -1; // undefinedUp (1000 for undefinedDown)
		return tiddlerArray.sort(function(t1,t2){
			var c1 = t1.getSCounter(fieldName), c2 = t2.getSCounter(fieldName);
			c1 = (!c1 && c1 != 0) ? defaultValue : c1;
			c2 = (!c2 && c2 != 0) ? defaultValue : c2;
			return  c1 - c2;
		});
	},

	currentlyDragged: [],
	setCurrentlyDragged: function(tidName,sortField,dropAction,itemMenuElement,
					onKeyDown,onKeyUp)
	{
		this.currentlyDragged.push({
			name:		 tidName,
			field:		 sortField,
			dropAction:	 dropAction,
			itemMenuElement: itemMenuElement,
			onKeyDown:	 onKeyDown,
			onKeyUp:	 onKeyUp
		});

		if(itemMenuElement) {
			jQuery(itemMenuElement).bind("keyup",onKeyUp);
			jQuery(itemMenuElement).bind("keydown",onKeyDown);
		}
	},
	clearCurrentlyDragged: function()
	{
		if(!this.currentlyDragged[0]) return;
		var i, context, upHandler, downHandler;
		for(i = 0; i < this.currentlyDragged.length; i++)
		{
			context = this.currentlyDragged[i];
			upHandler = context.onKeyUp;
			downHandler = context.onKeyDown;

			if(!context.itemMenuElement) continue;

			if(downHandler)
			    jQuery(context.itemMenuElement).unbind("keydown",downHandler);
			if(upHandler)
			    jQuery(context.itemMenuElement).unbind("keyup",upHandler);
		}
		this.currentlyDragged = [];

		jQuery(".buttonSortState").parent().parent().removeClass("selected");
		//# check if /\ grandparent is <tr> and remove only in that case
		jQuery(".buttonSortState").removeClass("buttonSortState");
	},
	getCurrentlyDragged: function() {
	//# for now, works only with the first selection (if multiple)
		return this.currentlyDragged[0] ? this.currentlyDragged[0].name : null;
	},
	getCurrentSourceListContext: function() {
	//# for now, works only with the first selection (if multiple)
		return this.currentlyDragged[0];
	},
	markSelected: function(itemMenu)
	{
		if(!itemMenu) return;
		jQuery(itemMenu).addClass("buttonSortState").focus();
		if(itemMenu.parentElement.tagName.toLowerCase() == "td")
			jQuery(itemMenu).parent().parent().addClass("selected");
	},
	ensureFocusOnCurrentlyDragged: function()
	{
		var currentlyDragged = this.getCurrentSourceListContext();
console.log("currentlyDragged:");console.log(currentlyDragged);
		if(!currentlyDragged) return;

		var itemMenu = currentlyDragged.itemMenuElement;
		// because of refreshing, itemMenu can be no longer attached to the ~root
		// element (document? html?). If that's the case, find the new one
		var newItemMenu, itemMenus = jQuery("."+this.itemMenuClass+
			'[filter=\''+itemMenu.getAttribute("filter")+'\']')
			.each(function(i,el){
				if(el.tiddler == itemMenu.tiddler)
					newItemMenu = el;
			});
		if(newItemMenu != itemMenu) {
			// remember actual element, reattach onkeyup, onkeydown handlers
			currentlyDragged.itemMenuElement = newItemMenu;
			jQuery(newItemMenu).bind("keyup",currentlyDragged.onKeyUp);
			jQuery(newItemMenu).bind("keydown",currentlyDragged.onKeyDown);
		}
		this.markSelected(newItemMenu);
	},

	actionStepsWithArguments: {},
	actionStepsWithoutArguments: {},
	applyActionStep: function(tiddler, actionStep, rootElement)
	{
		var actionRegExp = /(.\w+)\.\.(.*)/,
		    match = actionRegExp.exec(actionStep),
		    actionStepName;

		if(match) { // action with an argument
			for(actionStepName in this.actionStepsWithArguments)
				if(actionStepName == match[1])
					return this.actionStepsWithArguments[actionStepName](tiddler,match[2],rootElement);
			//# may be throw an error/warning?
			return;
		} else      // actionStep without arguments
			for(actionStepName in this.actionStepsWithoutArguments)
				if(actionStepName == actionStep)
					return this.actionStepsWithoutArguments[actionStepName](tiddler,rootElement);

		//# may be throw an error/warning?
		return;
	},
	parseAndApplyAction: function(tiddler, actionLine, rootElement, noNotify)
	{
		if(!tiddler || !actionLine)
			return;
		var actionStepsArray = actionLine.split(",,");

		for(var i = 0; i < actionStepsArray.length; i++)
			this.applyActionStep(tiddler,jQuery.trim(actionStepsArray[i]), rootElement);

		if(!noNotify)
			store.notify(tiddler.title,true);
	},
	parseAndSeparateActions: function(actionsLine)
	{
		var actionsArray = actionsLine.split(";;");
		if(actionsArray.length == 1 && !actionsArray[0].contains("::"))
			return jQuery.trim(actionsArray[0]);

		var actionMap = {},
		    name_and_action_RegExp = /(.+?)::(.*)/, match;
		for(var i = 0; i < actionsArray.length; i++)
		{
			match = name_and_action_RegExp.exec(actionsArray[i]);
			if(!match || !match[1] || !match[2])
				//# may be throw an error/warning?
				continue;
			actionMap[jQuery.trim(match[1])] = jQuery.trim(match[2]);
			actionMap["default"] = actionMap["default"] || jQuery.trim(match[2]);
		}
		return actionMap;
	},

	// moving helpers
	checkCounters: function(filter,field)
	{
		var tids = store.filterTiddlers(filter);
		tids = this.sortByCounter(tids, field);
	
		for(var i = 0; i < tids.length; i++)
			tids[i].setSCounter(field,i);

		return tids;
	},
	moveToArbitraryPlace: function(filter,tiddler,field,index,doCycle)
	{
		var tids     = this.checkCounters(filter,field), i,
		    tidIndex = tids.indexOf(tiddler),
		    tidsNum  = tids.length;

		// parse "top"/"bottom" values
		index = (index == "top") ? 0 : (index == "bottom" ? tidsNum-1 : index);
		if(doCycle) {
			index = index % tidsNum;
			index = (index < 0) ? (index + tidsNum) : index;
		}
		// do nothing in cases.. ("tidIndex < 0" = "tiddler is not in the list")
		if(tidIndex == index || tidIndex < 0 || index < 0 || index >= tids.length)
			return;

//timeTester.init()
//console.log("~middle of moveToArbitraryPlace: time elapsed (ms): " + timeTester.calcElapsedTime())
		// move items
		tiddler.setSCounter(field,index);
		if(tidIndex > index)
			for(i = index; i < tidIndex; i++)
				tids[i].setSCounter(field,i+1);
		else
			for(i = index; i > tidIndex; i--)
				tids[i].setSCounter(field,i-1);
//console.log("pre-end of moveToArbitraryPlace: time elapsed (ms): " + timeTester.calcElapsedTime())

		// refresh the list (order)
		store.notify(field,true);
//console.log("end of moveToArbitraryPlace: time elapsed (ms): "+timeTester.calcElapsedTime())
// ~200 ms for notifying / in which browser?
	},
	moveToTop: function(filter,tiddler,field)
		{ this.moveToArbitraryPlace(filter,tiddler,field,"top"); },
	moveToBottom: function(filter,tiddler,field)
		{ this.moveToArbitraryPlace(filter,tiddler,field,"bottom"); },

	// syntax: <<itemMenu tiddlerName filter field:fieldName addAction:actionSyntax
	//		dropAction:actionSyntax switchActions:actionsSyntax>>
	handler: function(place,macroName,params,wikifier,paramString,tiddler)
	{
		// parse params: tiddlerName, context filter; field, noConflicts
		var pParams = paramString.parseParams("pP",null,true,false,true),
		    tName   = pParams[0]["pP"][0], // name of the tid governed by itemMenu
		    tid     = store.fetchTiddler(tName),
		    filter  = pParams[0]["pP"][1], // tids among which the sorting is done
		    field   = getParam(pParams,"field",this.defaultField), // field which holds the counter value
		    addAction     = getParam(pParams,"addAction",""),     // 
		    dropAction    = getParam(pParams,"dropAction",""),    // 
		    switchActions = getParam(pParams,"switchActions",""); // 

		var cmi = config.macros.itemMenu; // shortcut

		var serapartedSwitchActions = this.parseAndSeparateActions(switchActions);

		var checkCounters = function()
			{ return cmi.checkCounters(filter,field); };
		
		// selecting/rearranging helpers
		var cancelSelection = function()
		{
			cmi.clearCurrentlyDragged();
			Popup.remove(); // close the popup, including on esc
		};
		var getSelected = function()
		{
			// get currently "dragged" itemMenu element
			var draggedContext = cmi.getCurrentSourceListContext();
			return draggedContext ? draggedContext.itemMenuElement : null;
		};
		// jQuery of itemMenu elements with the same filter
		var getJMenus = function()
		{
			return jQuery("."+cmi.itemMenuClass+"[filter='"+filter+"']");
		};
		var reselectByIndex = function(jMenus,index) // jMenus is "jQuery(menus)"
		{
			var menuToSelect = jMenus[index];
			if(!menuToSelect) return;

			cancelSelection();

			// select (focus is needed for keyboard events to fire)
			jQuery(menuToSelect).focus().click();
console.log("click generated in reselectByIndex");

			//# may be optimize the two previous steps (do reselection directly)
		};
		var selectNext = function()
		{
			var itemMenu = getSelected();
			if(!itemMenu) return;

			// other item menus, found by the same filter
			var menus = getJMenus(),
			    currentIndex = menus.index(itemMenu);
			reselectByIndex(menus,currentIndex+1);
		};
		var selectPrev = function()
		{
			var itemMenu = getSelected();
			if(!itemMenu) return;

			// other item menus, found by the same filter
			var menus = getJMenus(),
			    currentIndex = menus.index(itemMenu);
			reselectByIndex(menus,currentIndex-1);
		};
		var selectNeighbour = function()
		{
			var itemMenu = getSelected();
			if(!itemMenu) return;

			// other item menus, found by the same filter
			var menus = getJMenus(),
			    currentIndex = menus.index(itemMenu);
			if(currentIndex > 0)
				reselectByIndex(menus,currentIndex-1);
			else
				reselectByIndex(menus,currentIndex+1);

			// returns true when successfully selected another item
			return getSelected() != itemMenu;
		};
		var selectFirst = function()
		{
			// other item menus, found by the same filter
			var menus = getJMenus();
			reselectByIndex(menus,0);
		};
		var selectLast = function()
		{
			// other item menus, found by the same filter
			var menus = getJMenus();
			reselectByIndex(menus,menus.length-1);
		};
		var moveDown = function(doReselect)
		{
			var tids = checkCounters(), i = tids.indexOf(tid);
			if(i < 0) return; // not among tids
			if(i >= tids.length-1) // out of boundaries
				return doReselect ? "" : cancelSelection();

			// make the switch with the neighbour
			tids[i].setSCounter(field,i+1);
			tids[i+1].setSCounter(field,i);

			// refresh the list (order)
			store.notify(field,true);
			//# try to avoid extra refreshing on long press down

			if(doReselect) reselectByIndex(getJMenus(),i+1);
		};
		var moveUp = function(doReselect)
		{
			var tids = checkCounters(), i = tids.indexOf(tid);
			if(i < 0) return; // not among tids
			if(i <= 0) // out of boundaries
				return doReselect ? "" : cancelSelection();

			// make the switch with the neighbour
			tids[i].setSCounter(field,i-1);
			tids[i-1].setSCounter(field,i);

			// refresh the list (order)
			store.notify(field,true);

			if(doReselect) reselectByIndex(getJMenus(),i-1);
		};
		var moveToTop = function(doReselect)
		{
			cmi.moveToTop(filter,tid,field);
			if(doReselect) reselectByIndex(getJMenus(),0);
		};
		var moveToBottom = function(doReselect)
		{
			cmi.moveToBottom(filter,tid,field);
			var menus = getJMenus();
			if(doReselect) reselectByIndex(menus,menus.length-1);
		};
		var moveToArbitraryPlace = function(movingTidName, above)
		{ // filter, targetTidName is got via enclosure
			var tids = checkCounters(),
			    movingTid = store.fetchTiddler(movingTidName),
			    targetTid = store.fetchTiddler(tName),
			    movingInd = tids.indexOf(movingTid),
			    targetInd = tids.indexOf(targetTid),
			    index = targetInd + ((movingInd > targetInd) ? (above ? 0 : 1) : (above ? -1 : 0));
			cmi.moveToArbitraryPlace(filter,movingTid,field, index);
		};
		var onClickItemMenu = wrapNoClose(function(e)
		{
console.log("onClickItemMenu");
console.log("e.which is "+e.which);
console.log(e);
			if(!cmi.getCurrentlyDragged())		// ~drag
			{
				clearMessage(); // useful on smartphones

				// open the popup menu:
				var manageButton = this;
				var popup = Popup.create(this);
				jQuery(popup).addClass("itemMenu");
				// then, add buttons there:

				// the "cancel selection" button
				createTiddlyButton(popup,"","cancel selection",
					cancelSelection,"cancelSelectionButton button");

				// switch action(s) button
				var createActionButtonInPopup = function(place,actionName)
				{
					var li = createTiddlyElement(place,"li"),
					    action = serapartedSwitchActions[actionName];

					createTiddlyButton(li,actionName,"",function(){
						cmi.parseAndApplyAction(tid,action,this);
						cancelSelection();
					},"listedDoActionButton button");
				};
				var bringActions = wrapNoClose(function() {
					if(serapartedSwitchActions["default"])
					{
						// named actions
						var dPopup = Popup.create(this), li, actName;
						for(actName in serapartedSwitchActions) {
							if(actName == "default")
								continue;
							createActionButtonInPopup(dPopup, actName);
						}
						Popup.show("bottom","left");
					} else { // single unnamed action is defined
						cmi.parseAndApplyAction(tid,switchActions, this);
						cancelSelection();
					}

					cmi.clearCurrentlyDragged();
					// no class removing since refreshing is applied
				});
				if(serapartedSwitchActions && tid)
					createTiddlyButton(popup,"","drop this item",
						bringActions,"doActionButton button");

				// tag toggler
				var macroText = "<<tagToggler [["+tName+"]] \"\">>";
				wikify(macroText, popup);
				var tagButton = popup.lastChild,
				    startTagToggling = function() {
					jQuery(tagButton).click();
console.log("click generated in startTagToggling");
				    };

				// the "info" button
				var showTiddlerInfo = wrapNoClose(function(){
					var infoPopup = Popup.create(popup);
					createTiddlyText(infoPopup,"references:");
					config.commands.references.handlePopup(infoPopup, tid.title);
					Popup.show("bottom","left");
					return false;
				});
				createTiddlyButton(popup,"","tiddler info", showTiddlerInfo,"tiddlerInfoButton button");

				// the "rename" button
				var startRenaming = wrapNoClose(function(){
					var renamePopup = Popup.create(popup),
					    li = createTiddlyElement(renamePopup,"li"),
					    initTitle = tid.title,
					    nameField = createTiddlyElement(li,"textarea",null,"nameInput"),
					    changeName = function(doSave,goOnSelected)
					    {
					       var newTitle = jQuery.trim(nameField.value);
					       store.saveModifiedTiddler(initTitle,newTitle);
					       story.refreshTiddler(tid.title,null,true);
					       if(doSave) autoSaveChanges();
					       if(!goOnSelected)
							cancelSelection();
					       else
							cmi.ensureFocusOnCurrentlyDragged();
					    };

					nameField.value = tid.title;
					createTiddlyButton(li,"rename","rename \""+tid.title
						+"\"",changeName,"button renameButton");
					nameField.onclick = wrapNoClose(); // prevent popup closing
					// press enter to apply or esc to exit
					// (shift and ctrl are also taken into account)
					nameField.onkeydown = function(ev)
					{
						var e = ev || window.event;
						if(e.which == $enter) {
							changeName(e.ctrlKey,e.shiftKey);
							Popup.remove(1); // renamePopup only

							window.preventOtherHandling(e);
						}
						if(e.which == $esc) {
						    if(e.shiftKey) {
							cmi.ensureFocusOnCurrentlyDragged();
							Popup.remove(1);
							window.preventOtherHandling(e);
						    } else
							cancelSelection();
						}
					};

					Popup.show("bottom","left");
					nameField.focus(); // put the cursor inside the name edit area
				});
				createTiddlyButton(popup,"","rename this tiddler", startRenaming,"renameTiddlerButton button");

				// the "edit text" button
				var startEditing = wrapNoClose(function()
				{
					var textEditPopup = Popup.create(popup),
					    li = createTiddlyElement(textEditPopup,"li"),
					    textField = createTiddlyElement(li,"textarea",null,"ttextInput"),
					    changeText = function(e)
					    // e is either click or keydown (enter) event
					    {
						var goOnSelected = e.shiftKey;
						tid.set(null,textField.value);
						store.saveModifiedTiddler(tid);
						story.refreshTiddler(tid.title,null,true);
						autoSaveChanges();
						if(goOnSelected)
							cmi.ensureFocusOnCurrentlyDragged();
						else
							cancelSelection();
					    };


					textField.value = tid.text;
					// change the width of the popup and hence of {{{textField}}}
					textEditPopup.style.width = "100%";
					createTiddlyButton(li,"save","save the text of \""+
								tid.title+"\"",changeText,
								"button saveTextButton");
					textField.onclick = wrapNoClose(); // prevent popup closing
					// press enter to apply or esc to exit
					// (shift and ctrl are also taken into account)
					textField.onkeydown = function(ev)
					{
						var e = ev || window.event;
						if(e.ctrlKey && e.which == $enter) {
							changeText(e);
							Popup.remove(1); //textEditPopup only

							window.preventOtherHandling(e);
						}
						if(e.which == $esc) {
						    if(e.shiftKey) {
							cmi.ensureFocusOnCurrentlyDragged();
							Popup.remove(1);
							window.preventOtherHandling(e);
						    } else {
							Popup.remove();
							cancelSelection();
						    }
						}
					};
					textField.onkeypress = function(e)
					{
						adjustHeightToContent.apply(this);
						// avoid firing of story.onTiddlerKeyPress
						if(e.which == $enter) {
							if(e.stopPropagation)
								e.stopPropagation();
							e.cancelBubble = true;
						}
						// don't return false to insert linebreaks
						// on enter (when typing)
					}

					Popup.show("bottom","left");
					adjustHeightToContent.apply(textField);
					textField.focus(); // put the cursor inside the text edit area
				});
				createTiddlyButton(popup,"","edit the tiddler text",
					startEditing,"editTiddlerTextButton button");

				// the "open" button
				var i = getTiddlyLinkInfo(tName,"openTiddlerButton button"),
				    openTiddler = function(e) {
					onClickTiddlerLink(e);
					cancelSelection();
				    },
				    openButton = createTiddlyButton(popup,"","open "+tName,
					openTiddler,i.classes,null,null,{
						refresh: "link", tiddlyLink: tName
					}), // see createTiddlyLink source
				    clickOpenTiddler = function(){
					jQuery(openButton).click();
console.log("click generated in clickOpenTiddler");
				    };

				// "add" button
				var cma = config.macros.addTiddler;
				if(cma && addAction) {
					var btn = createTiddlyButton(popup,"",
						"add tiddler here",cma.onClick,
						"addTiddlerButton button"),
					    currentIndex = getJMenus().index(this);
					btn.params = { title:"", text:"", commonTags:[],
						addAction:	addAction,
						orderCounter:	field,
						orderFilter:	filter,
						orderMode:	currentIndex
					}
					var addTiddler = function(e) { jQuery(btn).click();
window.preventOtherHandling(e);
console.log("click generated in addTiddler"); };
				}

				// the "delete" button
				var confirmDeleteMsg = config.commands.deleteTiddler.warning,
				    deleteTiddler = function(e,goOnSelected)
				    {
					if(e) // on click
						goOnSelected = goOnSelected || e.shiftKey;
					if(confirm(confirmDeleteMsg.format([tName]))) {
						if(goOnSelected)
							goOnSelected = selectNeighbour();
						  // if no neighbour, nothing to reselect
						store.removeTiddler(tName);
						autoSaveChanges();
					}
					if(goOnSelected)
						cmi.ensureFocusOnCurrentlyDragged();
					else
						cancelSelection();
				    };
				createTiddlyButton(popup,"","delete this tiddler",
					deleteTiddler,"deleteTiddlerButton button");

				// support keyboard
				//  keypress ignores "delete", arrows etc, hence keydown
				var onKeyDownItemMenu = function(e) {
					switch(e.which) {
						case $enter: clickOpenTiddler();break;
						case $down:
							if(e.ctrlKey) moveDown(true);
							else selectNext();
							break;
						case $up:
							if(e.ctrlKey) moveUp(true);
							else selectPrev();
							break;
						case $home:
							if(e.ctrlKey) moveToTop(true);
							else selectFirst();
							break;
						case $end:
							if(e.ctrlKey) moveToBottom(true);
							else selectLast();
							break;
						case $esc:
						// onkeydown here, because onkeyup ~can't be
						// prevented from ~propagation in subpopups
							if(!e.shiftKey)
								cancelSelection();
							else
								//for the info and tag popups
								Popup.remove(1);
							break;
						
//# add keyboard support for moving to the beginning/end (ctrl+)"Home"/"End"
					}
					// allow browser tab switching
					if(e.which != $tab || !e.ctrlKey)
						return false;
				};
				var onKeyUpItemMenu = function(e) {
					switch(e.which) {
						case $i:  showTiddlerInfo();	break;
						case $r:  startRenaming();	break;
						case $e:  startEditing();	break;
						case $t:  startTagToggling();	break;
						case $plus:
							if(cma && addAction)
								addTiddler(e);
						break;
						case $delete: case $x:
							// tries to reselect if e.shiftKey!
							deleteTiddler(null,e.shiftKey);
						break;
						
//# add keyboard support for actions D[A?]
					}
					return false;
				};

				cmi.setCurrentlyDragged(tName, field, dropAction, this, onKeyDownItemMenu, onKeyUpItemMenu);
				cmi.markSelected(this);

/*# create array of { handler: .. ,  label: .. , activationKeys: .. }
    and try to make button creation, keyboard support "for each ..", extendable set of .. */

				Popup.show("top","right");
			} else {				// ~drop
				// if taken from another list, the item must be changed by the drop+add actions
				var dragListContext = cmi.getCurrentSourceListContext(),
				    dropActionNow = dragListContext.dropAction,
/* not 100% accurate */		    sameList = (dropActionNow == dropAction) && (dragListContext.field == field),
					dropFromOtherList = !sameList && (dropActionNow || addAction),
					dropAndAddAction = (dropActionNow && addAction) ?
						(dropActionNow+",,"+addAction)
						: (dropActionNow || addAction);

				if(dropFromOtherList)
				{
					var draggedTid = store.fetchTiddler(cmi.getCurrentlyDragged());
					cmi.parseAndApplyAction(draggedTid,dropAndAddAction);
					draggedTid.deleteSCounter(dragListContext.field);
				}

				// move the item
				Popup.remove(); // close the popup
				if(cmi.getCurrentlyDragged() == tName) {
					jQuery(this).removeClass("buttonSortState");
					moveDown(); // on drop-on-self
				} else
					moveToArbitraryPlace(cmi.getCurrentlyDragged(),true);
					// refreshing removes the "buttonSortState" class
				cmi.clearCurrentlyDragged();
			}
		});

		// create the 2 sets of classes for the button and for the table row
		// (if itemMenu is inside a td element) based on the tiddler's tags
		var itemMenuClasses = this.itemMenuClass,
		    rowClasses = "",
		    badSymbolsRE = /[\.,;:`!@#\$%\^&\*\(\)\+=\[\]\{\}\|\\/'"~ ]/g;
		for(var i = 0 ; tid && i < tid.tags.length ; i++) {
			// process each tag: substitute spaces and symbols .,;:!'"()[]{}=+\|/*&^%$#@`~ with "_"
			itemMenuClasses += " marker_"+ tid.tags[i].replace(badSymbolsRE,"_");
			rowClasses += "line_"+ tid.tags[i].replace(badSymbolsRE,"_")+" ";
		}

		// create the button(s) // text is set via CSS
		var btn = createTiddlyButton(place,"", "",onClickItemMenu,itemMenuClasses);
		btn.setAttribute("filter",filter);
		btn.tiddler = tid;

		// in table, add the line_className classes based on the tiddler's tags
		if(place.tagName.toLowerCase() == "td")
			jQuery(place).parent().addClass(rowClasses);
	}
}

// define the actions
  // add tag
config.macros.itemMenu.actionStepsWithArguments["+tag"] = function(tiddler,tag)
{
	if(!tiddler.isTagged(tag)) {
		tiddler.tags.push(tag);
		tiddler.modifier = config.options.txtUserName;
		tiddler.modified = new Date();
	}
};
  // remove tag
config.macros.itemMenu.actionStepsWithArguments["-tag"] = function(tiddler,tag)
{
	if(tiddler.isTagged(tag)) {
		tiddler.tags.splice(tiddler.tags.indexOf(tag),1);
		tiddler.modifier = config.options.txtUserName;
		tiddler.modified = new Date();
	}
};
  // toggle tag
config.macros.itemMenu.actionStepsWithArguments["!tag"] = function(tiddler,tag)
{
	if(tiddler.isTagged(tag))
		tiddler.tags.splice(tiddler.tags.indexOf(tag),1);
	else
		tiddler.tags.push(tag);
	tiddler.modifier = config.options.txtUserName;
	tiddler.modified = new Date();
};
  // set field
config.macros.itemMenu.actionStepsWithArguments["setField"] = function(tiddler,argument)
{
	var argRE = /(\w+)\.\.(.*)/, argMatch = argRE.exec(argument);
	if(!argMatch) return;
	var field = argMatch[1], value = argMatch[2];
	value = value || null; // to remove the field if empty

	store.setValue(tiddler,field,value,true);
};
  // delete field
config.macros.itemMenu.actionStepsWithArguments["deleteField"] = function(tiddler,argument)
{
	store.setValue(tiddler,argument,null,true);
};
//config.macros.itemMenu.actionStepsWithArguments["<name>"] = function(tiddler,arg) {
//};
  // save changes
config.macros.itemMenu.actionStepsWithoutArguments["save"] = function() {
	saveChanges();
};
config.macros.itemMenu.actionStepsWithoutArguments["delete"] = function(tiddler) {
	var confirmDeleteMsg = config.commands.deleteTiddler.warning;
	if(confirm(confirmDeleteMsg.format([tiddler.name])))
	{
		store.removeTiddler(tiddler.title);
		autoSaveChanges();
	}
};
//config.macros.itemMenu.actionStepsWithoutArguments["<name>"] = function(tiddler) {
//};

// implement the styling
var iMenuClass = config.macros.itemMenu.itemMenuClass;
config.shadowTiddlers["StyleSheetManualSorter"] = "/*{{{*/\n"+
	".editTTextItem  { width: 100% }\n"+
	".ttextInput     { width:  99%; margin-right: 0.5%; margin-left: 0.5%; }\n\n"+

	"."+iMenuClass+"               { color: inherit; font-weight: bold; }\n"+
	"."+iMenuClass+":hover         { background-color: inherit; color: green; }\n"+ // use ColorPalette?
	"."+iMenuClass+":before        { content: \"⊙\"; }\n"+
	".cancelSelectionButton:before { content: \"⊗\"; }\n"+
	".doActionButton:before        { content: \"D\"; }\n"+
	".tiddlerInfoButton:before     { content: \"I\"; }\n"+
	".renameTiddlerButton:before   { content: \"R\"; }\n"+
	".editTiddlerTextButton:before { content: \"E\"; }\n"+
	".openTiddlerButton:before     { content: \"O\"; font-weight: normal; }\n"+
	".addTiddlerButton:before      { content: \"+\"; }\n"+
	".deleteTiddlerButton:before   { content: \"X\"; }\n"+
	".itemMenu .tagTogglerButton:before { content: \"T\"; }\n"+
	".itemMenu .button                  { padding-left: 0.25em; padding-right: 0.25em; }\n"+
	".buttonSortState:hover,\n"+
	".buttonSortState { background-color: inherit; color: #00B000; }\n"+
	// for "lists" with buttons as "markers" (using tables)
	".tableList,\n"+
	".tableList > tbody,\n"+
	".tableList > tbody > tr,\n"+
	".tableList > tbody > tr > td,\n"+
	".tableList .button { border: none !important; }\n"+
	".tableList td            { vertical-align: top; padding: 0px 3px; }\n"+
	".tableList ul,\n"+
	".tableList ol,\n"+
	"li .tableList,\n"+
	".tableList .tableList    { margin: 0; }\n"+
	".tableList > tbody > .selected { background-color: #ddddff; }\n\n"+

	// full width of table with minimal width of the first column
	".tableList { width: calc(100% - 2em); }\n"+ // 2em of margins on both sides
	".tableList > tbody > tr > td:first-child { width: 1px; }\n"+
	/* is some cases
	 *	.tableList td { width: 1px; }
	 *	.tableList td:last-child { width: 100%; }
	 * would be more suitable */
	"/*}}}*/";
store.addNotification("StyleSheetManualSorter", refreshStyles);
//}}}
/***
!!!Tagging helpers and tagToggler macro
***/
//{{{
config.filters.existingTags = function(results,match)
{
	var allTags = store.getTags();
	for(var i = 0; i < allTags.length; i++)
		allTags[i] = allTags[i][0];

	if(match[3] == "-") {
		for(i = 0; i < results.length; i++)
			if(!allTags.contains(results[i].title))
				results.splice(i--,1);
		return results;
	}

	for(var i = 0; i < allTags.length; i++)
		allTags[i] = store.fetchTiddler(allTags[i]) || new Tiddler(allTags[i]);

	// default action: add all the tags
	for(i = 0; i < allTags.length; i++)
		results.pushUnique(allTags[i]);
	return results;
};

config.filters.tagging = function(results,match)
{
	var prefix = match[3].substr(0,1), title = match[3].substr(1), tid = store.fetchTiddler(title),
	    tagging = [], notTagging = [];

	if(!tid)
		return [];
//# this behaviour may be changed after some testing

	for(var i = 0; i < results.length; i++)
		if(tid.tags.contains(results[i].title))
			tagging.push(results[i]);
		else
			notTagging.push(results[i]);

	switch(prefix) {
		case ">":
			return tagging.concat(notTagging)
		case "<":
			return notTagging.concat(tagging)
		case "+":
			return tagging
		case "-":
			return notTagging
	}
	displayMessage("Warning: the \"tagging\" filter must be used with one of the prefixes +, -, > or <, which is not the case.");
// use cookie to decide whether to suppress the message?
	return results;
};

config.macros.tagToggler = {};
config.macros.tagToggler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
	// parse params
	var pParams = paramString.parseParams("pParams",null,true,false,true),
	    tid     = store.fetchTiddler(pParams[0]["pParams"][0]) || tiddler; // tid. which tags will be toggled
	if(!tid) return;

	var label = pParams[0]["pParams"][1];
	if(label == "." || label == undefined)
		label = "toggle tags";

	var tooltip = getParam(pParams,"tooltip","toggle tags of "+tid.title),
	    doAutoSave = params.contains('doAutoSave') || false,
	    tagsSet = getParam(pParams,"tags","[existingTags[+]] [tagging[>"+tid.title+"]]");

	// for compability with SetManagerPlugin
	var cmi = config.macros.itemMenu,
	    clearSelected = function()
	    {
		if(cmi) cmi.clearCurrentlyDragged();
	    },
	    returnItemMenuSelection = function()
	    {
		if(cmi) cmi.ensureFocusOnCurrentlyDragged();
	    };

	var whereToScroll;
	var toggleTag = function(tid,tag,refreshTagListIfNotClosing)
	{
		if(tid.isTagged(tag))
			tid.tags.splice(tid.tags.indexOf(tag),1);
		else
			tid.tags.push(tag);

		if(refreshTagListIfNotClosing) return refreshTagListIfNotClosing();
		
		store.saveModifiedTiddler(tid);
		story.refreshTiddler(tid.title,null,true);
		if(doAutoSave)
			autoSaveChanges();
		
		if(whereToScroll)
			window.scrollTo(0,ensureVisible(whereToScroll));
		clearSelected();
	};

	// define the onclick handlers
	var onTagClick = wrapNoClose(function(e)
	{
		var tag = this.getAttribute("tag"),
		    shiftWasHold = (e || window.event).shiftKey;
		toggleTag(tid,tag,shiftWasHold ? this.refreshTagList : null);
		return false;
	});
	var onClick = wrapNoClose(function()
	{
		// form the list of the tags to choose from, all tags to start somewhere
		var availableTags = [],
		    allTags = store.getTags(),
		    noTagsMsg = "No tags found by the provided criterion";
		for(var i = 0; i < allTags.length; i++)
			availableTags.push(allTags[i][0]);

		// build the list of tags and labels
		var tagsToWorkWith = store.filterTiddlers(tagsSet);
		for(i = 0; i < tagsToWorkWith.length; i++)
			tagsToWorkWith[i] = tagsToWorkWith[i].title;
		var menuTags = [], menuLabels = [], menuItems = [], t;
		for(i = 0; i < tagsToWorkWith.length; i++) {
			t = tagsToWorkWith[i];
			menuItems.push({
				tag: t,
				label: t // '[x] ' or '[ ] ' addition is now defined via css
			})
		}

		// create the popup menu
		var popup = Popup.create(this), li, tagButton;

		// arbitrary tag toggler
		li = createTiddlyElement(popup,"li");
		var newTagField = createTiddlyElement(li,"input",null,"newTagInput",{type:"text"});
		newTagField.onclick = wrapNoClose(); // prevent the popup from closing on click here

		var selectedTagIndex = 0, // 0 means "not selected"
		    selectNextTag = function()
		    {
			var nextListItem = popup.childNodes[selectedTagIndex+1];
			if(!nextListItem) return // out of boundaries, won't move

			jQuery(popup).children().children().removeClass("selectedTag");
			selectedTagIndex++;
			nextListItem.childNodes[0].classList.add("selectedTag");
		    },
		    selectTag15ahead = function()
		    {
			var numberOfItems = jQuery(popup).children().length; // tags+1
			selectedTagIndex  = Math.min(selectedTagIndex + 15,numberOfItems-1);
			var newListItem   = popup.childNodes[selectedTagIndex];
			jQuery(popup).children().children().removeClass("selectedTag");
			newListItem.childNodes[0].classList.add("selectedTag");
		    },
		    selectPrevTag = function()
		    {
			var prevListItem = popup.childNodes[selectedTagIndex-1];
			if(selectedTagIndex == 0) return;

			jQuery(popup).children().children().removeClass("selectedTag");
			selectedTagIndex--;
			if(selectedTagIndex == 0) return; // don't color the main field
				//# or do so but color it in the beginning as well
			prevListItem.childNodes[0].classList.add("selectedTag");
		    },
		    selectTag15back = function()
		    {
			var numberOfItems = jQuery(popup).children().length; // tags+1
			selectedTagIndex  = Math.max(selectedTagIndex - 15,0);
			var newListItem   = popup.childNodes[selectedTagIndex];
			jQuery(popup).children().children().removeClass("selectedTag");
			if(selectedTagIndex > 0)
				newListItem.childNodes[0].classList.add("selectedTag");
		    },
		    selectTag = function(tag)
		    {
			var button = jQuery(popup).children().children("*[tag='"+tag+"']");
			if(!button.length) return;

			button.addClass("selectedTag");
			// get index, set selectedTagIndex
			selectedTagIndex = jQuery(popup).children().children().index(button)
				- 1;
		    },
		    getSelectedTagButton = function() {
			if(selectedTagIndex == 0) // fosuced on new tag field
				return null;
			return popup.childNodes[selectedTagIndex].childNodes[0];
		    },
		    toggleSelected = function(goOnToggling)
		    {
			var button = getSelectedTagButton();
			if(!button) return;
			tag = button.getAttribute("tag");
			fieldValue = newTagField.value;
			toggleTag(tid,tag,goOnToggling ? refreshTagList : null);

			newTagField.value = fieldValue;
			newTagField.select();
			selectTag(tag);
			return tag;
		    },
		    toggleNew = function(goOnToggling) {

			var tag = jQuery.trim(newTagField.value), isNewTag = true, i;

			// add the tag to the list if it's totally new:
			for(i = 0; i < menuItems.length; i++)
				if(menuItems[i].tag == tag)
					isNewTag = false;
			if(isNewTag)
				menuItems.push({ tag:tag, label:tag });

			toggleTag(tid,tag,goOnToggling ? refreshTagList : null);
		    };

		// push the button to apply/click elsewhere to cancel..
		createTiddlyButton(li,"toggle","toggle the entered tag in the tiddler",
						toggleNew,"button tagAdderButton");
		// ..or use keyboard (see below)

		// tags from the set
		var refreshTagList = function()
		{
//console.log("caller is " + arguments.callee.caller.toString());
			// clear the list (but don't remove the first item)
			while(popup.childNodes[1])
				popup.removeChild(popup.childNodes[1]);
			newTagField.focus();
			selectedTagIndex = 0;

			// refill the list
			if(menuItems.length == 0) {
				createTiddlyText(createTiddlyElement(popup,"li"),noTagsMsg);
				return;
			}

			var fieldValueLowered = newTagField.value.toLocaleLowerCase(),
			    sortedMenuItems = menuItems.concat([]).sort(function(a,b){
				if(!fieldValueLowered) return 0;
				// store where the value from the field starts in the tag
				a.index= a.tag.toLocaleLowerCase().search(fieldValueLowered);
				b.index= b.tag.toLocaleLowerCase().search(fieldValueLowered);
//				if(tid.tags.contains(a.tag) && a.index != -1) a.index = -2;
//				if(tid.tags.contains(b.tag) && b.index != -1) b.index = -2;
				return a.index > b.index;
			    }),
			    item;

			for(i = 0; i < sortedMenuItems.length; i++)
			{
				item = fieldValueLowered ? sortedMenuItems[i] : menuItems[i];
				if(fieldValueLowered && item.index == -1) continue;

				li = createTiddlyElement(popup,"li");
				tagButton = createTiddlyButton(li,item.label,
					"toggle '"+item.tag+"'",onTagClick,"button tag"+
					(tid.tags.contains(item.tag)? "" : "Not")+"Present");
				tagButton.setAttribute("tag",item.tag);
				tagButton.refreshTagList = refreshTagList;
			}
		};
		refreshTagList();

		// show the popup menu
		Popup.show("bottom","left");

		// support keyboard navigation
		jQuery(newTagField).bind('input',function(e)
		{
			refreshTagList();
		});
		jQuery(newTagField).bind('keyup',function(e)
		{
			goOnToggling = e.shiftKey;
			if(e.which == $enter) {
				toggleSelected(goOnToggling) || toggleNew(goOnToggling);
				if(!goOnToggling) Popup.remove();
//document.getElementById("displayArea").focus()
				return;
			}
			if(e.which == $esc) {
				if(whereToScroll)
					window.scrollTo(0,ensureVisible(whereToScroll));
				if(e.shiftKey) {
					returnItemMenuSelection();
					Popup.remove(1);
//# make sure doesn't get propagated?
				} else {
					clearSelected();
					Popup.remove();
				}
//document.getElementById("displayArea").focus()
			}
		});
		jQuery(newTagField).bind('keydown',function(e)
		{
			if(e.which == $down && !e.ctrlKey)
				selectNextTag();
			if(e.which == $up && !e.ctrlKey)
				selectPrevTag();
			if(e.which == $pgDn || (e.which == $down && e.ctrlKey))
				selectTag15ahead();
			if(e.which == $pgUp || (e.which == $up && e.ctrlKey))
				selectTag15back();
			if(selectedTagIndex)
				window.scrollTo(0,ensureVisible(getSelectedTagButton()));
			else
				window.scrollTo(0,ensureVisible(newTagField));
		});
	});

	// create the button
	whereToScroll = createTiddlyButton(place,label,tooltip,onClick,"button tagTogglerButton");
}

// set styling
config.shadowTiddlers["TagAdderStyleSheet"] = "/*{{{*/\n"+
	".tagPresent:before	{ content: \"[x] \"; }\n"+
	".tagNotPresent:before	{ content: \"[ ] \"; }\n"+
	".newTagInput		{ float: left; margin-right: 0.5em; }\n"+
	".tagAdderButton	{ text-align: center; }\n"+
	".selectedTag		{ color: blue !important; }\n"+
	"/*}}}*/";
store.addNotification("TagAdderStyleSheet", refreshStyles);
//}}}
/***
!!!Hijack forEachTiddler macro to enable the new params
***/
//{{{
// helper filter for hiding hidden tiddlers
config.filters.hideFromFet = function(results,match)
{
	var contextName = match[3], noContext = contextName == "-";
	for(var i = 0; i < results.length; i++)
		if(results[i].fields["hideInFet".toLowerCase()] &&
		   (results[i].fields["hideInFet".toLowerCase()] && noContext ||
		    results[i].fields["hideInFet".toLowerCase()]
							.split(" ").contains(contextName)))
			results.splice(i--,1);
	return results;
};

// hijack config.macros.forEachTiddler.parseParams so that it handles
//  "set", "sortable"/"sortableBy", "addAction", "dropAction", "switchAction" params
// the "params" array is not changed as its usages in parseParams don't need it,
//  same story for preParsedParams[i] (i > 0)
if(config.macros.forEachTiddler && !config.macros.forEachTiddler.hijacked_sortable)
{
//# check if the proper version of FET (1.3.0 or above) is used
	config.macros.forEachTiddler.hijacked_sortable = true;
	config.macros.forEachTiddler.oldFashionParams =
		config.macros.forEachTiddler.oldFashionParams.concat([
		"sortableBy", "addAction", "dropAction", "switchActions", "writeToList"]);

	config.extensions.ManualSortMacroPlugin = {
		orig_fet_parseParams: config.macros.forEachTiddler.parseParams
	};
	var origParse = config.extensions.ManualSortMacroPlugin.orig_fet_parseParams;
	config.macros.forEachTiddler.parseParams = function(preParsedParams,params)
	{
		// parse the "set" param
		var setDescription = getParam(preParsedParams,"set",""), filter, sortField,
		    setAddAction, setDropAction, cmd = config.macros.defineSet;
		if(setDescription) {
			if(!setDescription.contains("[")) {
				filter = "[set["+ setDescription +"]]"; // named set
				sortField = cmd.getSortFieldForNamedSet(setDescription);
				setAddAction = cmd.getAddToNamedSetAction(setDescription);
				setDropAction= cmd.getDropFromNamedSetAction(setDescription);
			} else {
				filter = "set: "+setDescription; // inline set
				var setDefinition = cmd.parseSetDefinition(setDescription);
				// don't overwrite setDefinition as it is passed to adder
				sortField = setDefinition.sortField;
				setAddAction = cmd.getAddToSetAction(setDefinition);
				setDropAction = cmd.getDropFromSetAction(setDefinition);
			}
			sortField = sortField || config.macros.itemMenu.defaultField;
		}

		// remember the filter (calc from both "set" and "filter" params)
		var filterParam = getParam(preParsedParams,"filter","") +
				 (setDescription ? " [hideFromFet[-]]" : "");
		if(filter && filterParam) {
			if(filter.indexOf("set: ") == 0) {
				filter = filter + " modify: " + filterParam;
			} else
				filter = filter + " " + filterParam;
		} else if(filterParam)
			filter = filterParam;
		if(!filter)
			return origParse.apply(this,arguments);

		// the "in", "where" params stay untouched; change the filter param
		preParsedParams[0]["filter"] = [filter];

		// hijack the "script" param (define the "insert" and "adder" helpers)
		var usedScript = getParam(preParsedParams,"script",""),
		    insertDefinition = "var insert = "+
		        "function(container,params,defaultText,preprocessScript) {"+
			"container = container || 't';"+
			"params = params || '';"+
			"if(defaultText)"+
				"params = 'showIfNoValue:\\''+defaultText+'\\' '+params;"+
			"if(preprocessScript)"+
				"params = 'preprocess:\\''+preprocessScript+'\\''+params;"+
			"return '<<insertEditable tiddler:['+'['+tiddler.title+']] container:\"'+container+'\" '+(params||'')+'>>';"+
		    "};";
		    adderDefinitionBegin = 'var adder = '+
			'function(args,label,orderMode,title) {'+
			'return "<<addTiddler"'+
			'	+(" label:\'"+(label || "+")+"\'")'+
			'	'+(setDescription ? ('+" set:\''+setDescription+'\'"') : ''),
		    adderDefinitionEnd =
			'	+(" title:\'"+(title || "")+"\'")'+ // empty by default
			'	+" "+(args||"")'+
			'	+">>"'+
		    '};',
		    adderDefinition = adderDefinitionBegin + adderDefinitionEnd,
			// unless sortableBy is defined, orderMode is ignored (see below)
		    fullScript = insertDefinition + adderDefinition + usedScript;

		preParsedParams[0]["script"] = [fullScript];

		// process and apply sortable/sortableBy params/sortField from set definition
		var sortableParamIndex = params.indexOf("sortable"),
		    justSortable       = sortableParamIndex != -1,
		    sortableBy         = getParam(preParsedParams,"sortableBy");

		if(params.contains("sortableBy") && !sortableBy)
			return { errorText: "Field name expected behind 'sortableBy'."};
		if(justSortable)
			sortableBy = config.macros.itemMenu.defaultField;

		 // support the deprecated {{{sortableBy '"orderCountName"'}}} syntax
		var fieldWithQuotsMatch = /^"(.+)"$/.exec(sortableBy);
		sortableBy = fieldWithQuotsMatch ? fieldWithQuotsMatch[1] : sortableBy;
		 // sortField can be defined directly or from the set (see above)
		sortField = sortableBy || sortField;

//# rethink from here: either move this stuff below actions parsing etc (more meaninglful)
//	or add "&& !setDescription" (this is to enable actions and other stuff for sets,
//	even if sortField is not defined)
//+ from here
		if(!justSortable && !sortField)
			return origParse.apply(this,arguments);

		// support the "extended scope for sorting"
//# is it extended or narrowed?
		var fieldAndFilterMatch = /^(\w+) (\[.+)$/.exec(sortField),
		    sortFilter = fieldAndFilterMatch ? fieldAndFilterMatch[2] : filter;
		sortField = fieldAndFilterMatch ? fieldAndFilterMatch[1] : sortField;

		// set the "sortBy" param
		var undefinedUp = true;
		var sortScript = "(function(){ var c = tiddler.getSCounter(\"" + sortField +
				"\"); return (c != 0 && !c)?" + (undefinedUp ? "-1" : "1000") + ": c; })()";
				// lists of 999+ tiddlers long are not supposed to be used with manual sorting

		preParsedParams[0]["sortBy"] = [sortScript];
		// the sortable/sortableBy part is left in preParsedParams[0] as is

		// extend the "adder" helper in the "script" param using specified sortField
		adderDefinition = adderDefinitionBegin +
			'	+(orderMode ? (" order:\''+sortField+','
					+filter.replace(/"/g,'\\"')
//# do smth about "'"s in filter (macro param parsing)
					+',"+orderMode+"\'") : "")'+
			adderDefinitionEnd;
		fullScript = insertDefinition + adderDefinition + usedScript;

		preParsedParams[0]["script"] = [fullScript];
//= up to here

		// for actions other than "write" (and "writeToList" ~action), do no more
		for(var knownActionName in config.macros.forEachTiddler.actions)
			if(knownActionName != "write" && params.contains(knownActionName))
				return origParse.apply(this,arguments);
		// in original FETP, that's "addToList" action only

		// parse the [SMP] actions-defining params
		var addAction     = getParam(preParsedParams,"addAction",setAddAction),
		    dropAction    = getParam(preParsedParams,"dropAction",setDropAction),
		    switchActions = preParsedParams ? (
				preParsedParams[0]["switchActions"] ?
					preParsedParams[0]["switchActions"].join(";;")
				: ""
			) : "";
		    // allow multiple switchActions params (but each must have a name..)

		var commonText = "description is expected behind";
		if(!addAction && !(addAction == "") && params.contains("addAction"))
			return { errorText: "An action "+commonText+" 'addAction'." };
		if(!dropAction && !(dropAction == "") && params.contains("dropAction"))
			return { errorText: "An action "+commonText+" 'dropAction'." };
		if(!switchActions && !(switchActions=="")&& params.contains("switchActions"))
			return { errorText: "An action(s) "+commonText+" 'switchActions'."};

		// parse [FET] action
		var action = "writeToList"; // default pseudo-action

		//  when action is not specified it is considered as writeToList with..
		var defaultText = '"["+"["+tiddler.title+"]]"';
//# unknown actions are considered as the default one.. which is bad for other extensions

		//  when "writeToList" is used, in fact it preparses the argument for "write"
		writeToListText = getParam(preParsedParams,"writeToList",defaultText);
		var writeText = '"| "+itemMenu()+" |"+(' +writeToListText+ ')+"|\\n"';

		//  when "write" is used, its argument is used after only "minimal"preparsing
		if(preParsedParams[0]["write"])
			action = "write";
		writeText = getParam(preParsedParams,"write",writeText);

		//  substitute all the "itemMenu()" expressions in the argument of "write"
		//   with their intended "meaning" (hence use non-greedy regexp)
		var itemMenuRegExp = /(.*?)itemMenu\(\)/g;
		var insertItemMenu = function($0,$1) {
			var escapedFilter = sortFilter.contains('"') ?
				('\\\''+sortFilter.replace(/"/g,'\\\"')+'\\\'') :
				("\\\""+sortFilter.replace(/'/g,"\\\'")+"\\\"") ;
// this is a semi-fix: tags with both ' and " will cause troubles with manual sorting..
// escape the actions as well

			return $1 + "\"<<itemMenu [[\"+tiddler.title+\"]] "+ escapedFilter +
				(sortField ? (" field:\\\""+ sortField +"\\\"") : "")+
				(addAction ? " addAction:\\\""+addAction+"\\\"" : "")+
				(dropAction ? " dropAction:\\\""+dropAction+"\\\"" : "")+
				(switchActions ? " switchActions:\\\""+switchActions+"\\\"" : "")+">>\"";
		}
		writeText = writeText.replace(itemMenuRegExp,insertItemMenu);

		//  change preParsedParams accordingly (use writeText, "write" action)
		preParsedParams[0]["write"] = [writeText];

		// change the begin argument (leave end, none, toFile parts unchanged)
		if(action == "writeToList")
			preParsedParams[0]["begin"] = [(preParsedParams[0]["begin"] ?
				preParsedParams[0]["begin"][0]:'""') + '+"|tableList|k\\n"'];

		// call the parser with the new arguments
		return origParse.apply(this,arguments);
	}
}
//}}}
/***
!!!addTiddler macro
***/
//{{{
config.macros.addTiddler = {
	handler: function(place,macroName,params,wikifier,paramString) {
		if(readOnly)
			return;

		// param parsing (partially taken from the newTiddler macro)
		params = paramString.parseParams("anon",null,true,false,false);
		var title  = params[1] && params[1].name == "anon" ? params[1].value : config.macros.newTiddler.title;
		    title  = getParam(params,"title",title);
		var label  = getParam(params,"label",label),
		    prompt = getParam(params,"prompt",config.macros.newTiddler.prompt),
		    text   = getParam(params,"text",""),
		    set    = getParam(params,"set",""),
		    commonTags = [], t,

		    orderParts      = getParam(params,"order",""),
		    orderPartsMatch = /^(\w*),(.+),([\w\d\-]+(?:,\w+)?)$/.exec(orderParts),
		    orderCounter    = orderPartsMatch ? orderPartsMatch[1] : undefined,
		    orderFilter     = orderPartsMatch ? orderPartsMatch[2] : undefined,
		    orderMode       = orderPartsMatch ? orderPartsMatch[3] : undefined,
		    orderParamDefault;
		if(orderMode) {
			orderPartsMatch = /^(\w+),(\w+)$/.exec(orderMode);
			orderMode = orderPartsMatch ? orderPartsMatch[1] : orderMode;
			orderParamDefault = orderPartsMatch ? orderPartsMatch[2] : undefined;
		}
		var cmd = config.macros.defineSet;
		// get addAction for the set and orderCounter
		if(set && cmd) {
			// set may be either set name or set definition
			if(!set.contains("[")) {
				orderCounter = orderCounter ||
					cmd.getSortFieldForNamedSet(set);
				var action = cmd.getAddToNamedSetAction(set);
			} else {
				var setDefinition = cmd.parseSetDefinition(set);
				orderCounter = orderCounter || setDefinition.sortField;
				var action = cmd.getAddToSetAction(setDefinition)
			}
		}

		for(t = 1; t < params.length; t++)
			if((params[t].name == "anon" && t != 1) || (params[t].name == "tag"))
				commonTags.push(params[t].value);
		if((orderCounter =="default" || orderCounter =="") && config.macros.itemMenu)
			orderCounter = config.macros.itemMenu.defaultField;

		// create button, attach params to it
		var btn = createTiddlyButton(place,label,prompt,this.onClick);
		btn.params =
		{
			title:		title,
			commonTags:	commonTags,
			addAction:	(set && cmd) ? action : "",
			text:		text,
			orderCounter:	orderCounter,
			orderFilter:	orderFilter,
			orderMode:	orderMode,
			orderParamDefault: orderParamDefault
		};
	},
	onClick: window.wrapNoClose(function()
	{
		// extract params
		var title = this.params.title,
		    text  = this.params.text,
		    tags  = [].concat(this.params.commonTags), // should be a new array
			//# do the same "copying" for fields, if are set here
		    addAction		= this.params.addAction,
		    orderCounter	= this.params.orderCounter,
		    orderFilter		= this.params.orderFilter,
		    orderMode		= this.params.orderMode,
		    orderParamDefault	= this.params.orderParamDefault,

		// create DOM
		    popup = Popup.create(this),
		    wrapper = createTiddlyElement(popup,"li"),
		    nameField = createTiddlyElement(wrapper,"input",null,"newTitleInput",{type:"text"});

		nameField.onclick = window.wrapNoClose();
		nameField.value = title;

		var cmi = config.macros.itemMenu,
		    addTidToSet = function(tiddler)
		    {
			if(!addAction || !cmi) return;
			cmi.parseAndApplyAction(tiddler, addAction);
		    };

		var createTheTiddler = function(goOnSelected)
		{
			var theTiddler = new Tiddler(jQuery.trim(nameField.value)),
			    modifier   = config.options.txtUserName;
			theTiddler.assign(null,text,modifier,null,tags);

			if(store.fetchTiddler(theTiddler.title) && !confirm("A tiddler named \""+theTiddler.title+"\" already exists. Do you want to overwrite it?"))
				return;
			store.saveTiddler(theTiddler);

//#			var theTiddler = new Tiddler(jQuery.trim(nameField.value));
//#			store.saveModifiedTiddler(theTiddler,null,text,tags);

			addTidToSet(theTiddler);

			if(orderCounter && Tiddler.prototype.setSCounter && cmi)
				if(orderMode == "top")
				    cmi.moveToTop(orderFilter, theTiddler, orderCounter);
				if(orderMode == "bottom")
				    cmi.moveToBottom(orderFilter, theTiddler, orderCounter);
				var orderModeIndex = parseInt(orderMode);
				if(!isNaN(orderModeIndex))
				    cmi.moveToArbitraryPlace(orderFilter,theTiddler, orderCounter,orderModeIndex,true);
//  use:  orderParamDefault
			autoSaveChanges();

			// for compability with SetManagerPlugin (usage in itemMenus)
			if(cmi && !goOnSelected) cmi.clearCurrentlyDragged();
		};

		// process enter/esc key presses
		// compatible with SetManagerPlugin (for usage in itemMenus)
		nameField.onkeydown = function(ev)
		{
			var e = ev || window.event;
			if(e.which == $enter) {
				createTheTiddler(e.shiftKey);
				Popup.remove();

				if(e.shiftKey && cmi)
					cmi.ensureFocusOnCurrentlyDragged();

				window.preventOtherHandling(e);
			}
			if(e.which == $esc) {
				if(e.shiftKey) {
					if(cmi) cmi.ensureFocusOnCurrentlyDragged();
					Popup.remove(1);
				} else {
					if(cmi) cmi.clearCurrentlyDragged();
					Popup.remove();
				}
			}
		};

if(orderMode == "checkboxes") {
// add possibilities to put the tiddler on top/bottom of a certain list (certain orderCounter):
//    create 2 checkboxes (t: [] b: []), add .. behaviour
			createTiddlyText(popup,"t:");
			var checkBoxTop = createTiddlyElement(popup,"input","test"/*null*/,null,null,{
				type:'checkbox',
				value:false
// calc the value the way it should be calced (chkAddToTop, ..)
			});
			checkBoxTop.onclick = window.wrapNoClose(function(){
				checkBoxTop.setAttribute(!checkBoxTop.value);
// checkboxes should deactivate each other, ..
			});
// add the onclick handler (change .., no close)
createTiddlyText(popup,"b:");
config.macros.option.handler(popup,"option",null,wikifier,"chkAddToBottom");
checkBox = popup.lastChild;
checkBox.onclick = window.wrapNoClose(checkBox.onclick);
// this works, but the checkbox being checked/unchecked is not displayed unless the popup is reopened
// - try config.macros.option.genericCreate(place,type,opt,className,desc)
// process the orderCounter taken from the check box
}

		// "ok" button
		createTiddlyButton(popup,"ok","create the tiddler",createTheTiddler);
		// cancel - on click elsewhere

		// show the popup menu, focus inside the text field
		Popup.show();
		nameField.focus();
		nameField.select()
	})
}
//}}}
/***
!!!insertEditable macro
***/
//{{{
// Sets the section value if it is present, appends otherwise
//  tip: if sectionName is "!!smth", then "!!!smth" is appended
//
Tiddler.prototype.setSection = function(sectionName,value)
{
	var beginSectionRegExp = new RegExp("(^!{1,6}[ \t]*" + sectionName.escapeRegExp() + "[ \t]*(\n|$))","mg"),
	    sectionTerminatorRegExp = /^!/mg,
	    match = beginSectionRegExp.exec(this.text);

	if(match) // edit existing section
	{
		var sectionTitle = match[1],
		    emptyAtEnd = match[2] != "\n",
		    beforeSection = this.text.substr(0,match.index),
		    sectionAndAfter = this.text.substr(match.index + match[1].length);

		match = sectionTerminatorRegExp.exec(sectionAndAfter);
		var afterSection = match ? sectionAndAfter.substr(match.index) : "";

		this.text = beforeSection + sectionTitle + (emptyAtEnd ? "\n" : "") + value
				+ (afterSection ? ("\n" + afterSection) : "");
	} else // add anew
		this.text = this.text + "\n!"+sectionName + "\n"+value;

	// setting dirty, notifying is not done here
};

// Sets the slice value if it is present, otherwise appends it as |name|value|
//  either after the last slice or to the beginning of the text (if no slices are present)
//
Tiddler.prototype.setSlice = function(sliceName,value)
{
	var replaceSliceSubPart = function(text,part,oldValue)
	{
		if(oldValue == value)
			return text;
		var eOldValue  = oldValue.escapeRegExp(),
		    eSliceName = sliceName.escapeRegExp();

		// "table" notation
		var simplifiedPattern = "^(.*"+eSliceName+".*\\|.*)"+eOldValue+"(.*\\|)$",
		    simplifiedRegExp = new RegExp(simplifiedPattern),
		    newPart = part.replace(simplifiedRegExp,function($0,$1,$2){
			return $1 + value + $2;
		    });
		if(newPart != part)
			return text.replace(part, newPart);

		// "sliceName: sliceValue" notation
		simplifiedPattern = "^(.*"+eSliceName+"\\:[\\s\\t])"+eOldValue+"(.*)$";
		simplifiedRegExp = new RegExp(simplifiedPattern),
		newPart = part.replace(simplifiedRegExp,function($0,$1,$2){
			return $1 + value + $2;
		});
		if(newPart != part)
			return text.replace(part, newPart);
	};
	// modification of TiddlyWiki.prototype.slicesRE to process "|..sliceName..||" syntax
	// empty slices in the "sliceName:" notation are not supported
	var re = /(?:^([\'\/]{0,2})~?([\.\w]+)\:\1[\t\x20]*([^\n]+)[\t\x20]*$)|(?:^\|\x20?([\'\/]{0,2})~?([^\|\s\:\~\'\/]|(?:[^\|\s~\'\/][^\|\n\f\r]*[^\|\s\:\'\/]))\:?\4[\x20\t]*\|[\t\x20]*([^\n\t\x20]?(?:[^\n]*[^\n\t\x20])?)[\t\x20]*\|$)/gm;
	re.lastIndex = 0;
	var m = re.exec(this.text);
	while(m) {
		if(m[2]) {
			if(m[2] == sliceName) {
				this.text = replaceSliceSubPart(this.text,m[0],m[3]);
				break;
			}
		} else {
			if(m[5] == sliceName) {
				this.text = replaceSliceSubPart(this.text,m[0],m[6]);
				break;
			}
		}
		m = re.exec(this.text);
	}

	if(!m || !m[2] && !m[5]) // if the slice is not present
	{
		// append after the last slice/to the start of text (adapted from GridPlugin)
		var matches = this.text.match(re),
		    lastSlice = matches ? matches[matches.length-1] : null,
		    where = lastSlice ? this.text.indexOf(lastSlice)+lastSlice.length : 0;

		this.text = this.text.substr(0,where)+
			(lastSlice ? '\n|%0|%1|' : '|%0|%1|\n').format(sliceName,value)+
			this.text.substr(where);
	}

	// recalc "stored" slices for this tiddler:
	delete store.slices[this.title];

	// setting dirty, notifying is not done here
};

config.macros.insertEditable = {
	handler: function(place,macroName,params,wikifier,paramString,tiddler)
	{
		// parse and attach params to DOM
		var pParams   = paramString.parseParams("tiddler",null,true,false,true),
		    cell      = params.contains("cell"),
		    fill      = cell || params.contains("fillElement"),
		    wrapper   = fill ? place : createTiddlyElement(place,"span"),
		    tidName   = getParam(pParams,"tiddler",""),
		    partName  = getParam(pParams,"container","t"),
		    applyOnEnter = getParam(pParams,"applyOnEnter");

		wrapper.options = {
			tiddler:	tidName ? store.fetchTiddler(tidName) : tiddler,
			part:		partName,
			viewType:	getParam(pParams,"viewType","wikified"),
			withButton:	params.contains("button") || window.isOpenedOnTouchScreen(), // default for touch screens
			defaultShowText:  getParam(pParams,"showIfNoValue"),
			preprocess:	getParam(pParams,"preprocess",""),
			size:		getParam(pParams,"size",""),
			fill:		fill,
			transparentEmpty: cell || params.contains("transparentEmpty"),
			saveOnApply:	params.contains("saveOnApply"),
			applyOnEnter:	(partName[0] == ":") || (partName[0] == "!") ||
					(applyOnEnter === undefined ?
					params.contains("applyOnEnter") : applyOnEnter),
			keepOnBlur:	params.contains("keepOnBlur"),
			noNotify_partial: cell || params.contains("noNotify"),
			noedit:		params.contains("noedit")
		};

		this.turnViewMode(wrapper);
	},
	getData: function(tiddler, part)
	{
		var partName = part.substr(1);
		switch(part[0]) {
			case "t":
				return tiddler.text || "";
			case "!":
				return tiddler.title;
			case "#":
				return (tiddler.getSection ? tiddler.getSection(partName)
					: store.getTiddlerText(tiddler.title+"##"+partName))
						|| "";
			case ":":
				return (tiddler.getSlice ? tiddler.getSlice(partName)
					: store.getTiddlerText(tiddler.title+"::"+partName))
						|| "";
			case "@":
				return store.getValue(tiddler,partName) || "";
		}
	},
	setData: function(tiddler, part, value, noNotify_partial)
	{
		var partName = part.substr(1);
		//# deal with the case when tiddler doesn't exist yet

		switch(part[0]) {
			case "t":
				store.saveTiddler(tiddler,null,value);
				break;
			case "!":
				store.saveTiddler(tiddler,value); // requires my fix to .sT
				break;
			case "#":
				tiddler.setSection(partName,value);
				store.setDirty(true);
				// refresh display of the corresponding tiddler:
				if(!noNotify_partial) store.notify(tiddler.title,true);
				break;
			case ":":
				tiddler.setSlice(partName,value);
				store.setDirty(true);
				if(!noNotify_partial) store.notify(tiddler.title,true);
				break;
			case "@":
				store.setValue(tiddler,partName,value);
				break;
		}
		//# change the "modifier/d" fields?
	},
	turnViewMode: function(place)
	{
		if(place.options.part[0] != "c" && !place.options.tiddler)
			return;
		//# may be add warning; also, do same for unsupported container types

		var value  = this.getData(place.options.tiddler, place.options.part) ||
				place.options.defaultShowText || "",
		    fill   = place.options.fill,
		    noedit = place.options.noedit,
		    classEmpty = place.options.transparentEmpty ? "transparentEmptyViewer" : "emptyViewer";
 		var preprocessScript = place.options.preprocess;
 		if(preprocessScript) {
			var fullScript =
				"place.options.preprocessFunc = function(text){"+
				"var q = \"'\"\n"+
				preprocessScript+
				"\nreturn text;};";
			eval(fullScript);
			value = place.options.preprocessFunc(value);
		}
 		if(fill)
			place.style.padding = (place.options.initialPadding !== undefined)
 				? place.options.initialPadding
				: place.style.padding;
		if(fill && !noedit && !value)
			place.classList.add(classEmpty);
		var html = '<span'+
		(!fill && !value ? ' class="'+classEmpty+'"' : '')+
		'></span>';
		place.innerHTML = html;
		place.onclick = function(e)
		{	
			// prevent editor-containing popup closing etc:
			if(e.stopPropagation) e.stopPropagation();
			e.cancelBubble = true;
			// prevent editing when clicking on links,buttons,images; if noedit:
			if(noedit || e.target.tagName in {A:1,IMG:1}) return true;

			place.classList.remove(classEmpty);
			config.macros.insertEditable.turnEditMode(this);
			return false;
		}
		var container = fill ? place : place.firstChild;
		switch(place.options.viewType) {
			case "plain":
				createTiddlyText(container,value);
				break;
			case "html":
				container.innerHTML = value;
				break;
			case "wikified":
			default:
				wikify(value,container,null,place.options.tiddler);
		}
	},
	turnEditMode: function(place) {
		var applyChanges = function()
		    {
			config.macros.insertEditable.setData(place.options.tiddler, place.options.part, editarea.value, place.options.noNotify_partial);
			config.macros.insertEditable.turnViewMode(place);
			if(place.options.saveOnApply)
				autoSaveChanges();
		    },
		    applyingChanges = false,
		    value = this.getData(place.options.tiddler, place.options.part),
		    rowslimit = 1,
		    size = place.options.size,
		    html = (size == "minimal" || size == "min") ?
			   '<input type="text" class="mini-inline-editor"></input>'
			 :('<textarea class="inline-editor"' +
			   ' style="height: '+rowslimit+'.1em;'+
			   (size == "max" ? 'width:100%;' : 
			    (place.options.fill ? 'width:98%;' : ''))+'"' +
			   '></textarea>'),
		    button;
		place.innerHTML = html;
		place.onclick = null;
		if(place.options.withButton) {
			button = createTiddlyButton(place,"save",null,applyChanges);
			// fires before onblur, allowing to save:
			button.onmousedown = function(){applyingChanges = true;};
		}

		if(place.options.fill) {
			place.options.initialPadding = place.style.padding;
			place.style.padding = "0";
		}
		var editarea = place.firstChild;
		editarea.value = value;
		editarea.onkeydown = function(ev)
		{
			var e = ev || window.event;
			if(e.which == $esc) {
				// for now, don't check if was changed
				config.macros.insertEditable.turnViewMode(place);
				jQuery('html').off("click",switchToViewOnClick);
			}
			if((e.which == $enter) && (e.ctrlKey || place.options.applyOnEnter))
			{
				applyChanges();
				e.cancelBubble = true;
				if(e.stopPropagation) e.stopPropagation();
				return false;
			}
		};
		// on click outside the edit area, switch to the view mode (useful for touchscreens)
		var inside = false,
		    switchToViewOnClick = function(e) {
			if(applyingChanges || inside || place.options.keepOnBlur) {
				inside = false;
				return;
			}
			config.macros.insertEditable.turnViewMode(place);
			jQuery('html').off("click",switchToViewOnClick);
		};
		jQuery('html').click(switchToViewOnClick);
//# avoid creating of a "global" handler? (to remove extra code from .onkeydown handler)
		jQuery(editarea).click(function() {
			inside = true;
		});
		//# add handlers for touch? (ph: no ctrl+enter)
		editarea.focus();
		if(!window.isOpenedOnTouchScreen()) // with FF for Android, better not to
			editarea.select();

		editarea.onkeypress = adjustHeightToContent;
		adjustHeightToContent.apply(editarea);
	}
};

// define styles
var bgColor = store.getTiddlerText("ColorPalette::Background");
setStylesheet(
	'.emptyViewer { color: #dddddd; background-color: #dddddd; }\n'+
	'.emptyViewer:before, .transparentEmptyViewer:before { content: "__" }\n'+
	'.mini-inline-editor { width: 1.5em; }\n'+
	'@-moz-document url-prefix() {.inline-editor { font-family: Consolas !important; font-size: 100% !important; }}\n'+
	'.transparentEmptyViewer { color: '+bgColor+'; background-color: '+bgColor+'; }',
"StyleSheetInsertEditable");
//# think about better styling (no "__" when copying)

// =========== Extras ===========
// hijack edit macro to make tiddler titles editable inline
(function(){
/* config.macros.chkEditableTitles = (config.macros.chkEditableTitles === undefined) ?
	!('ontouchstart' in window) : config.macros.chkEditableTitles;
	// default for non-touch screens for now

 if(!config.macros.chkEditableTitles)
	return;

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

	if(readOnly || !(tiddler instanceof Tiddler) || tiddler.isReadOnly() ||
	   params[0] != "title" || !place.classList.contains("title"))
	// the last is a hack for avoiding insertEditable in the <<list>> macro
		return this.iep_orig_handler.apply(this,arguments);

	wikify('<<insertEditable container:"!" size:max viewType:plain>>',place,null,tiddler);
	//story.setDirty(tiddler.title,true); ?
 };
*/
})()
//}}}
/***
!!!Sets API and macros
***/
//{{{
// overwrite filterTiddlers to enable different kinds of hijacking
//# to be incorporated into the core
TiddlyWiki.prototype.filterTiddlers = function(filter,results)
{
	var re = /([^\s\[\]]+)|(?:\[([ \w\.\-]+)\[([^\]]+)\]\])|(?:\[\[([^\]]+)\]\])/mg;

	results = results || [];
	var match, handler;
	if(filter)
		while(match = re.exec(filter)) {
			handler = ( match[1] || match[4] ) ? 'tiddler' :
					config.filters[match[2]] ? match[2] : 'field';
			results = config.filters[handler].call(this,results,match);
		}

	return results;
};

// extendable set of elementary sets definitions
// "?" is for the belongCheck, "+" - for addAction getter, "-" - for dropAction getter
config.elementarySets =
{
	tiddler: {
		"?": function(title,tiddler) {
			return tiddler.title == title;
		},
		"+": function(title) {
			return ""; // no action for now
		},
		"-": function(title) {
			return ""; // no action for now
		}
	},
	tag: {
		"?": function(tagName,tiddler) {
			return tiddler.tags.contains(tagName);
		},
		"+": function(tagName) {
			//# check the absence of ",,","::",";;"
			return "+tag.."+tagName;
		},
		"-": function(tagName) {
			//# check the absence of ",,","::",";;"
			return "-tag.."+tagName;
		}
	}
}

config.macros.defineSet =
{
	sets: {},

	add: function (setName, setDefinition, setTags)
	{
		if(this.sets[setName])
			displayMessage("the set \""+setName+"\" will be redifined");
		//# this behaviour may be changed if necessary

		this.sets[setName] = { definition: setDefinition, tags: setTags };
	},

	// this returns a function(tiddler) which checks if the tiddler is in the set;
	//  if there's some "do" parts, it ignores them if forceFunc and
	//  returns an array of tiddlers instead of a function otherwise
	//
	getIsInSet: function (setDefinition,forceFunc)
	{
		var returnFunc = true, definedPart, resultRecursive,
		    parts = setDefinition.parts, type, i,
		    singleTokenRE = /(?:(\w*)\[((?:[^\]]|(?:\]\]))*)\])/,
		    tokenMatch, tokenType, tokenValue;
//# precalc setDefinition.parts[i].definedPart in .parseSetDefinition? (although func/not func stuff ..)

		// for each part..
		for(i = 0; i < parts.length; i++)
		{
			definedPart = null;

			// process tokens first
			if(parts[i].token)
			{
				tokenMatch = singleTokenRE.exec(parts[i].token);
//# single tokens first; .oO when multiple are needed, implement parsing
				if(!tokenMatch) continue;
				tokenType  = tokenMatch[1];
				tokenValue = tokenMatch[2];
				definedPart = {
					type:  tokenType,
					value: tokenValue
				};

				// process elementaries with corresponding handlers
				if(config.elementarySets[tokenType]) {
					definedPart.checkTiddler = function(tiddler) {
						return config.elementarySets[this.type]["?"]
							(this.value, tiddler);
					};
					parts[i].definedPart = definedPart;
					continue;
				}

				//# process non-elementary tokens,
				//  for "set" and "setsTagged", launch recursively,
				//  if returns an array of tids instead of a function
				//  and !forceFunc, set returnFunc = false
			// in contrast to inline sets, named sets can cause infinite loops..
			}

			// next, process "sets" (defined for brackets)
			if(parts[i].set)
			{
				resultRecursive = this.getIsInSet(parts[i].set,forceFunc);
				parts[i].definedPart = resultRecursive instanceof Function ?
					{ checkTiddler: resultRecursive } : resultRecursive;
				// in the latter case resultRecursive is an array with tids
				continue;
			}

			//# for each "do" if !forceFunc, /skip/ it;
			//  set returnFunc = false otherwise
		}

		if(returnFunc) {
		// combine checks from parts via setDefinition.combine (example: "+4*1-3+5")
			return function(tiddler) {
				var re = /([\+\-\*])(\d+)/g, m, isGood = false, val;
//console.log(".combine: "+setDefinition.combine+", parts:");console.log(parts);
//console.log("find definedParts in parts[i].definedPart");
				while(m = re.exec(setDefinition.combine)) {
					i = parseInt(m[2]);
					val = parts[i].definedPart.checkTiddler(tiddler);
//console.log(i+": val is "+val+", isGood is "+isGood);
					switch(m[1])
					{
						case "+": isGood = isGood ||  val; break;
						case "*": isGood = isGood &&  val; break;
						case "-": isGood = isGood && !val; break;
					}
				}
				return isGood;
			};
		} //else
			//# build and return an array of tiddlers
	},
	getIsInNamedSet: function (setName,forceFunc)
	{
		var set = this.sets[setName];
		if(!set) return null;
		return this.getIsInSet(set.definition,forceFunc);
	},
	getSetTiddlers: function (setDefinition,results)
	{
		results = results || [];

		var check = this.getIsInSet(setDefinition);
		if(!check)
			return results;

		if(check instanceof Function)
			store.forEachTiddler(function(tName,tiddler) {
				if(check(tiddler)) results.pushUnique(tiddler);
			});
		else
			// check is not a function, but an array of tiddlers
			for(var i = 0; i < check.length; i++)
				results.pushUnique(check[i]);

		return results;
	},
	getNamedSetTiddlers: function (setName,results)
	{
		var set = this.sets[setName];
		if(!set) return results;
		return this.getSetTiddlers(set.definition,results);
	},
	calcActionStepsInDefinition: function (setDefinition)
	{
		var parts = setDefinition.parts, i,
		    singleTokenRE = /(?:(\w*)\[((?:[^\]]|(?:\]\]))*)\])/,
		    tokenMatch, tokenType, tokenValue;

		// for each part
		for(i = 0; i < parts.length; i++)
		{
			// create descriptions of 2 actions sequences:
			// one adds a tiddler to the set, another removes the tiddler from it

			// process tokens first
			if(parts[i].token)
			{
				tokenMatch = singleTokenRE.exec(parts[i].token);
//# single tokens first; .oO when multiple are needed, implement parsing
				if(!tokenMatch) continue;
				tokenType  = tokenMatch[1];
				tokenValue = tokenMatch[2];
				
				// process elementaries with corresponding handlers
				if(config.elementarySets[tokenType]) {
//# add "don't recalc if already calced"
					parts[i].addAction = config.elementarySets[tokenType]
						["+"](tokenValue);
					parts[i].dropAction= config.elementarySets[tokenType]
						["-"](tokenValue);
					continue;
				}

				//# process non-elementary tokens,
				//# ...
			}

			// next, process "sets" (defined for brackets)
			if(parts[i].set)
			{
//# add "don't recalc if already calced"
				parts[i].addAction = this.getAddToSetAction(parts[i].set);
				parts[i].dropAction= this.getDropFromSetAction(parts[i].set);
				continue;
			}

			//# do anything about "do"s?
		}
		return;
	},
	getAddToSetAction: function (setDefinition)
	{
		// combine the already calced actions into one
		var re = /([\+\-\*])(\d+)/g, m, i, partActions, actions = "";
		while(m = re.exec(setDefinition.combine))
		{
			i = parseInt(m[2]);
			partActions = setDefinition.parts[i];
			switch(m[1])
			{
				case "+":
					if(actions || !partActions.addAction) continue;
					// unless that's the ~first action~, do nothing
					// (we suppose that if one describes a set like
					// "this OR that", than add action adds to "this"
					actions = partActions.addAction;
				break;
				case "*":
					if(!partActions.addAction) continue;
					if(actions) actions += ",,";
					actions += partActions.addAction;
				break;
				case "-":
					if(!partActions.dropAction) continue;
					if(actions) actions += ",,";
					actions += partActions.dropAction;
				break;
			}
		}
		return actions;
	},
	getAddToNamedSetAction: function (setName)
	{
		var set = this.sets[setName];
		if(!set) return null;
		return this.getAddToSetAction(set.definition);
	},
	getDropFromSetAction: function (setDefinition)
	{
		// combine the already calced actions into one
		var re = /([\+\-\*])(\d+)/g, m, i, partActions, actions = "";
		while(m = re.exec(setDefinition.combine))
		{
			i = parseInt(m[2]);
			partActions = setDefinition.parts[i];
			switch(m[1])
			{
				// case "*": do nothing (if a tiddler is droped from "a",
				// it is dropped from "a OR b" 
				// case "-": same (consider "a" and "a AND NOT b")
				case "+":
					if(!partActions.dropAction) continue;
					if(actions) actions += ",,";
					actions += partActions.dropAction;
				break;
			}
		}
		return actions;
	},
	getDropFromNamedSetAction: function (setName)
	{
		var set = this.sets[setName];
		if(!set) return null;
		return this.getDropFromSetAction(set.definition);
	},
	getSortFieldForNamedSet: function (setName)
	{
		return this.sets[setName] ? this.sets[setName].definition.sortField : null;
	},

	parseSetDefinition: function (text)
	{
		var set = { parts: [], combine: null, sortField: null };

		// remember tokens (..[..]..[..]...), substitute them with their numbers
		var tokenRegExp = /(?:\w*\[(?:[^\]]|(?:\]\]))*\])+/, // "]]" = escaped "]"
		    tokenMatch, origTokenText, tokenText,
		    sortFieldRegExp = /sortField\[(.*)\]/, sortFieldMatch;

		while(tokenMatch = tokenRegExp.exec(text))
		{
			origTokenText = tokenMatch[0];
			tokenText = origTokenText[0]=="[" ?
				("tiddler"+origTokenText) : origTokenText;
			sortFieldMatch = sortFieldRegExp.exec(tokenText);
			if(sortFieldMatch) {
				text = text.replace(origTokenText,"");
				set.sortField = sortFieldMatch[1];
			} else {
				text = text.replace(origTokenText,set.parts.length);
				set.parts.push({ token: tokenText });
			}
		}

		// find first-level brackets, add definitions, substitute in text
		var openPosition = text.indexOf("("), i, level = 0, closePosition, setText;
		while(openPosition > -1)
		{
			// find closing bracket position
			level = 1; i = openPosition+1;
			while(level > 0) {
				if(text[i] == "(")
					level++;
				if(text[i] == ")")
					level--;
				i++;
			}
			closePosition = i;

			// add definition, parse it recursively, subsititute
			setText = text.substring(openPosition+1, closePosition-1)
					// substitute numbers in setText back with tokens:
					.replace(/\d+/g,function(match){
						return set.parts[parseInt(match)].token
					});

			set.parts.push({ set: this.parseSetDefinition(setText) });
			text = text.substring(0,openPosition) + (set.parts.length-1)
				+ text.substring(closePosition);

			// find next open
			openPosition = text.indexOf("(");
		}

		// find <num> DO <num>, add them to parts, substitute
		var doRegExp = /(\d+)\s+DO\s+(\d+)/m, doMatch, target, action;
		while(doMatch = doRegExp.exec(text))
		{
			action = parseInt(doMatch[2]); target = parseInt(doMatch[1]);
			action = set.parts[action].token;
			target = set.parts[target].token || target;

			// add the definition part, substitute the DO expression in the text
			set.parts.push({ do: action , to: target });
			text = text.replace(doMatch[0],set.parts.length-1);
		}

		set.combine = "+"+text.replace(/\s+AND\s+/g,"*")
				 .replace(/\s+NOT\s+/g,"-")
				.replace(/\s+OR\s+/g,"+")
				.replace(/^ +/,"").replace(/ +$/,"").replace(/ +/g,"+");

		this.calcActionStepsInDefinition(set);

		return set;
	},
	handler: function(place,macroName,params,wikifier,paramString,tiddler)
	{
		// parse params
		var parsedParams = paramString.parseParams("name",null,true,false,true),
		    setName = getParam(parsedParams,"name"),
		    setText = getParam(parsedParams,"tids"),
		    setSortField = getParam(parsedParams,"sortField",""),
		    setTagsLine = getParam(parsedParams,"tags",""),
		    setTags = setTagsLine.readBracketedList();
		if(!setName || !setText)
			return;
		if(setSortField) setText += " sortField["+ setSortField +"]";
		var setDefinition = this.parseSetDefinition(setText);

		// show macro text
		var w = wikifier, macroTWcode = w.source.substring(w.matchStart,w.nextMatch),
		    hide = getFlag(parsedParams, "hide", false) || params.contains('hide');
		if (!hide)
			createTiddlyText(createTiddlyElement(place,"code"),macroTWcode);

		// define the set
		this.add(setName, setDefinition, setTags);
	}
}

// hijack filterTiddlers so that if there's "set:..." part with an optional terminator
//  ":set", then that part is parsed as a definition of a set
TiddlyWiki.prototype.ds_orig_filterTiddlers = TiddlyWiki.prototype.filterTiddlers;
TiddlyWiki.prototype.filterTiddlers = function(filter,results)
{
	var beginSetMark = "set:", endSetMark = " modify:";

	// set definition starts with "set:", if no such thing, use ordinary filtering
	if(filter.indexOf(beginSetMark) != 0)
		return this.ds_orig_filterTiddlers(filter,results);

	// add tiddlers from the set
	results = results || [];
	var modifyPos = filter.indexOf(endSetMark), filterAsWell = (modifyPos != "-1"),
	    setDef = filterAsWell ? filter.substring(4,modifyPos) : filter.substr(4),
	    tids = config.macros.defineSet.getSetTiddlers(
		config.macros.defineSet.parseSetDefinition(setDef),results);

	// if necessary, apply the additional filters, return
	if(!filterAsWell)
		return results;
	filter = filter.substr(modifyPos + endSetMark.length);
	return this.ds_orig_filterTiddlers(filter,results);
};
config.filters.set = function(results,match)
{
	var setName = match[3];
	return config.macros.defineSet.getNamedSetTiddlers(setName,results);
};

//-------------------------------------------------------------------------------
// wikify SetsList on startup
//
var readSetsList = function()
{
	if(!window.store)
		return setTimeout(readSetsList,100);

	var setsList = store.fetchTiddler("SetsList"),
	    setsListText = setsList ? setsList.text : "";

	if(setsListText)
		wikify(setsListText,document.createElement("div"),null,setsList);
};
setTimeout(readSetsList,100);
//# test why this first timeout is needed (copied from CTP, STP)
//}}}
/***
|Requires|ForEachTiddlerPlugin SetManagerPlugin|
|Version|0.4|
этот плагин предназначен для дополнительных elementary sets, switch actions и др. необязательных расширений SetManagerPlugin
* ForEachTiddlerPlugin нужен, пока {{{.getSlice}}} и {{{.getSection}}} не определены в ядре

to do<<tiddler [[to do list template##main]] with:"set extras todo" with:orderSEtodo noedit>>
***/
//{{{
(function(){
if(!config.macros.itemMenu)
	return;

config.macros.itemMenu.actionStepsWithoutArguments["markChanged"] = function(tiddler)
{ // for coordinator
	tiddler.modifier = config.options.txtUserName;
	tiddler.modified = new Date();
};

config.elementarySets.hasPart =
{
	"?": function(param,tiddler)
	{
		var regExpText, re, type;

		switch(param.substr(0,2)) {
			case config.textPrimitives.sectionSeparator: // ##
				return !!(tiddler.getSection(param.substr(2)));
			case config.textPrimitives.sliceSeparator:   // ::
				return !!(tiddler.getSlice(param.substr(2)));
			case "@@":
				return !!(tiddler.fields[param.substr(2).toLowerCase()]);
			case "r@": // regExp (for tiddler.text) mode
			case "R@":
			case "t@": // title mode
			case "T@":
				regExpText = store.getTiddlerText(param.substr(2));
				type = param.substr(0,1);
				if(!regExpText) {
					if(type == "r" || type == "t")
						return true; // "forgiving mode", nothing is filtered out in this case
					else
						throw("RegExp for checkTiddler is not found in " + param.substr(2));
//# test this case out
				}
				// no break here
			case "r[":
			case "R[":
			case "t[":
			case "T[":
				if(!regExpText) {
					regExpText = param.substr(2);
					type = param.substr(0,1);
				}
				if(type == "r" || type == "t") {
					try {
						re = new RegExp(regExpText);
					} catch(e) {
						return false; // "forgiving mode"
					}
				} else
					re = new RegExp(regExpText);

				if(type == "r" || type == "R")
					return !!(tiddler.text.match(re));
				else
					return !!(tiddler.title.match(re));
		}
		return false;
	},
	"+": function(param) {
		return ""; // for now, no idea what to do here; this won't matter frequently
	},
	"-": function(param) {
		return ""; // for now, no idea what to do here; this won't matter frequently
	}
};

config.macros.itemMenu.actionStepsWithoutArguments["-tags"] = function(tiddler) {
	tiddler.tags = [];
};
config.elementarySets.taggedOnly = 
config.elementarySets.oTag = {
	"?": function(tag,tiddler) {
		return (tiddler.tags.length == 1) && (!tag || tag == tiddler.tags[0]);
	},
	"+": function(tag) {
		return "-tags,,+tag.."+tag;
	},
	"-": function(tag) { return "-tag.."+tag; }
};

config.elementarySets.unclassified = {
//# implemented "as was"; may be scanning the whole tree instead
	"?": function(metaTag,tiddler) {

		var tags = [], tag, i;
		for(i = 0; i < tiddler.tags.length; i++) {

			tag = store.fetchTiddler(tiddler.tags[i]);
			if(tag && tag.tags.contains(metaTag))
				return false;
		}
		return true;
	},
//# implement
	"+": function(metaTag) {
		return ""; /*<addToSetAction> - show "available" tags, ask for a tag to add
				(may be new; in that case create a tag-tid with metaTag) */
	},
	"-": function(metaTag) {
		return "" /*<dropFromSetAction> - remove tags tagged with metaTag*/;
	}
};

//config.macros.itemMenu.actionStepsWithArguments["<name>"] = function(tiddler,arg) {
//};
//config.macros.itemMenu.actionStepsWithoutArguments["<name>"] = function(tiddler) {
//};
//config.elementarySets["<name>"] = {
//	"?": function(param,tiddler) { return <isInSet> },
//	"+": function(param) { return <addToSetAction> },
//	"-": function(param) { return <dropFromSetAction> }
//}
})()
//}}}
/***
|''Name''|SharedTiddlersPlugin|
|''Description''|Introduces a possibility to use tiddlers from other ~TiddlyWikis (with or without importing them)|
|''Documentation''|http://yakovl.bplaced.net/TW/STP/STP.html#SharedTiddlersPluginInfo|
|''Type''|plugin|
|''Version''|2.4.0"'|
|''~CoreVersion''|2.2.6|
|''Requires''|UpToDateFiltersPlugin|
|''Requirements note''|[[UpToDateFiltersPlugin|http://yakovl.bplaced.net/TW/ExtraFilters.html#UpToDateFiltersPlugin]] is necessary only for TW below v2.6.2|
|''Source''|http://yakovl.bplaced.net/TW/STP/STP.html#SharedTiddlersPlugin|
|''Author''|Yakov Litvin|
|''Forked from''|[[IncludePlugin|http://tiddlywiki.abego-software.de/#IncludePlugin]], by Udo Borkowski|
|''Contact''|see [[docs|SharedTiddlersPluginInfo]]|
|''Copyright''|Yakov Litvin, 2013|
|''Licence''|[[BSD-like open source license|http://yakovl.bplaced.net/TW/STP/STP.html#%5B%5BYakov%20Litvin%20Public%20Licence%5D%5D]] |
|>| In this tiddler, the code is minified and hidden; the full code can be found [[here|http://yakovl.bplaced.net/TW/STP/STP.html#SharedTiddlersPluginCode]]. |
''Config:''
***/
//{{{
config.options.STP_hijackPrettyLink = true;
config.options.STP_hijackImageFormatter = true;
//}}}
// /%
config.filters.all = function(results,match) {
	if(match[3] == "with included") { // brings included tiddlers as well
		var m,matched = this.reverseLookup();
		for(m = 0; m < matched.length; m++)
			results.pushUnique(matched[m]);
	} else
		this.forEachTiddler(function(tName,tiddler){
			results.pushUnique(tiddler);
		});
	return results;
};
config.filters.includedFrom = function(results,match) {

	var url = twWeb.getStoreUrlById(match[3]);
	if(!url)
		return [];

	for(var i = 0; i < results.length; i++)
		if(results[i].getIncludeURL() != url)
			results.splice(i--,1);
	return results;
};
config.filters.external = function(results,match) {

	for(var i = 0; i < results.length; i++)
		if(!results[i].getIncludeURL())
			results.splice(i--,1);
	return results;
};
config.filters.internal = function(results,match) {

	for(var i = 0; i < results.length; i++)
		if(results[i].getIncludeURL())
			results.splice(i--,1);
	return results;
};

(function(){
//==============================================================================
// install only once stuff

// Ensure the global abego namespace is set up.
if (!window.abego) window.abego = {};

// Install only once (don't install if abego.IncludePlugin is installed as well)
if (abego.TiddlyWikiIncluder)
	return alert("Warning: abego.TiddlyWikiIncluder already exists, so probably two copies of SharedTiddlersPlugin and/or IncludePlugin are installed and activated. It is highly recommended to deactivate all but one copy. You can find those by searching 'abego.TiddlyWikiIncluder'.");

// abego.TiddlyWikiIncluder is defined near the end of the code

// Define the API namespace:
window.sharedTiddlersAPI = {};

//==============================================================================
// Helpers

// This is used instead of displayMessage, because the latter sometimes doesn't show the messages
var displayAndLogMessage = function(text,linkText) {
	displayMessage(text,linkText);
	console.log(text);
};

var invokeLater = function(func, delay, priority) {
	return setTimeout(func,delay);
};

//------------------------------------------------------------------------------
// url helpers

var isRelativeURL = function(url) {
// as Unix filesystem root is "/", urls starting with it are not considered as relative

	return (url.search(/^(?:((http(s)?)|(file)):)|(.\:\\)|(\\\\)|(\/)/) != 0);
};

var getPathFromURL = function(url) {

	return (url.lastIndexOf("/") > -1) ?
		url.substr(0, url.lastIndexOf("/") + 1) : "";
};

var resolveUrlFrom = function(urlToResolve, sourceUrl) {

	return (isRelativeURL(urlToResolve) && sourceUrl) ?
		getPathFromURL(sourceUrl) + urlToResolve : urlToResolve;
};

// limitedly turns URI (URL) reference into an absolute URI (URL) and windows paths into URL
var stp_resolveURL = function(url) {

	if (url.search(/^((http(s)?)|(file)):/) != 0) {
	// no protocol prefix..

		if (isRelativeURL(url))

			url = resolveUrlFrom(url, document.location.toString());
		else
		// "url" is an "absolute" path to a local file. Prefix it with file://

			url = "file://" + url;

		// replace every \ by a /, to cover Windows style pathes
		url = url.replace(/\\/mg,"/");
	}
	return url;
};

//------------------------------------------------------------------------------
// file/tw loading functions

// an evolution of the deprecated loadRemoteFile function with TW 2.7.0 codes
var stp_loadRemoteFile = function(url,callback,params) {
	if(version.major < 2 || version.major == 2 && version.minor < 7) {
		var httpSuccess = function(xhr) {
			try {
				return (!xhr.status && location.protocol === "file:") ||
					(xhr.status >= 200 && xhr.status < 300) ||
					xhr.status === 304 || xhr.status === 1223;
			} catch(e) {}
			return false;
		};
		var options = {
			type:"GET",
			url:url,
			processData:false,
			data:undefined, // cut off?
			cache:false,
			beforeSend: function(xhr) {;},
			complete: function(xhr,textStatus) {
				if(httpSuccess(xhr))
					callback(true,params,xhr.responseText,url,xhr);
				else
					callback(false,params,null,url,xhr);
			}
		};
		try {
			if(window.Components && window.netscape && window.netscape.security && document.location.protocol.indexOf("http") == -1)
				window.netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
		} catch (ex) {}
		return jQuery.ajax(options);
	} else
		return httpReq("GET",url,callback,params);
};

// Asynchronously load the given (local or remote) file.
// 
// @param	url
// value:	either an URL or a local file path to a file
//
//	Examples:
//	* http://www.abego-software.de/index.html
//	* file:///C:/abegoWebSite-Copy/index.html
//	* C:\abegoWebSite-Copy\index.html    (for Windows machines)
//
//	Notice: backslashes in JavaScript string constants must be escaped, 
//	i.e. the last example must be written as: "C:\\abegoWebSite-Copy\\index.html"
//	when "hardcoded" in JavaScript source code.
// 
// @param	callback
// value:	function(content,url,params,errorMessage) 
//		called at the end of the operation. 
//		On success content holds the content of the loaded file. 
//		On error content is undefined and errorMessage holds an error message. 
//		params is the params passed into stp_LoadFile.
//
// @param	params
//		passed through to the callback function
// 
var stp_LoadFile = function(url,callback,params) {

	var onLoad = function(status,params,responseText,url,xhr) {
		return status 
				? callback(responseText, url, params)
				: callback(undefined, url, params, "Error loading %0".format([url]));
	};

	// Make sure the URL is a real URL, with protocol prefix etc.
	url = stp_resolveURL(url);
	
	stp_loadRemoteFile(url,onLoad,params);
};

// Asynchronously load the given (local or remote) TiddlyWiki store.
// 
// @param	url
// value:	either an URL or a local file path to a TiddlyWiki file (absolute or relative)
//
//	Examples:
//	* http://www.abego-software.de/index.html
//	* file:///C:/abegoWebSite-Copy/index.html
//	* include/beta.html
//	* C:\abegoWebSite-Copy\index.html    (for Windows machines)
//
//	Notice: backslashes in JavaScript string constants must be escaped, 
//	i.e. the last example must be written as: "C:\\abegoWebSite-Copy\\index.html"
//	when "hardcoded" in JavaScript source code.
// 
// @param	callbackWithStore
// value:	function(theStore,url,params,errorMessage) 
//			called at the end of the operation. 
//			On success theStore holds the loaded store (a TiddlyWiki object). 
//			On error theStore is undefined and errorMessage holds an error message. 
//			params is the params passed into stp_loadTiddlyWikiStore
//
// @param	params
//		passed through to the callbackWithStore
//
// @param	progress	[optional]
// value:	function(message, sender, state, url, params)
//		called in various situations during the operation,
//		typically used to show "the progress" of the operation.
//		sender: the constant "stp_loadTiddlyWikiStore"
//		state: one of these: "Started", "Processing", "Done", "Failed"
//		"Processing" means the data has been received and in now processed.
// 
var stp_loadTiddlyWikiStore = function(url,callbackWithStore,params,progress) {
	
	var sendProgress = function(message, state) {
		if (progress)
			progress(message,"stp_loadTiddlyWikiStore",state,url,params);
	};
	
	var sendError = function(message) {
		sendProgress("Error when loading %0".format([url]),"Failed");
		callbackWithStore(undefined, url, params, message);
		return message;
	};

	var sendStore = function(store) {
		sendProgress("Loaded %0".format([url]),"Done");
		callbackWithStore(store, url, params);
		return null;
	};
	
	
	var callback = function(content,theURL,params,errorMessage) {
		if (content === undefined) {
			sendError(errorMessage);
			return;
		};
		
		sendProgress("Processing %0".format([url]),"Processing");
		var invalidFileErrorMsg = "The file '%0' does not appear to be a valid TiddlyWiki file";
		try {
			// Load the content from the "content" string into a TiddlyWiki() object
			var importStore = new TiddlyWiki();
			var errorText = importStore.importTiddlyWiki(content)? null :
				"Problem with importing TiddlyWiki, probable reason is: "+
				invalidFileErrorMsg.format([url]);
			if(errorText)
				sendError(errorText);
			else
				sendStore(importStore);
		} catch (ex) {
			sendError(exceptionText(ex));
		};
	};
	
	sendProgress("Start loading %0".format([url]),"Started");
	stp_LoadFile(url,callback,params);
};

//------------------------------------------------------------------------------
// plugin installation helpers

var getArbitraryPluginInfo = function(tiddler) {
// getPluginInfo can't be used (for included tiddlers) because of the ugly slice handling of the core
//  (it uses {store} where it could handle the tiddler directly)
	var pInfo = {};

	var slices = {};
	var requiredInfo = ["Name","Description","Version","Requires","CoreVersion","Date","Source", "Author","License","Browsers"];
	store.slicesRE.lastIndex = 0;
	var match = store.slicesRE.exec(tiddler.text);
	while(match) {
		if(match[2])
			slices[match[2]] = match[3];
		else
			slices[match[5]] = match[6];
		match = store.slicesRE.exec(tiddler.text);
	}
	for(var i = 0; i < requiredInfo.length; i++)
		if(slices[requiredInfo[i]])
			pInfo[requiredInfo[i]] = slices[requiredInfo[i]];
	pInfo.tiddler = tiddler;
	pInfo.title = tiddler.title;
	pInfo.log = [];

	return pInfo;
}

var checkPluginInstalled = function(pluginName) {
	for(var i = 0; i < installedPlugins.length; i++)
		if(installedPlugins[i].title == pluginName || installedPlugins[i].Name == pluginName)
			return true;
	return false;
}

var installPlugin = function(tiddler,force) {

	var pluginName = getArbitraryPluginInfo(tiddler).Name || tiddler.title;
	// check if such a plugin was installed previously, return if so
	if(!force) // two layers to improve the speed in the "force == true" case
		if(checkPluginInstalled(pluginName))
			return;

	// get the plugin info
	var pluginInfo = getArbitraryPluginInfo(tiddler);
	if(tiddler.getIncludeURL())
		pluginInfo.log.push("included from "+tiddler.getIncludeURL());

	// install the plugin
	pluginInfo.executed = true;
	var startTime = new Date();

	try {
		window.eval(tiddler.text);
	} catch(ex) {
		pluginInfo.log.push(config.messages.pluginError.format([exceptionText(ex)]));
		pluginInfo.error = true;
		console.log("error evaluating " + tiddler.title, ex);
		story.displayTiddler(null,"PluginManager");
		displayMessage(config.messages.customConfigError);
	}
	pluginInfo.startupTime = String((new Date()) - startTime) + "ms";

	// register the plugin
	installedPlugins.push(pluginInfo);
}


//==============================================================================
// Shared Tiddlers Plugin

// Constants

var WAITING = "waiting";
var LOADING = "loading";

var ANI_DURATION_HIDE_STATE = 1000;

var REFRESH_PRIORITY = -200;
var ANIMATION_PRIORITY = -100;
var UPDATE_STATE_PRIORITY = -300;

// --------------------------------------------------
// Variables

var useInclude;         // this variable can be used to control include enabling by other things than cookies
var includedStores = {};  // url(String) -> TiddlyWiki or String; if not (yet) loaded a status or an error string
var pendingOnLoadURLs = []; // [] of String: a list of urls that should be passed with the next "notifyListeners"
var refreshTiddlyWikiTimerID; // for delayed refresh
var listeners = [];
var progress;

// rules pointing which stores tiddlers with conflicting names should be fetched from
function FetchPreferences() {
	var prefs = {};
	
	this.add = function(tiddlerName,sourceUrl,substitute,substituteShadow) {
		if(!substitute && !substituteShadow)
			return;
		if(prefs[tiddlerName] == undefined)
			prefs[tiddlerName] = {urlsSubs: [], urlsSubsSh: []};
		if(substitute)
			prefs[tiddlerName].urlsSubs.pushUnique(sourceUrl);
		if(substituteShadow)
			prefs[tiddlerName].urlsSubsSh.pushUnique(sourceUrl);
	};
/*	this.getPrefs = function(tiddlerName) {
		return jQuery.extend(true, {}, prefs[tiddlerName]);
	};
*/	this.getSubsUrl = function(tiddlerName) {
		var pref = prefs[tiddlerName];
		if(pref == null)
			return null;
		if(pref.urlsSubs.length == 0)
			return null;
		return pref.urlsSubs[0];
	};
	this.getSubsShUrl = function(tiddlerName) {
		var pref = prefs[tiddlerName];
		if(pref == null)
			return null;
		if(pref.urlsSubsSh.length == 0)
			return null;

		// check if there's a tiddler with both preferences
		var i, j;
		for(i = 0; i < pref.urlsSubs.length; i++)
			for(j = 0; i < pref.urlsSubsSh.length; j++)
				if(pref.urlsSubs[i] == pref.urlsSubsSh[j])
					return pref.urlsSubs[i];

		return pref.urlsSubsSh[0];
	};
	this.containSubsUrl = function(tName,url) {
		var pref = prefs[tName];
		if(pref == null)
			return false;
		return pref.urlsSubs.contains(url);
	};
	this.containSubsShUrl = function(tName,url) {
		var pref = prefs[tName];
		if(pref == null)
			return false;
		return pref.urlsSubsSh.contains(url);
	};
	this.removeByUrl = function(url) {  // removes all priorities of tiddlers, included from the url

		var tName, pref, i;

		for(tName in prefs) {
			pref = prefs[tName]
			for(i = 0; i < pref.urlsSubs.length; i++)
				if(pref.urlsSubs[i] == url)
					pref.urlsSubs.splice(i--,1);
			for(i = 0; i < pref.urlsSubsSh.length; i++)
				if(pref.urlsSubsSh[i] == url)
					pref.urlsSubsSh.splice(i--,1);
			// if (pref.urlsSubs.length == 0 && pref.urlsSubsSh.length == 0), not nec. to delete pref
		}
	};
};
var fetchPreferences = new FetchPreferences();

function Conflicts() {

	// hashmaps by tiddler name of potentially conflicting tiddlers with that name;
	// each element is a hashmap by url of tiddlers with such names
	var pConfs = {},  // doesn't contain info about tiddlers in the main store
	    pSConfs = {}; // for tiddlers that may conflict when substituting a shadow

	this.init = function() {
		// notify of name conflicts in the whole set of initial and included tiddlers?
		if(config.options.chkWarnOnSharedTiddlersConflicts == undefined) config.options.chkWarnOnSharedTiddlersConflicts = true;
		// use alert() for notifications?
		if(config.options.chkAlertOnSharedTiddlersConflicts == undefined) config.options.chkAlertOnSharedTiddlersConflicts = false;
	};
	// check whether a new tiddler (not included yet) may cause a conflict (now or in the future)
	this.checkNew = function(tName, url, subs, subsSh) {

		// use to add when a tiddler with such name is already present
		var addData = function(confsMap, tidParams) {

			var includeData = function(tidParams) {		// better to create a separate class
				return { subs: tidParams.substitute, warned: false };
			};
			var getSubsPriority = function(data) {
				return data.subs;
			}

			var conf = confsMap[tidParams.title];
			if(!conf) {
				conf = confsMap[tidParams.title] = {};
				conf[tidParams.url] = includeData(tidParams);
			} else {
				var confPart = conf[tidParams.url];
				if(!confPart)
					conf[tidParams.url] = includeData(tidParams);
				else {
					if(!getSubsPriority(confPart) && tidParams.substitute)
						conf[tidParams.url] = includeData(tidParams);
						// better to set subs to true and warned to false
				}
			}
		}

		// check if there's a tiddler with such a name
		var existingTid = forEachLoadedStore(function(theStore, storeUrl) {	// in included stores
			if(storeUrl != url)
				return theStore.fetchTiddler(tName);
		}) || window.sharedTiddlersAPI.orig_fetchTiddler(tName);		// or in the main one
		// if the tiddler is added to the main store after exactly one tiddler is included, no conflict is
		// detected by this algorithm; also, it doesn't account deleting the tiddler from the main store

		if(!existingTid)
			return;
		var mainStoreId = "main store",
		    existingTidUrl = existingTid.getIncludeURL();

		// check conflicts among all included tiddlers (important when no shadow with such name exists)
		if(!pConfs[tName])
			addData(pConfs, {
				title: tName,
				url: existingTidUrl || mainStoreId,
				substitute: existingTidUrl ? fetchPreferences.containSubsUrl(tName,existingTidUrl) : undefined
			});
		addData(pConfs, { title: tName, url: url, substitute: subs });

		// check conflicts among tiddlers that substitute a shadow
		if(subsSh) {
			var existingTidSubsSh = forEachLoadedStore(function(theStore, storeUrl) {
				if(storeUrl != url && fetchPreferences.containSubsShUrl(tName,storeUrl))
					return theStore.fetchTiddler(tName);
			}) || window.sharedTiddlersAPI.orig_fetchTiddler(tName);
			// same problems
			
			if(!existingTidSubsSh)
				return;
			var existingTidSubsShUrl = existingTidSubsSh.getIncludeURL();

			// conflicts among substituting tiddlers are important when a shadow with such name exists
			if(!pSConfs[tName])
				addData(pSConfs, {
					title: tName,
					url: existingTidSubsShUrl || mainStoreId,
					substitute: existingTidSubsShUrl ? fetchPreferences.containSubsShUrl(tName,existingTidSubsShUrl) : undefined
				});
			addData(pSConfs, { title: tName, url: url, substitute: subs });
		}
	};
	this.markNodeUnloaded = function(url) {
		var t;
		for(t in pConfs)
			if(pConfs[t][url])
				delete pConfs[t][url];
		for(t in pSConfs)
			if(pSConfs[t][url])
				delete pSConfs[t][url];
	};
	this.notify = function() {
		var msgAndLog = config.options.chkWarnOnSharedTiddlersConflicts;
		var doAlert   = config.options.chkAlertOnSharedTiddlersConflicts;
		if(!doAlert && !msgAndLog)
			return; // no conflict is marked as "warned" - this is by intent
		var tName, tUrl;

		var checkOrDisplayConflicts = function(map,msgAndLog,markWarned) {
			var msg = ""; // message to return (for alerting etc)
			var addM = function(m) { // pushes all notification messages
				msg += ("\n"+m);
				if(msgAndLog)
					displayAndLogMessage(m);
			};
			var tName, tSources, tUrl, subs, nOfConflicting, newPresent,
				subsMsg = "  (with the subsitute priority)";
				// mark conflicts among tiddlers with the substitute priority

			// find out where necessary, notify
			for(tName in map) {
				tSources = map[tName];

				// find out which priority is of interest
				subs = false;
				for(tUrl in tSources)
					if(tSources[tUrl].subs)
						subs = true;

				// if there's only one tiddler with "substitute", there's no conflict
				nOfConflicting = 0;
				for(tUrl in tSources)
					if(tSources[tUrl].subs == subs)
						nOfConflicting++;
				if(nOfConflicting < 2)
					break;

				// find out if new conflicting tiddlers are present
				newPresent = false;
				for(tUrl in tSources)
					if(tSources[tUrl].subs == subs && !tSources[tUrl].warned)
						newPresent = true;

				// start notification
				if(newPresent) {
					addM("* "+tName+" in:");
					for(tUrl in tSources)
						if(tSources[tUrl].subs == subs && !tSources[tUrl].warned) {
							addM("** "+tUrl);
							if(markWarned)
								tSources[tUrl].warned = true;
						}
					if(subs)
						addM(subsMsg);
				}
			}
			return msg;
		};

		var msg = "";
		if(checkOrDisplayConflicts(pConfs,false,false)) { // new conflicts present
			msg += "New conflicts:";
			if(msgAndLog) displayAndLogMessage(msg);
			msg += checkOrDisplayConflicts(pConfs,msgAndLog,true);
		};
		if(checkOrDisplayConflicts(pSConfs,false,false)) { // new conflicts among tiddlers substituting shadows present
			if(msg) msg += "\n";
			var m = "New conflicts among tiddlers competing for substituting shadows:";
			msg += m;
			if(msgAndLog) displayAndLogMessage(m);
			msg += checkOrDisplayConflicts(pSConfs,msgAndLog,true);
		};
		if(doAlert && msg)
			alert(msg);
	};
//	this.state = // return current state as a string (for includeState)
};
conflicts = new Conflicts();
conflicts.init();

// --------------------------------------------------
// Helper functions

var isIncludeEnabled = function() {
	if (useInclude === undefined)
		useInclude = config.options.chkUseInclude === undefined || config.options.chkUseInclude;
	return useInclude;
};

var getMissingIncludeMsg = function(url) {
	return "No include specified for %0".format([url])
};

// Called after one or more included TiddlyWikis are loaded
//
var notifyListeners = function() {
	var urls = pendingOnLoadURLs;
	pendingOnLoadURLs = [];
	if (urls.length)
		for (var i = 0; i < listeners.length; i++)
			listeners[i](urls);
};

var idleCount; // Reset to 0 when the system is "not idle", incremented inside refreshTiddlyWiki

var refreshTiddlyWiki = function() {
	// To avoid to much refreshing/flickering don't refresh immediately 
	// but wait until the system was idle for a certain time.

	if (refreshTiddlyWikiTimerID !== undefined) clearInterval(refreshTiddlyWikiTimerID);

	idleCount = 0;

	var sendDone = function() {
		twWeb.sendProgress("","","Done");
	};

	refreshTiddlyWikiTimerID = setInterval(function() {
		idleCount++;
		if (idleCount <= 10)
			return;

		clearInterval(refreshTiddlyWikiTimerID);
		refreshTiddlyWikiTimerID = undefined;

		twWeb.sendProgress("Refreshing...","","");
		refreshDisplay();
		invokeLater(sendDone,0,REFRESH_PRIORITY);
	},1);
};

// Calls callback for every loaded store and returns the first non-false/null.. value returned by callback.
//
// @param	callback
// value:	function(store, url)
//
var forEachLoadedStore = function(callback) {
	var result;
	for(var url in includedStores) {
		var theStore = twWeb.getStore(url);
		if (theStore && (result = callback(theStore, url)))
			return result;
	}
};

// hijack fetchTiddler so that it works with tiddlers from included stores as well
var attachToStore = function() {
	if (!window.store)
		return invokeLater(attachToStore,100);

	var orig_fetchTiddler = store.fetchTiddler;
	window.sharedTiddlersAPI.orig_fetchTiddler = orig_fetchTiddler;
	// reserve access to the original method to be able to fetch tiddlers from main store,
	// including substituted ones

	store.fetchTiddler = function(title) {
		var t, subsUrl = fetchPreferences.getSubsUrl(title), subsShUrl;

		// first, look for the tiddler in the main store, unless there's nothing to substitute with
		if(!subsUrl) {
			t = orig_fetchTiddler.apply(this,arguments);
			if(t) return t;
		} else
			return includedStores[subsUrl].fetchTiddler(title);
		
		// then, look for shadowed tiddlers in main store and external ones to substitute those
		if(config.shadowTiddlers[title] !== undefined) {
			if(subsShUrl = fetchPreferences.getSubsShUrl(title))
				return includedStores[subsShUrl].fetchTiddler(title);
			else
				return undefined;
		};

		// Don't look for the "New Tiddler" tiddler in the included TiddlyWikis,
		// since returning such a tiddler (that is readonly) will make it impossible
		// in the Main TiddlyWiki to create new tiddlers via standart "new tiddler" button.
		if (title == config.macros.newTiddler.title) return undefined;

		// finally, look for an external one without "substitute" preference
		return forEachLoadedStore(function(theStore, url) {
			return theStore.fetchTiddler(title);
		});
	};

	// refresh TiddlyWiki to reflect the new included Tiddlers (if we have any).
	if(twWeb.getIncludedStoresUrls().length)
		refreshTiddlyWiki();
};

var includeFromIncludeList = function() {
	if (!window.store)
		return invokeLater(includeFromIncludeList,100);
	
	var includeListText = store.getTiddlerText("IncludeList");
	if (includeListText)
		wikify(includeListText,document.createElement("div"),undefined,store.fetchTiddler("IncludeList"));
};

var getFunctionUsingForReallyEachTiddler = function(func) {
	var wrapper = function() {
		var orig_forEachTiddler = store.forEachTiddler;

		var forEachTiddlerWithIncludes = function(callback) {
			var done = {};

			var callbackWrapper = function(title, tiddler) {
				// ensure every title is only processed once
				if(done[title]) 
					return;
				
				// do and set done for appropriate tiddlers
				if(!fetchPreferences.getSubsUrl(title)||
				   (fetchPreferences.getSubsUrl(title) == tiddler.getIncludeURL())) {
					done[title] = 1;
					callback.apply(this,arguments);
				};
			};
			
			// first, forEachTiddler over the original tiddlers
			orig_forEachTiddler.call(store, callbackWrapper);
			
			// add nonsubstituted shadowTiddler titles to done 
			// (to avoid an included store hide a shadow tiddler)
			for (var n in config.shadowTiddlers)
				if(!fetchPreferences.getSubsShUrl(n))
					done[n] = 1;

			// add the "New Tiddler" tiddler to done
			// to avoid an included store (with such tiddler) prevent creating new tiddlers
			done[config.macros.newTiddler.title] = 1;

			// forEachTiddler over every included store
			forEachLoadedStore(function(theStore, url) {
				theStore.forEachTiddler(callbackWrapper);
			});
		};
		
		store.forEachTiddler = forEachTiddlerWithIncludes;
		try {
			return func.apply(this,arguments);
		} finally {
			store.forEachTiddler = orig_forEachTiddler;
		};
	};
	
	return wrapper;
};

var useForReallyEachTiddler = function(object,property) {
	return object[property] = getFunctionUsingForReallyEachTiddler(object[property]);
};


//================================================================================
// config.extensions.SharedTiddlersPlugin (the "includer" engine)

config.extensions.SharedTiddlersPlugin = {

	// function config.extensions.SharedTiddlersPlugin.getFunctionUsingForReallyEachTiddler(func)
	//
	// Returns a function that behaves as func, but every call to store.forEachTiddler will actually 
	// be a call to forReallyEachTiddler (see below), i.e. iterate over the tiddlers of the main store 
	// and of the included TiddlyWikis
	//
	// @return the patched function
	//
	getFunctionUsingForReallyEachTiddler: getFunctionUsingForReallyEachTiddler,
	
	// function config.extensions.SharedTiddlersPlugin.useForReallyEachTiddler(object,property)
	//
	// Patches the function hold in the given property of the object in such a way that every call
	// to store.forEachTiddler will actually be a call to forReallyEachTiddler (see below), i.e.
	// iterate over the tiddlers of the main store and of the included TiddlyWikis
	//
	// @param  object
	// @param  property the name of the property of the object containing the function to be patched.
	// @return the patched function
	//
	useForReallyEachTiddler: useForReallyEachTiddler,
	
	// Add a listener function to the TiddlyWikiIncluder.
	//
	// @param  listener  function(urls)
	//			urls: [] of Strings, containing the urls of the TiddlyWiki just included
	//			(see url@config.extensions.SharedTiddlersPlugin.include)
	//			called whenever one or more TiddlyWiki store are successfully included.
	//
	addListener: function(listener) {
		listeners.push(listener);
	}
};


// -------------------------------------------------------------------------------
// TiddlyWikiIncluder initialization code

config.extensions.SharedTiddlersPlugin.addListener(refreshTiddlyWiki);

config.shadowTiddlers.AdvancedOptions +=
	("\n~IncludePlugin settings:"+
	 "\n<<option chkUseInclude>> Include ~TiddlyWikis"+
	 "\n<<option chkAlertOnSharedTiddlersConflicts>> Alert on tiddler name conflicts"+
	 "\n<<option chkWarnOnSharedTiddlersConflicts>> Display messages and write log in the browser console on conflicts"+
	 "\nIncludeList | IncludeState | ImportIncluded | [[help|http://yakovl.bplaced.net/TW/SharedTiddlersPlugin.html/#SharedTiddlersPluginInfo]]"+
	 "\n^^(Reload this ~TiddlyWiki to make changes become effective)^^");
config.shadowTiddlers.IncludeState = "<<includeState>>";

// add the "importer" engine
config.shadowTiddlers.ImportIncluded =
"| the url of the document to import from | <<option txtStoreUrl>>               |\n"+
"|    the filter of tiddlers to import    | <<option txtFilterTiddlersToImport>> |\n"+
"|           the importing mode           | <<option txtImportMode>>             |\n"+
"<html><a href='javascript:;' onclick='\n"+
"	var storeUrl = config.options.txtStoreUrl,\n"+
"	    params = {\n"+
"		filterLine: config.options.txtFilterTiddlersToImport,\n"+
"		importMode: config.options.txtImportMode,\n"+
"		noRefresh: true,\n"+
"	};\n"+

"	if(!storeUrl)\n"+
"		return displayMessage(\"please specify the url to import from\");\n"+
"	if(!params.filterLine)\n"+
"		return displayMessage(\"please specify the filter of tiddlers to import\");\n"+
"	if(!sharedTiddlersAPI.getStore(storeUrl))\n"+
"		displayMessage(\"warning: no store was loaded from \"+storeUrl+\" previously, trying now\");\n"+

"	twWeb.include(storeUrl,params);\n"+
"'>import (without saving)<a/></html>";

//================================================================================
// Tiddler extension/modification

Tiddler.prototype.isIncluded = function() {
	return this.includeURL != undefined;
};

Tiddler.prototype.getIncludeURL = function() {
	return this.includeURL;
};

Tiddler.prototype.setIncludeURL = function(url) {
	this.includeURL = url;
};

Tiddler.prototype.deleteIncludeURL = function() {
	delete this.includeURL;
};

// make included tiddlers readonly
config.extensions.SharedTiddlersPlugin.orig_Tiddler_isReadOnly = Tiddler.prototype.isReadOnly;

Tiddler.prototype.isReadOnly = function() {
	return config.extensions.SharedTiddlersPlugin.orig_Tiddler_isReadOnly.apply(this,arguments) || this.isIncluded();
}

//================================================================================
// TiddlyWiki modifications

// In some TiddlyWiki functions the "forEachTiddler" should work on all tiddlers, also those from 
// included store. (E.g. TiddlyWiki.prototype.getTags)
//
// But not for all (e.g. TiddlyWiki.prototype.getTiddlers is used for saving, but only the "own" tiddlers should be saved)
//
// Therefore explicitly list the functions that should be "wrapped" to use the "forReallyEachTiddler".
//
var tiddlyWikiFunctionsUsingForReallyEachTiddler = {
	getMissingLinks: 1, getOrphans: 1,
	getTags: 1, reverseLookup: 1, updateTiddlers: 1};
	
for (var n in tiddlyWikiFunctionsUsingForReallyEachTiddler)
	useForReallyEachTiddler(TiddlyWiki.prototype,n);

//================================================================================
// Web of TiddlyWikis

function IncludingMemorizer() { // the structure to store info about already handled include macros
	var used = {};
	this.isUsed  = function(line) { return used[line]? true : false; };
	this.setUsed = function(line) { used[line] = 1; };
	this.getUsed = function() { return jQuery.extend(true, {}, used) };
	this.markUnused = function(usedMap) {

		for(var u in usedMap)
			used[u] = undefined; // don't delete to keep the order of inclusion (for reloading)
	};
};

function TwWeb() {

	var nodes = {};			// hashmap by node name of nodes' metadata
	var nodeConflicts = {};		// hashmap by node name of arrays of conflicts
	var nodeWaitingTasks = {};	// hashmap by node name of arrays of Waiting Tasks
	var nodeDescription = function(url) { // PoG: can be turned into a separated "class"
		return { url : url };
	};
	var selfNodeName;
	var self = this;

	this.includeUsages = new IncludingMemorizer();

// ----- Nodes desctiptions part ---------------------------------------------------------------------

	// set/get the name of "main" (including) node
	this.setSelfNodeName = function(name) {
		selfNodeName = name;
	};
	this.getSelfNodeName = function() {
		return selfNodeName;
	};

	// set/get/delete node description
	this.setNodeDesc = function(nodeName, desc) {

		var existing = this.getNodeDesc(nodeName);

		// currently, doesn't change metadata on conflict
		if(existing)
			this.addConflict(nodeName, desc);
		else
			nodes[nodeName] = desc;

		this.callWaitingTasks(nodeName);
	};
	this.getNodeDesc = function(nodeName) {
		return nodes[nodeName];
	};
	this.deleteNodeDesc = function(nodeName) {
		nodes[nodeName] = null;
	};

	// "API" method to be used in the macro
	this.addNodeDesc = function(nodeName, url) {
		this.setNodeDesc(nodeName, nodeDescription(url));
	};

	// returns "better" description if one is "strictly better" than the other or "even" and null otherwise
	this.compareNodeDesc = function(desc1, desc2) {

// looks like here's a mistake: probably ": desc2" (?)
		return (desc1.url == desc2.url)? desc1 : null;
	};

	this.getNodeUrl = function(nodeName) {

		var desc = this.getNodeDesc(nodeName);
		return desc? desc.url : null;
	};

	// @param	nodeId
	//		url or "node: nodeName" id of the node
	// @return	url of the node (if it is defined) or null
	//
	this.getStoreUrlById = function(nodeId) {

		var node = self.checkNodeNotation(nodeId),
		    url  = node ? self.getNodeUrl(node) : nodeId;
		return url;
	}

	this.setNodeNotation = function(nodeName) {

		return "node: " + nodeName;
	};
	this.checkNodeNotation = function(urlParam) {

		var nodeCalcRE = /node: (.*)/,
		    nodeCalcMatch = nodeCalcRE.exec(urlParam);

		return nodeCalcMatch ? nodeCalcMatch[1] : null;
	};

	this.addConflict = function(nodeName, nodeDesciption) {

		var betterDesc = this.compareNodeDesc(nodeDesciption,this.getNodeDesc(nodeName));
		if(betterDesc) {
			this.deleteNodeDesc(nodeName);
			this.setNodeDesc(nodeName, betterDesc);
			return;
		}

		if(nodeConflicts[nodeName])
			nodeConflicts[nodeName].push(nodeDesciption);
		else
			nodeConflicts[nodeName] = [ nodeDesciption ];

		alert(  "Warning: more than one description of the "+nodeName+" node was pushed. "+
			"The earlier version is kept."  );
	};

// ----- Waiting tasks part --------------------------------------------------------------------------

	this.setWaitingTask = function(nodeName, waitingTaskFunc, waitingTaskSelf) {

		var waitingTask = { action: waitingTaskFunc, self: waitingTaskSelf };

		if(nodeWaitingTasks[nodeName])
			nodeWaitingTasks[nodeName].push(waitingTask);
		else
			nodeWaitingTasks[nodeName] = [ waitingTask ];

		if(this.getNodeUrl(nodeName))
			this.callWaitingTasks(nodeName);
	};
	this.callWaitingTasks = function(nodeName) {

		var toDo = nodeWaitingTasks[nodeName];

		if(toDo)
			for(var i = 0; i < toDo.length; i++)
				toDo[i].action.call(toDo[i].self);

		nodeWaitingTasks[nodeName] = null; // remove called Waiting Tasks
	};

// ----- Including stuff part ------------------------------------------------------------------------

	// ---- helpers ----

	this.setProgressFunction = function(func) {
		progress = func;
	};
/*	this.getProgressFunction = function() {	// is not in use
		return progress;
	};
*/	this.sendProgress = function(message, sender, state) {
		if (progress)
			progress.apply(this,arguments);
	};

	// Returns true when there are "pending" includes, i.e. TiddlyWiki that are not yet loaded.
	// A TiddlyWiki that failed loading is not pending.
	//
	this.hasPendingIncludes = function() {
		var state;
		for(var url in includedStores) {
			state = this.getState(url);
			if (state == WAITING || state == LOADING)
				return true;
		};
		return false;
	};

	// Called when an included TiddlyWiki could not be loaded.
	// By default an error message is displayed.
	//
	this.onError = function(url, errorMessage) {
		displayAndLogMessage("Error when including '%0':\n%1".format([url, errorMessage]));
	};

	// import a tiddler from an included store
	//
	// @param  tiddler - a tiddler to import
	// @param  mode: undefined or 1 - import anyway (other modes, like "don't substitute" will be supported)
	// @return the result object:
	//  .status means: -1 = error, 0 = tiddler is imported, >0 - reserved for other situations
	//  .errorText is not empty on error
	//  .tiddler is the tiddler with /the title/ which is in the store after "it is over" (may be null)
	//  .from is the url line equal to the includeURL of the imported tiddler
	//
	this.importIncluded = function(tiddler,mode) {

		if(mode == undefined)
			mode = 1;
		else
			mode = parseInt(mode);

		var t, tInMain, result = { tiddler: null };
		var doImport = function(t) {
			store.addTiddler(t);
			store.setDirty(true);
			result.status = 0;
			result.tiddler = t;
			result.from = url;
		};

		if(tiddler instanceof Tiddler) {
			// see return value, this is for the case when the import is not done
			result.tiddler = window.sharedTiddlersAPI.orig_fetchTiddler(tiddler.title);
			t = jQuery.extend(true, new Tiddler(), tiddler);
		} else {
			result.status = -1;
			result.errorText = "not a Tiddler instance";
			return result;
		};
		var url;
		if(url = t.getIncludeURL())
			t.deleteIncludeURL();

		switch(mode) {
			case 4: { // import only newer and on confirm
				tInMain = window.sharedTiddlersAPI.orig_fetchTiddler(t.title);
				if(!tInMain || tInMain.modified < t.modified)
					if(confirm("Up-to-date "+t.title+" from "+url+" is availabe, import?"))
						doImport(t);
				break
			}
			case 2: { // import only newer versions/unexisting tiddlers
				tInMain = window.sharedTiddlersAPI.orig_fetchTiddler(t.title);
				if(!tInMain || tInMain.modified < t.modified)
					doImport(t);
				break
			}
			case 3: { // import on confirm
				if(confirm(t.title+" from "+url+" is availabe, import?"))
					doImport(t);
				break
			}
			case 1: { // import anyway
				doImport(t);
				break
			}
			default: {
				result.status = -1;
				result.errorText = "unknown import mode";
			}
		};
		return result;
	};
	this.importAndLog = function(tiddler,mode) {

		var name   = tiddler.title,
		    result = twWeb.importIncluded(tiddler,mode);
		// this.importIncluded is not used to be able to pass the method to the API

		switch(result.status) {
			case 0:	 console.log("imported: "+name+" from "+result.from);
				 break
			case -1: console.log("error importing "+name+": "+result.errorText);
				 break
		}
	};

	// ---- main ----

	// Includes the (local or remote) TiddlyWiki store with the given url.
	//
	// @param url		see url@stp_loadTiddlyWikiStore
	// @param includeParams	a set of the following params for including:
	//	filterLine		a filter expression defining a set of tiddlers to include
	//	substituting		points whether an included tiddler should sustitute
	//				one in the main document if there's a conflict of names
	//	substituteShadows	points whether shadowed tiddlers of the main document with names equal to
	//				those of included ones should be substituted (in cases of conflicts);
	//				works only with (substituting == true)
	//	delayMilliSeconds	addition delay of loading
	//	noRefresh
	//	importMode
	//	evalTiddlers
	//	wikifyTiddlers
	//
	this.include = function(urlOrNodeParam, includeParams) {

		if (!isIncludeEnabled())
			return;
		var self = this;

		var includeOrHandleUrl = function(url) {

			var loadStoreCallback = function(theStore,urlInCallback,params,errorMessage) {

				if(theStore === undefined) {
				
					includedStores[url] = errorMessage;
					self.onError(url, errorMessage);
					return;
				} else
					includedStores[url] = theStore;

				// keep orig_store not to load tw-documents multiple times
				// because of multiple include macros:
				includedStores[url].orig_store = new TiddlyWiki();
				includedStores[url].forEachTiddler(function(tName,tiddler){
					tiddler.setIncludeURL(url);
					includedStores[url].orig_store.addTiddler(tiddler);
				});
				includedStores[url].clear();

				// include, create fetchPreferences and notify of conflicts; or import
				addFromLoadedStore();
			};

			var addFromLoadedStore = function() {
			// uses url and includeParams exploiting closure

				var substituting = includeParams.substitute,
				    substituteShadows = includeParams.substituteShadows,
				    importMode = includeParams.importMode;

				if(twWeb.getStore(url) === null)
					return invokeLater(addFromLoadedStore, 100);	// 100 milliseconds
				// new tiddlers should be added *when the store is loaded*

				// add tiddlers to included stores and check new conflicts (among non-subs. tids)
				var i, t,
				    new_tiddlers = includedStores[url].orig_store.filterTiddlers(includeParams.filterLine);

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

					t = jQuery.extend(true, new Tiddler(), new_tiddlers[i]); //copy (by value)

					if(includedStores[url].orig_store.fetchTiddler(t.title)) {
					// ignore empty tiddlers created by the "tiddler" filter
					    if(importMode)
					        twWeb.importAndLog(t,importMode);
					    else {
					        // check for upcoming conflicts first
					        conflicts.checkNew(t.title, url, substituting, substituteShadows);
					        // then include
					        includedStores[url].addTiddler(t);
					    };
					    if(includeParams.evalTiddlers)
					        installPlugin(t);
					    if(includeParams.wikifyTiddlers)
					        wikify(t.text,document.createElement("div"),undefined,t);
					};
				};

				// add items to fetchPreferences if have to
				if(!importMode && (substituting || substituteShadows))
					for(i = 0; i < new_tiddlers.length; i++)
						fetchPreferences.add(new_tiddlers[i].title,url,substituting, substituteShadows);
				conflicts.notify();

				// "recalc" slices
				store.slices = {};
				// refresh things to get included stylesheets, PageTemplate and ViewTemplate applied
				if(!includeParams.noRefresh) {
					refreshAll();
					story.refreshAllTiddlers();
				}

				pendingOnLoadURLs.push(url);
				invokeLater(notifyListeners);
			};

			var loadStore = function() {
				includedStores[url] = LOADING;
				stp_loadTiddlyWikiStore(url,loadStoreCallback,null,progress);
				// {includeParams:includeParams} can be used instead of null so that
				// loadStoreCallback will have access to the includeParams
			};

			var urlIsNew = !(includedStores[url]);

			if(urlIsNew) {

				includedStores[url] = WAITING;

				if (includeParams.delayMilliSeconds)
					invokeLater(loadStore, includeParams.delayMilliSeconds);
				else
					loadStore();
			} else
				addFromLoadedStore();
		};

		var nodeName = this.checkNodeNotation(urlOrNodeParam);

		if(nodeName) {
			if(nodeName == this.getSelfNodeName()) // don't include from the main (self) TiddlyWiki
				return;
			this.setWaitingTask(nodeName,function(){
				var url = this.getNodeUrl(nodeName);
				includeOrHandleUrl(url);
			},this);
		} else
			includeOrHandleUrl(urlOrNodeParam);
	};
	
// ----- Methods for dealing with included stuff -----------------------------------------------------

	// @return [] of Strings, the URLs of the includes
	//
	this.getIncludedStoresUrls = function() { // in a form of an array

		var includes = [];
		for(var url in includedStores)
			includes.push(url);
		return includes;
	};
	// @return the (TiddlyWiki) store with the given URL or "node: nodeName", or null if not (yet) loaded.
	//
	this.getStore = function(nodeId) {

		var url = self.getStoreUrlById(nodeId)
		if(!url)
			return null;

		var s = includedStores[url];
		if(s && s instanceof TiddlyWiki)
			return s;
		return null;
	};
	// @return a state/error text of the store with the given URL, or null when the store is already loaded
	//
	this.getState = function(nodeId) {

		var url = self.getStoreUrlById(nodeId)
		if(!url)
			return "the node "+self.checkNodeNotation(nodeId)+" is not described yet, the address is unknown";

		var s = includedStores[url];
		if (!s)
			return getMissingIncludeMsg(url);
		return typeof s == "string" ? s : null;
	};
	// reload one included store or all of them
	// previous inclusions are done again, by default only those without eval and import
	// important: reload doesn't work correctly with <<include>> usages with multiple urls
	//
	// @param  reloadParams  an object containing some of these configuration properties:
	//	urlOrNodeList	an array of TWs' IDs (url or "node: nodeName") to reload;
	//			if undefined, all TWs are reloaded
	//	eval		points whether to redo inclusions with the "eval" parameter (undefined == false)
	//	import		same for the "import" parameter, but undefined/null -> true
	//	wikify		same for the "wikify" parameter
	this.reload = function(reloadParams) {

		// determine a TW(s) to reload (undefined => all)
		var i, twsToReload = reloadParams.urlOrNodeParam;

		// turn "IDs" into actual urls; filter out node names for which urls are not defined
		// so waiting tasks are not doubled
		if(twsToReload)
			for(i = 0; i < twsToReload.length; i++){ 
				nodeName = this.checkNodeNotation(twsToReload[i]);
				twsToReload[i] = nodeName ? this.getNodeUrl(nodeName) : twsToReload[i];
				if(!twsToReload[i])
					twsToReload.splice(i--,1);
			}

		if(reloadParams.import === undefined || reloadParams.import === null)
			reloadParams.import = true;
		if(reloadParams.wikify === undefined || reloadParams.wikify === null)
			reloadParams.wikify = true;

		// collect macro expressions to reload
			// may the order of including be important?
		var usage, usages = this.includeUsages.getUsed(),
		    paramString, params, pParams, urlParam, nodeName,
			dontReload, i;

		for(usage in usages) {

			// parsing copied from config.macros.include.handler (to get macro params)
			paramString = usage.substring(10,usage.length - 2);
			params = paramString.readMacroParams();
			pParams = paramString.parseParams("url",null,true,false,true);
			urlParam = pParams[0]["url"][0];
			nodeName = this.checkNodeNotation(urlParam);
			urlParam = nodeName ? this.getNodeUrl(nodeName) : urlParam;

			// keep only inclusions that contain IDs corresponding to urls from twsToReload
			dontReload = true;
			if(twsToReload) {
				for(i = 0; i < twsToReload.length; i++)
					if(urlParam == twsToReload[i])
						dontReload = false;
			} else
				dontReload = false;

			// exclude inclusions with import and eval, if necessary
			if(!reloadParams.import && getParam(pParams,"import",undefined) ||
			   !reloadParams.eval && params.contains('eval') ||
			   !reloadParams.wikify && params.contains('wikify'))
				dontReload = true;

			if(dontReload) {
				delete usages[usage];
				continue;
			}

			// clean stuff: unload included store, clear priorities, remove conflicts;
			// deleting "used" marks is outside this loop
			includedStores[urlParam] = undefined;
			fetchPreferences.removeByUrl(urlParam);
			conflicts.markNodeUnloaded(urlParam);
		}
		// delete "used" marks
		this.includeUsages.markUnused(usages);
		
		// include again
		for(usage in usages)
			wikify(usage,document.createElement("div"),undefined,null);
	};
};
twWeb = new TwWeb();

//================================================================================
// Default Progress Handling for config.extensions.SharedTiddlersPlugin

var showAnimated = function(e, showing, duration) {
//	if (!anim || !abego.ShowAnimation) {
		e.style.display = showing ? "block" : "none";
		return;
//	}
	
//	anim.startAnimating(new abego.ShowAnimation(e,showing,duration));
};

config.extensions.SharedTiddlersPlugin.getDefaultProgressFunction = function() {

	setStylesheet(
		".includeProgressState{\n"+
		"background-color:#FFCC00;\n"+
		"position:absolute;\n"+
		"right:0.2em;\n"+
		"top:0.2em;\n"+
		"width:7em;\n"+
		"padding-left:0.2em;\n"+
		"padding-right:0.2em\n"+
		"}\n",
		"stp_Include");

	var createStateElem = function() {
		var e = document.createElement("div");
		e.className = "includeProgressState";
		e.style.display = "none";
		document.body.appendChild(e);
		return e;
	};
	
	var stateElem = createStateElem();


	var showState = function(message) {
		removeChildren(stateElem);
		createTiddlyText(stateElem,message);
		showAnimated(stateElem,true,0);
	};

	var hideState = function() {
		// hide the state the next idle time 
		invokeLater(function() {
			showAnimated(stateElem,false,ANI_DURATION_HIDE_STATE);
		},100,ANIMATION_PRIORITY);
	};
	
	var myProgressFunction = function(message, sender, state, url, params) {
		
		if (state == "Done" || state == "Failed") {
			hideState();
			return;
		}
		
		if (sender == "stp_loadTiddlyWikiStore") {
			idleCount = 0;
			if (state == "Processing")
				showState("Including...");
		} else {
			showState(message);
		}
	};
	return myProgressFunction;
};

twWeb.setProgressFunction(config.extensions.SharedTiddlersPlugin.getDefaultProgressFunction());


//================================================================================
// The "describeNode" macro
//
// Syntax: <<describeNode nodeName {nodeUrl|self}>>
//
config.macros.describeNode = {};
config.macros.describeNode.handler = function(place,macroName,params,wikifier,paramString,tiddler) {

	var macroTWcode = wikifier.source.substring(wikifier.matchStart, wikifier.nextMatch);
	createTiddlyText(createTiddlyElement(place,"code"),macroTWcode);

	// node description duplicates are handled when adding, so no "handle only once" here

	var includeURL = tiddler.getIncludeURL(),
	    nodeName = params[0],
	    urlParam = params[1],
	    self = (urlParam == "self");

	if(self) {
		var oldSelf = twWeb.getSelfNodeName();
		if(oldSelf && (oldSelf != nodeName))
			return alert("The \'"+oldSelf+"\' alias is already assigned as the name of the current "+
					"TiddlyWiki; the new attempt to assign \'"+nodeName+"\' is ignored.");
		twWeb.setSelfNodeName(nodeName);
		return;
	}

	var url = resolveUrlFrom(urlParam, includeURL);
	url = stp_resolveURL(url); // if no includeURL

	twWeb.addNodeDesc(nodeName,url);
};

//================================================================================
// The "include" macro
//
// Syntax:	<<include [url:]url [filters:filterLine] [substitute]
//			  [substituteShadows] [hide:hideFlag] [delay:delayDuration]>>
//
config.macros.include = {};
config.macros.include.handler = function(place,macroName,params,wikifier,paramString,tiddler) {

	var macroTWcode = wikifier.source.substring(wikifier.matchStart, wikifier.nextMatch),
	    pParams = paramString.parseParams("url",null,true,false,true); // allowEval, cascadeDefaults, names allowed

	var hide = getFlag(pParams, "hide", false) || params.contains('hide');
	if (!hide)
		createTiddlyText(createTiddlyElement(place,"code"),macroTWcode);
	if (twWeb.includeUsages.isUsed(macroTWcode))
		return;
	twWeb.includeUsages.setUsed(macroTWcode);

	var urls = pParams[0]["url"],
	    includeParams = {
		delayMilliSeconds:	parseInt(getParam(pParams,"delay","0")),
		filterLine:		getParam(pParams,"filters","[all[-]]"),
		substitute:		params.contains('substitute'),
		substituteShadows:	params.contains('substituteShadows'),
		noRefresh:		params.contains('noRefresh'),
		importMode:		getParam(pParams,"import",undefined),
		evalTiddlers:		params.contains('eval'),
		wikifyTiddlers:		params.contains('wikify')
	};

	var checkUrlAndInclude = function(url) {

		if(url == 'hide' || url == 'substituteShadows' || url == 'substitute' || url == 'eval' ||
				url == 'wikify' || url == 'noRefresh')
			return;

		twWeb.include(url,includeParams);
	};

	for (var i = 0; urls && i < urls.length; i++)
		checkUrlAndInclude(urls[i]);
};

//================================================================================
// The "reloadIncluded" macro
//
// Syntax: <<reloadIncluded [urls:urlsJSON] [reloadParams:otherReloadParamsJSON]
//		[label:labelText] [tooltip:tooltipText] [class:className]>>
// (for reloadParams, see twWeb.reload)
//
config.macros.reloadIncluded = {};
config.macros.reloadIncluded.handler = function(place,macroName,params,wikifier,paramString,tiddler) {

	// parse params
	var pParams = paramString.parseParams("url",null,true,false,true),
	    label    = getParam(pParams,"label","refresh"),
	    tooltip  = getParam(pParams,"tooltip",undefined),
	    elClass  = getParam(pParams,"class"," "), // " " overwrites the default "button" class
	    urlsText = getParam(pParams,"urls",undefined),
	    otherReloadParamsText = getParam(pParams,"reloadParams","{}"),
	    reloadParams = JSON && JSON.parse(otherReloadParamsText) || jQuery.parseJSON(otherReloadParamsText);
	reloadParams.urlOrNodeList = !urlsText ? undefined :
			(JSON && JSON.parse(urlsText) || jQuery.parseJSON(urlsText));
	if(!tooltip) {
		if(reloadParams.urlOrNodeList) {
			tooltip = "refresh '"+reloadParams.urlOrNodeList[0]+"'";
			for(var i = 1; i < reloadParams.urlOrNodeList.length; i++)
				tooltip += ", '"+reloadParams.urlOrNodeList[i]+"'";
			tooltip += (i > 0) ? " nodes" : " node";
		} else
			tooltip = "refresh all included nodes";
	}

	// create button, add handler
	createTiddlyButton(place,label,tooltip,function(){
		var returnHere = function() {
			if(twWeb.hasPendingIncludes()) {
				invokeLater(returnHere,100);
				return;
			} // wait until all the stores are loaded and the page is refreshed
			var t = tiddler.title, te = DEFAULT_VIEW_TEMPLATE;
			story.displayTiddler(this,t,story.chooseTemplateForTiddler(t,te));
		}
		twWeb.reload(reloadParams);
		invokeLater(returnHere,100); // wait a bit for the nodes to unload
	},elClass);
};

//================================================================================
// The "includeState" macro
//
// Syntax: <<includeState>>
//
config.macros.includeState = {};
config.macros.includeState.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
	var getFullState = function () {
		var s = "";
		var includes = twWeb.getIncludedStoresUrls();
		if (!includes.length)
			return "{{noIncludes{\nNo TiddlyWiki is included or including is disabled (see AdvancedOptions)\n}}}\n";
			
		s += "|!Address|!State|\n";
		for (var i = 0; i < includes.length; i++) {
			var inc = includes[i];
			s += "|{{{"+inc+"}}}|";
			var t = twWeb.getState(inc);
			s += t ? "{{{"+t+"}}}" : "included";
			s += "|\n"
		}
		s += "|includeState|k\n";
		return s;
	};
	
	var updateState = function(){
		removeChildren(div);
		wikify(getFullState(),div);
		if (twWeb.hasPendingIncludes())
			invokeLater(updateState,500,UPDATE_STATE_PRIORITY);
	};

	var div = createTiddlyElement(place,"div");
	
	invokeLater(updateState,0,UPDATE_STATE_PRIORITY);
};

//================================================================================
// Change standart formatters

var getFormatterIndex = function(formatterName) {
	for(var i = 0; i < config.formatters.length; i++)
		if(config.formatters[i].name == formatterName)
			return i;
	return null;
}

//--------------------------------------------------------------------------------
// Change the prettyLink formatter so that it
// * recognizes [[text|target]]@nodeName and [[target]]@nodeName syntax
// * takes into account includeURL of the tiddler and propagates inclusion

if(config.options.STP_hijackPrettyLink) {
	var prettyLinkFormatterIndex = getFormatterIndex("prettyLink");
	config.extensions.SharedTiddlersPlugin.orig_prettyLinkFormatter = config.formatters[prettyLinkFormatterIndex];
	config.formatters[prettyLinkFormatterIndex] = {

		name:  "prettyLink",
		match: "\\[\\[",
		lookaheadRegExp: /\[\[(.*?)(?:\|(~)?(.*?))?\]\](?:(?:@(\w+))(?:@([\w\s\:]+)@)?)?/mg,
		handler: function(w) {

			this.lookaheadRegExp.lastIndex = w.matchStart;
			var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
			if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {

				// call the formatter
				config.extensions.SharedTiddlersPlugin.orig_prettyLinkFormatter.handler(w);

				// call the include.handler, if necessary
				var node = lookaheadMatch[4]? lookaheadMatch[4] : undefined,
				    includeURL = w.tiddler? w.tiddler.getIncludeURL() : null,
				    urlParam = node? twWeb.setNodeNotation(node) : (includeURL? includeURL : undefined),
				    target = lookaheadMatch[3]? lookaheadMatch[3] : lookaheadMatch[1],
				    paramString = '"'+urlParam+'" filters:"[['+target+']]" hide noRefresh ';
				if(lookaheadMatch[5])
					paramString += lookaheadMatch[5];
				if(urlParam)
					config.macros.include.handler(w.output,"include",
						paramString.readMacroParams(true),w,paramString,w.tiddler);

				// move nextMatch according to this.lookaheadRegExp, not original prettyLink
				w.nextMatch = this.lookaheadRegExp.lastIndex;
			}
		}
	};
}

//--------------------------------------------------------------------------------
// Change the image formatter so that it
// * takes into account includeURL of the tiddler and recalcs relative urls

if(config.options.STP_hijackImageFormatter) {
	var imageFormatterIndex = getFormatterIndex("image");
	config.extensions.SharedTiddlersPlugin.orig_imageFormatterHandler = config.formatters[imageFormatterIndex].handler;
	config.formatters[imageFormatterIndex].handler = function(w) {

		var lastChildBeforeHandling = w.output.lastChild;
		config.extensions.SharedTiddlersPlugin.orig_imageFormatterHandler.apply(this,arguments);

		if(w.output.lastChild != lastChildBeforeHandling) {

			var img = w.output.lastChild,
			    includeURL = w.tiddler ? w.tiddler.getIncludeURL() : "",
			    imgSrc = jQuery(img).attr("src");

			// take includeURL into account:
			img.src = resolveUrlFrom(imgSrc, includeURL);
		}
	}
}

//================================================================================
// Add inline-management tools by hijacking .edit.handler

config.extensions.SharedTiddlersPlugin.orig_editHandler = config.macros.edit.handler;
config.macros.edit.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
	var includeUrl = tiddler.getIncludeURL();
	if(params[0] == "text" && includeUrl){ // only for "text", not other fields
		var e = createTiddlyElement(null,"div");
		e.className = "manageIncludedPanel";
		createTiddlyText(e,"manage the included tiddler: ");

		// go to the source
		var sourceUrl = includeUrl + "#[["+tiddler.title+"]]";
		createExternalLink(e, sourceUrl, "open in the source TiddlyWiki");
		// view the link to the source
		createTiddlyText(e," (");
		createTiddlyButton(e,"view link","view the link to the source",function(e){
			var popup = Popup.create(this);
			createTiddlyText(popup,sourceUrl);
			Popup.show();
			var ev = e || window.event;
			ev.cancelBubble = true;
			if(ev.stopPropagation)
				ev.stopPropagation();
			return false;
		}," ");
		createTiddlyText(e,")");

		// import
		if(!readOnly) {
			createTiddlyText(e," | ");
			createTiddlyButton(e,"import","import this tiddler",function(){
				twWeb.importAndLog(tiddler,1);
			 }," ");
		}

		// reload
		createTiddlyText(e," | ");
		config.macros.reloadIncluded.handler(e,"",null,null,'urls:\'["'+includeUrl+'"]\'',tiddler);

		// other actions

		// if the read only mode is not set, display all tools otherwise ...
		place.appendChild(e);
	}
	return config.extensions.SharedTiddlersPlugin.orig_editHandler(place,macroName,params,wikifier,paramString,tiddler);
};

//================================================================================
// Perform plugin startup tasks

// add this for the "install only once" check (which also prevents conflicts with abego.IncludePlugin)
// (this is also deprecated API for backward compability)
abego.TiddlyWikiIncluder = {
	getIncludes:	twWeb.getIncludedStoresUrls,
	getState:	twWeb.getState,
	getStore:	twWeb.getStore
};
attachToStore();
invokeLater(includeFromIncludeList,100);

// add several more methods to the "API namespace"

window.sharedTiddlersAPI.getIncludes  = twWeb.getIncludedStoresUrls;
window.sharedTiddlersAPI.getState     = twWeb.getState;
window.sharedTiddlersAPI.getStore     = twWeb.getStore;
window.sharedTiddlersAPI.importAndLog = twWeb.importAndLog;

// iterates over all tiddlers of "the store" and all tiddlers of included (and loaded) stores
//
window.sharedTiddlersAPI.forReallyEachTiddler = function(callback) {
	var caller = function() {
		store.forEachTiddler(callback);
	};
	getFunctionUsingForReallyEachTiddler(caller).call(store);		
};
})();
//%/ //
/***
|Description|highlights saving button (bold red) when there's unsaved changes|
|Version|1.0|
|Short status|<<insertEditable container:"@stateComment" size:max cell>>|
|Author|Yakov Litvin|
***/
//{{{
// add the "saveChangesButton" class to the save changes button
config.macros.saveChanges.SCM_orig_handler = config.macros.saveChanges.handler;
config.macros.saveChanges.handler = function(place,macroName,params)
{
	this.SCM_orig_handler.apply(this,arguments);
	place.lastChild.classList.add("saveChangesButton");
};

// styles that highlight save button when there's something to save
var css = ".saveChangesButton { font-weight: bold; color: red !important; }";

TiddlyWiki.prototype.SCM_orig_setDirty = TiddlyWiki.prototype.setDirty;
TiddlyWiki.prototype.setDirty = function(dirty)
{
	if(dirty)
		setStylesheet(css,"highlightSaving");
	else
		removeStyleSheet("highlightSaving");
	return this.SCM_orig_setDirty.apply(this,arguments);
};
//}}}
/***
|Name|TaggedTemplateTweak|
|Source|http://www.TiddlyTools.com/#TaggedTemplateTweak|
|Documentation|http://www.TiddlyTools.com/#TaggedTemplateTweakInfo|
|Version|1.6.1|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|use alternative ViewTemplate/EditTemplate for specific tiddlers|
This plugin extends the core function, {{{story.chooseTemplateForTiddler()}}}, so that any given tiddler can be viewed and/or edited using alternatives to the standard tiddler templates.
!!!!!Documentation
>see [[TaggedTemplateTweakInfo]]
!!!!!Code
***/
//{{{
version.extensions.TaggedTemplateTweak = {major: 1, minor: 6, revision: 1, date: new Date(2009,9,2)};

if (!config.options.txtTemplateTweakFieldname)	
	config.options.txtTemplateTweakFieldname = 'template';

Story.prototype.taggedTemplate_chooseTemplateForTiddler = Story.prototype.chooseTemplateForTiddler;
Story.prototype.chooseTemplateForTiddler = function(title,template)
{
	// get core template and split into theme and template name
	var coreTemplate = this.taggedTemplate_chooseTemplateForTiddler.apply(this,arguments);
	var theme = "", template = coreTemplate;
	var parts = template.split(config.textPrimitives.sectionSeparator);
	if (parts[1]) { theme = parts[0]; template = parts[1]; }
	else theme = config.options.txtTheme || ""; // if theme is not specified
	theme += config.textPrimitives.sectionSeparator;

	// look for template using title as prefix
	if (!store.getTaggedTiddlers(title).length) { // if tiddler is not a tag
		if (store.getTiddlerText(theme+title+template))
			return theme+title+template;	// theme##TitleTemplate
		if (store.getTiddlerText(title+template))
			return title+template;		// TitleTemplate
	}

	// look for templates using custom field value as prefix
	var v = store.getValue(title,config.options.txtTemplateTweakFieldname);
	if (store.getTiddlerText(theme+v+template))
		return theme+v+template;	// theme##valueTemplate
	if (store.getTiddlerText(v+template))
		return v+template;		// valueTemplate

	// look for template using tags as prefix
	var tiddler = store.getTiddler(title);
	if (!tiddler) return coreTemplate; // tiddler doesn't exist... use core result
	for (i = 0; i < tiddler.tags.length; i++) {
		var t = tiddler.tags[i]+template; // add tag prefix to template
		var c = t.substr(0,1).toUpperCase()+t.substr(1); // capitalized for WikiWord title
		if (store.getTiddlerText(theme+t))	{ return theme+t; } // theme##tagTemplate
		if (store.getTiddlerText(theme+c))	{ return theme+c; } // theme##TagTemplate
		if (store.getTiddlerText(t)) 		{ return t; }	    // tagTemplate
		if (store.getTiddlerText(c))		{ return c; }	    // TagTemplate
	}

	// no match... use core result
	return coreTemplate;
}
//}}}
/***
Пример кода:
{{{
config.commands.closeTiddler.text = "close";
config.commands.closeTiddler.tooltip = "Close this tiddler";

config.commands.closeOthers.text = "close others";
config.commands.closeOthers.tooltip = "Close all other tiddlers";
}}}
(возможно, стоит заменить текст на картинки -- для mobile)
***/
//{{{

//}}}
/***
|Description|Replaces toolbar buttons with icons|
|Source|Lewcid TW {{DDnc{add the link}}}|
|Tweaked by|Yakov Litvin|
|Version|2.0|
!!!Usage
For each command that you want to set an icon for, just add a line like the following into a tiddler tagged {{{systemConfig}}}, specifying the icon image location:
{{{
config.commands.editTiddler.imgLoc = "jump.bmp";
}}}
No need to edit ViewTemplate!

!!!To do:
* fix "close" SVG: [[it creates bad "padding"|"close" SVG wrong "padding"]]
** then fix padding for images (horizontal = vertical)
* extend so that ~SVGs can be used as icons as well (directly, not using base64); or find out how to convert [[an SVG|closeOthers SVG]] to base64
***/
//{{{
// based on TW 2.7.1 core code
config.macros.toolbar.createCommand = function(place,commandName,tiddler,className)
{
	if(typeof commandName != "string")
		for(var t in config.commands)
			if(config.commands[t] == commandName)
				commandName = t;
	
	if(!(tiddler instanceof Tiddler) || (typeof commandName != "string"))
		return
	var command = config.commands[commandName];

	if(command.isEnabled ? !command.isEnabled(tiddler) :
	   !this.isCommandEnabled(command,tiddler))
		return;

	var text = command.getText ? command.getText(tiddler) : this.getCommandText(command,tiddler);
	var tooltip = command.getTooltip ? command.getTooltip(tiddler) : this.getCommandTooltip(command,tiddler);
	var cmd = command.type == "popup" ? this.onClickPopup : this.onClickCommand;
	var btn = createTiddlyButton(null,text,tooltip,cmd);
	btn.setAttribute("commandName",commandName);
	btn.setAttribute("tiddler",tiddler.title);
	jQuery(btn).addClass("command_" + commandName);
	if(className)
		jQuery(btn).addClass(className);

	// main change:
	if(command.imgLoc) {
		btn.innerHTML = "<img src='"+command.imgLoc+"'>";
		jQuery(btn).addClass("imageWrappingLink");
	}

	place.appendChild(btn);
}

setStylesheet(".toolbarImg {vertical-align: middle; cursor:pointer;}\n"+
	".imageWrappingLink {display: inline-block;}\n", "commandIconStyles");

// base64 png work:
//config.commands.jump.imgLoc= "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAuCAIAAADcLmxdAAAAtUlEQVRYhe3QSw7EIAwD0Nz/0nQ1/cxQYjuRppWcLdh5EOMBE/8GjGHEMe9BRGF6EBUB6DDi3Yj0vhAxwggjjDCCRwQ2AiKtCirWi9jbjPhCgElEILRFmsQXU6DLBfAFjet/2ybtXQ68Z15dd1ANt70VB5tNSgWHElkfs6Xa5+nf2yVAEcgCWUAg1msqAg5xt6wooBGgg+5kA6lDKRQyC4fYpsWmDr1KTp4p1ZJivmWM+MwjEBvNTi/A51SXaAAAAABJRU5ErkJggg==";

// base64 SVG work as well:
//config.commands.closeTiddler.imgLoc = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDI0IDI0IiBoZWlnaHQ9IjI0cHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjI0cHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxwYXRoIGQ9Ik0yMi4yNDUsNC4wMTVjMC4zMTMsMC4zMTMsMC4zMTMsMC44MjYsMCwxLjEzOWwtNi4yNzYsNi4yN2MtMC4zMTMsMC4zMTItMC4zMTMsMC44MjYsMCwxLjE0bDYuMjczLDYuMjcyICBjMC4zMTMsMC4zMTMsMC4zMTMsMC44MjYsMCwxLjE0bC0yLjI4NSwyLjI3N2MtMC4zMTQsMC4zMTItMC44MjgsMC4zMTItMS4xNDIsMGwtNi4yNzEtNi4yNzFjLTAuMzEzLTAuMzEzLTAuODI4LTAuMzEzLTEuMTQxLDAgIGwtNi4yNzYsNi4yNjdjLTAuMzEzLDAuMzEzLTAuODI4LDAuMzEzLTEuMTQxLDBsLTIuMjgyLTIuMjhjLTAuMzEzLTAuMzEzLTAuMzEzLTAuODI2LDAtMS4xNGw2LjI3OC02LjI2OSAgYzAuMzEzLTAuMzEyLDAuMzEzLTAuODI2LDAtMS4xNEwxLjcwOSw1LjE0N2MtMC4zMTQtMC4zMTMtMC4zMTQtMC44MjcsMC0xLjE0bDIuMjg0LTIuMjc4QzQuMzA4LDEuNDE3LDQuODIxLDEuNDE3LDUuMTM1LDEuNzMgIEwxMS40MDUsOGMwLjMxNCwwLjMxNCwwLjgyOCwwLjMxNCwxLjE0MSwwLjAwMWw2LjI3Ni02LjI2N2MwLjMxMi0wLjMxMiwwLjgyNi0wLjMxMiwxLjE0MSwwTDIyLjI0NSw0LjAxNXoiLz48L3N2Zz4=";
//}}}
/***
|Version|0.9.5|
|state|ready; stickToTheTop option needs more refinement; meta to be written|
!~ToDo
* add style sheet for better positioning of items inside menu (~left/right/center classes)
* refine sticky menu behaviour (see the code, especially "when a tiddler is opened on top" issue)
* check how this interacts with TiddlersBarPlugin
To edit: [[TopLineMenu]]
!Code
***/
//{{{
// menu generating handler; create a macro instead?
config.macros.topLineMenu = {
	menuId: "topLineMenu",
	tiddlerName: "TopLineMenu",
	menuPlaceId: "displayArea",
	beforeSelector: "#tiddlerDisplay",
	stickToTheTop: true,
	handler: function() {

		if (document.getElementById(this.menuId))
			return;
		var place = document.getElementById(this.menuPlaceId);
		var theMenu = document.createElement("div");
		theMenu.setAttribute("id", this.menuId);
		place.insertBefore(theMenu, place.firstChild);

		wikify("<<tiddler [["+this.tiddlerName+"]]>>",theMenu);
		if(this.stickToTheTop) {
			theMenu.style.maxWidth = place.offsetWidth+"px";
			//# make this adapt when a sidebar is hidden
			
			var height = theMenu.offsetHeight;
			//# update on this.tiddlerName content edit
			
			jQuery(place).find(this.beforeSelector).css("margin-top",height+"px");
			//# fix: when a tiddler is opened on top, this doesn't ensures tiddler's
			//  title is visible
			
			// fix: with margins on startup theMenu was positioned
			// to the top of tiddlerDisplay, not to the top of displayArea
			jQuery(window).scroll();
			
			//# set z-index to hover above CodeMirror editor
			
			// may be of interest:
			// - http://stackoverflow.com/questions/37849710/making-one-navbar-sticky-when-user-scrolls
		}
	},
	init: function() {
		
		if(!this.stickToTheTop) return;
		
		jQuery(window).on("scroll",function(){
			
			var menuElement = document.getElementById(config.macros.topLineMenu.menuId),
			    header = document.getElementsByClassName("header")[0],
			    headerBottom = header.offsetTop + header.offsetHeight - findScrollY(),
			    position = findScrollY() + Math.max(headerBottom,0);
			menuElement.style.top = position+"px";
		});
		setStylesheet("#"+config.macros.topLineMenu.menuId+" {"+
			      "position:absolute;"+
		"}","topLinePositionCSS")
	}
};

// old-fashioned way to launch handler, copied from http://tiddlywiki.squize.org/#HoverMenuPlugin
window.old_restart_before_topLineMenu = restart;
restart = function() {
	window.old_restart_before_topLineMenu();
	config.macros.topLineMenu.handler();
};

// make TopLineMenu resistant to refreshing (refreshPageTemplate)
window.old_refreshPageTemplate_before_topLineMenu = refreshPageTemplate;
refreshPageTemplate = function(title) {
	window.old_refreshPageTemplate_before_topLineMenu(title);
	config.macros.topLineMenu.handler();
};
//}}}
/***
|''Name:''|YourSearchPlugin|
|''Version:''|2.1.6 (2012-04-19)|
|''Summary:''|Search your TiddlyWiki with advanced search features such as result lists, tiddler preview, result ranking, search filters, combined searches and many more.|
|''Source:''|http://tiddlywiki.abego-software.de/#YourSearchPlugin|
|''~CoreVersion:''|2.1|
|''Requires:''|~SavingWithRenamingFix|
|~|~SavingWithRenamingFix is not required, but if is used, should be launched first|
|''Twitter:''|[[@abego|https://twitter.com/#!/abego]]|
|''GitHub:''|https://github.com/abego/YourSearchPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''License:''|[[BSD open source license|http://www.abego-software.de/legal/apl-v10.html]]|
!About ~YourSearch
~YourSearch gives you a bunch of new features to simplify and speed up your daily searches in ~TiddlyWiki. It seamlessly integrates into the standard ~TiddlyWiki search: just start typing into the 'search' field and explore!

For more information see [[Help|YourSearch Help]].
!Source Code
***/
/***
This plugin's source code is compressed (and hidden). 
Use this [[link|https://github.com/abego/YourSearchPlugin/blob/master/src/main/js/YourSearchPlugin-src.js]] to get the readable source code.
***/
///%
if(!version.extensions.YourSearchPlugin){version.extensions.YourSearchPlugin={major:2,minor:1,revision:6,source:"http://tiddlywiki.abego-software.de/#YourSearchPlugin",licence:"[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]",copyright:"Copyright (c) abego Software GmbH, 2005-2012 (www.abego-software.de)"};if(!window.abego){window.abego={}}if(!Array.forEach){Array.forEach=function(c,e,d){for(var b=0,a=c.length;b<a;b++){e.call(d,c[b],b,c)}};Array.prototype.forEach=function(d,c){for(var b=0,a=this.length;b<a;b++){d.call(c,this[b],b,this)}}}abego.toInt=function(b,a){if(!b){return a}var c=parseInt(b);return(c==NaN)?a:c};abego.createEllipsis=function(a){var b=createTiddlyElement(a,"span");b.innerHTML="&hellip;"};abego.shallowCopy=function(b){if(!b){return b}var a={};for(var c in b){a[c]=b[c]}return a};abego.copyOptions=function(a){return !a?{}:abego.shallowCopy(a)};abego.countStrings=function(d,c){if(!c){return 0}var a=c.length;var f=0;var e=0;while(true){var b=d.indexOf(c,e);if(b<0){return f}f++;e=b+a}return f};abego.getBracedText=function(j,e,a){if(!e){e=0}var k=/\{([^\}]*)\}/gm;k.lastIndex=e;var d=k.exec(j);if(d){var l=d[1];var b=abego.countStrings(l,"{");if(!b){if(a){a.lastIndex=k.lastIndex}return l}var g=j.length;for(var f=k.lastIndex;f<g&&b;f++){var h=j.charAt(f);if(h=="{"){b++}else{if(h=="}"){b--}}}if(!b){if(a){a.lastIndex=f-1}return j.substring(d.index+1,f-1)}}};abego.select=function(d,c,b,a){if(!a){a=[]}d.forEach(function(e){if(c.call(b,e)){a.push(e)}});return a};abego.consumeEvent=function(a){if(a.stopPropagation){a.stopPropagation()}if(a.preventDefault){a.preventDefault()}a.cancelBubble=true;a.returnValue=true};abego.TiddlerFilterTerm=function(d,b){if(!b){b={}}var c=d;if(!b.textIsRegExp){c=d.escapeRegExp();if(b.fullWordMatch){c="\\b"+c+"\\b"}}var a=new RegExp(c,"m"+(b.caseSensitive?"":"i"));this.tester=new abego.MultiFieldRegExpTester(a,b.fields,b.withExtendedFields)};abego.TiddlerFilterTerm.prototype.test=function(a){return this.tester.test(a)};abego.parseNewTiddlerCommandLine=function(c){var a=/(.*?)\.(?:\s+|$)([^#]*)(#.*)?/.exec(c);if(!a){a=/([^#]*)()(#.*)?/.exec(c)}if(a){var d;if(a[3]){var b=a[3].replace(/#/g,"");d=b.parseParams("tag")}else{d=[[]]}var e=a[2]?a[2].trim():"";d.push({name:"text",value:e});d[0].text=[e];return{title:a[1].trim(),params:d}}else{return{title:c.trim(),params:[[]]}}};abego.parseTiddlerFilterTerm=function(queryText,offset,options){var re=/\s*(?:(?:\{([^\}]*)\})|(?:(=)|([#%!])|(?:(\w+)\s*\:(?!\/\/))|(?:(?:("(?:(?:\\")|[^"])+")|(?:\/((?:(?:\\\/)|[^\/])+)\/)|(\w+\:\/\/[^\s]+)|([^\s\)\-\"]+)))))/mg;var shortCuts={"!":"title","%":"text","#":"tags"};var fieldNames={};var fullWordMatch=false;re.lastIndex=offset;while(true){var i=re.lastIndex;var m=re.exec(queryText);if(!m||m.index!=i){throw"Word or String literal expected"}if(m[1]){var lastIndexRef={};var code=abego.getBracedText(queryText,0,lastIndexRef);if(!code){throw"Invalid {...} syntax"}var f=Function("tiddler","return ("+code+");");return{func:f,lastIndex:lastIndexRef.lastIndex,markRE:null}}if(m[2]){fullWordMatch=true}else{if(m[3]){fieldNames[shortCuts[m[3]]]=1}else{if(m[4]){fieldNames[m[4]]=1}else{var textIsRegExp=m[6];var text=m[5]?window.eval(m[5]):m[6]?m[6]:m[7]?m[7]:m[8];options=abego.copyOptions(options);options.fullWordMatch=fullWordMatch;options.textIsRegExp=textIsRegExp;var fields=[];for(var n in fieldNames){fields.push(n)}if(fields.length==0){options.fields=options.defaultFields}else{options.fields=fields;options.withExtendedFields=false}var term=new abego.TiddlerFilterTerm(text,options);var markREText=textIsRegExp?text:text.escapeRegExp();if(markREText&&fullWordMatch){markREText="\\b"+markREText+"\\b"}return{func:function(tiddler){return term.test(tiddler)},lastIndex:re.lastIndex,markRE:markREText?"(?:"+markREText+")":null}}}}}};abego.BoolExp=function(i,c,j){this.s=i;var h=j&&j.defaultOperationIs_OR;var e=/\s*\)/g;var f=/\s*(?:(and|\&\&)|(or|\|\|))/gi;var b=/\s*(\-|not)?(\s*\()?/gi;var a;var d=function(p){b.lastIndex=p;var l=b.exec(i);var o=false;var k=null;if(l&&l.index==p){p+=l[0].length;o=l[1];if(l[2]){var n=a(p);e.lastIndex=n.lastIndex;if(!e.exec(i)){throw"Missing ')'"}k={func:n.func,lastIndex:e.lastIndex,markRE:n.markRE}}}if(!k){k=c(i,p,j)}if(o){k.func=(function(m){return function(q){return !m(q)}})(k.func);k.markRE=null}return k};a=function(s){var n=d(s);while(true){var p=n.lastIndex;f.lastIndex=p;var k=f.exec(i);var o;var q;if(k&&k.index==p){o=!k[1];q=d(f.lastIndex)}else{try{q=d(p)}catch(r){return n}o=h}n.func=(function(t,m,l){return l?function(u){return t(u)||m(u)}:function(u){return t(u)&&m(u)}})(n.func,q.func,o);n.lastIndex=q.lastIndex;if(!n.markRE){n.markRE=q.markRE}else{if(q.markRE){n.markRE=n.markRE+"|"+q.markRE}}}};var g=a(0);this.evalFunc=g.func;if(g.markRE){this.markRegExp=new RegExp(g.markRE,j.caseSensitive?"mg":"img")}};abego.BoolExp.prototype.exec=function(){return this.evalFunc.apply(this,arguments)};abego.BoolExp.prototype.getMarkRegExp=function(){return this.markRegExp};abego.BoolExp.prototype.toString=function(){return this.s};abego.MultiFieldRegExpTester=function(b,a,c){this.re=b;this.fields=a?a:["title","text","tags"];this.withExtendedFields=c};abego.MultiFieldRegExpTester.prototype.test=function(b){var d=this.re;for(var a=0;a<this.fields.length;a++){var c=store.getValue(b,this.fields[a]);if(typeof c=="string"&&d.test(c)){return this.fields[a]}}if(this.withExtendedFields){return store.forEachField(b,function(e,g,f){return typeof f=="string"&&d.test(f)?g:null},true)}return null};abego.TiddlerQuery=function(b,a,d,c,e){if(d){this.regExp=new RegExp(b,a?"mg":"img");this.tester=new abego.MultiFieldRegExpTester(this.regExp,c,e)}else{this.expr=new abego.BoolExp(b,abego.parseTiddlerFilterTerm,{defaultFields:c,caseSensitive:a,withExtendedFields:e})}this.getQueryText=function(){return b};this.getUseRegExp=function(){return d};this.getCaseSensitive=function(){return a};this.getDefaultFields=function(){return c};this.getWithExtendedFields=function(){return e}};abego.TiddlerQuery.prototype.test=function(a){if(!a){return false}if(this.regExp){return this.tester.test(a)}return this.expr.exec(a)};abego.TiddlerQuery.prototype.filter=function(a){return abego.select(a,this.test,this)};abego.TiddlerQuery.prototype.getMarkRegExp=function(){if(this.regExp){return"".search(this.regExp)>=0?null:this.regExp}return this.expr.getMarkRegExp()};abego.TiddlerQuery.prototype.toString=function(){return(this.regExp?this.regExp:this.expr).toString()};abego.PageWiseRenderer=function(){this.firstIndexOnPage=0};merge(abego.PageWiseRenderer.prototype,{setItems:function(a){this.items=a;this.setFirstIndexOnPage(0)},getMaxPagesInNavigation:function(){return 10},getItemsCount:function(a){return this.items?this.items.length:0},getCurrentPageIndex:function(){return Math.floor(this.firstIndexOnPage/this.getItemsPerPage())},getLastPageIndex:function(){return Math.floor((this.getItemsCount()-1)/this.getItemsPerPage())},setFirstIndexOnPage:function(a){this.firstIndexOnPage=Math.min(Math.max(0,a),this.getItemsCount()-1)},getFirstIndexOnPage:function(){this.firstIndexOnPage=Math.floor(this.firstIndexOnPage/this.getItemsPerPage())*this.getItemsPerPage();return this.firstIndexOnPage},getLastIndexOnPage:function(){return Math.min(this.getFirstIndexOnPage()+this.getItemsPerPage()-1,this.getItemsCount()-1)},onPageChanged:function(a,b){},renderPage:function(a){if(a.beginRendering){a.beginRendering(this)}try{if(this.getItemsCount()){var d=this.getLastIndexOnPage();var c=-1;for(var b=this.getFirstIndexOnPage();b<=d;b++){c++;a.render(this,this.items[b],b,c)}}}finally{if(a.endRendering){a.endRendering(this)}}},addPageNavigation:function(c){if(!this.getItemsCount()){return}var k=this;var g=function(n){if(!n){n=window.event}abego.consumeEvent(n);var i=abego.toInt(this.getAttribute("page"),0);var m=k.getCurrentPageIndex();if(i==m){return}var l=i*k.getItemsPerPage();k.setFirstIndexOnPage(l);k.onPageChanged(i,m)};var e;var h=this.getCurrentPageIndex();var f=this.getLastPageIndex();if(h>0){e=createTiddlyButton(c,"Previous","Go to previous page (Shortcut: Alt-'<')",g,"prev");e.setAttribute("page",(h-1).toString());e.setAttribute("accessKey","<")}for(var d=-this.getMaxPagesInNavigation();d<this.getMaxPagesInNavigation();d++){var b=h+d;if(b<0){continue}if(b>f){break}var a=(d+h+1).toString();var j=b==h?"currentPage":"otherPage";e=createTiddlyButton(c,a,"Go to page %0".format([a]),g,j);e.setAttribute("page",(b).toString())}if(h<f){e=createTiddlyButton(c,"Next","Go to next page (Shortcut: Alt-'>')",g,"next");e.setAttribute("page",(h+1).toString());e.setAttribute("accessKey",">")}}});abego.LimitedTextRenderer=function(){var l=40;var c=4;var k=function(p,z,v){var q=p.length;if(q==0){p.push({start:z,end:v});return}var u=0;for(;u<q;u++){var w=p[u];if(w.start<=v&&z<=w.end){var o;var s=u+1;for(;s<q;s++){o=p[s];if(o.start>v||z>w.end){break}}var x=z;var y=v;for(var t=u;t<s;t++){o=p[t];x=Math.min(x,o.start);y=Math.max(y,o.end)}p.splice(u,s-u,{start:x,end:y});return}if(w.start>v){break}}p.splice(u,0,{start:z,end:v})};var d=function(n){var q=0;for(var p=0;p<n.length;p++){var o=n[p];q+=o.end-o.start}return q};var b=function(n){return(n>="a"&&n<="z")||(n>="A"&&n<="Z")||n=="_"};var f=function(p,r){if(!b(p[r])){return null}for(var o=r-1;o>=0&&b(p[o]);o--){}var q=o+1;var t=p.length;for(o=r+1;o<t&&b(p[o]);o++){}return{start:q,end:o}};var a=function(o,q,p){var n;if(p){n=f(o,q)}else{if(q<=0){return q}n=f(o,q-1)}if(!n){return q}if(p){if(n.start>=q-c){return n.start}if(n.end<=q+c){return n.end}}else{if(n.end<=q+c){return n.end}if(n.start>=q-c){return n.start}}return q};var j=function(r,q){var n=[];if(q){var u=0;do{q.lastIndex=u;var o=q.exec(r);if(o){if(u<o.index){var p=r.substring(u,o.index);n.push({text:p})}n.push({text:o[0],isMatch:true});u=o.index+o[0].length}else{n.push({text:r.substr(u)});break}}while(true)}else{n.push({text:r})}return n};var i=function(p){var n=0;for(var o=0;o<p.length;o++){if(p[o].isMatch){n++}}return n};var h=function(v,u,q,t,o){var w=Math.max(Math.floor(o/(t+1)),l);var n=Math.max(w-(q-u),0);var r=Math.min(Math.floor(q+n/3),v.length);var p=Math.max(r-w,0);p=a(v,p,true);r=a(v,r,false);return{start:p,end:r}};var m=function(r,y,o){var n=[];var v=i(r);var u=0;for(var p=0;p<r.length;p++){var x=r[p];var w=x.text;if(x.isMatch){var q=h(y,u,u+w.length,v,o);k(n,q.start,q.end)}u+=w.length}return n};var g=function(t,p,o){var n=o-d(p);while(n>0){if(p.length==0){k(p,0,a(t,o,false));return}else{var q=p[0];var v;var r;if(q.start==0){v=q.end;if(p.length>1){r=p[1].start}else{k(p,v,a(t,v+n,false));return}}else{v=0;r=q.start}var u=Math.min(r,v+n);k(p,v,u);n-=(u-v)}}};var e=function(p,x,w,n,o){if(n.length==0){return}var u=function(z,I,D,F,C){var H;var G;var E=0;var B=0;var A=0;for(;B<D.length;B++){H=D[B];G=H.text;if(F<E+G.length){A=F-E;break}E+=G.length}var y=C-F;for(;B<D.length&&y>0;B++){H=D[B];G=H.text.substr(A);A=0;if(G.length>y){G=G.substr(0,y)}if(H.isMatch){createTiddlyElement(z,"span",null,"marked",G)}else{createTiddlyText(z,G)}y-=G.length}if(C<I.length){abego.createEllipsis(z)}};if(n[0].start>0){abego.createEllipsis(p)}var q=o;for(var r=0;r<n.length&&q>0;r++){var t=n[r];var v=Math.min(t.end-t.start,q);u(p,x,w,t.start,t.start+v);q-=v}};this.render=function(p,q,o,t){if(q.length<o){o=q.length}var r=j(q,t);var n=m(r,q,o);g(q,n,o);e(p,q,r,n,o)}};(function(){function alertAndThrow(msg){alert(msg);throw msg}if(version.major<2||(version.major==2&&version.minor<1)){alertAndThrow("YourSearchPlugin requires TiddlyWiki 2.1 or newer.\n\nCheck the archive for YourSearch plugins\nsupporting older versions of TiddlyWiki.\n\nArchive: http://tiddlywiki.abego-software.de/archive")}abego.YourSearch={};var lastResults=undefined;var lastQuery=undefined;var setLastResults=function(array){lastResults=array};var getLastResults=function(){return lastResults?lastResults:[]};var getLastResultsCount=function(){return lastResults?lastResults.length:0};var matchInTitleWeight=4;var precisionInTitleWeight=10;var matchInTagsWeight=2;var getMatchCount=function(s,re){var m=s.match(re);return m?m.length:0};var standardRankFunction=function(tiddler,query){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);var lengthOfMatchesInTitle=matchesInTitle?matchesInTitle.join("").length:0;var precisionInTitle=tiddler.title.length>0?lengthOfMatchesInTitle/tiddler.title.length:0;var rank=nMatchesInTitle*matchInTitleWeight+nMatchesInTags*matchInTagsWeight+precisionInTitle*precisionInTitleWeight+1;return rank};var findMatches=function(store,searchText,caseSensitive,useRegExp,sortField,excludeTag){lastQuery=null;var candidates=store.reverseLookup("tags",excludeTag,false);try{var defaultFields=[];if(config.options.chkSearchInTitle){defaultFields.push("title")}if(config.options.chkSearchInText){defaultFields.push("text")}if(config.options.chkSearchInTags){defaultFields.push("tags")}lastQuery=new abego.TiddlerQuery(searchText,caseSensitive,useRegExp,defaultFields,config.options.chkSearchExtendedFields)}catch(e){return[]}var results=lastQuery.filter(candidates);var rankFunction=abego.YourSearch.getRankFunction();for(var i=0;i<results.length;i++){var tiddler=results[i];var rank=rankFunction(tiddler,lastQuery);tiddler.searchRank=rank}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);return results};var maxCharsInTitle=80;var maxCharsInTags=50;var maxCharsInText=250;var maxCharsInField=50;var itemsPerPageDefault=25;var itemsPerPageWithPreviewDefault=10;var yourSearchResultID="yourSearchResult";var yourSearchResultItemsID="yourSearchResultItems";var lastSearchText=null;var resultElement=null;var searchInputField=null;var searchButton=null;var lastNewTiddlerButton=null;var initStylesheet=function(){if(version.extensions.YourSearchPlugin.styleSheetInited){return}version.extensions.YourSearchPlugin.styleSheetInited=true;setStylesheet(store.getTiddlerText("YourSearchStyleSheet"),"yourSearch")};var isResultOpen=function(){return resultElement!=null&&resultElement.parentNode==document.body};var closeResult=function(){if(isResultOpen()){document.body.removeChild(resultElement)}};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)};var adjustResultPositionAndSize=function(){if(!searchInputField){return}var root=searchInputField;var rootLeft=findPosX(root);var rootTop=findPosY(root);var rootHeight=root.offsetHeight;var popupLeft=rootLeft;var popupTop=rootTop+rootHeight;var winWidth=findWindowWidth();if(winWidth<resultElement.offsetWidth){resultElement.style.width=(winWidth-100)+"px";winWidth=findWindowWidth()}var popupWidth=resultElement.offsetWidth;if(popupLeft+popupWidth>winWidth){popupLeft=winWidth-popupWidth-30}if(popupLeft<0){popupLeft=0}resultElement.style.left=popupLeft+"px";resultElement.style.top=popupTop+"px";resultElement.style.display="block"};var scrollVisible=function(){if(resultElement){window.scrollTo(0,ensureVisible(resultElement))}if(searchInputField){window.scrollTo(0,ensureVisible(searchInputField))}};var ensureResultIsDisplayedNicely=function(){adjustResultPositionAndSize();scrollVisible()};var indexInPage=undefined;var currentTiddler=undefined;var pager=new abego.PageWiseRenderer();var MyItemRenderer=function(parent){this.itemHtml=store.getTiddlerText("YourSearchItemTemplate");if(!this.itemHtml){alertAndThrow("YourSearchItemTemplate not found")}this.place=document.getElementById(yourSearchResultItemsID);if(!this.place){this.place=createTiddlyElement(parent,"div",yourSearchResultItemsID)}};merge(MyItemRenderer.prototype,{render:function(pager,object,index,indexOnPage){indexInPage=indexOnPage;currentTiddler=object;var item=createTiddlyElement(this.place,"div",null,"yourSearchItem");item.innerHTML=this.itemHtml;applyHtmlMacros(item,null);refreshElements(item,null)},endRendering:function(pager){currentTiddler=null}});var refreshResult=function(){if(!resultElement||!searchInputField){return}var html=store.getTiddlerText("YourSearchResultTemplate");if(!html){html="<b>Tiddler YourSearchResultTemplate not found</b>"}resultElement.innerHTML=html;applyHtmlMacros(resultElement,null);refreshElements(resultElement,null);var itemRenderer=new MyItemRenderer(resultElement);pager.renderPage(itemRenderer);ensureResultIsDisplayedNicely()};pager.getItemsPerPage=function(){var n=(config.options.chkPreviewText)?abego.toInt(config.options.txtItemsPerPageWithPreview,itemsPerPageWithPreviewDefault):abego.toInt(config.options.txtItemsPerPage,itemsPerPageDefault);return(n>0)?n:1};pager.onPageChanged=function(){refreshResult()};var reopenResultIfApplicable=function(){if(searchInputField==null||!config.options.chkUseYourSearch){return}if((searchInputField.value==lastSearchText)&&lastSearchText&&!isResultOpen()){if(resultElement&&(resultElement.parentNode!=document.body)){document.body.appendChild(resultElement);ensureResultIsDisplayedNicely()}else{abego.YourSearch.onShowResult(true)}}};var invalidateResult=function(){closeResult();resultElement=null;lastSearchText=null};var isDescendantOrSelf=function(self,e){while(e!=null){if(self==e){return true}e=e.parentNode}return false};var onDocumentClick=function(e){if(e.target==searchInputField){return}if(e.target==searchButton){return}if(resultElement&&isDescendantOrSelf(resultElement,e.target)){return}closeResult()};var onDocumentKeyup=function(e){if(e.keyCode==27){closeResult()}};addEvent(document,"click",onDocumentClick);addEvent(document,"keyup",onDocumentKeyup);var myStorySearch=function(text,useCaseSensitive,useRegExp){lastSearchText=text;setLastResults(findMatches(store,text,useCaseSensitive,useRegExp,"title","excludeSearch"));abego.YourSearch.onShowResult()};var myMacroSearchHandler=function(place,macroName,params,wikifier,paramString,tiddler){initStylesheet();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){e=window.event}searchInputField=this;switch(e.keyCode){case 13:if(e.ctrlKey&&lastNewTiddlerButton&&isResultOpen()){lastNewTiddlerButton.onclick.apply(lastNewTiddlerButton,[e])}else{doSearch(this)}break;case 27: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){if(this.value!=lastSearchText){if(!config.options.chkUseYourSearch||config.options.chkSearchAsYouType){if(searchTimeout){clearTimeout(searchTimeout)}var txt=this;searchTimeout=setTimeout(function(){doSearch(txt)},500)}}else{if(searchTimeout){clearTimeout(searchTimeout)}}}if(this.value.length==0){closeResult()}};var focusHandler=function(e){this.select();clearMessage();reopenResultIfApplicable()};var args=paramString.parseParams("list",null,true);var buttonAtRight=getFlag(args,"buttonAtRight");var sizeTextbox=getParam(args,"sizeTextbox",this.sizeTextbox);var txt=createTiddlyElement(null,"input",null,"txtOptionInput searchField",null);if(params[0]){txt.value=params[0]}txt.onkeyup=keyHandler;txt.onfocus=focusHandler;txt.setAttribute("size",sizeTextbox);txt.setAttribute("accessKey",this.accessKey);txt.setAttribute("autocomplete","off");if(config.browser.isSafari){txt.setAttribute("type","search");txt.setAttribute("results","5")}else{if(!config.browser.isIE){txt.setAttribute("type","text")}}var btn=createTiddlyButton(null,this.label,this.prompt,clickHandler);if(place){if(!buttonAtRight){place.appendChild(btn)}place.appendChild(txt);if(buttonAtRight){place.appendChild(btn)}}searchInputField=txt;searchButton=btn};var openAllFoundTiddlers=function(){closeResult();var results=getLastResults();var n=results.length;if(n){var titles=[];for(var i=0;i<n;i++){titles.push(results[i].title)}story.displayTiddlers(null,titles)}};var createOptionWithRefresh=function(place,optionParams,wikifier,tiddler){invokeMacro(place,"option",optionParams,wikifier,tiddler);var elem=place.lastChild;var oldOnClick=elem.onclick;elem.onclick=function(e){var result=oldOnClick.apply(this,arguments);refreshResult();return result};return elem};var removeTextDecoration=function(s){var removeThis=["''","{{{","}}}","//","<<<","/***","***/"];var reText="";for(var i=0;i<removeThis.length;i++){if(i!=0){reText+="|"}reText+="("+removeThis[i].escapeRegExp()+")"}return s.replace(new RegExp(reText,"mg"),"").trim()};var getShortCutNumber=function(){var i=indexInPage;return(i>=0&&i<=9)?(i<9?(i+1):0):-1};var limitedTextRenderer=new abego.LimitedTextRenderer();var renderLimitedText=function(place,s,maxLen){limitedTextRenderer.render(place,s,maxLen,lastQuery.getMarkRegExp())};var oldTiddlyWikiSaveTiddler=TiddlyWiki.prototype.saveTiddler;TiddlyWiki.prototype.saveTiddler=function(title,newTitle,newBody,modifier,modified,tags,fields){oldTiddlyWikiSaveTiddler.apply(this,arguments);invalidateResult()};var oldTiddlyWikiRemoveTiddler=TiddlyWiki.prototype.removeTiddler;TiddlyWiki.prototype.removeTiddler=function(title){oldTiddlyWikiRemoveTiddler.apply(this,arguments);invalidateResult()};config.macros.yourSearch={label:"yourSearch",prompt:"Gives access to the current/last YourSearch result",handler:function(place,macroName,params,wikifier,paramString,tiddler){if(params.length==0){return}var name=params[0];var func=config.macros.yourSearch.funcs[name];if(func){func(place,macroName,params,wikifier,paramString,tiddler)}},tests:{"true":function(){return true},"false":function(){return false},found:function(){return getLastResultsCount()>0},previewText:function(){return config.options.chkPreviewText}},funcs:{itemRange:function(place){if(getLastResultsCount()){var lastIndex=pager.getLastIndexOnPage();var s="%0 - %1".format([pager.getFirstIndexOnPage()+1,lastIndex+1]);createTiddlyText(place,s)}},count:function(place){createTiddlyText(place,getLastResultsCount().toString())},query:function(place){if(lastQuery){createTiddlyText(place,lastQuery.toString())}},version:function(place){var t="YourSearch %0.%1.%2".format([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>"},copyright:function(place){var e=createTiddlyElement(place,"a");e.setAttribute("href","http://www.abego-software.de");e.innerHTML='<font color="black" face="Arial, Helvetica, sans-serif">&copy; 2005-2008 <b><font color="red">abego</font></b> Software<font>'},newTiddlerButton:function(place){if(lastQuery){var r=abego.parseNewTiddlerCommandLine(lastQuery.getQueryText());var btn=config.macros.newTiddler.createNewTiddlerButton(place,r.title,r.params,"new tiddler","Create a new tiddler based on search text. (Shortcut: Ctrl-Enter; Separators: '.', '#')",null,"text");var oldOnClick=btn.onclick;btn.onclick=function(){closeResult();oldOnClick.apply(this,arguments)};lastNewTiddlerButton=btn}},linkButton:function(place,macroName,params,wikifier,paramString,tiddler){if(params<2){return}var tiddlyLink=params[1];var text=params<3?tiddlyLink:params[2];var tooltip=params<4?text:params[3];var accessKey=params<5?null:params[4];var btn=createTiddlyButton(place,text,tooltip,closeResultAndDisplayTiddler,null,null,accessKey);btn.setAttribute("tiddlyLink",tiddlyLink)},closeButton:function(place,macroName,params,wikifier,paramString,tiddler){createTiddlyButton(place,"close","Close the Search Results (Shortcut: ESC)",closeResult)},openAllButton:function(place,macroName,params,wikifier,paramString,tiddler){var n=getLastResultsCount();if(n==0){return}var title=n==1?"open tiddler":"open all %0 tiddlers".format([n]);var button=createTiddlyButton(place,title,"Open all found tiddlers (Shortcut: Alt-O)",openAllFoundTiddlers);button.setAttribute("accessKey","O")},naviBar:function(place,macroName,params,wikifier,paramString,tiddler){pager.addPageNavigation(place)},"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{showIt=(!eval(testName))==negate}}catch(ex){}if(!showIt){place.style.display="none"}},chkPreviewText:function(place,macroName,params,wikifier,paramString,tiddler){var elem=createOptionWithRefresh(place,"chkPreviewText",wikifier,tiddler);elem.setAttribute("accessKey","P");elem.title="Show text preview of found tiddlers (Shortcut: Alt-P)";return elem}}};config.macros.foundTiddler={label:"foundTiddler",prompt:"Provides information on the tiddler currently processed on the YourSearch result page",handler:function(place,macroName,params,wikifier,paramString,tiddler){var name=params[0];var func=config.macros.foundTiddler.funcs[name];if(func){func(place,macroName,params,wikifier,paramString,tiddler)}},funcs:{title:function(place,macroName,params,wikifier,paramString,tiddler){if(!currentTiddler){return}var shortcutNumber=getShortCutNumber();var tooltip=shortcutNumber>=0?"Open tiddler (Shortcut: Alt-%0)".format([shortcutNumber.toString()]):"Open tiddler";var btn=createTiddlyButton(place,null,tooltip,closeResultAndDisplayTiddler,null);btn.setAttribute("tiddlyLink",currentTiddler.title);btn.setAttribute("withHilite","true");renderLimitedText(btn,currentTiddler.title,maxCharsInTitle);if(shortcutNumber>=0){btn.setAttribute("accessKey",shortcutNumber.toString())}},tags:function(place,macroName,params,wikifier,paramString,tiddler){if(!currentTiddler){return}renderLimitedText(place,currentTiddler.getTags(),maxCharsInTags)},text:function(place,macroName,params,wikifier,paramString,tiddler){if(!currentTiddler){return}renderLimitedText(place,removeTextDecoration(currentTiddler.text),maxCharsInText)},field:function(place,macroName,params,wikifier,paramString,tiddler){if(!currentTiddler){return}var name=params[1];var len=params.length>2?abego.toInt(params[2],maxCharsInField):maxCharsInField;var v=store.getValue(currentTiddler,name);if(v){renderLimitedText(place,removeTextDecoration(v),len)}},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)}}}};var opts={chkUseYourSearch:true,chkPreviewText:true,chkSearchAsYouType:true,chkSearchInTitle:true,chkSearchInText:true,chkSearchInTags:true,chkSearchExtendedFields:true,txtItemsPerPage:itemsPerPageDefault,txtItemsPerPageWithPreview:itemsPerPageWithPreviewDefault};for(var n in opts){if(config.options[n]==undefined){config.options[n]=opts[n]}}config.shadowTiddlers.AdvancedOptions+="\n<<option chkUseYourSearch>> Use 'Your Search' //([[more options|YourSearch Options]]) ([[help|YourSearch Help]])// ";config.shadowTiddlers["YourSearch Help"]="!Field Search\nWith the Field Search you can restrict your search to certain fields of a tiddler, e.g only search the tags or only the titles. The general form is //fieldname//'':''//textToSearch// (e.g. {{{title:intro}}}). In addition one-character shortcuts are also supported for the standard fields {{{title}}}, {{{text}}} and {{{tags}}}:\n|!What you want|!What you type|!Example|\n|Search ''titles only''|start word with ''!''|{{{!jonny}}} (shortcut for {{{title:jonny}}})|\n|Search ''contents/text only''|start word with ''%''|{{{%football}}} (shortcut for {{{text:football}}})|\n|Search ''tags only''|start word with ''#''|{{{#Plugin}}} (shortcut for {{{tags:Plugin}}})|\n\nUsing this feature you may also search the extended fields (\"Metadata\") introduced with TiddlyWiki 2.1, e.g. use {{{priority:1}}} to find all tiddlers with the priority field set to \"1\".\n\nYou may search a word in more than one field. E.g. {{{!#Plugin}}} (or {{{title:tags:Plugin}}} in the \"long form\") finds tiddlers containing \"Plugin\" either in the title or in the tags (but does not look for \"Plugin\" in the text). \n\n!Boolean Search\nThe Boolean Search is useful when searching for multiple words.\n|!What you want|!What you type|!Example|\n|''All words'' must exist|List of words|{{{jonny jeremy}}} (or {{{jonny and jeremy}}})|\n|''At least one word'' must exist|Separate words by ''or''|{{{jonny or jeremy}}}|\n|A word ''must not exist''|Start word with ''-''|{{{-jonny}}} (or {{{not jonny}}})|\n\n''Note:'' When you specify two words, separated with a space, YourSearch finds all tiddlers that contain both words, but not 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\nUsing parenthesis you may change the default \"left to right\" evaluation of the boolean search. E.g. {{{not (jonny or jeremy)}}} finds all tiddlers that contain neither \"jonny\" nor \"jeremy. In contrast to this {{{not jonny or jeremy}}} (i.e. without parenthesis) finds all tiddlers that either don't contain \"jonny\" or that contain \"jeremy\".\n\n!'Exact Word' Search\nBy default a search result all matches that 'contain' the searched text. E.g. if you search for {{{Task}}} you will get all tiddlers containing 'Task', but also '~CompletedTask', '~TaskForce' etc.\n\nIf you only want to get the tiddlers that contain 'exactly the word' you need to prefix it with a '='. E.g. typing '=Task' will find the tiddlers that contain the word 'Task', ignoring words that just contain 'Task' as a substring.\n\n!~CaseSensitiveSearch and ~RegExpSearch\nThe standard search options ~CaseSensitiveSearch and ~RegExpSearch are fully supported by YourSearch. However when ''~RegExpSearch'' is on Filtered and Boolean Search are disabled.\n\nIn addition you may do a \"regular expression\" search even with the ''~RegExpSearch'' set to false by directly entering the regular expression into the search field, framed with {{{/.../}}}. \n\nExample: {{{/m[ae][iy]er/}}} will find all tiddlers that contain either \"maier\", \"mayer\", \"meier\" or \"meyer\".\n\n!~JavaScript Expression Filtering\nIf you are familiar with JavaScript programming and know some TiddlyWiki internals you may also use JavaScript expression for the search. Just enter a JavaScript boolean expression into the search field, framed with {{{ { ... } }}}. In the code refer to the variable tiddler and evaluate to {{{true}}} when the given tiddler should be included in the result. \n\nExample: {{{ { tiddler.modified > new Date(\"Jul 4, 2005\")} }}} returns all tiddler modified after July 4th, 2005.\n\n!Combined Search\nYou 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\n!Access Keys\nYou 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\n//If some of these shortcuts don't work for you check your browser if you have other extensions installed that already \"use\" these shortcuts.//";config.shadowTiddlers["YourSearch Options"]="|>|!YourSearch Options|\n|>|<<option chkUseYourSearch>> Use 'Your Search'|\n|!|<<option chkPreviewText>> Show Text Preview|\n|!|<<option chkSearchAsYouType>> 'Search As You Type' Mode (No RETURN required to start search)|\n|!|Default Search Filter:<<option chkSearchInTitle>>Title ('!')     <<option chkSearchInText>>Text ('%')     <<option chkSearchInTags>>Tags ('#')    <<option chkSearchExtendedFields>>Extended Fields<html><br><font size=\"-2\">The fields of a tiddlers that are searched when you don't explicitly specify a filter in the search text <br>(Explictly specify fields using one or more '!', '%', '#' or 'fieldname:' prefix before the word/text to find).</font></html>|\n|!|Number of items on search result page: <<option txtItemsPerPage>>|\n|!|Number of items on search result page with 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: #ffd;\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 newTiddlerButton"></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 newTiddlerButton"></span>\n\t\t<span macro="yourSearch linkButton \'YourSearch Options\' options \'Configure YourSearch\'"></span>\n\t\t<span macro="yourSearch linkButton \'YourSearch Help\' help \'Get help how to use YourSearch\'"></span>\n\t\t<span macro="yourSearch closeButton"></span>\n\t</td>\n  </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 field tags 50'/></span>\n<span macro=\"yourSearch if previewText\"><div class='yourSearchText' macro='foundTiddler field text 250'/></div></span>\n<!--\n}}}\n-->";config.shadowTiddlers.YourSearch="<<tiddler [[YourSearch Help]]>>";config.shadowTiddlers["YourSearch Result"]="The popup-like window displaying the result of a YourSearch query.";config.macros.search.handler=myMacroSearchHandler;var checkForOtherHijacker=function(){if(config.macros.search.handler!=myMacroSearchHandler){alert("Message from YourSearchPlugin:\n\n\nAnother plugin has disabled the 'Your Search' features.\n\n\nYou may disable the other plugin or change the load order of \nthe plugins (by changing the names of the tiddlers)\nto enable the 'Your Search' features.")}};setTimeout(checkForOtherHijacker,5000);abego.YourSearch.getStandardRankFunction=function(){return standardRankFunction};abego.YourSearch.getRankFunction=function(){return abego.YourSearch.getStandardRankFunction()};abego.YourSearch.getCurrentTiddler=function(){return currentTiddler};abego.YourSearch.closeResult=function(){closeResult()};abego.YourSearch.getFoundTiddlers=function(){return lastResults};abego.YourSearch.getQuery=function(){return lastQuery};abego.YourSearch.onShowResult=function(useOldResult){highlightHack=lastQuery?lastQuery.getMarkRegExp():null;if(!useOldResult){pager.setItems(getLastResults())}if(!resultElement){resultElement=createTiddlyElement(document.body,"div",yourSearchResultID,"yourSearchResult")}else{if(resultElement.parentNode!=document.body){document.body.appendChild(resultElement)}}refreshResult();highlightHack=null}})()};
//%/
<!--{{{-->
<!-- <div class='header' role='banner' 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' role='navigation' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' role='navigation' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' role='complementary' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea' role='main'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
-->
<!--}}}-->
|PageTemplate|WebPageTemplate|
|ViewTemplate|WebViewTemplate|
|StyleSheet|WebStyleSheet|
<!--{{{-->
<div id='displayArea' role='main'>
  <div id='messageArea'></div>
  <div id='tiddlerDisplay'></div>
</div>
<!--<div id='footer' refresh='content' tiddler='SiteFooter'></div>-->
<!--}}}-->
<!--{{{-->
<div class='toolbar' role='navigation' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
<!--{{{-->
<link rel='stylesheet' type='text/css' href='css/bootstrap.min.css' />
<link rel='stylesheet' type='text/css' href='css/course.css' />
<div id="navigation">
	<div class="container">
		<div class="row">
			<div class="col-md-4 hidden-xs hidden-sm"><a href="http://childrenscience.ru/"><img src="images/logo_small.svg?crc=140944996" width="176" height="25" /></a></div>
			<div class="col-md-3 col-xs-6"><a href="http://childrenscience.ru/courses.html" class="nav-link">Курсы</a></div>
			<div class="col-md-3 col-xs-6"><a href="http://childrenscience.ru/about.html" class="nav-link">О проекте</a></div>
		</div>
	</div>
</div>

<div class="container" id="main_content">
	<div class="row">
		<div class="col-md-12 col-xs-12" id="course-header">
			<h2 id="course-name">Решение генетических задач</h2>
			<h4 id="course-author">Екатерина Романовна Черткова</h4>
			<h4 id="course-audience">9–11 класс</h4>
			<img src="images/cpm.png" width="70px" height="67px" class="pull-left" id="course-sponsor-img" />
			<div class="pull-left center-block" id="course-sponsor-txt">Курс создан при поддержке<br/>Центра Педагогического мастерства</div>
		</div>
	</div>
	<div macro='fillCourseHeader'></div> <!-- TW stuff -->
	<div class="row row-flex" id="course-content">
	
		<div class="col-sm-4 col-xs-12" id="lessons-list">
			<div class="lesson">
				<a href="#" class="lesson-name h4">О курсе</a>
			</div>
		
			<div class="lesson">
				<div class="lesson-name h4">Урок с подтемами или часть.</div>
				<div class="sub-lessons">
					<a href="#" class="lesson-name">Урок 0</a>
					<a href="#" class="lesson-name lesson-active">Урок 1 (активен)</a>
					<a href="#" class="lesson-name">Урок 2. С очень длинным, просто отвратительным названием.</a>
					<a href="#" class="lesson-name">Урок 3</a>
				</div>
			</div>
			<div class="lesson">
				<div class="lesson-name h4">Урок с подтемами или часть.</div>
				<div class="sub-lessons">
					<a href="#" class="lesson-name">Урок 0</a>
					<a href="#" class="lesson-name">Урок 1</a>
					<a href="#" class="lesson-name">Урок 2. С очень длинным, просто отвратительным названием.</a>
					<a href="#" class="lesson-name">Урок 3</a>
				</div>
			</div>
			
			<div class="lesson">
				<a href="#" class="lesson-name h4">Урок без подтем</a>
			</div>
			<div class="lesson">
				<a href="#" class="lesson-name h4">Урок без подтем</a>
			</div>
			<div class="lesson">
				<a href="#" class="lesson-name h4 lesson-active">Активный урок без подтем</a>
			</div>
			<div class="lesson">
				<a href="#" class="lesson-name h4">Урок без подтем</a>
			</div>
			
		</div>
		
		<div class="col-sm-8" id="lesson-content">
		
			<h2>Строение нуклеиновых кислот</h2>
			<h5>Занятие 2</h5>
			<p>Курс «Решение генетических задач» поможет разобраться в алгоритме решения генетических задач, встречающихся на ЕГЭ и олимпиадах.</p>
			<p>На первом занятии автор курса, преподаватель школы «Интеллектуал» Екатерина Черткова расскажет о строении нуклеиновых кислот и процессах, которые с ними происходят.</p>
			<a class="btn btn-primary" href="#">Пройти занятие</a>
			
			<ol id="lesson-steps">
				<li>
					<p><a href="#"><img src="images/video.svg" width="24px" height="24px" class="pull-left"/><span class="lesson-label">Шаг 1 - видео</span></a><br/><small>видео - 5:99</small></p>
				</li>
				<li>
					<p><a href="#"><img src="images/text.svg" width="24px" height="24px" class="pull-left"/><span class="lesson-label">Шаг 2 - конспект</span></a><br/><small>конспект</small></p>
				</li>
				<li>
					<p><a href="#"><img src="images/test.svg" width="24px" height="24px" class="pull-left"/><span class="lesson-label">Шаг 3 - тест</span></a><br/><small>текст</small></p>
				</li>
				<li>
					<p><a href="#"><img src="images/task.svg" width="24px" height="24px" class="pull-left"/><span class="lesson-label">Шаг 4 - задание</span></a><br/><small>задание</small></p>
				</li>
			</ol>
			
			<p><a href="#"><img src="images/dwnld_solid.svg" width="24px" height="24px" />Конспект лекции <img src="images/pdf.png" width="28px" height="18px" /> 278 kB </a></p>
			<p><a href="#"><img src="images/dwnld_solid.svg" width="24px" height="24px" />Слайды видеолекции <img src="images/pdf.png" width="28px" height="18px" /> 278 kB </a></p>
		
		</div>
		
	</div>
	<div macro='fillCourseContents'></div> <!-- TW stuff -->
</div>

<div id="footer">
	<div class="container">
		<div class=row>
			<div class="col-md-8 col-sm-6">
				<h4><strong>Пишите!</strong></h4>
				<h4><a href="mailto:info@childrenscience.ru">info@childrenscience.ru</a></h4>
			</div>
			<div class="col-md-4 col-sm-6">
				<h4><strong>Мы в соцсетях:</strong></h4>
				<div class="row">
					<div class="col-sm-3 col-xs-2"><a href="http://vk.com/childrenscience"><img src="images/vk.png" width="46px" height="46px"></a></div>
					<div class="col-sm-3 col-xs-2"><a href="https://www.facebook.com/detinauka"><img src="images/fb.png" width="46px" height="46px"></a></div>
					<div class="col-sm-3 col-xs-2"><a href="http://childrenscience.livejournal.com/"><img src="images/lj.png" width="46px" height="46px"></a></div>
					<div class="col-sm-3 col-xs-2"><a href="https://www.youtube.com/childrenscience"><img src="images/yt.png" width="46px" height="46px"></a></div>
				</div>
			</div>
			
		</div>
	</div>
</div>
<script src="js/bootstrap.min.js"></script>
<!--}}}-->
<!--{{{-->
<div class='centralAllColomn'>
 <div macro='unitStep'>
 </div><div class='unitStepFooter' macro='unitStepFooter'>
 </div>	<!-- implement this, make sure it's visible and on bottom if possible -->
</div>
<!--}}}-->
[[StyleSheet]]
/*{{{*/
/* indicate visitor mode */
body { background-color: #ffffff; }
.tiddler {
	padding: 0;
	/*display: flex;
	flex-direction: column;
	min-height: 100vh;*/
}
*[tiddlyLink] { cursor: pointer }
/* .tiddlyLink { cursor: pointer } */
a:hover {
	background-color: inherit;
}
a { cursor: pointer; }
h2 {
	border: none;
}

#displayArea { position: relative; width: 100%; margin-left: 0; margin-right: 0; margin-top: 0; }

/* links' style adjustments */
#navigation a { padding: 25px 0 2px !important; }
#navigation .nav-link { border-bottom: thin solid rgba(0,0,0,0.2); }
#navigation .nav-link:hover { background-color: white !important; color: #00d5e0; border-bottom: 1px solid rgba(0,213,224,0.3);}

/* lesson */
.unitStep {
	width: 100%;
}
.lessonNavigation	  {
	padding-top: 9px;
	padding-bottom: 5px;
	border-bottom: 2px solid rgb(233,233,233);
}
.lessonNavigation a	  {
	display: inline-block;
	vertical-align: middle;
	color:   #198C99;
	font-family: open-sans, sans-serif; font-weight: normal; font-size: 16px;
	margin-left: 0.55em; margin-right:0.55em;
	padding-left: 0.2em; padding-right: 0.2em;
	cursor:  pointer;
}
.lessonNavigation .currentStep {
	padding-top: 0.2em; /*padding-bottom: 0.2em;*/
	background-color: #AEF0FF; border-radius: 50%;
}
.lessonNavigation .currentStep:hover {
	background-color: #AEF0FF;
}
.lessonNavigation .noLinkToContents	{ padding-left: 3em; }
.lessonNavigation .noLinkToContents,
.lessonNavigation .noLink		{ cursor: default; }
/*.lessonNavigation .noLinkToContents:hover { color: black; }*/
.lessonNavigation div	  { display: inline-block; }
/* the magic to enable vertical align (see http://christopheraue.net/2014/02/20/centering-with-vertical-align-middle/) */
.lessonNavigation	  { height: 30px; }
.lessonNavigation a:after { vertical-align: middle; display: inline-block; height: 100%; content: ''; }

.lessonNavigation a:hover { background-color: white; color: #198C99; }
.currentStep div { border-radius: 50%; background-color: #AEF0FF; }

.unitStep {
	margin-top: 10px; margin-bottom: 10px; /* temporal solution until this is set by the plugin */
}

.goPrevContainer, .unitStepContentContainer, .goNextContainer {
	display: inline-block;
	content: ""; vertical-align: middle; /* trick from http://codepen.io/edge0703/pen/iHJuA */
}
.goPrevContainer, .goNextContainer {
	width: calc(50% - 512px);/* calc version behaves bad on zoom in *//*15%;*/
	height: 100%;
	text-align: center;
	line-height: 1em;
}
.goPrevContainer:hover, .goNextContainer:hover {
	margin-top: calc(1em + 8px);
}
.goPrevContainer:hover:after {
	content: "Назад"; display: block; margin-right: 4px;
	margin-top: 8px;
}
.goNextContainer:hover:after {
	content: "Дальше"; display: block; margin-left: 4px;
	margin-top: 8px;
}
.goPrevContainer > div, .goNextContainer > div {
	display: inline-block;
	width: 80px; height: 80px;
}
.backContainer, .forthContainer { cursor: pointer; }
.backward, .forward {
	margin-top: 16px;
	display: inline-block;
/*	vertical-align: middle;*/
}
.backward { margin-right: 4px; }
.forward  { margin-left:  4px; }
/*.backward:before, .forward:before {
	display: inline-block; height: 100%;
	content: ''; vertical-align: middle;
}*/
.goPrevContainer > div:hover, .goNextContainer > div:hover {
	background-color: #eeeeee;
	border-radius: 50%;
}
.unitStepContentContainer {
	width: 1024px;/*70%;*/
}
.contentIframe, .contentImage {
	width: 100%;
}
.unitStepFooter {
	border-top: 2px solid rgb(233,233,233);
}

/* course */
.courseTopLineMenu, .courseHeader, .courseTableOfContents { padding-left: 40px; }
.courseTopLineMenu {
	min-height: 70px;
}
.courseTopLineMenu a:hover {
	background-color: inherit;
}
.courseTopLineMenu .svg {
	height: 27px;
	display: inline-block;
/*	vertical-align: middle;*/
}
/*.courseTopLineMenu .svg:after { vertical-align: middle; display: inline-block; height: 100%; content: ''; }*/
.courseHeader	{
	background-color: #77cc77; color: white;
	background-repeat: no-repeat;
	background-size: cover;
}
#course-header	{ background-size: cover !important; }
 .courseTitle	{ font-size: 3em; }
div[tiddler="Химия. Материалы для самостоятельного обучения"]
 #course-header	{ background-color: rgba(0,0,0,0.4); }
div[tiddler="Олимпиадная математика"]
 #course-name	{ margin-top: 10px; }
.courseContent	{}
 .courseTableOfContents {
	display: table-cell; width: 25%;
	min-height: 1em; background-color: #dddddd;
 }
  .lesson-name {
	cursor: pointer;
  }
  .lesson-name.h4 {line-height: 19px;}
  /* indent-0 inherits padding-left from .lesson-name */
  /* .indent-1 { padding-left: 45px !important; } */
  .indent-2 { padding-left: 60px !important; }
  .h3, .h2 {
	cursor: default; /* this is until we implement accordeon */
  }
  .h3:hover, .h2:hover {
	text-decoration: none; /* this is until we implement accordeon */
  }
  .mute, .mute:hover {
	color: #999999 !important;
	cursor: default;
  }
 .courseLessonTableOfContents {
	display: table-cell; width: 75%;
	min-height: 1em;
	padding-top: 100px;
	padding-left: 50px;
 }
  .lessonTitle {
	font-size: 3em;
  }
  .startLessonButton {
	display: inline-block;
	padding: 0.7em 1em;
	margin: 1em 0;
	background-color: #1F98AE;
	color: white;
	border-radius: 0.5em;
  }
  .chem_page_preview { width:50%; }
  .startLessonButton:hover {
	background-color: #006F82;
  }
  .lessonStepInCourseContents {
	height: 45px;
	color: rgb(123, 133, 135);
	border-bottom: 1px solid rgb(222, 222, 222);
  }
   .lessonStepInCourseContents div {
	display: inline-block;
	vertical-align: middle;
   }
   .lessonStepInCourseContents div:before {
	display: inline-block; height: 100%; vertical-align: middle; content: '';
   }
   .lessonStepNumber {
	width: 2em;
	text-align: center;
	font-weight: bold;
   }
  .attachment-icon     { width: 39px; height: 24px; }
  .pdf-attachment-icon { width: 37px; height: 18px; }

  div[tiddler="Окружающий мир. 1й класс"] #lessons-list div:last-child {
	margin-top: 25px;
  }
  div[tiddler="Окружающий мир. 1й класс"] #lessons-list div:last-child a {
	border-bottom: none;
  }
  div[tiddler="Окружающий мир. 1й класс"] #lessons-list div:last-child a:hover {
	border-bottom: none;
  }
  #lesson-content table {
	font-size: 18px; /* for paragraphs is set inside course.css */
	margin-bottom: 0.5em;
  }
  #lesson-content table td {
	padding-bottom: 0.5em;
	padding-right: 1em;
	vertical-align: top;
  }
  #lesson-content table.logos td {
	vertical-align: middle;
  }

.courseFooter	{
	min-height: 1em; background-color: black; color: white;
}
/*}}}*/
chkAnimate: false
chkAutoSave: true
chkSaveBackups: false
/*{{{*/
/* indicate author mode */
body { background-color: #f6fff0; }

/* smartphones and tablets */

/* smartphones */
@media screen and (max-width: 700px) {
	.header	{ display: none; }
}

/**/
div[tags~="systemConfig"] .viewer pre	{ clear: both !important; }

div[tags~="unit"] .tagging,
div[tags~="course"] .tagging	{ display: none; }

/* arrows, 80:140 */
.backward, .forward {
	display: inline-block;
/*	width:   64px;
	height:  112px;
*/	width:   25px;
	height:  48px;
}
.backward {
	/* background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTIuNSAyMyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMTIuNSAyMzsiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+LnN0MHtmaWxsOiMyMTJCMkI7fTwvc3R5bGU+PGc+PHBhdGggY2xhc3M9InN0MCIgZD0iTTExLjEsMjNsMS40LTEuNEwyLjQsMTEuNUwxMi41LDEuNEwxMS4xLDBMMC4zLDEwLjhDMC4xLDExLDAsMTEuMiwwLDExLjVzMC4xLDAuNSwwLjMsMC43TDExLjEsMjN6Ii8+PC9nPjwvc3ZnPg==); */
	background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAwCAMAAADTlCGvAAAAG1BMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4Gco9AAAACHRSTlMAEDBQwNDg8PRX3M8AAAAySURBVDjL7cmxDQAwCAOwAKXw/8VIncnSOV4NrDxAojpYpCkUz2WB0+X7WKpUH8UG5hgU7gQbPMr73AAAAABJRU5ErkJggg==);
	background-repeat: no-repeat;
}
.forward {
	/* background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTIuNSAyMyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMTIuNSAyMzsiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+LnN0MHtmaWxsOiMyMTJCMkI7fTwvc3R5bGU+PGc+PHBhdGggY2xhc3M9InN0MCIgZD0iTTEuNCwyM0wwLDIxLjZsMTAuMS0xMC4xTDAsMS40TDEuNCwwbDEwLjgsMTAuOGMwLjIsMC4yLDAuMywwLjQsMC4zLDAuN3MtMC4xLDAuNS0wLjMsMC43TDEuNCwyM3oiLz48L2c+PC9zdmc+); */
	background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAwCAMAAADTlCGvAAAAG1BMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4Gco9AAAACHRSTlMAEDBQwNDg8PRX3M8AAAAySURBVDjL7ckxEgAgCAMwCiL8/8V6znRxbtaYOWzmlWDTKtVHrXmQvVmUKxRPsLhF4gD0rwQb4xAyFgAAAABJRU5ErkJggg==);
	background-repeat: no-repeat;
}

.logo-sign {
	background-image: url(./images/int_logo.gif);background-size: auto 30px; width:42px; height:30px;
	background-repeat: no-repeat;
}

/* step types */
.pager {
	background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0i0KHQu9C+0LlfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjQgMjQiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxnPjxnPjxnPjxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzM5NEI0RCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIGQ9Ik0xOS41LDIwLjVjMCwxLjEtMC45LDItMiwyaC0xMWMtMS4xLDAtMi0wLjktMi0ydi0xNGMwLTEuMSwwLjktMiwyLTJoMTFjMS4xLDAsMiwwLjksMiwyVjIwLjV6Ii8+PC9nPjxnPjxsaW5lIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzM5NEI0RCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHgxPSI4IiB5MT0iOS41IiB4Mj0iMTYiIHkyPSI5LjUiLz48L2c+PGc+PGxpbmUgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzk0QjREIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgeDE9IjgiIHkxPSIxNy41IiB4Mj0iMTYiIHkyPSIxNy41Ii8+PC9nPjxnPjxsaW5lIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzM5NEI0RCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHgxPSI4IiB5MT0iMTMuNSIgeDI9IjE2IiB5Mj0iMTMuNSIvPjwvZz48L2c+PC9nPjwvc3ZnPg==);
}
.video {
	background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0i0KHQu9C+0LlfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjQgMjQiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxnPjxwYXRoIGZpbGw9IiMzOTRCNEQiIGQ9Ik02Ljk4LDUuNzljMC4wNDMsMC4wMTUsMC4xNjUsMC4wNDEsMC4yMzcsMC4wODVsMTEuNzU2LDcuMTM2YzAuMTQ2LDAuMDg5LDAuMjI0LDAuMTY4LDAuMjQsMC4xNjhjMC4wMDIsMCwwLjAxMS0wLjAwMSwwLjAxMi0wLjAwM2MtMC4wMDgsMC4wMjItMC4wNjIsMC4xMDgtMC4yMjMsMC4yMDZMNy4yNzksMjAuNTE5Yy0wLjA3MiwwLjA0NC0wLjI1OCwwLjA3LTAuMzAxLDAuMDg1Yy0wLjAwOS0wLjA0Ni0wLjE0Ni0wLjEwOS0wLjE0Ni0wLjE5MVY1Ljk4MUM2LjgzMyw1Ljg5OSw2Ljk3MSw1LjgzNiw2Ljk4LDUuNzkgTTYuODg4LDQuNzY4Yy0wLjU0OCwwLTEuMDU1LDAuNDQzLTEuMDU1LDEuMjEzdjE0LjQzMmMwLDAuNzcsMC41MDcsMS4yMTMsMS4wNTUsMS4yMTNjMC4yMzUsMCwwLjU2Ni0wLjA4MSwwLjg0OC0wLjI1M2wxMS43NTYtNy4xMzZjMC45NC0wLjU3MiwwLjk1Ni0xLjUwOCwwLjAxNi0yLjA4TDcuNzI4LDUuMDJDNy40NDcsNC44NDksNy4xMjMsNC43NjgsNi44ODgsNC43NjhMNi44ODgsNC43Njh6Ii8+PC9nPjwvc3ZnPg==);
}
.task {
	background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0i0KHQu9C+0LlfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjQgMjQiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxnPjxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzM5NEI0RCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIGQ9Ik0xMC42NjYsMjEuNWMtMC41NSwwLTEuNDUsMC0yLDBINS43M2MtMC41NSwwLTEuMjMtMC4yMDItMS4yMy0wLjc1MlY4Ljg4OUM0LjUsOC4zMzksNS4xOCw3LjUsNS43Myw3LjVoMi4yNzhjMC41NSwwLDEuNDIzLDAuMDQxLDEuOTQtMC4xNDdjMCwwLDEuMzItMC4zODMsMC41NDQtMS4xOUM5Ljg5Myw1LjU0LDkuNTg5LDUuMDE1LDkuNTg5LDQuMTMyYzAtMS4yMDQsMS4zOTYtMi42NTYsMi43MTEtMi42NTZjMS4zMTYsMCwyLjcxMSwxLjQ4OCwyLjcxMSwyLjY5M2MwLDAuODg0LTAuMzA0LDEuNDYzLTAuOTAzLDIuMDg2Yy0wLjc3NiwwLjgwNywwLjU0NCwxLjA5NiwwLjU0NCwxLjA5NmMwLjUxNywwLjE4OCwxLjM5LDAuMTUsMS45NCwwLjE1aDIuMjc4YzAuNTUsMCwwLjYzLDAuODM5LDAuNjMsMS4zODl2MS44NmMwLDAuNTUsMC4wMjcsMS40MjEtMC4xNjUsMS45MzdjMCwwLTAuNDU1LDEuNDYyLTEuMzM2LDAuNzUxYy0wLjY4LTAuNTQ4LTEuMjYxLTEuMTI4LTIuMjI2LTEuMTI4Yy0xLjMxNiwwLTIuNzI1LDEuMzA1LTIuNzI1LDIuNTA5YzAsMS4yMDQsMS40NDQsMi41MDksMi43NiwyLjUwOWMwLjk2NSwwLDEuNTk4LTAuNTc5LDIuMjc4LTEuMTI4YzAuODgyLTAuNzExLDEuMjQ3LDAuNzUxLDEuMjQ3LDAuNzUxYzAuMTkzLDAuNTE1LDAuMTY4LDEuMzg3LDAuMTY4LDEuOTM3djEuODZjMCwwLjU1LTAuMDgsMC43NTItMC42MywwLjc1MmgtMi45MzZjLTAuNTUsMC0xLjQ1LDAtMiwwSDEwLjY2NnoiLz48L2c+PC9zdmc+);
}
.test {
	background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0i0KHQu9C+0LlfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjQgMjQiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxnPjxnPjxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzM5NEI0RCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIGQ9Ik0xOS4zMzMsMTkuNWMwLDEuMTA1LTAuODk1LDItMiwyaC0xMWMtMS4xMDUsMC0yLTAuODk1LTItMnYtMTFjMC0xLjEwNSwwLjg5NS0yLDItMmgxMWMxLjEwNSwwLDIsMC44OTUsMiwyVjE5LjV6Ii8+PC9nPjxwb2x5bGluZSBmaWxsPSJub25lIiBzdHJva2U9IiMzOTRCNEQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBwb2ludHM9IjcuMDM1LDExLjIwNSAxMC43MTcsMTcuODkzIDIwLjI0OSwyLjE4OCAiLz48L2c+PC9zdmc+);
}
.pager, .video, .test, .task {
	height:24px; width:24px;
	background-repeat: no-repeat;
}
/*}}}*/
/***
<html>
<div class="explanation" style=""></div><div class="pager" style=""></div>
<div class="video" style=""></div>
<div class="task" style=""></div>
<div class="test" style=""></div>
</html>
***/

div[tiddler="Анатомия растений"] #navigation, div[tiddler="Анатомия растений"] #footer { display: none !important; }
<!--{{{-->
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' />
<link rel="shortcut icon" href="http://childrenscience.ru/images/favicon.ico"/>
<!--}}}-->
(изменяющийся заголовок)
Дети и наука
Анатомия растений
[[Анатомия растений]]