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'>
  <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]]; }
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: -moz-linear-gradient(to bottom, [[ColorPalette::PrimaryLight]], [[ColorPalette::PrimaryMid]]);
	background: linear-gradient(to bottom, [[ColorPalette::PrimaryLight]], [[ColorPalette::PrimaryMid]]);
}
.header a:hover {background:transparent;}
.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]]; }
.wizard__title    { color:[[ColorPalette::PrimaryDark]]; border:none; }
.wizard__subtitle { color:[[ColorPalette::Foreground]]; border:none; }
.wizardStep { background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]]; }
.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 { background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; box-shadow: 1px 2px 5px [[ColorPalette::TertiaryMid]]; }
.messageToolbar__button { color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none; }
.messageToolbar__button_withIcon { background:inherit; }
.messageToolbar__button_withIcon:active { background:inherit; border:none; }
.messageToolbar__icon { fill:[[ColorPalette::TertiaryDark]]; }
.messageToolbar__icon:hover { fill:[[ColorPalette::Foreground]]; }

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

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

.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::TertiaryLight]]; }
.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]]; }

.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 th, .viewer thead td, .twtable th, .twtable thead td { background: [[ColorPalette::SecondaryMid]]; color: [[ColorPalette::Background]]; }
.viewer td, .viewer tr, .twtable td, .twtable tr { border: 1px solid [[ColorPalette::TertiaryLight]]; }
.twtable caption { color: [[ColorPalette::TertiaryMid]]; }

.viewer pre {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);}
/*}}}*/
/*{{{*/
body { font-size:.75em; font-family:arial,helvetica,sans-serif; margin:0; padding:0; }

* html .tiddler {height:1%;}

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

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

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


a {text-decoration:none;}

.externalLink {text-decoration:underline;}

.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;}
.headerShadow {position:relative; padding:3em 0 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:3em 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 2em 0; }
.wizard__title    { font-size:2em; }
.wizard__subtitle { font-size:1.2em; }
.wizard__title, .wizard__subtitle { font-weight:bold; background:none; padding:0; margin:0.4em 0 0.2em; }
.wizardStep { padding:1em; }
.wizardFooter { padding: 0.8em 0; }
.wizardFooter .status { padding: 0.3em 1em; }
.wizardFooter .button { margin:0.5em 0 0; font-size:1.2em; padding:0.2em 0.5em; }

#messageArea { position:fixed; top:2em; right:0; margin:0.5em; padding:0.7em 1em; z-index:2000; }
.messageToolbar { text-align:right; padding:0.2em 0; }
.messageToolbar__button { text-decoration:underline; }
.messageToolbar__icon { height: 1em; width: 1em; } /* width for IE */
.messageArea__text a { text-decoration:underline; }

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

.tiddlerPopupButton {padding:0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em; margin: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;}
.tagged li, .tagging li { margin: 0.3em 0; }
.tagClear {clear:both;}

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

.annotation { padding: 0.5em 0.8em; margin: 0.5em 1px; }

.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 0; }
.viewer th, .viewer td, .viewer tr, .viewer caption, .twtable th, .twtable td, .twtable tr, .twtable caption { padding: 0.2em 0.4em; }
.twtable caption { font-size: 0.9em; }
table.listView { margin: 0.8em 1.0em; }
table.listView th, table.listView td, table.listView tr { text-align: left; }
.listView > thead { position: sticky; top: 0; }

* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer pre {padding:0.5em; overflow:auto;}
pre, code { font-family: monospace, monospace; font-size: 1em; }
.viewer pre, .viewer code { line-height: 1.4em; }

.editor {font-size:1.1em; line-height:1.4em;}
.editor input, .editor textarea {display:block; width:100%; box-sizing: border-box; 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 0;}

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

* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0; right:0;}
#backstageButton a {padding:0.1em 0.4em; margin:0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel { display:none; z-index:100; position:absolute; width:90%; margin-left:3em; }
.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; }
}
/*}}}*/
<!--{{{-->
<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>
<!--}}}-->
/***
|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;
};
//}}}
/***
|''Name''|DarkModePlugin|
|''Description''|This plugin introduces "dark mode" (changes styles) and switching it by the {{{darkMode}}} macro and operating system settings|
|''Documentation''|https://yakovl.github.io/TiddlyWiki_DarkModePlugin/|
|''Author''|Yakov Litvin|
|''Version''|1.3.1|
|''Source''|https://github.com/YakovL/TiddlyWiki_DarkModePlugin/blob/master/DarkModePlugin.js|
|''License''|[[MIT|https://github.com/YakovL/TiddlyWiki_DarkModePlugin/blob/master/LICENSE]]|
!!!Demo
<<darkMode>>
<<darkMode label:"☀️/🌘">>
!!!Syntax
{{{
<<darkMode>> (<<switchNightMode>> also works, for backward compatibility)
<<darkMode label:"☀️/🌘">>
}}}
!!!Installation
Is as usual: import or copy the plugin with the {{{systemConfig}}} tag, reload. Note: for the plugin to work correctly, you should keep its name (DarkModePlugin).

!!!Optional configuration
When the dark mode is applied, the {{{darkMode}}} class is added to the {{{html}}} element. This allows to add ''styles for dark mode'' only, like this:
{{{
.darkMode code { color:red }
code { color: green }
}}}
Ordinary styles are applied to both modes, but {{{.darkMode}}} ones have higher precedence and "overwrite" the oridinary ones.

The palette applied for the dark mode can be ''customized'' by editing ColorPaletteDark (removing it restores the default values).

!!!Additional notes
Styles of some browser interface bits (like <html><button class="button" onclick='alert("this is known as an alert")'>alert</button</html> are only affected by OS/browser's "dark mode"/theme, so for good UI it is recommended to switch OS dark mode (DarkModePlugin will follow). For Windows users, [[switching by hotkey|https://superuser.com/a/1724237/576393]] may be useful.

The plugin ''adds extra styles'' (see ~FollowDarkMode and ~FewerColors sections) which are not yet configurable.

The option {{{chkDarkMode}}} is now ''deprecated'': later it will be either removed or re-implemented.
!!!Code
***/
//{{{
config.macros.switchNightMode = // backward compatibility
config.macros.darkMode = {
	pluginName: "DarkModePlugin",
	optionName: "chkDarkMode",
	getDarkPaletteText: function() {
		return store.getTiddlerText(this.darkPaletteTitle)
	},
	// this helper may become more complex for custom themes
	getMainPaletteTitle: function() {
		return "ColorPalette"
	},
	lightPaletteTitle: "ColorPaletteLight",
	darkPaletteTitle: "ColorPaletteDark",

	// setDark, setLight, and applyAdjustments are "governed outside": they don't check or change the cookie-parameter
	setDark: function() {
		var paletteTitle = this.getMainPaletteTitle()

		var lightPaletteTiddler = new Tiddler(this.lightPaletteTitle)
		lightPaletteTiddler.text = store.getTiddlerText(paletteTitle) || "shadow"
		store.saveTiddler(lightPaletteTiddler)

		var darkPaletteTiddler = new Tiddler(paletteTitle)
		darkPaletteTiddler.text = this.getDarkPaletteText()
		// attach the tiddler, recalc slices, invoke notifiers
		store.saveTiddler(darkPaletteTiddler)

		this.applyAdjustments(true)
	},
	setLight: function() {
		var paletteTitle = this.getMainPaletteTitle()

		var lightPaletteText = store.getTiddlerText(this.lightPaletteTitle)
		if(!lightPaletteText || lightPaletteText === "shadow")
			store.removeTiddler(paletteTitle) // to recalc slices of ColorPalette
		else
			store.saveTiddler(paletteTitle, paletteTitle, lightPaletteText)

		store.deleteTiddler(this.lightPaletteTitle)

		this.applyAdjustments(false)
	},
	applySectionCSS: function(sectionName) {
		var sectionText = store.getRecursiveTiddlerText(this.pluginName + "##" + sectionName, "", 1)
		var css = sectionText.replace(/^\s*{{{((?:.|\n)*?)}}}\s*$/, "$1")
		return setStylesheet(css, sectionName)
	},
	applyAdjustments: function(isDarkMode) {
		if(isDarkMode) {
			jQuery('html').addClass('darkMode')
			this.applySectionCSS("FollowDarkMode")
			this.applySectionCSS("~FewerColors")
		} else {
			jQuery('html').removeClass('darkMode')
			removeStyleSheet("FollowDarkMode")
			removeStyleSheet("~FewerColors")
		}
	},

	// "governance" methods
	isDarkMode: function() {
		return !!store.fetchTiddler(this.lightPaletteTitle)
	},
	switchMode: function() {
		var me = config.macros.darkMode
		config.options[me.optionName] = !config.options[me.optionName]

		config.options[me.optionName] ? me.setDark() : me.setLight()

// "baking" doesn't work yet..
		if(saveOption)
			saveOption(me.optionName)
		else
			saveOptionCookie(me.optionName)

		refreshColorPalette()
	},
	followOsMode: function(followLight) {
		// old browsers may fail to detect
		var isOsDarkModeDetected = window.matchMedia &&
			window.matchMedia('(prefers-color-scheme: dark)').matches

		if(isOsDarkModeDetected && !this.isDarkMode()) {
			config.options[this.optionName] = false
			this.switchMode()
		}

		if(!isOsDarkModeDetected && this.isDarkMode() && followLight) {
			config.options[this.optionName] = true
			this.switchMode()
		}
	},
	restoreSavedMode: function() {
		if(!this.isDarkMode()) return

		// TODO: check if styles are really missing (avoid applying twice)
		this.applyAdjustments(true)
		config.options[this.optionName] = true
	},
	handler: function(place, macroName, params, wikifier, paramString, tiddler) {
		var pParams = paramString.parseParams("anon", null, true, false, true)
		var label = getParam(pParams, "label", "switch")
		var tooltip = ""

		createTiddlyButton(place, label, tooltip, this.switchMode)
	}
}

// We avoid using .init to support installation via SharedTiddlersPlugin, TiddlerInFilePlugin, and reinstalling via CookTiddlerPlugin.
// This also helps to avoid extra refreshing.
;(function(macro) {
	// Save the palette as shadow so that one can cusomize it
	config.shadowTiddlers[macro.darkPaletteTitle] =
		store.getTiddlerText(macro.pluginName + "##DarkModeColorPalette")

	// Set dark mode on start if OS dark mode is set or dark mode was saved previously
	macro.followOsMode(false)
	macro.restoreSavedMode()

	// Detect OS mode change, apply (install only once)
	if(window.matchMedia && !macro.isOsModeWatcherSet) {
		macro.isOsModeWatcherSet = true
		window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(event) {
			macro.followOsMode(true)
		})
	}
})(config.macros.darkMode)
//}}}
/***
!!!FollowDarkMode
{{{
input, select, textarea {
	color:[[ColorPalette::Foreground]];
	background-color:[[ColorPalette::Background]];
}

.darkMode {
	color-scheme: dark;
}
}}}
!!!~FewerColors
{{{
.title, h1, h2, h3, h4, h5, h6 {
	color: [[ColorPalette::PrimaryDark]];
}
::selection {
	background: [[ColorPalette::TertiaryMid]];
}
}}}
!!!DarkModeColorPalette
Background: #000
Foreground: #ddd
~PrimaryPale: #730
~PrimaryLight: #e70
~PrimaryMid: #fb4
~PrimaryDark: #feb
~SecondaryPale: #003
~SecondaryLight: #017
~SecondaryMid: #24b
~SecondaryDark: #7be
~TertiaryPale: #111
~TertiaryLight: #333
~TertiaryMid: #666
~TertiaryDark: #999
Error: #f44
!!!
***/
/***
|''Version''|0.2|
|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;";
		};
	};
//# put this after "rule" and before "characterFormat" formatters
/*config.formatters.splice(0,0,{
	name: "mdash",
	match: "---",
	handler: function(w)
	{
		createTiddlyElement(w.output,"span").innerHTML = "&mdash;";
	}
});*/
//}}}
[[TwFormulaePlugin]]
/***
|Documentation|https://yakovl.github.io/TiddlyWiki_ExtraFilters/#ExtraFiltersPluginInfo|
|Source       |https://github.com/YakovL/TiddlyWiki_ExtraFilters/blob/master/ExtraFiltersPlugin.js|
|Author       |Yakov Litvin|
|Version      |1.4|
|~CoreVersion |2.6.1|
|Licence      |[[BSD-like open source license|https://yakovl.github.io/TiddlyWiki_ExtraFilters/#%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;
};
//}}}
!The ~Cauchy-Schwarz Inequality
\[ \left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2
\right) \left( \sum_{k=1}^n b_k^2 \right) \]
!A Cross Product Formula
\[\mathbf{V}_1 \times \mathbf{V}_2 =  \left|\begin{array}{ccc}
\mathbf{i} & \mathbf{j} & \mathbf{k} \\
\frac{\partial X}{\partial u} &  \frac{\partial Y}{\partial u} & 0 \\ 
             \frac{\partial X}{\partial v} &  \frac{\partial Y}{\partial v}
& 0
\end{array}\right|  \]
!The probability of getting \(k\) heads when flipping \(n\) coins is: 
\[P(E)   = {n \choose k} p^k (1-p)^{ n-k} \]
!An Identity of Ramanujan
\[ \frac{1}{\left(\sqrt{\phi \sqrt{5}}-\phi\right) e^{\frac25 \pi}} =
1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}}
{1+\frac{e^{-8\pi}} {1+\ldots} } } } \]
!The Lorenz Equations
\[\begin{eqnarray}
\dot{x} & = & \sigma(y-x) \\
\dot{y} & = & \rho x - y - xz \\
\dot{z} & = & -\beta z + xy
\end{eqnarray}\]
!A ~Rogers-Ramanujan Identity
\[  1 +  \frac{q^2}{(1-q)}+\frac{q^6}{(1-q)(1-q^2)}+\cdots =
\prod_{j=0}^{\infty}\frac{1}{(1-q^{5j+2})(1-q^{5j+3})},
\quad\quad \textrm{for}\quad |q|<1. \]
!Maxwell's Equations
\[\begin{eqnarray}
\nabla \times \vec{\mathbf{B}} -\, \frac1c\,\frac{\partial\vec{\mathbf{E}}}{\partial t} & = & \frac{4\pi}{c}\vec{\mathbf{j}} \\
\nabla \cdot \vec{\mathbf{E}} & = & 4 \pi \rho \\
\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\,\frac{\partial\vec{\mathbf{B}}}{\partial t} & = &\vec{\mathbf{0}} \\
\nabla \cdot \vec{\mathbf{B}} & = & 0
\end{eqnarray}\]
Finally, while display equations look good for a page of samples, the ability to mix math and text in a paragraph is also important. This expression \(\sqrt{3x-1}+(1+x)^2\) is an example of an inline equation.  As you see, ~MathJax equations can be used this way as well, without unduly disturbing the spacing between lines.
/***
|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 = /closeTiddler/,
		    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.3'|
|''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.|
***/
//{{{
// defines whether to set "none" param to "same" if it is not used (i.e. = begin + end)
if(config.options.chkFetUseSameIfNone === undefined)
	config.options.chkFetUseSameIfNone = true;

(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") 
		|| (config.options.chkFetUseSameIfNone ? "same" : "");

	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+"+"+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 = !preParsedParams ? "" :
		(!preParsedParams[0]["script"] ? "" :
		 preParsedParams[0]["script"].join(";"));

	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;

	for (var 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||"") +">>"
};
//}}}
/***
|Description|This plugin introduces new formatters that generate sub- and superscripts|
|Version|1.1|
|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
superscript containing link syntax: {{{text^[[25|note]]}}} → text^[[25|note]]
***/
//{{{
var optionalSign = "(?:\\+|-|±|\\+-|∓|-\\+)?"
var floatNumber = "\\d+(?:(?:\\.|,)\\d+)?"
var linkOrFloatPattern = "(?:\\[\\[)?"+ optionalSign + floatNumber +"(?:\\|[^\\]]+\\]\\])?"

config.formatters.push({
	name: "subscript",
	match: ",,(?!\\s|$)",
	handler: function(w) {
		w.subWikifyTerm(createTiddlyElement(w.output,"sub"),/(,,)/mg)
	}
},{
	name: "power",
	match: "\\^" + linkOrFloatPattern,
	lookaheadRegExp: new RegExp("\\^(" + linkOrFloatPattern +")", 'g'),
	handler: function(w) {
		this.lookaheadRegExp.lastIndex = w.matchStart
		var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
		var power = lookaheadMatch[1].replace("+-", "±").replace("-+", "∓")
		
		var supElement = createTiddlyElement(w.output, 'sup', null, null/*class*/)
		wikify(power, supElement)
		
		w.nextMatch = this.lookaheadRegExp.lastIndex
	}
})
//}}}
/***
|Source|http://www.amved.com/milindsweb/mathscribe/jqmath-0.4.0.css|
***/
/*{{{*/
/* "fm" classes are mostly for imitating MathML in browsers without it; we try to roughly mimic
	Firefox's MathML layout, which seems better than http://www.w3.org/TR/mathml-for-css/ */

/* Cambria [Math]'s line height currently (2/11) is large in most non-Microsoft browsers: */
fmath, .fm-math		{ font-family: STIXGeneral, 'DejaVu Serif', 'DejaVu Sans',
						/* Cambria, 'Cambria Math', */ Times, 'Lucida Sans Unicode',
						OpenSymbol, 'Standard Symbols L', serif; line-height: 1.2 }
fmath mo, .fm-mo, .ma-sans-serif, fmath mi[mathvariant*=sans-serif],
fmath mn[mathvariant*=sans-serif], fmath mtext[mathvariant*=sans-serif],
fmath ms[mathvariant*=sans-serif]
/* some (crossbrowsertesting/browsershots) IE7s require no line break before
	'Lucida Sans Unicode': */
{ font-family: STIXGeneral, 'DejaVu Sans', 'DejaVu Serif', /* Cambria, 'Cambria Math', */ 'Lucida Sans Unicode',
						'Arial Unicode MS', 'Lucida Grande', Times,
						OpenSymbol, 'Standard Symbols L', sans-serif }
.fm-mo-Luc	/* avoid extra space at character tops, especially when stretched */
{ font-family: STIXGeneral, 'DejaVu Sans', 'DejaVu Serif', /* Cambria, 'Cambria Math', */ 'Lucida Sans Unicode',
						'Lucida Grande', 'Arial Unicode MS', Times,
						OpenSymbol, 'Standard Symbols L', sans-serif }
* html fmath, * html .fm-math, * html fmath mo, * html .fm-mo, * html .IE6-LSU
					{ font-family: 'Lucida Sans Unicode' !important }	/* IE <=6 only */
mo.fm-radic			{ font-family: 'Lucida Sans Unicode', 'Lucida Grande',
						Verdana, sans-serif !important }
.ma-script, fmath mi[mathvariant*=script], fmath mo[mathvariant*=script],
fmath mn[mathvariant*=script], fmath mtext[mathvariant*=script], fmath ms[mathvariant*=script]
					{ font-family:
						'England Hand DB', 'Embassy BT', 'Amazone BT', 'Bank Script D',
						'URW Chancery L', 'Lucida Calligraphy', 'Apple Chancery',
						'Monotype Corsiva', Corsiva,
						'Vivaldi D', Gabriola, 'Segoe Script', cursive }
.ma-fraktur, fmath mi[mathvariant*=fraktur], fmath mo[mathvariant*=fraktur],
fmath mn[mathvariant*=fraktur], fmath mtext[mathvariant*=fraktur],
fmath ms[mathvariant*=fraktur]
					{ font-family: UnifrakturMaguntia, Impact, fantasy }
fmath mi[mathvariant*=monospace], fmath mo[mathvariant*=monospace],
fmath mn[mathvariant*=monospace], fmath mtext[mathvariant*=monospace],
fmath ms[mathvariant*=monospace]
					{ font-family: monospace }
/* .ma-double-struck currently ignored */

.fm-mi-length-1			{ font-style: italic }
fmath mi[mathvariant]		{ font-style: normal }

.ma-bold, fmath mi[mathvariant*=bold], fmath mo[mathvariant*=bold],
fmath mn[mathvariant*=bold], fmath mtext[mathvariant*=bold], fmath ms[mathvariant*=bold]
					{ font-weight: bold }
.ma-nonbold			{ font-weight: normal }
.ma-upright			{ font-style: normal !important }
.ma-italic, fmath mi[mathvariant*=italic], fmath mo[mathvariant*=italic],
fmath mn[mathvariant*=italic], fmath mtext[mathvariant*=italic], fmath ms[mathvariant*=italic]
					{ font-style: italic }

fmath.ma-block		{ display: block; text-align: center; text-indent: 0;
						page-break-inside: avoid }

/* note an operator might be 'mo' or "embellished": */
.fm-separator		{ padding: 0 0.56ex 0 0 }
.fm-infix-loose		{ padding: 0 0.56ex }	/* typically a relation */
.fm-infix		{ padding: 0 0.44ex }
.fm-prefix		{ padding: 0 0.33ex 0 0 }
.fm-postfix		{ padding: 0 0 0 0.33ex }
.fm-prefix-tight	{ padding: 0 0.11ex 0 0 }
.fm-postfix-tight	{ padding: 0 0 0 0.11ex }
.fm-quantifier		{ padding: 0 0.11ex 0 0.22ex }	/* to match MathML */
/* fences should have no padding */
.ma-non-marking				{ display: none }

.fm-large-op				{ font-size: 1.3em }
.fm-inline .fm-large-op			{ font-size: 1em }

fmath mrow				{ white-space: nowrap }

.fm-vert				{ display: inline-block; vertical-align: middle }

fmath table, fmath tbody, fmath tr, fmath td	/* reset to default(?) styles */
					{ border: 0 !important; padding: 0 !important; margin: 0 !important; outline: 0 !important }

fmath table				{ border-collapse: collapse !important; text-align: center !important; table-layout: auto !important; float: none !important }

.fm-frac				{ padding: 0 1px !important }
td.fm-den-frac				{ border-top: solid thin !important }

.fm-root				{ font-size: 0.6em }
.fm-radicand				{ padding: 0 1px 0 0; border-top: solid; margin-top: 0.1em }

.fm-script				{ font-size: 0.71em }
.fm-script .fm-script .fm-script	{ font-size: 1em }

td.fm-underover-base			{ line-height: 1 !important }

td.fm-mtd				{ padding: 0.5ex 0.4em !important; vertical-align: baseline !important }

fmath mphantom				{ visibility: hidden }
fmath menclose, menclose.fm-menclose	{ display: inline-block }
fmath menclose[notation=top], menclose.fm-menclose[notation=top]
					{ border-top: solid thin }
fmath menclose[notation=right], menclose.fm-menclose[notation=right]
					{ border-right: solid thin }
fmath menclose[notation=bottom], menclose.fm-menclose[notation=bottom]
					{ border-bottom: solid thin }
fmath menclose[notation=left], menclose.fm-menclose[notation=left]
					{ border-left: solid thin }
fmath menclose[notation=box], menclose.fm-menclose[notation=box]
					{ border: solid thin }
fmath none				{ display: none }	/* probably unnecessary */

mtd.middle, fmath td.middle		{ vertical-align: middle !important }

fmath table[columnalign=left], fmath tr[columnalign=left], fmath td[columnalign=left]
					{ text-align: left !important }
fmath table[columnalign=right], fmath tr[columnalign=right], fmath td[columnalign=right]
					{ text-align: right !important }
fmath td[rowalign=top]			{ vertical-align: top !important }
fmath td[rowalign=bottom]		{ vertical-align: bottom !important }
fmath td[rowalign=center]		{ vertical-align: middle !important }

mtable.ma-join-align > mtr > mtd:first-child,
fmath span.ma-join-align > table > tbody > tr > td:first-child
					{ text-align: right; padding-right: 0 !important }
mtable.ma-join-align > mtr > mtd:first-child + mtd,
fmath span.ma-join-align > table > tbody > tr > td:first-child + td
					{ text-align: left; padding-left: 0 !important }
mtable.ma-join1-align > mtr > mtd:first-child,	/* e.g. for cases after a stretched { */
fmath span.ma-join1-align > table > tbody > tr > td:first-child
					{ text-align: left; padding-left: 0 !important }

mtable.ma-binom > mtr > mtd, fmath span.ma-binom > table > tbody > tr > td
					{ padding: 0 !important }
mtable.ma-binom > mtr:first-child > mtd,
fmath span.ma-binom > table > tbody > tr:first-child > td
					{ padding: 0 0 0.18em 0 !important }
/*}}}*/
/***
|Source|http://www.amved.com/milindsweb/mathscribe/jqmath-etc-0.4.0.min.js|
changes:
this
{{{
   M.parseMathQ=true;
}}}
was changed to
{{{
   M.parseMathQ=false;
}}}
***/
// /%
var jsCurry=function(){var sliceMF=Array.prototype.slice;if(!Function.prototype.bind)Function.prototype.bind=function(thisArg){var f=this,args0=sliceMF.call(arguments,1);return function(){return f.apply(thisArg,args0.concat(sliceMF.call(arguments,0)))}};if(!String.prototype.trim)String.prototype.trim=function(){return String(this).replace(/^\s+|\s+$/g,"")};if(!Array.isArray)Array.isArray=function(x){return typeof x=="object"&&x!==null&&Object.prototype.toString.call(x)==="[object Array]"};if(!Object.keys)Object.keys=
function(obj){var res=[];for(var p in obj)if(obj.hasOwnProperty(p))res.push(p);return res};if(!Date.now)Date.now=function(){return(new Date).getTime()};function F(x){if(typeof x=="function")return F.curry.apply(undefined,arguments);if(arguments.length==2){var y=arguments[1];if(typeof x=="string")return y[x].bind(y);if(typeof y=="function")return(typeof x=="number"?F.aritize:F.partial)(x,y)}arguments.length==1||F.err(err_F_1_);if(typeof x=="number"||typeof x=="string")return F.pToF(x);if(x.nodeType==
1)return jQuery.data(x);if(x&&typeof x=="object")return F.aToF(x);F.err(err_F_2_)}F.err=function(){throw Error("Assertion failed");};F.id=function(x){return x};F.constant=function(x){return function(){return x}};F.applyF=function(f,args){return f.apply(undefined,args)};F.curry=function(f){var g=f;arguments[0]=undefined;return g.bind.apply(g,arguments)};F._={};F.partial=function(a,f){var n=a.length;return function(){var args=sliceMF.call(arguments,0);for(var i=0;i<n;i++)if(a[i]!==F._)args.splice(i,
0,a[i]);return f.apply(this,args)}};F.uncurry=function(f){return function(x,y){return f(x)(y)}};F.o=function(){var fs=arguments;return function(){var n=fs.length,res=fs[--n].apply(undefined,arguments);while(n>0)res=fs[--n](res);return res}};F.oMap=function(f,g){return function(){return F.applyF(f,F.map(g,arguments))}};F.flip=function(f){return function(x,y){return f(y,x)}};F.seqF=function(){var fs=arguments,n=fs.length;return function(){var y;for(var i=0;i<n;i++)y=fs[i].apply(undefined,arguments);
return y}};F.cor=function(){var fs=arguments;return function(){return F.any(F([F._,arguments],F.applyF),fs)}};F.aritize=function(n,f){return function(){return F.applyF(f,sliceMF.call(arguments,0,n))}};F.not=function(x){return!x};F.cmpX=function(x,y){return x-y};F.cmpJS=function(s,t){return s<t?-1:s==t?0:1};F.cmpLex=function(cmpE,v,w){return F.any(function(e,i){return i==w.length?1:cmpE(e,w[i])},v)||v.length-w.length};F.eqTo=function(x,cmpP){if(!cmpP)cmpP=function(y,z){return y!==z};return F.o(F.not,
F(cmpP,x))};F.pToF=function(p){return function(obj){return obj[p]}};F.aToF=function(obj){return function(p){return obj[p]}};F.fToA=function(f,n){var a=new Array(n);for(var i=0;i<n;i++)a[i]=f(i);return a};F.memoF=function(f,memo){if(!memo)memo={};return function(p){return memo.hasOwnProperty(p)?memo[p]:memo[p]=f(p)}};F.replicate=function(n,e){return F.fToA(F.constant(e),n)};F.setF=function(obj,p,v){obj[p]=v};F.obj1=function(p,v){var res={};res[p]=v;return res};F.slice=function(a,startP,endP){if(startP==
null)startP=0;if(Array.isArray(a))return arguments.length<3?a.slice(startP):a.slice(startP,endP);var n=a.length;startP=startP<0?Math.max(0,n+startP):Math.min(n,startP);endP=endP==null?n:endP<0?Math.max(0,n+endP):Math.min(n,endP);var res=[];while(startP<endP)res.push(a[startP++]);return res};F.array=function(){return sliceMF.call(arguments,0)};F.concatArgs=F.oMap(F("concat",[]),function(a){return Array.isArray(a)?a:F.slice(a)});F.concatMap=function(f,a){return F.applyF(F.concatArgs,F.map(f,a))};F.findIndex=
function(qF,a){var n=a.length;for(var i=0;i<n;i++)if(qF(a[i],i,a))return i;return-1};F.findLastIndex=function(qF,a){for(var i=a.length;--i>=0;)if(qF(a[i],i,a))return i;return-1};F.find=function(qF,a){var j=F.findIndex(qF,a);return j==-1?undefined:a[j]};F.elemIndex=function(e,a,cmpP){if(a.indexOf&&!cmpP&&Array.isArray(a))return a.indexOf(e);return F.findIndex(F.eqTo(e,cmpP),a)};F.elemLastIndex=function(e,a,cmpP){if(a.lastIndexOf&&!cmpP&&Array.isArray(a))return a.lastIndexOf(e);return F.findLastIndex(F.eqTo(e,
cmpP),a)};F.elem=function(e,a,cmpP){return F.elemIndex(e,a,cmpP)!=-1};F.all=function(qF,a){if(a.every&&Array.isArray(a))return a.every(qF);var n=a.length;for(var i=0;i<n;i++)if(!qF(a[i],i,a))return false;return true};F.any=function(f,a){var n=a.length,y=false;for(var i=0;i<n;i++){y=f(a[i],i,a);if(y)return y}return y};F.iter=function(f,a){if(arguments.length==2){if(a.forEach&&Array.isArray(a))return a.forEach(f);var n=a.length;for(var i=0;i<n;i++)f(a[i],i,a)}else{arguments.length>2||F.err(err_iter_);
var args=sliceMF.call(arguments,1),n=F.applyF(Math.min,F.map(F("length"),args));for(var i=0;i<n;i++)F.applyF(f,F.map(F(i),args).concat(i,args))}};F.map=function(f,a){if(a.map&&Array.isArray(a))return a.map(f);var n=a.length,res=new Array(n);for(var i=0;i<n;i++)res[i]=f(a[i],i,a);return res};F.map1=function(f,a){return F.map(F(1,f),a)};F.zipWith=function(f){arguments.length>1||F.err(err_zipWith_);var res=[];for(var i=0;;i++){var args=[];for(var j=1;j<arguments.length;j++){var a=arguments[j];if(i<a.length)args.push(a[i]);
else return res}res.push(F.applyF(f,args))}return res};F.zip=F(F.zipWith,F.array);F.unzip=function(zs){return zs.length?F.applyF(F.zip,zs):[]};F.filter=function(qF,a){if(a.filter&&Array.isArray(a))return a.filter(qF);return F.fold(function(y,e,i,a){if(qF(e,i,a))y.push(e);return y},a,[])};F.fold=function(op,a,xOpt){if(a.reduce&&Array.isArray(a))return arguments.length<3?a.reduce(op):a.reduce(op,xOpt);var n=a.length,i=0;if(arguments.length<3)xOpt=n?a[i++]:F.err(err_fold_);for(;i<n;i++)xOpt=op(xOpt,
a[i],i,a);return xOpt};F.foldR=function(op,a,xOpt){if(a.reduceRight&&Array.isArray(a))return arguments.length<3?a.reduceRight(op):a.reduceRight(op,xOpt);var n=a.length;if(arguments.length<3)xOpt=n?a[--n]:F.err(err_foldR_);while(--n>=0)xOpt=op(xOpt,a[n],n,a);return xOpt};F.sum=function(a){var n=a.length,res=0;for(var i=0;i<n;i++)res+=a[i];return res};F.test=function(t){if(t===0||t==="")t=typeof t;if(typeof t=="string")return function(x){return typeof x==t};if(t===Array||t===Date||t===RegExp)return function(x){return x!=
null&&x.constructor==t};if(t===null)return F.eqTo(null);if(t.constructor==RegExp)return F("test",t);if(typeof t=="function")return t;if(Array.isArray(t))if(t.length==1){t=F.test(t[0]);return function(x){return Array.isArray(x)&&F.all(t,x)}}else{t=F.map(F.test,t);return function(x){return F.any(function(qF){return qF(x)},t)}}if(typeof t=="object"){var ks=Object.keys(t),fs=F.map(F.o(F.test,F(t)),ks);return function(x){return x!=null&&F.all(function(k,i){return fs[i](x[k])},ks)}}F.err(err_test_)};return F}();
var F;if(F===undefined)F=jsCurry;var jqMath=function(){var $=jQuery,F=jsCurry;function M(x,y,z){if(typeof x=="number")x=String(x);if(typeof x=="string"||Array.isArray(x))return M.sToMathE(x,y,z);if(x.nodeType==1&&x.tagName.toLowerCase()=="math")return M.eToMathE(x);F.err(err_M_)}M.toArray1=function(g){return Array.isArray(g)?g:[g]};M.sign=function(x){return x>0?1:x<0?-1:x==0?0:NaN};M.getSpecAttrP=function(e,attrName){var attrP=e.getAttributeNode(attrName);return attrP&&attrP.specified!==false?attrP.value:null};M.objToAttrs=function(obj){var res=
[];for(var p in obj)res.push({name:p,value:obj[p]});return res};M.setAttrs=function(e,attrsP){if(attrsP&&attrsP.length==null)attrsP=M.objToAttrs(attrsP);F.iter(function(attr){if(attr.specified!==false)e.setAttribute(attr.name,attr.value)},attrsP||[]);return e};M.replaceNode=function(newNode,oldNode){oldNode.parentNode.replaceChild(newNode,oldNode);return newNode};M.addClass=function(e,ws){if(typeof e.className!="undefined"){var classes=e.className;e.className=(classes?classes+" ":"")+ws}else{var classes=
e.getAttribute("class");e.setAttribute("class",(classes?classes+" ":"")+ws)}return e};M.eToClassesS=function(e){var sP=typeof e.className!="undefined"?e.className:e.getAttribute("class");return sP||""};M.hasClass=function(e,w){return(" "+M.eToClassesS(e)+" ").replace(/[\n\t]/g," ").indexOf(" "+w+" ")!=-1};M.inlineBlock=function(){var res$=$("<div/>").css("display","inline-block");if(arguments.length)res$.append.apply(res$,arguments);return res$[0]};M.tr$=function(){var appendMF=$().append;function td$(g){return appendMF.apply($("<td/>"),
M.toArray1(g))}return appendMF.apply($("<tr/>"),F.map(td$,arguments))};M.mathmlNS="http://www.w3.org/1998/Math/MathML";function appendMeArgs(e,args,attrsP){if(args==null);else if(typeof args=="string")e.appendChild(e.ownerDocument.createTextNode(args));else if(args.nodeType)e.appendChild(args);else{if(args.constructor!=Array)args=F.slice(args);F.iter(function(x){e.appendChild(x)},args)}return M.setAttrs(e,attrsP)}var fixMathMLQ_=false;(function(){var ua=navigator.userAgent.toLowerCase(),match=ua.match(/webkit[ \/](\d+)\.(\d+)/);
if(match){M.webkitVersion=[Number(match[1]),Number(match[2])];fixMathMLQ_=M.webkitVersion[0]<=540}else{match=ua.match(/(opera)(?:.*version)?[ \/]([\w.]+)/)||ua.match(/(msie) ([\w.]+)/)||ua.indexOf("compatible")<0&&ua.match(/(mozilla)(?:.*? rv:([\w.]+))?/);if(match)M[match[1]+"Version"]=match[2]||"0"}})();if(M.msieVersion)document.write('<object id=MathPlayer classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987">','</object><?IMPORT namespace="m" implementation="#MathPlayer" ?>');function checkMathMLAttrs(e){if(M.MathML&&
!fixMathMLQ_)return e;var tagName=e.tagName.toLowerCase(),doc=e.ownerDocument;function new1(k){return doc.createElementNS(M.mathmlNS,k)}if(tagName=="mi"){if(!e.getAttribute("mathvariant")&&e.firstChild&&e.firstChild.nodeType==3)e.setAttribute("mathvariant",e.firstChild.data.length==1?"italic":"normal")}else if(tagName=="mo"){if(e.childNodes.length==1&&e.firstChild.nodeType==3){var s=e.firstChild.data;if(/^[\u2061-\u2064]$/.test(s))M.addClass(e,"ma-non-marking")}}else if(tagName=="mspace"){if(M.webkitVersion&&
M.MathML){e.style.display="inline-block";e.style.minWidth=e.getAttribute("width")||"0px"}}else if(tagName=="menclose"){if(M.webkitVersion&&M.MathML)M.addClass(e,"fm-menclose")}else if(tagName=="mmultiscripts")if(M.webkitVersion){var a=F.slice(e.childNodes);if(a.length==0)throw"Wrong number of <mmultiscripts> arguments: 0";var rowA=[a[0]];for(var i=1;i<a.length;i++){if(a[i].tagName=="mprescripts"){rowA.unshift(new1("none"));continue}if(i+1==a.length)throw"Missing argument in <mmultiscripts>";var a3=
[rowA[0],a[i],a[i+1]];i++;rowA[0]=new1("msubsup");F.iter(function(arg){rowA[0].appendChild(arg)},a3)}var oldE=e;e=appendMeArgs(new1("mrow"),rowA,e.attributes);if(oldE.parentNode)oldE.parentNode.replaceChild(e,oldE)}var colorOpt=e.getAttribute("mathcolor"),hrefOpt=e.getAttribute("href");if(colorOpt&&e.style)e.style.color=colorOpt;if(hrefOpt&&(!M.MathML||M.webkitVersion)){var aE=doc.createElement("A"),parentP=e.parentNode,nextP=e.nextSibling;aE.appendChild(e);aE.href=hrefOpt;e=aE;if(parentP)parentP.insertBefore(e,
nextP)}return e}function newMeNS(tagName,argsP,attrsP,doc){if(!doc)doc=document;var e=M.MathPlayer?doc.createElement("m:"+tagName):doc.createElementNS(M.mathmlNS,tagName);return checkMathMLAttrs(appendMeArgs(e,argsP,attrsP))}(function(){if(self.location){var match=location.search.match(/[?&;]mathml=(?:(off|false)|(on|true))\b/i);if(match)M.MathML=!match[1];else if(M.webkitVersion&&F.cmpLex(F.cmpX,M.webkitVersion,[537,17])<0||M.operaVersion)M.MathML=false}})();M.canMathML=function(){if(M.msieVersion&&
!M.MathPlayer)try{new ActiveXObject("MathPlayer.Factory.1");if(M.MathPlayer==null)M.MathPlayer=true;else if(!M.MathPlayer)return false}catch(exc){M.MathPlayer=false}if(!M.MathPlayer&&typeof document.createElementNS=="undefined")return false;function mathMeToE(mathMe){mathMe.setAttribute("display","block");return $("<div/>").append(mathMe)[0]}var e1=newMeNS("math",newMeNS("mn","1")),e2=newMeNS("math",newMeNS("mfrac",[newMeNS("mn","1"),newMeNS("mn","2")])),es$=$(F.map(mathMeToE,[e1,e2]));es$.css("visibility",
"hidden").appendTo(document.body);var res=$(es$[1]).height()>$(es$[0]).height()+2;es$.remove();return res};function checkVertStretch(up,dn,g,doP){if(g.nodeName.toLowerCase()=="mo"&&g.childNodes.length==1){var c=g.firstChild,s=c.data;if(c.nodeType==3&&(up>0.9||dn>0.9)&&(M.prefix_[s]<25||M.postfix_[s]<25||"|\u2016\u221a".indexOf(s)!=-1||doP)){var r=(up+dn)/1.2,radicQ=s=="\u221a",v=(radicQ?0.26:0.35)+((radicQ?0.15:0.25)-dn)/r;g.style.fontSize=r.toFixed(3)+"em";g.style.verticalAlign=v.toFixed(3)+"em";
g.fmUp=up;g.fmDn=dn;g.style.display="inline-block";g.style.transform=g.style.msTransform=g.style.MozTransform=g.style.WebkitTransform="scaleX(0.5)"}}}function vertAlignE$(up,dn,fmVert){var e$=$("<span/>").append(fmVert);e$[0].fmUp=up;e$[0].fmDn=dn;e$[0].style.verticalAlign=(0.5*(up-dn)).toFixed(3)+"em";return e$}M.mtagName=function(e){if(e.tagName=="A"&&e.childNodes.length==1)e=e.firstChild;return e.getAttribute("mtagname")||e.tagName.toLowerCase().replace(/^m:/,"")};M.mchilds=function(e){if(e.tagName==
"A"&&e.childNodes.length==1)e=e.firstChild;var mSP=e.getAttribute("mtagname");while(e.tagName=="SPAN")e=e.firstChild;function span0(g){g.tagName=="SPAN"||F.err(err_span0_);return g.firstChild}if(e.tagName=="TABLE"){e=e.firstChild;e.tagName=="TBODY"||F.err(err_mchilds_tbody_);if(mSP=="mtable")return e.childNodes;var a=e.childNodes;if(mSP=="mover")a=[a[1],a[0]];else if(mSP=="munderover")a=[a[1],a[2],a[0]];return F.map(function(tr){return tr.firstChild.firstChild},a)}else if(e.tagName=="MROW"&&mSP){var a=
e.childNodes;if(mSP=="msqrt")return[span0(span0(a[1]))];if(mSP=="mroot")return[span0(span0(a[2])),span0(a[0])];mSP=="mmultiscripts"||F.err(err_mchilds_mrow_);var nPrescripts=Number(e.getAttribute("nprescripts"));0<=nPrescripts&&nPrescripts<a.length&&nPrescripts%2==0||F.err(err_mchilds_mmultiscripts_);var res=[a[nPrescripts]];for(var i=nPrescripts+1;i<a.length;i++)res.push(span0(a[i]));if(nPrescripts){res.push(e.ownerDocument.createElement("mprescripts"));for(var i=0;i<nPrescripts;i++)res.push(span0(a[i]))}return res}else if(F.elem(e.tagName,
["MSUB","MSUP","MSUBSUP"]))return F.map(function(c,i){return i?span0(c):c},e.childNodes);else if(e.tagName=="MSPACE")return[];else return e.childNodes};var mtokens_=["mn","mi","mo","mtext","mspace","ms"],impMRows_=["fmath","msqrt","mtd","mstyle","merror","mpadded","mphantom","menclose"],accentDsByS_={"\u00af":[0,0.85],"\u203e":[0,0.85],"\u02d9":[0,0.75],"\u02c7":[0,0.7],"^":[0,0.5],"~":[0,0.4],"\u2192":[0.25,0.25],"_":[0.7,0],"\u2212":[0.25,0.45],".":[0.6,0.1]};M.newMe=function(tagName,argsP,attrsP,
docP){if(!docP)if(attrsP&&attrsP.nodeType==9){docP=attrsP;attrsP=null}else docP=document;M.MathML!=null||F.err(err_newMe_MathML_);if(M.MathML)return newMeNS(tagName,argsP,attrsP,docP);if(tagName=="math")tagName="fmath";var e$=$(appendMeArgs(docP.createElement(tagName.toUpperCase()),argsP)),a=F.slice(e$[0].childNodes);if(F.elem(tagName,impMRows_)&&a.length!=1){a=[M.newMe("mrow",a,null,docP)];e$[0].childNodes.length==0||F.err(err_newMe_imp_mrow_);e$.append(a[0])}var ups=F.map(function(g){return Number(g.fmUp||
0.6)},a),dns=F.map(function(g){return Number(g.fmDn||0.6)},a);if(tagName=="fmath"||tagName=="mn"||tagName=="mtext"||tagName=="mstyle"||tagName=="merror"||tagName=="mpadded"||tagName=="mphantom"||tagName=="menclose"||tagName=="mprescripts"||tagName=="none");else if(tagName=="mi"){var c=a.length==1?a[0]:{};if(c.nodeType==3&&c.data.length==1){e$.addClass("fm-mi-length-1");if("EFHIJKMNTUVWXYZdfl".indexOf(c.data)!=-1)e$.css("padding-right","0.44ex")}}else if(tagName=="mo"){var c=a.length==1?a[0]:{};if(c.nodeType==
3&&/[\]|([{\u2016)}]/.test(c.data))e$.addClass("fm-mo-Luc")}else if(tagName=="mspace"){var e=M.setAttrs(e$[0],attrsP);attrsP=null;e.style.marginRight=e.getAttribute("width")||"0px";e.style.paddingRight="0.001em";e$.append("\u200c");e$.css("visibility","hidden")}else if(tagName=="mrow"){var up=F.applyF(Math.max,ups),dn=F.applyF(Math.max,dns);if(up>0.65||dn>0.65){e$[0].fmUp=up;e$[0].fmDn=dn;F.iter(F([up,dn,F._,null],checkVertStretch),a)}}else if(tagName=="mfrac"){if(a.length!=2)throw"Wrong number of <mfrac> arguments: "+
a.length;var num$=$('<td class="fm-num-frac fm-inline"></td>',docP).append(a[0]),den$=$('<td class="fm-den-frac fm-inline"></td>',docP).append(a[1]);e$=vertAlignE$(ups[0]+dns[0]+0.03,ups[1]+dns[1]+0.03,$('<span class="fm-vert fm-frac"></span>',docP).append($("<table/>",docP).append($("<tbody/>",docP).append($("<tr/>",docP).append(num$)).append($("<tr/>",docP).append(den$))))).attr("mtagname",tagName)}else if(tagName=="msqrt"||tagName=="mroot"){if(a.length!=(tagName=="msqrt"?1:2))throw"Wrong number of <"+
tagName+"> arguments: "+a.length;e$=$("<mrow/>",docP).attr("mtagname",tagName);var t=0.06*(ups[0]+dns[0]),up=ups[0]+t+0.1,dn=dns[0];if(tagName=="mroot"){var ht=0.6*(ups[1]+dns[1]),d=0.25/0.6-0.25;if(up>ht)d+=up/0.6-ups[1];else{d+=dns[1];up=ht}e$.append($('<span class="fm-root fm-inline"></span>',docP).append(a[1]).css("verticalAlign",d.toFixed(2)+"em"))}var mo$=$("<mo/>",docP).addClass("fm-radic").append("\u221a"),y$=vertAlignE$(up,dn,$('<span class="fm-vert fm-radicand"></span>',docP).append(a[0]).css("borderTopWidth",
t.toFixed(3)+"em"));checkVertStretch(up,dn,mo$[0]);e$.append(mo$).append(y$);e$[0].fmUp=up;e$[0].fmDn=dn}else if(tagName=="msub"||tagName=="msup"||tagName=="msubsup"||tagName=="mmultiscripts"){if(tagName!="mmultiscripts"&&a.length!=(tagName=="msubsup"?3:2))throw"Wrong number of <"+tagName+"> arguments: "+a.length;var up=ups[0],dn=dns[0],oddQ=tagName=="msup",dUp=up/0.71-0.6,dDn=dn/0.71-0.6;for(var i=1;i<a.length;i++){if(tagName=="mmultiscripts"){var w=M.mtagName(a[i]);if(w=="none")continue;if(w=="mprescripts"){if(oddQ)throw'Repeated "mprescripts"';
oddQ=true;continue}}if(i%2==(oddQ?0:1))dDn=Math.max(dDn,ups[i]);else dUp=Math.max(dUp,dns[i])}var preAP=null,postA=[],nPrescripts=0;for(var i=1;i<a.length;i++){if(tagName=="mmultiscripts"){var w=M.mtagName(a[i]);if(w=="mprescripts"){preAP=[];nPrescripts=a.length-i-1;continue}}var d=0.25/0.71-0.25;if(i%2==(preAP?0:1)&&tagName!="msup"){d-=dDn;dn=Math.max(dn,0.71*(dDn+dns[i]))}else{d+=dUp;up=Math.max(up,0.71*(dUp+ups[i]))}$(a[i]).wrap('<span class="fm-script fm-inline"></span>').parent().css("verticalAlign",
d.toFixed(2)+"em");if(M.msieVersion&&(document.documentMode||M.msieVersion)<8)a[i].style.zoom=1;if(tagName=="mmultiscripts")(preAP||postA).push(a[i].parentNode)}if(tagName=="mmultiscripts")e$=$("<mrow/>").append($((preAP||[]).concat(a[0],postA))).attr({mtagname:"mmultiscripts",nprescripts:nPrescripts});e$[0].fmUp=up;e$[0].fmDn=dn}else if(tagName=="munder"||tagName=="mover"||tagName=="munderover"){if(a.length!=(tagName=="munderover"?3:2))throw"Wrong number of <"+tagName+"> arguments: "+a.length;var tbody$=
$("<tbody/>",docP),td$,up=0.85*ups[0],dn=0.85*dns[0];if(tagName!="munder"){var overE=a[a.length-1],accentDsP=null;td$=$("<td/>",docP).append(overE);if(overE.nodeName=="MO"&&overE.childNodes.length==1){var c=overE.firstChild;if(c.nodeType==3)accentDsP=accentDsByS_[c.data]}if(accentDsP){overE.style.display="block";overE.style.marginTop=(-accentDsP[0]).toFixed(2)+"em";overE.style.marginBottom=(-accentDsP[1]).toFixed(2)+"em";up+=1.2-F.sum(accentDsP)}else{td$.addClass("fm-script fm-inline");up+=0.71*(ups[a.length-
1]+dns[a.length-1])}tbody$.append($("<tr/>",docP).append(td$))}if(a[0].nodeName=="MI"&&a[0].childNodes.length==1){var c=a[0].firstChild,s=c.data;if(c.nodeType==3&&s.length==1){var d="acegmnopqrsuvwxyz".indexOf(s)!=-1?0.25:s=="t"?0.15:0;if(d){a[0].style.display="block";a[0].style.marginTop=(-d).toFixed(2)+"em";up-=d}}}td$=$('<td class="fm-underover-base"></td>',docP).append(a[0]);tbody$.append($("<tr/>",docP).append(td$));if(tagName!="mover"){td$=$('<td class="fm-script fm-inline"></td>',docP).append(a[1]);
tbody$.append($("<tr/>",docP).append(td$));dn+=0.71*(ups[1]+dns[1])}e$=vertAlignE$(up,dn,$('<span class="fm-vert"></span>',docP).append($("<table/>",docP).append(tbody$))).attr("mtagname",tagName)}else if(tagName=="mtable"){var tbody$=$("<tbody/>",docP).append($(a));e$=$('<span class="fm-vert" mtagname="mtable"></span>',docP).append($("<table/>",docP).append(tbody$));var r=F.sum(ups)+F.sum(dns);e$[0].fmUp=e$[0].fmDn=0.5*r}else if(tagName=="mtr"){e$=$('<tr class="fm-mtr" mtagname="mtr"></tr>',docP).append($(a));
var up=0.6,dn=0.6;F.iter(function(e,i){if((e.getAttribute(M.MathML?"rowspan":"rowSpan")||1)==1){up=Math.max(up,ups[i]);dn=Math.max(dn,dns[i])}},a);e$[0].fmUp=up+0.25;e$[0].fmDn=dn+0.25}else if(tagName=="mtd"){e$=$('<td class="fm-mtd" mtagname="mtd"></td>',docP).append($(a));if(ups[0]>0.65)e$[0].fmUp=ups[0];if(dns[0]>0.65)e$[0].fmDn=dns[0];var e=M.setAttrs(e$[0],attrsP);attrsP=null;var rowspan=e.getAttribute("rowspan"),colspan=e.getAttribute("columnspan");if(rowspan){e.setAttribute("rowSpan",rowspan);
if(!M.hasClass(e,"middle"))M.addClass(e,"middle")}if(colspan)e.setAttribute("colSpan",colspan)}else if(tagName=="mfenced"){var e=M.setAttrs(e$[0],attrsP);return M.newMe("mrow",M.mfencedToMRowArgs(e),attrsP,docP)}else throw"Unrecognized or unimplemented MathML tagName: "+tagName;return checkMathMLAttrs(M.setAttrs(e$[0],attrsP))};M.mfencedToMRowArgs=function(e){e.tagName.toLowerCase()=="mfenced"||F.err(err_mfencedToMRowArgs_);var doc=e.ownerDocument;function newMo(s){return M.newMe("mo",s,null,doc)}
var openSP=M.getSpecAttrP(e,"open"),closeSP=M.getSpecAttrP(e,"close"),res=[newMo(openSP==null?"(":openSP),newMo(closeSP==null?")":closeSP)],es=F.slice(e.childNodes);if(es.length==0)return res;var inner;if(es.length==1)inner=es[0];else{var sepsSP=M.getSpecAttrP(e,"separators"),sepsP=(sepsSP==null?",":sepsSP).match(/\S/g),n=sepsP?es.length-1:0;for(var i=0;i<n;i++)es.splice(2*i+1,0,newMo(sepsP[Math.min(i,sepsP.length-1)]));inner=M.newMe("mrow",es,null,doc)}res.splice(1,0,inner);return res};M.spaceMe=
function(widthS,docP){return M.newMe("mspace",null,{width:widthS},docP)};M.fenceMe=function(me1,leftP,rightP,docP){return M.newMe("mrow",[M.newMe("mo",leftP==null?"(":leftP,docP),me1,M.newMe("mo",rightP==null?")":rightP,docP)],docP)};F.iter(function(tagName){M[tagName]=F(M.newMe,tagName)},["mn","mi","mo","mtext","mspace","mrow","mfenced","mfrac","msqrt","mroot","msub","msup","msubsup","mmultiscripts","mprescripts","none","munder","mover","munderover","mtable","mtr","mtd"]);M.setMathBlockQ=function(e,
blockQ){if(blockQ){e.setAttribute("display","block");M.addClass(e,"ma-block")}else if(!M.MathML)$(e).addClass("fm-inline");return e};M.math=function(argsP,blockQ,docP){return M.setMathBlockQ(M.newMe("math",argsP,docP),blockQ)};M.eToMathE=function(mathE){if(M.MathML==null||mathE.tagName.toLowerCase()!="math")F.err(err_eToMathE_);function fixMathMLDeep(nod){if(nod.nodeType!=1)return nod;if(!F.elem(nod.tagName,mtokens_))F.iter(fixMathMLDeep,nod.childNodes);return checkMathMLAttrs(nod)}if(M.MathML&&mathE.tagName==
"math")return fixMathMLQ_?fixMathMLDeep(mathE):mathE;var doc=mathE.ownerDocument;function newMeDeep(me){var tagName=me.tagName.toLowerCase(),args=me.childNodes;function nodeToMes(nod){if(nod.nodeType==3)return/^\s*$/.test(nod.data)?[]:[M.mtext(nod.data,doc)];if(nod.nodeType==8)return[];me.nodeType==1||F.err(err_newMeDeep_);return[newMeDeep(nod)]}if(F.elem(tagName,mtokens_)){if(tagName=="mo"&&args.length==1&&args[0].nodeType==3&&args[0].data=="-")args=M["-"]}else args=F.concatMap(nodeToMes,args);var res=
M.newMe(tagName,args,me.attributes,doc);if(tagName=="math")M.setMathBlockQ(res,me.getAttribute("display")=="block");return res}return newMeDeep(mathE)};M["-"]="\u2212";M.trimNumS=function(s){return s.replace(/(\d\.\d*?)0+(?!\d)/g,"$1").replace(/(\d)\.(?!\d)/g,"$1").replace(/[-\u2212]0(?![.\d])/g,"0")};M.numS=function(s,trimQ){if(trimQ)s=M.trimNumS(s);return s.replace(/Infinity/ig,"\u221e").replace(/NaN/ig,"{?}").replace(/e(-\d+)/ig,"\u00b710^{$1}").replace(/e\+?(\d+)/ig,"\u00b710^$1").replace(/-/g,
M["-"])};M.combiningChar_="[\u0300-\u036f\u1dc0-\u1dff\u20d0-\u20ff\ufe20-\ufe2f]";M.surrPair_="[\ud800-\udbff][\udc00-\udfff]";var decComma_,escPat_="[\\\\`]([A-Za-z]+|.)";M.decimalComma=function(qP){if(qP!=null){decComma_=qP;var numPat=(qP?"\\d*,\\d+|":"")+"\\d+\\.?\\d*|\\.\\d+";M.re_=RegExp("("+numPat+")|"+escPat_+"|"+M.surrPair_+"|\\S"+M.combiningChar_+"*","g")}return decComma_};var commaLangs="af|an|ar|av|az|ba|be|bg|bs|ca|ce|co|cs|cu|cv|da|de|el|es|et|eu|fi|fo|fr|"+"gl|hr|hu|hy|id|is|it|jv|kk|kl|kv|lb|lt|lv|mk|mn|mo|nl|no|os|pl|pt|ro|ru|sc|sk|sq|sr|"+
"su|sv|tr|tt|ug|uk|vi|yi";M.decimalComma(RegExp("^("+commaLangs+")\\b","i").test(document.documentElement.lang));M.infix_={"\u2282\u20d2":240,"\u2283\u20d2":240,"\u226a\u0338":260,"\u226b\u0338":260,"\u2aaf\u0338":260,"\u2ab0\u0338":260,"\u223d\u0331":265,"\u2242\u0338":265,"\u224e\u0338":265,"\u224f\u0338":265,"\u2266\u0338":265,"\u227f\u0338":265,"\u228f\u0338":265,"\u2290\u0338":265,"\u29cf\u0338":265,"\u29d0\u0338":265,"\u2a7d\u0338":265,"\u2a7e\u0338":265,"\u2aa1\u0338":265,"\u2aa2\u0338":265,
"\u2009":390,"":500};M.prefix_={};M.postfix_={};function setPrecs(precs,precCharsA){F.iter(function(prec_chars){var prec=prec_chars[0];F.iter(function(c){precs[c]=prec},prec_chars[1].split(""))},precCharsA)}setPrecs(M.infix_,[[21,"|"],[30,";"],[40,",\u2063"],[70,"\u2234\u2235"],[100,":"],[110,"\u03f6"],[150,"\u2026\u22ee\u22ef\u22f1"],[160,"\u220b"],[170,"\u22a2\u22a3\u22a4\u22a8\u22a9\u22ac\u22ad\u22ae\u22af"],[190,"\u2228"],[200,"\u2227"],[240,"\u2201\u2208\u2209\u220c\u2282\u2283\u2284\u2285\u2286\u2287\u2288\u2289\u228a\u228b"],
[241,"\u2264"],[242,"\u2265"],[243,">"],[244,"\u226f"],[245,"<"],[246,"\u226e"],[247,"\u2248"],[250,"\u223c\u2249"],[252,"\u2262"],[255,"\u2260"],[260,"=\u221d\u2224\u2225\u2226\u2241\u2243\u2244\u2245\u2246\u2247\u224d\u2254\u2257\u2259\u225a\u225c\u225f\u2261\u2268\u2269\u226a\u226b\u226d\u2270\u2271\u227a\u227b\u227c\u227d\u2280\u2281\u22a5\u22b4\u22b5\u22c9\u22ca\u22cb\u22cc\u22d4\u22d6\u22d7\u22d8\u22d9\u22ea\u22eb\u22ec\u22ed\u25a0\u25a1\u25aa\u25ab\u25ad\u25ae\u25af\u25b0\u25b1\u25b3\u25b4\u25b5\u25b6\u25b7\u25b8\u25b9\u25bc\u25bd\u25be\u25bf\u25c0\u25c1\u25c2\u25c3"+
"\u25c4\u25c5\u25c6\u25c7\u25c8\u25c9\u25cc\u25cd\u25ce\u25cf\u25d6\u25d7\u25e6\u29c0\u29c1\u29e3\u29e4\u29e5\u29e6\u29f3\u2a87\u2a88\u2aaf\u2ab0"],[265,"\u2044\u2206\u220a\u220d\u220e\u2215\u2217\u2218\u2219\u221f\u2223\u2236\u2237\u2238\u2239\u223a\u223b\u223d\u223e\u223f\u2242\u224a\u224b\u224c\u224e\u224f\u2250\u2251\u2252\u2253\u2255\u2256\u2258\u225d\u225e\u2263\u2266\u2267\u226c\u2272\u2273\u2274\u2275\u2276\u2277\u2278\u2279\u227e\u227f\u228c\u228d\u228e\u228f\u2290\u2291\u2292\u2293\u2294\u229a\u229b\u229c\u229d\u22a6\u22a7\u22aa\u22ab\u22b0\u22b1\u22b2\u22b3\u22b6\u22b7\u22b9\u22ba\u22bb\u22bc\u22bd\u22be\u22bf\u22c4\u22c6\u22c7"+
"\u22c8\u22cd\u22ce\u22cf\u22d0\u22d1\u22d2\u22d3\u22d5\u22da\u22db\u22dc\u22dd\u22de\u22df\u22e0\u22e1\u22e2\u22e3\u22e4\u22e5\u22e6\u22e7\u22e8\u22e9\u22f0\u22f2\u22f3\u22f4\u22f5\u22f6\u22f7\u22f8\u22f9\u22fa\u22fb\u22fc\u22fd\u22fe\u22ff\u25b2\u2758\u2981\u2982\u29a0\u29a1\u29a2\u29a3\u29a4\u29a5\u29a6\u29a7\u29a8\u29a9\u29aa\u29ab\u29ac\u29ad\u29ae\u29af\u29b0\u29b1\u29b2\u29b3\u29b4\u29b5\u29b6\u29b7\u29b8\u29b9\u29ba\u29bb\u29bc\u29bd\u29be\u29bf\u29c2\u29c3\u29c4"+"\u29c5\u29c6\u29c7\u29c8\u29c9\u29ca\u29cb\u29cc\u29cd\u29ce\u29cf\u29d0\u29d1\u29d2\u29d3\u29d4\u29d5\u29d6\u29d7\u29d8\u29d9\u29db\u29dc\u29dd\u29de\u29e0\u29e1\u29e2\u29e7\u29e8\u29e9\u29ea\u29eb\u29ec\u29ed\u29ee\u29f0\u29f1\u29f2\u29f5\u29f6\u29f7\u29f8\u29f9\u29fa\u29fb\u29fe\u29ff\u2a1d\u2a1e\u2a1f\u2a20\u2a21\u2a22\u2a23\u2a24\u2a25\u2a26\u2a27\u2a28\u2a29\u2a2a\u2a2b\u2a2c\u2a2d\u2a2e\u2a30\u2a31\u2a32\u2a33\u2a34\u2a35\u2a36\u2a37\u2a38\u2a39"+
"\u2a3a\u2a3b\u2a3c\u2a3d\u2a3e\u2a40\u2a41\u2a42\u2a43\u2a44\u2a45\u2a46\u2a47\u2a48\u2a49\u2a4a\u2a4b\u2a4c\u2a4d\u2a4e\u2a4f\u2a50\u2a51\u2a52\u2a53\u2a54\u2a55\u2a56\u2a57\u2a58\u2a59\u2a5a\u2a5b\u2a5c\u2a5d\u2a5e\u2a5f\u2a60\u2a61\u2a62\u2a63\u2a64\u2a65\u2a66\u2a67\u2a68\u2a69\u2a6a\u2a6b\u2a6c\u2a6d\u2a6e\u2a6f\u2a70\u2a71\u2a72\u2a73\u2a74\u2a75\u2a76\u2a77\u2a78\u2a79\u2a7a\u2a7b\u2a7c\u2a7d\u2a7e\u2a7f\u2a80\u2a81\u2a82\u2a83\u2a84\u2a85\u2a86\u2a89\u2a8a\u2a8b\u2a8c\u2a8d\u2a8e\u2a8f"+
"\u2a90\u2a91\u2a92\u2a93\u2a94\u2a95\u2a96\u2a97\u2a98\u2a99\u2a9a\u2a9b\u2a9c\u2a9d\u2a9e\u2a9f\u2aa0\u2aa1\u2aa2\u2aa3\u2aa4\u2aa5\u2aa6\u2aa7\u2aa8\u2aa9\u2aaa\u2aab\u2aac\u2aad\u2aae\u2ab1\u2ab2\u2ab3\u2ab4\u2ab5\u2ab6\u2ab7\u2ab8\u2ab9\u2aba\u2abb\u2abc\u2abd\u2abe\u2abf\u2ac0\u2ac1\u2ac2\u2ac3\u2ac4\u2ac5\u2ac6\u2ac7\u2ac8\u2ac9\u2aca\u2acb\u2acc\u2acd\u2ace\u2acf\u2ad0\u2ad1\u2ad2\u2ad3\u2ad4\u2ad5\u2ad6\u2ad7\u2ad8\u2ad9\u2ada\u2adb\u2add\u2add\u2ade\u2adf\u2ae0\u2ae1\u2ae2\u2ae3\u2ae4\u2ae5\u2ae6"+
"\u2ae7\u2ae8\u2ae9\u2aea\u2aeb\u2aec\u2aed\u2aee\u2aef\u2af0\u2af1\u2af2\u2af3\u2af4\u2af5\u2af6\u2af7\u2af8\u2af9\u2afa\u2afb\u2afd\u2afe"],[270,"\u2190\u2191\u2192\u2193\u2194\u2195\u2196\u2197\u2198\u2199\u219a\u219b\u219c\u219d\u219e\u219f\u21a0\u21a1\u21a2\u21a3\u21a4\u21a5\u21a6\u21a7\u21a8\u21a9\u21aa\u21ab\u21ac\u21ad\u21ae\u21af\u21b0\u21b1\u21b2\u21b3\u21b4\u21b5\u21b6\u21b7\u21b8\u21b9\u21ba\u21bb\u21bc\u21bd\u21be\u21bf\u21c0\u21c1\u21c2\u21c3\u21c4\u21c5\u21c6\u21c7\u21c8\u21c9\u21ca\u21cb\u21cc\u21cd\u21ce\u21cf\u21d0\u21d1"+
"\u21d2\u21d3\u21d4\u21d5\u21d6\u21d7\u21d8\u21d9\u21da\u21db\u21dc\u21dd\u21de\u21df\u21e0\u21e1\u21e2\u21e3\u21e4\u21e5\u21e6\u21e7\u21e8\u21e9\u21ea\u21eb\u21ec\u21ed\u21ee\u21ef\u21f0\u21f1\u21f2\u21f3\u21f4\u21f5\u21f6\u21f7\u21f8\u21f9\u21fa\u21fb\u21fc\u21fd\u21fe\u21ff\u22b8\u27f0\u27f1\u27f5\u27f6\u27f7\u27f8\u27f9\u27fa\u27fb\u27fc\u27fd\u27fe\u27ff\u2900\u2901\u2902\u2903\u2904"+"\u2905\u2906\u2907\u2908\u2909\u290a\u290b\u290c\u290d\u290e\u290f\u2910\u2911\u2912\u2913\u2914\u2915\u2916\u2917\u2918\u2919\u291a\u291b\u291c\u291d\u291e\u291f\u2920\u2921\u2922\u2923\u2924\u2925\u2926\u2927\u2928\u2929\u292a\u292b\u292c\u292d\u292e\u292f\u2930\u2931\u2932\u2933\u2934\u2935\u2936\u2937\u2938\u2939\u293a\u293b\u293c\u293d\u293e\u293f\u2940\u2941\u2942\u2943\u2944\u2945\u2946\u2947\u2948\u2949\u294a\u294b\u294c\u294d\u294e\u294f\u2950\u2951\u2952"+
"\u2953\u2954\u2955\u2956\u2957\u2958\u2959\u295a\u295b\u295c\u295d\u295e\u295f\u2960\u2961\u2962\u2963\u2964\u2965\u2966\u2967\u2968\u2969\u296a\u296b\u296c\u296d\u296e\u296f\u2970\u2971\u2972\u2973\u2974\u2975\u2976\u2977\u2978\u2979\u297a\u297b\u297c\u297d\u297e\u297f\u2999\u299a\u299b\u299c\u299d\u299e\u299f\u29df\u29ef\u29f4\u2b45\u2b46"],[275,"+-\u00b1\u2212\u2213\u2214\u229e\u229f"],[300,"\u2295\u2296\u2298"],[340,"\u2240"],[350,"\u2229\u222a"],[390,"*.\u00d7\u2022\u2062\u22a0\u22a1\u22c5\u2a2f\u2a3f"],
[400,"\u00b7"],[410,"\u2297"],[640,"%"],[650,"\\\u2216"],[660,"/\u00f7"],[710,"\u2299"],[825,"@"],[835,"?"],[850,"\u2061"],[880,"^_\u2064"]]);setPrecs(M.prefix_,[[10,"\u2018\u201c"],[20,"([{\u2016\u2308\u230a\u2772\u27e6\u27e8\u27ea\u27ec\u27ee\u2980\u2983\u2985\u2987\u2989\u298b\u298d\u298f\u2991\u2993\u2995\u2997\u29fc"],[230,"\u2200\u2203\u2204"],[290,"\u2211\u2a0a\u2a0b"],[300,"\u222c\u222d\u2a01"],[310,"\u222b\u222e\u222f\u2230\u2231\u2232\u2233\u2a0c\u2a0d\u2a0e\u2a0f\u2a10\u2a11\u2a12\u2a13\u2a14\u2a15\u2a16\u2a17\u2a18\u2a19\u2a1a\u2a1b\u2a1c"],
[320,"\u22c3\u2a03\u2a04"],[330,"\u22c0\u22c1\u22c2\u2a00\u2a02\u2a05\u2a06\u2a07\u2a08\u2a09\u2afc\u2aff"],[350,"\u220f\u2210"],[670,"\u2220\u2221\u2222"],[680,"\u00ac"],[740,"\u2202\u2207"],[845,"\u2145\u2146\u221a\u221b\u221c"]]);setPrecs(M.postfix_,[[10,"\u2019\u201d"],[20,")]}\u2016\u2309\u230b\u2773\u27e7\u27e9\u27eb\u27ed\u27ef\u2980\u2984\u2986\u2988\u298a\u298c\u298e\u2990\u2992\u2994\u2996\u2998\u29fd"],[800,"\u2032\u266d\u266e\u266f"],[810,"!"],[880,"&'`~\u00a8\u00af\u00b0\u00b4\u00b8\u02c6\u02c7\u02c9\u02ca\u02cb\u02cd\u02d8\u02d9\u02da\u02dc\u02dd\u02f7\u0302\u0311\u203e\u20db\u20dc\u23b4\u23b5\u23dc\u23dd\u23de\u23df\u23e0\u23e1"]]);
var s_,s_or_mx_a_,s_or_mx_i_,docP_,precAdj_;function newMe_(tagName,argsP){return M.newMe(tagName,argsP,docP_)}function emptyMe_(){return newMe_("mspace")}function scanWord(descP){var re=/\s*([-\w.]*)/g;re.lastIndex=M.re_.lastIndex;var match=re.exec(s_);if(!match[1])throw"Missing "+(descP||"word");M.re_.lastIndex=re.lastIndex;return match[1]}function scanString(descP){var re=/\s*(?:(["'])|([-\w.]*))/g;re.lastIndex=M.re_.lastIndex;var match=re.exec(s_);if(match[2]){M.re_.lastIndex=re.lastIndex;return match[2]}if(!match[1])throw"Missing "+
(descP||"string");var c=match[1],re2=RegExp("[^\\`"+c+"]+|[\\`](.|\n)|("+c+")","g"),res="";re2.lastIndex=re.lastIndex;while(true){match=re2.exec(s_);if(!match)throw"Missing closing "+c;if(match[2])break;res+=match[1]||match[0]}M.re_.lastIndex=re2.lastIndex;return res}function scanMeTok(afterP){var tokP=scanTokP();if(!tokP||!tokP[0])throw"Missing expression argument"+(afterP?" after "+afterP:"")+", before position "+M.re_.lastIndex;return tokP}function mtokenScan(tagName){var s=scanString(tagName==
"mtext"?"text":tagName);return[newMe_(tagName,s),tagName=="mo"?s:null]}function htmlScan(){var h=scanString("html"),e=$("<div/>",docP_||document).css("display","inline-block").html(h)[0];if(e.childNodes.length==1)e=e.childNodes[0];return[newMe_("mtext",e),null]}function spScan(){var widthS=scanString("\\sp width");return[M.spaceMe(widthS,docP_),/^[^-]*[1-9]/.test(widthS)?"\u2009":null]}function braceScan(){var tokP=scanTokP();if(tokP&&tokP[1]=="\u2196"&&!tokP[0]){var meTokP_tokP=parseEmbel();tokP=
meTokP_tokP[1]||scanTokP();if(!(meTokP_tokP[0]&&tokP&&tokP[1]=="}"&&!tokP[0]))throw'Expected an embellished operator and "}" after "{\u2196", before position '+M.re_.lastIndex;return meTokP_tokP[0]}var mxP_tokP=parse_mxP_tokP(0,tokP);tokP=mxP_tokP[1];!tokP||tokP[1]=="}"&&!tokP[0]||F.err(err_braceScan_);return[mxP_tokP[0]||emptyMe_(),null]}function attrScan(nameP,mmlOnlyQ){if(!nameP)nameP=scanWord("attribute name");var v=scanString(nameP+" attribute"),tok=scanMeTok(nameP);if(!mmlOnlyQ||M.MathML)tok[0].setAttribute(nameP,
v);return tok}function clScan(){var desc="CSS class name(s)",ws=scanString(desc),tok=scanMeTok(desc);M.addClass(tok[0],ws);return tok}function mvScan(sP){var s=sP||scanString("mathvariant"),tok=scanMeTok(s),me=tok[0];if(!F.elem(M.mtagName(me),["mi","mn","mo","mtext","mspace","ms"]))throw"Can only apply a mathvariant to a MathML token (atomic) element, at "+"position "+M.re_.lastIndex;me.setAttribute("mathvariant",s);if(/bold/.test(s))M.addClass(me,"ma-bold");else if(s=="normal"||s=="italic")M.addClass(me,
"ma-nonbold");M.addClass(me,/italic/.test(s)?"ma-italic":"ma-upright");if(/double-struck/.test(s))M.addClass(me,"ma-double-struck");else if(/fraktur/.test(s))M.addClass(me,"ma-fraktur");else if(/script/.test(s))M.addClass(me,"ma-script");else if(/sans-serif/.test(s))M.addClass(me,"ma-sans-serif");return tok}function ovScan(){return[M.newMe("menclose",scanMeTok("\\ov")[0],{notation:"top"},docP_),null]}function minsizeScan(){var s=scanString("minsize"),tok=scanMeTok("minsize"),me=tok[0];if(M.mtagName(me)!=
"mo")throw"Can only stretch an operator symbol, before position "+M.re_.lastIndex;if(M.MathML)me.setAttribute("minsize",s);else{var r=Number(s);if(r>1)checkVertStretch(0.6*r,0.6*r,me,true);else me.style.fontSize=s}return tok}function mrow1Scan(){return[newMe_("mrow",scanMeTok("\\mrowOne")[0]),null]}function binomScan(){function mtr1(e){return newMe_("mtr",newMe_("mtd",e))}var xMe=scanMeTok("\\binom")[0],yMe=scanMeTok("\\binom")[0],zMe=newMe_("mtable",F.map(mtr1,[xMe,yMe]));M.addClass(zMe,"ma-binom");
if(!M.MathML){zMe.fmUp-=0.41;zMe.fmDn-=0.41}return[newMe_("mrow",[newMe_("mo","("),zMe,newMe_("mo",")")]),null]}M.macros_={mn:F(mtokenScan,"mn"),mi:F(mtokenScan,"mi"),mo:F(mtokenScan,"mo"),text:F(mtokenScan,"mtext"),html:htmlScan,sp:spScan,attr:attrScan,attrMML:F(attrScan,null,true),id:F(attrScan,"id"),dir:F(attrScan,"dir"),cl:clScan,mv:mvScan,bo:F(mvScan,"bold"),it:F(mvScan,"italic"),bi:F(mvScan,"bold-italic"),sc:F(mvScan,"script"),bs:F(mvScan,"bold-script"),fr:F(mvScan,"fraktur"),ds:F(mvScan,"double-struck"),
bf:F(mvScan,"bold-fraktur"),ov:ovScan,minsize:minsizeScan,mrowOne:mrow1Scan,binom:binomScan};M.alias_={"-":M["-"],"'":"\u2032","\u212d":["C","fraktur"],"\u210c":["H","fraktur"],"\u2111":["I","fraktur"],"\u211c":["R","fraktur"],"\u2128":["Z","fraktur"],"\u212c":["B","script"],"\u2130":["E","script"],"\u2131":["F","script"],"\u210b":["H","script"],"\u2110":["I","script"],"\u2112":["L","script"],"\u2133":["M","script"],"\u211b":["R","script"],"\u212f":["e","script"],"\u210a":["g","script"],"\u2134":["o",
"script"]};var spaces_={",":".17em",":":".22em",";":".28em","!":"-.17em"};function scanTokP(){var match=M.re_.exec(s_);while(!match){M.re_.lastIndex=s_.length;if(s_or_mx_i_==s_or_mx_a_.length)return null;var g=s_or_mx_a_[s_or_mx_i_++];if(typeof g=="string"){M.re_.lastIndex=0;s_=g;match=M.re_.exec(s_)}else if(g.nodeType==1)return[g,null];else F.err(err_scanTokP_)}var s1=match[2]||match[0],mvP=null;if(/^[_^}\u2196\u2199]$/.test(match[0])||match[2]&&M.macro1s_[s1])return[null,s1];if(match[0]=="{")return braceScan();
if(match[2]&&M.macros_[s1])return M.macros_[s1]();if(match[1])return[M.newMe("mn",s1,docP_),null];if(/^[,:;!]$/.test(match[2]))s1="\u2009";else if(match[2]=="/")s1="\u2215";else if(M.alias_[s1]&&!match[2]){var t=M.alias_[s1];if(typeof t=="string")s1=t;else{s1=t[0];mvP=t[1]}}var opSP=M.infix_[s1]||M.prefix_[s1]||M.postfix_[s1]?s1:null,e;if(s1=="\u2009")e=M.spaceMe(spaces_[match[2]||","],docP_);else if(opSP){e=M.newMe("mo",s1,docP_);if(/^[\u2200\u2203\u2204\u2202\u2207]$/.test(s1)){e.setAttribute("lspace",
".11em");e.setAttribute("rspace",".06em")}else if(s1=="!"){e.setAttribute("lspace",".06em");e.setAttribute("rspace","0")}else if(s1=="\u00d7"){e.setAttribute("lspace",".22em");e.setAttribute("rspace",".22em")}}else{e=M.newMe("mi",s1,docP_);if(match[2]&&s1.length==1){e.setAttribute("mathvariant","normal");M.addClass(e,"ma-upright");if(!M.MathML)e.style.paddingRight="0"}else if(mvP){e.setAttribute("mathvariant",mvP);M.addClass(e,"ma-upright");M.addClass(e,"ma-"+mvP)}if(/\w\w/.test(s1))M.addClass(e,
"ma-repel-adj")}return[e,opSP]}function parse_mtd_tokP(optQ){var mxP_tokP=parse_mxP_tokP(M.infix_[","]),tokP=mxP_tokP[1]||scanTokP(),mxP=mxP_tokP[0];if(!mxP){if(optQ&&!(tokP&&tokP[1]==","))return[null,tokP];mxP=emptyMe_()}var w=M.mtagName(mxP);if(w!="mtd"&&!(w=="mtr"&&optQ))mxP=M.newMe("mtd",mxP,docP_);return[mxP,tokP]}function parse_rowspan_tokP(){var v=scanString("rowspan"),mtd_tokP=parse_mtd_tokP(),mtd=mtd_tokP[0];mtd.setAttribute(M.MathML?"rowspan":"rowSpan",v);if(!M.hasClass(mtd,"middle"))M.addClass(mtd,
"middle");return mtd_tokP}function parse_colspan_tokP(){var v=scanString("colspan"),mtd_tokP=parse_mtd_tokP();mtd_tokP[0].setAttribute(M.MathML?"columnspan":"colSpan",v);return mtd_tokP}function parse_mtr_tokP(optQ){var mtds=[];while(true){var mtdP_tokP=parse_mtd_tokP(mtds.length==0),mtdP=mtdP_tokP[0],tokP=mtdP_tokP[1]||scanTokP();if(mtdP){if(M.mtagName(mtdP)=="mtr")return[mtdP,tokP];mtds.push(mtdP)}if(!(tokP&&tokP[1]==","))return[mtds.length||!optQ||tokP&&tokP[1]==";"?M.newMe("mtr",mtds,docP_):null,
tokP]}}function parse_table_tokP(){var mtrs=[];while(true){var mtrP_tokP=parse_mtr_tokP(mtrs.length==0),mtrP=mtrP_tokP[0],tokP=mtrP_tokP[1]||scanTokP();if(mtrP)mtrs.push(mtrP);if(!(tokP&&tokP[1]==";"))return[M.newMe("mtable",mtrs,docP_),tokP]}}function parse_math_tokP(){var mxP_tokP=parse_mxP_tokP(0);mxP_tokP[0]=M.newMe("math",mxP_tokP[0],docP_);return mxP_tokP}M.macro1s_={mtd:parse_mtd_tokP,rowspan:parse_rowspan_tokP,colspan:parse_colspan_tokP,mtr:parse_mtr_tokP,table:parse_table_tokP,math:parse_math_tokP};
var embelWs_={"_":"sub","^":"sup","\u2199":"under","\u2196":"over"};function embelKP(op){var wP=embelWs_[op];return wP&&(wP.length<4?"ss":"uo")}function parseEmbel(meTokP,tokP){while(true){if(!tokP)tokP=scanTokP();if(!tokP||tokP[0]||!embelWs_[tokP[1]]){if(tokP&&!meTokP){meTokP=tokP;tokP=null;continue}return[meTokP,tokP]}var k=embelKP(tokP[1]),parseMxs=function(){var mxs={},doneQs={};while(true){if(!tokP)tokP=scanTokP();if(!tokP||tokP[0])break;var op=tokP[1];if(embelKP(op)!=k||doneQs[op])break;doneQs[op]=
true;tokP=scanTokP();if(tokP&&embelKP(tokP[1])==k&&!tokP[0])continue;var mxP_tokP=parse_mxP_tokP(999,tokP);mxs[op]=mxP_tokP[0];tokP=mxP_tokP[1]}return mxs},mxs=parseMxs();if(k=="uo"||!tokP||(tokP[0]?meTokP:embelKP(tokP[1])!="ss")){if(!meTokP)meTokP=[emptyMe_(),null];var w="m",a=[meTokP[0]];F.iter(function(op){if(mxs[op]){w+=embelWs_[op];a.push(mxs[op])}},["_","^","\u2199","\u2196"]);if(a.length>1)meTokP=[M.newMe(w,a,docP_),meTokP[1]]}else{var mxsPA=[mxs];while(tokP&&!tokP[0]&&embelKP(tokP[1])=="ss")mxsPA.push(parseMxs());
if(!meTokP)if(!tokP||!tokP[0])meTokP=[emptyMe_(),null];else{meTokP=tokP;tokP=scanTokP();var postA=[];while(tokP&&!tokP[0]&&embelKP(tokP[1])=="ss")postA.push(parseMxs());mxsPA=postA.concat(null,mxsPA)}var a=[meTokP[0]];F.iter(function(mxsP){if(!mxsP)a.push(M.newMe("mprescripts",null,docP_));else a.push(mxsP["_"]||M.newMe("none",null,docP_),mxsP["^"]||M.newMe("none",null,docP_))},mxsPA);meTokP=[M.newMe("mmultiscripts",a,docP_),meTokP[1]]}}}function parse_mxP_tokP(prec,tokP){var mx0p=null;while(true){if(!tokP){tokP=
scanTokP();if(!tokP)break}var op=tokP[1];if(!op||mx0p&&(tokP[0]?!(M.infix_[op]||M.postfix_[op]):M.macro1s_[op]))if(!mx0p){mx0p=tokP[0];tokP=null}else{if(prec>=precAdj_)break;var mxP_tokP=parse_mxP_tokP(precAdj_,tokP),mx1=mxP_tokP[0];mx1||F.err(err_parse_mxP_tokP_1_);var e=M.newMe("mrow",[mx0p,mx1],docP_);if(M.hasClass(mx0p,"ma-repel-adj")||M.hasClass(mx1,"ma-repel-adj")){if(!(op&&tokP[0]&&M.prefix_[op]<25))$(mx0p).after(M.spaceMe(".17em",docP_));M.addClass(e,"ma-repel-adj")}mx0p=e;tokP=mxP_tokP[1]}else{var moP=
tokP[0];if(moP){var precL=M.infix_[op]||M.postfix_[op];if(precL&&prec>=precL)break;var precROpt=M.infix_[op]||!(mx0p&&M.postfix_[op])&&M.prefix_[op];if(!M.MathML&&!mx0p&&290<=precROpt&&precROpt<=350){$(moP).addClass("fm-large-op");moP.fmUp=0.85*1.3-0.25;moP.fmDn=0.35*1.3+0.25}var meTok_tokP=parseEmbel(tokP),a=[];meTok_tokP[0]||F.err(err_parse_mxP_tokP_embel_);var extOp=meTok_tokP[0][0];tokP=meTok_tokP[1];if(mx0p)a.push(mx0p);a.push(extOp);if(precROpt){var mxP_tokP=parse_mxP_tokP(precROpt,tokP);if(mxP_tokP[0])a.push(mxP_tokP[0]);
tokP=mxP_tokP[1];if(precROpt<25&&!mx0p){if(!tokP)tokP=scanTokP();if(tokP&&tokP[1]&&tokP[0]&&(M.postfix_[tokP[1]]||M.infix_[tokP[1]])==precROpt){a.push(tokP[0]);tokP=null}}}if(a.length==1)mx0p=a[0];else if(op=="/"&&mx0p&&a.length==3||op=="\u221a"&&!mx0p&&a.length==2)if(op=="\u221a"&&M.mtagName(a[0])=="msup")mx0p=M.newMe("mroot",[a[1],M.mchilds(a[0])[1]],docP_);else{a.splice(a.length-2,1);mx0p=M.newMe(op=="/"?"mfrac":"msqrt",a,docP_)}else{var e=M.newMe("mrow",a,docP_);if(op=="\u2009"||(precL||precROpt)>=
precAdj_);else{var k="";if(op=="=")k="infix-loose";else if(a.length==2){k=mx0p?"postfix":"prefix";if(M.infix_[op])k+="-tight";else{if(/^[\u2200\u2203\u2204\u2202\u2207]$/.test(op))k="quantifier";M.addClass(e,"ma-repel-adj")}}else if(mx0p){k=op==","||op==";"?"separator":precL<=270?"infix-loose":"infix";if(op=="|"&&M.MathML&&moP.tagName=="mo"){moP.setAttribute("lspace",".11em");moP.setAttribute("rspace",".11em")}}if(!M.MathML&&k&&!moP.style.fontSize)$(extOp).addClass("fm-"+k)}mx0p=e}}else if(op=="}")break;
else if(M.macro1s_[op]){!mx0p||F.err(err_parse_mxP_tokP_macro_);var mxP_tokP=M.macro1s_[op]();mx0p=mxP_tokP[0];tokP=mxP_tokP[1]}else{embelWs_[op]||F.err(err_parse_mxP_tokP_script_);if(prec>=999)break;var meTok_tokP=parseEmbel(mx0p&&[mx0p,null],tokP),meTok=meTok_tokP[0];meTok||F.err(err_parse_mxP_tokP_embel_2_);tokP=meTok_tokP[1];var a=[meTok[0]],opP=meTok[1];if(opP){var precROpt=M.infix_[opP]||M.prefix_[opP];if(precROpt){var mxP_tokP=parse_mxP_tokP(precROpt,tokP);if(mxP_tokP[0])a.push(mxP_tokP[0]);
tokP=mxP_tokP[1]}}mx0p=a.length==1?a[0]:M.newMe("mrow",a,docP_)}}}return[mx0p,tokP]}M.sToMe=function(g,docP){if(!docP)docP=document;M.infix_[""]&&M.infix_[","]||F.err(err_sToMe_1_);if(M.MathML==null)M.MathML=M.canMathML();M.re_.lastIndex=0;s_="";s_or_mx_a_=Array.isArray(g)?g:[g];s_or_mx_i_=0;docP_=docP;precAdj_=M.infix_[""];var mxP_tokP=parse_mxP_tokP(0);if(mxP_tokP[1])throw"Extra input:  "+mxP_tokP[1][1]+s_.substring(M.re_.lastIndex)+(s_or_mx_i_<s_or_mx_a_.length?"...":"");if(M.re_.lastIndex<s_.length||
s_or_mx_i_<s_or_mx_a_.length)F.err(err_sToMe_2_);return mxP_tokP[0]};M.sToMathE=function(g,blockQ,docP){var res=M.sToMe(g,docP);if(!(res&&F.elem(M.mtagName(res),["math","fmath"])))res=M.newMe("math",res,docP);if(typeof g=="string")res.setAttribute("alttext",g);return M.setMathBlockQ(res,blockQ)};M.$mathQ=true;M.inline$$Q=false;M.parseMath=function(nod){if(nod.nodeType==1&&nod.tagName!="SCRIPT")if(nod.tagName.toUpperCase()=="MATH"){var newE=M.eToMathE(nod);if(newE!=nod)nod.parentNode.replaceChild(newE,
nod)}else for(var p=nod.firstChild;p;){var restP=p.nextSibling;M.parseMath(p);p=restP}else if(nod.nodeType==3&&/[$\\]/.test(nod.data)){var doc=nod.ownerDocument,s=nod.data,a=[],t="",re=/\\([$\\])|\$\$?|\\[([]/g;while(true){var j=re.lastIndex,m=re.exec(s),k=m?m.index:s.length;if(j<k)t+=s.substring(j,k);if(m&&m[1])t+=m[1];else{var i=-1,z;if(m){z=m[0]=="\\("?"\\)":m[0]=="\\["?"\\]":m[0];if(re.lastIndex<s.length&&(m[0]!="$"||M.$mathQ)){i=s.indexOf(z,re.lastIndex);while(i!=-1&&s.charAt(i-1)=="\\")i=s.indexOf(z,
i+1)}if(i==-1){t+=m[0];continue}}if(t){a.push(doc.createTextNode(t));t=""}if(!m)break;var blockQ=m[0]=="$$"||m[0]=="\\[",e=M.sToMathE(s.substring(re.lastIndex,i),blockQ,doc);if(blockQ&&M.inline$$Q&&F.elem(nod.parentNode.nodeName,["P","SPAN"])){var wrap$=$("<div/>",doc).css("display","inline-block").append(e);e=wrap$[0]}a.push(e);re.lastIndex=i+z.length}}F.iter(function(x){nod.parentNode.insertBefore(x,nod)},a);nod.parentNode.removeChild(nod)}};M.parseMathQ=false;$(function(){if(M.MathML==null)M.MathML=
M.canMathML();if(M.parseMathQ)try{M.parseMath(document.body)}catch(exc){alert(exc)}});return M}();var M;if(M===undefined)M=jqMath;
// %/ //
/***
|Description|Adds jumping interface and jumping between tiddlers by hotkeys|
|Version|1.2.0|
|Author	|Yakov Litvin|
|Source ||
|License||
!!!Usage
The plugin works more or less like the tab switching in a browser: press {{{ctrl + j}}} or the "jump" command in tiddler toolbar to open the jumping interface and:
* hold {{{ctrl}}} and press {{{j}}} or ↑/↓ arrows to select a tiddler;
* unhold {{{ctrl}}} or click a row to jump.
* {{PoGc{other hotkeys/.. in UI}}}

!!!Dev
Main desires for the first version (all implemented):
* jump from tiddler to tiddler via hotkeys (like ctrl+tab jumps from tab to tab).. start with ctrl+j, +j/↑/↓
** compatibility with TiddlersBarPlugin
* show the list of tiddlers on hold ctrl
* list (and jump) order should be that of "usage history", not of TW story
* jumping (back) to a tiddler in edit mode should focus the editor and restore cursor (and scroll) position
* clicking outside jumper or pressing "←" in it closes it
** not Esc since Esc with ctrl hold opens Win start menu
Возможные расширения:
* {{PoG{после закрытия записи, выделять в jumper первую, а не вторую – возвращаться сейчас неудобно (но протестить др. вар-ты)}}}
* {{PoG{добавить опцию показывать для прыжка только записи, открытые на ред-е/показывать их "сверху"}}}
* {{PoG{merge with KeyboardNavigationPlugin?}}} UX: при ctrl+↓/↑ в jumper сейчас есть лишние перескоки
* {{PoG{key nav по MainMenu, SideBarOptions/SideBarTabs, между полями tw-записи и к меню}}}
* (+) изменить действие jump [toolbar command] на открытие jumper {{PoGc{сделать опциональным}}}
* (+) закрытие записи по нажатию `x`, редактирование по `e`

%%%(txtTabsJKPdev(
%% user stories %%
{{PoG{implement}}}
%% to do %%
to do <<tiddler [[to do list template##main]] with:"JumpKeys todo" with:"orderJKtodo" noedit>>
%% идеи %%
идеи <<tiddler [[to do list template##main]] with:"JumpKeys idea" with:"orderJKidea" noedit>>
))%%
***/
//{{{
if(!config.jumper) config.jumper = {};
merge(config.jumper, {
	getOpenTiddlersData: function() {
		const list = []
		story.forEachTiddler(function(title, tiddlerElement) {
			list.push({
				title: title, element: tiddlerElement,
				isEditable: !!jQuery(tiddlerElement).has('.editor').length,
				isShadow: tiddlerElement.classList.contains('shadow'),
				isMissing: tiddlerElement.classList.contains('missing')
			})
		})
		this.sortInAccordWithTouchedTiddlersStack(list)
		return list
	},
	getOpenTiddlerDataByIndex: function(index) {
		const list = this.getOpenTiddlersData()
		if(index >= list.length || index < 0) return null
		return list[index]
	},
	jumpToAnOpenTiddler: function(index) {
		const tiddlerData = this.getOpenTiddlerDataByIndex(index)
		if(!tiddlerData) return

		// for compatibility with TiddlersBarPlugin
		if(config.options.chkDisableTabsBar !== undefined)
			story.displayTiddler(null, tiddlerData.title)
		if(tiddlerData.isEditable) {
			const $editor = jQuery(tiddlerData.element).find('.editor textarea')
			// works with CodeMirror as well!
			$editor.focus()
		} else {
			window.scrollTo(0, ensureVisible(tiddlerData.element))
			// remove focus from element edited previously
			// (also fixes a problem with handsontable that steals focus on pressing ctrl)
			// will be substitited with focusing an editor when one is to be focused
			if(document.activeElement) document.activeElement.blur()
		}
		this.pushTouchedTiddler({ title: tiddlerData.title })
	},
	callCommand: function(toolbarCommandName, index) {
		const tiddlerData = this.getOpenTiddlerDataByIndex(index)
		if(!tiddlerData) return
		const command = config.commands[toolbarCommandName]
		if(!command || !command.handler) return

		// disable animation so that this methods finishes after closeTiddler etc finishes
		const chkAnimate = config.options.chkAnimate
		config.options.chkAnimate = false
		command.handler(null/*event*/, null/*src*/, tiddlerData.title)
		config.options.chkAnimate = chkAnimate
	},

	touchedTiddlersStack: [], // of { title: string }
	pushTouchedTiddler: function(tiddlerStackElement) {
		this.removeTouchedTiddler(tiddlerStackElement)
		this.touchedTiddlersStack.push(tiddlerStackElement)
	},
	removeTouchedTiddler: function(tiddlerStackElement) {
		this.touchedTiddlersStack = this.touchedTiddlersStack
			.filter(item => item.title != tiddlerStackElement.title)
	},
	sortInAccordWithTouchedTiddlersStack: function(itemsWithTitles) {
		for(var i = 0; i < this.touchedTiddlersStack.length; i++) {
			var touchedTitle = this.touchedTiddlersStack[i].title;
			for(var j = 0; j < itemsWithTitles.length; j++)
				if(itemsWithTitles[j].title == touchedTitle)
					itemsWithTitles.unshift(
						itemsWithTitles.splice(j, 1)[0]
					);
		}
	},

	css: store.getTiddlerText("JumpKeysPlugin##Jumper styles", "")
		.replace("//{{{", "/*{{{*/").replace("//}}}", "/*}}}*/"),
	modalClass: 'jump-modal',
	itemClass: 'jump-modal__item',
	selectedItemClass: 'jump-modal__item_selected',
	modal: null,
	isJumperOpen: function() {
		return !!this.modal
	},
	showJumper: function() {
		const openTiddlersData = this.getOpenTiddlersData()
		if(openTiddlersData.length < 2) return false
		if(!this.isJumperOpen()) {
			// TODO: try "modal" element
			this.modal = createTiddlyElement(document.body, 'div', null, this.modalClass)
			this.refreshJumper()
			return true
		} else
			return false
		// return value indicates whether the modal was opened by this call
	},
	refreshJumper: function() {
		if(!this.isJumperOpen()) return
		const openTiddlersData = this.getOpenTiddlersData()
		const $modal = jQuery(this.modal)
			.empty()
		const list = createTiddlyElement(this.modal, 'div', null, this.modalClass + '__list')
		
		
		//# find where are we (inside an editor; focus inside tiddlerElement;
		//  scroll between .. and ..)
			for(let i = 0; i < openTiddlersData.length; i++) {
				var listItem = createTiddlyElement(list, 'div', null,
					this.itemClass + (i != 1 ? '' :
					' ' + this.selectedItemClass) +
					(openTiddlersData[i].isShadow ?
					 ' ' + this.itemClass + '_shadow' :
					 openTiddlersData[i].isMissing ?
					 ' ' + this.itemClass + '_missing' : '') +
					 (openTiddlersData[i].isEditable ? 
					 ' ' + this.itemClass + '_editable' : ''),
					openTiddlersData[i].title)
				listItem.onclick = () => {
					this.selectByIndex(i)
					this.hideJumperAndJump()
				}
			}
		//# or append list after forming
	},
	hideJumper: function() {
		if(!this.isJumperOpen()) return
		this.modal.parentElement.removeChild(this.modal)
		//# ..or hide? (keep isJumperOpen coherent)
		this.modal = null
	},

	isCtrlHold: false,
	getSelectedIndex: function() {
		if(!this.isJumperOpen() || !this.modal.firstElementChild) return -1
		return Array.from(this.modal.firstElementChild.children)
			.findIndex(option => option.classList.contains(this.selectedItemClass))
	},
	selectByIndex: function(index) {
		if(!this.isJumperOpen() || !this.modal.firstElementChild) return
		const list = this.modal.firstElementChild

		jQuery(list.children[this.getSelectedIndex()])
			.removeClass(this.selectedItemClass)
		const option = list.children[index]
		jQuery(option).addClass(this.selectedItemClass)

		const stickOutBottom = option.offsetTop + option.offsetHeight - list.offsetHeight
		const stickOutTop = list.scrollTop - option.offsetTop
		if(stickOutBottom > 0) list.scrollTop += stickOutBottom
		if(stickOutTop > 0)    list.scrollTop -= stickOutTop
	},
	selectPrev: function() {
		var currentIndex = this.getSelectedIndex()
		var optionsCount = this.getOpenTiddlersData().length
		this.selectByIndex((currentIndex - 1 + optionsCount) % optionsCount)
	},
	selectNext: function() {
		var currentIndex = this.getSelectedIndex()
		var optionsCount = this.getOpenTiddlersData().length
		this.selectByIndex((currentIndex + 1) % optionsCount)
	},
	hideJumperAndJump: function() {
		if(!this.isJumperOpen()) return

		const index = this.getSelectedIndex()
		this.jumpToAnOpenTiddler(index)
		this.hideJumper()
	},
	handleKeydown: function(e) {
		const self = config.jumper
		if(e.key === 'Control') self.isCtrlHold = true
		if(self.isCtrlHold) self.handleKeydownOnCtrlHold(e)
	},
	// next: make configurable via UI
	defaultCommandsKeys: {
		x: "closeTiddler",
		e: "editTiddler"
	},
	getCommandsKeys: function() {
		const json = store.getTiddlerText('JumpKeysSettings')
		try {
			return JSON.parse(json)
		} catch {
			//# how/where to notify? ..probably after modifying JumpKeysSettings
			// return this.defaultCommandsKeys
		}
	},
	handleKeyup: function(e) {
		const self = config.jumper

		if(e.key === 'Control') {
			self.isCtrlHold = false
			self.hideJumperAndJump()
			return
		}

		const normalizedKeyCode = !e.originalEvent.code ? null :
			/^(Key)?(\w+)$/.exec(e.originalEvent.code)[2].toLowerCase()

		const commandsKeys = self.getCommandsKeys()
		if(self.isCtrlHold && self.isJumperOpen() && normalizedKeyCode in commandsKeys) {

			const index = self.getSelectedIndex()
			self.callCommand(commandsKeys[normalizedKeyCode], index)
			const numberOfOpen = self.getOpenTiddlersData().length
			if(numberOfOpen < 1) { // or < 2 ?
				self.hideJumper()
			} else {
				self.refreshJumper()
				self.selectByIndex(index < numberOfOpen ? index : index - 1)
			}
			if(e.preventDefault) e.preventDefault()
			return false // prevent _
		}
	},
	handleKeydownOnCtrlHold: function(e) {
		// make this work in different keyboard locale layouts:
		if(e.originalEvent.code == "KeyJ") {
			if(!this.showJumper()) this.selectNext()
			if(e.preventDefault) e.preventDefault()
			return false // prevent _
		}
		if(!this.isJumperOpen()) return

		switch(e.key) {
			case 'ArrowUp':   this.selectPrev(); break
			case 'ArrowDown': this.selectNext(); break
			case 'ArrowLeft': this.hideJumper(); break

			default: return
		}
		if(e.preventDefault) e.preventDefault()
		return false // prevent _
	},

	substituteJumpCommand: function() {
		config.commands.jump.type = null
		config.commands.jump.handler = function() {
			config.jumper.showJumper()
		}
	}
})

config.shadowTiddlers['JumpKeysStyleSheet'] = config.jumper.css
config.shadowTiddlers['JumpKeysSettings'] = JSON.stringify(config.jumper.defaultCommandsKeys, null, 2)

// reinstall-safe hijacking
if(!config.jumper.orig_story_onTiddlerKeyPress) {
	config.jumper.orig_story_onTiddlerKeyPress = story.onTiddlerKeyPress
	config.jumper.orig_story_displayTiddler = story.displayTiddler

	store.addNotification('JumpKeysStyleSheet', refreshStyles)
	store.addNotification("ColorPalette", (smth, doc) => refreshStyles('JumpKeysStyleSheet', doc))

	jQuery(document)
		.on('click', event => {
			const element = config.jumper.modal
			if (element && !element.contains(event.target))
				config.jumper.hideJumper()
		})
		//# these are not updated on reinstalling
		.on('keydown', config.jumper.handleKeydown)
		.on('keyup', config.jumper.handleKeyup)
	// avoid stucking ctrl as "hold" on ctrl + f etc
	//# doesn't seem to work anymore
	window.addEventListener('blur', () => config.jumper.isCtrlHold = false)

	config.jumper.substituteJumpCommand()
}

// a very simplistic implementation:
story.displayTiddler = function(srcElement, tiddler, template, animate, unused, customFields, toggle, animationSrc) {
	config.jumper.pushTouchedTiddler({
		title: (tiddler instanceof Tiddler) ? tiddler.title : tiddler
		//# ...: template == DEFAULT_EDIT_TEMPLATE
	})
	return config.jumper.orig_story_displayTiddler.apply(this, arguments)
}
//}}}
/***
!!!Jumper styles
//{{{
.jump-modal {
	position: fixed;
	top: 50vh;
	left: 50vw;
	transform: translate(-50%, -50%);
	z-index: 100;

	max-width: 80vw;
	max-height: 80vh;
	box-sizing: border-box;

	box-shadow: 1px 1px 10px #ccc;
	border-radius: 1em;
	background: [[ColorPalette::Background]];
	padding: 1em;
	display: flex;
}
.jump-modal__list {
	position: relative;
	overflow: auto;

	list-style: none;
	padding: 0;
	margin: 0;
}
.jump-modal__item {
	padding: 0.3em 0.8em;
	border-radius: 0.5em;
	margin-bottom: 0.5em;
	cursor: pointer;
}
.jump-modal__item_shadow {
	font-weight: bold;
	font-style: italic;
}
.jump-modal__item_missing {
	font-style: italic;
}
.jump-modal__item_editable {
	text-decoration: underline;
}
.jump-modal__item:hover {
	background: [[ColorPalette::SecondaryPale]];
}
.jump-modal__item_selected,
.jump-modal__item_selected:hover {
	background: [[ColorPalette::SecondaryLight]];
}
.darkMode .jump-modal__item:hover {
	background: rgba(0,0,255,0.35);;
}
.jump-modal__item:last-child {
	margin-bottom: 0;
}

.jump-modal ::-webkit-scrollbar {
  background-color: transparent;
  width: 1.5em;
}
.jump-modal ::-webkit-scrollbar-thumb {
  background: [[ColorPalette::TertiaryLight]];
  border-radius: 1em;
  width: 1em;
  border-left: 0.5em solid [[ColorPalette::Background]];
}
//}}}
!!!
***/
TwFormulaePlugin
<<tiddler {{ window.location = 'https://yakovl.github.io/TiddlyWiki_TwFormulaPlugin' }}>>
---- ~TiddlyWiki v<<version>>
<!--{{{-->
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' />
<!--}}}-->

<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2280%22>∑</text></svg>">
lunch \$7.53
dinner \$12.58

$${1 Н \over 2 м^2} = 0,5 Па$$
$${1 N\over 2 m^2} = 0.5 Pa$$

$$\begin{equation}
\operatorname{erfc}(x) =
\frac{2}{\sqrt{\pi}} \int\limits_x^{\infty} e^{-t^2}\,dt =
\frac{e^{-x^2}}{x\sqrt{\pi}}\sum_{n=0}^\infty (-1)^n \frac{(2n)!}{n!(2x)^{2n}}
\end{equation}$$
/***
|''Version''|1.6.16|
***/
//{{{
// 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("Typographics",		"CommonTypographicsStyleSheet");
setCssShadow("Semantics",		"CommonSemanticsStyleSheet");
setCssShadow("Representation",		"CommonRepresenationStyleSheet");
setCssShadow("Representation tools",	"CommonRepresToolsStyleSheet");
//if(jQuery.browser.mozilla) // now done via CSS
	setCssShadow("FireFox CSS",	"FireFoxFixesStyleSheet");

/*var csp_orig_wikify = wikify;
wikify = function(source,output,highlightRegExp,tiddler) {
	csp_orig_wikify.apply(this,arguments);
// no longer needed, left as an example:
	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)
//{{{
jQuery(document).on("click",function(e) {
	if(!jQuery(e.target).closest('#messageArea').length) // click outside messageArea
		clearMessage();
	return true;
});
// prevent message from being closed by click on a saveChanges button
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);
	};
}
//}}}
/***
!!!Typographics
{{{
.wizard, .wizardStep, pre, .viewer pre,
.selected .tagging, .selected .tagged,
.tagging, .tagged		{ border: none; }

#backstagePanel			{ padding: 0; }

.wizard				{ padding: 0.1em 2em 0 2em; }

#messageArea {
	top: 1.5em; padding: 0.7em 1em;

	max-height:calc(100vh - 5em);
	max-width:calc(100vw - 4em);
	overflow: auto;
}
#messageArea .button { background: inherit; }

.viewer pre, .viewer code, .editor { line-height: 1.4em; }
}}}
!!!Typographics details
* this section is meant to be incorporated to the core
* removing border from `#messageArea` is temporarily taken away because of visual .. with button in the sidebar
* padding for `.wizard`: instead of asymmetrical
* messageArea: paddings enlarged; dimensions restricted not to go beyond page boundaries
** {{DDn{padding-bottom "doesn't work" when the content is large.. change `box-sizing`?}}}
** {{PoG{horizontal margin is ok; top should be removed; bottom – ...}}}
** {{DDn{magic numbers 5em and 4em are not nice, though}}}
** `#messageArea .button { background: inherit; }` – non-BEM selector here until we modify the core
!!!Semantics
{{{
div[tags~="code"] .editor, #tiddlerStyleSheet .editor,
div[tags~="systemConfig"] .editor, pre, code {
	font-family: Consolas, monospace;
}
.viewer pre, .viewer code {
	font-size: inherit;
}

.tiddler[tags~="notepad"] textarea,
.tiddler[tags~="notepad"] .viewer {
	font-family: Consolas, monospace;
	white-space: pre;
}

.PoG, .PoGc	{ color: green; }

.DDn, .DDnc	{ color: purple; }

.FpG, .FpGc	{ color: #0000CC; }

.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		{ text-decoration: underline; text-decoration-color: #00aa00; }
.LimitGeneral	{ text-decoration: underline; text-decoration-color: #0000ff; }
}}}
!!!Semantics details
* "возможности роста" (possibilities of growth), в т.ч. комментарии (comments): `.PoG`, `.PoGc`
* сделанное/написанное с изъяном (done [written] to be done, but not ideally): `.DDn`, `.DDnc`
* подчёркнуто "замороженные" точки роста (frozen ~PoG or ~DDn): `.FpG`, `.FpGc` (dimmer than blue)
* комментарии/"подразумеваемые" вещи: `.c`
* тэг `code` улучшает редактор для CSS, JS и т.п. (в edit mode)
* тэг `notepad` задаёт отображение и редактор ~как в блокноте~
** {{PoG{add also save-in-edit-mode button}}}
** {{PoG{disable wikiwords}}}
* {{PoGc{стоит что-то придумать с `[[ColorPalette::Foreground]]` на случай night mode}}}
!!!Representation
{{{
#sidebarOptions .sliderPanel	{ background-color:[[ColorPalette::Background]] !important;
				  font-size: 95% !important; }
.alignRight			{ text-align: right; }
#tiddlerStyleSheet .viewer pre	{ clear: both !important; }

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

#displayArea			{ margin-bottom: 30% !important; }

.viewer .tabContents		{ background-color: inherit; }

body		{ letter-spacing: 0.02em; }
code, pre	{ letter-spacing: 0; }

.viewer img	{ max-width: 100%; }

.toolbar a	{ display: inline-block; }
.toolbar	{ float: right; }
}}}
!!!Representation details
* ...
* `margin-bottom` of `#displayArea` – to be able to keep the content on the eye level
* `letter-spacing` gives less tight font; non-zero breaks monospace when tabs are used, so we remove `letter-spacing` for code
!!!Representation tools
{{{
table.borderless,
table.borderless > tbody > tr,
table.borderless > tbody > tr > td,
table.borderless > tbody > tr > 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, monospace !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.5|
|Requires|ForEachTiddlerPlugin SetFieldPlugin SharedTiddlersPlugin|
|~|ForEachTiddlerPlugin is not required for the operation of the plugin, but if FETP is present, it should be evaluated before this plugin (same for SharedTiddlersPlugin); 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|
***/

//{{{
// 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() in keyup handlers
var $tab =	9,
    $enter =	13,
    $esc =	27,
    $home =	36,
    $end =	35,
    $up =	38,
    $down =	40,
    $pgUp =     33,
    $pgDn =     34

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

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

		// refresh the list (order)
		store.notify(field,true);
// ~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)
		{
			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(popup), 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+1
					}
					var addTiddler = function() {
 						jQuery(btn).click();
					};
				}

				// 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) {
console.log(`e.which is ${e.which}, e.key is ${e.key}`)
					switch(e.key) {
						case 'i':  showTiddlerInfo();	break;
						case 'r':  startRenaming();	break;
						case 'e':  startEditing();	break;
						case 't':  startTagToggling();	break;
						case 'd':
							if(serapartedSwitchActions && tid)
								bringActions();
						break;
						case '+':
						case '=':
							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");
jQuery(popup).addClass("hide");
			} 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)) return;

	tiddler.tags.push(tag);
	tiddler.modifier = config.options.txtUserName;
	tiddler.modified = new Date();
	store.setDirty(true);
};
  // remove tag
config.macros.itemMenu.actionStepsWithArguments["-tag"] = function(tiddler,tag)
{
	if(!tiddler.isTagged(tag)) return;

	tiddler.tags.splice(tiddler.tags.indexOf(tag),1);
	tiddler.modifier = config.options.txtUserName;
	tiddler.modified = new Date();
	store.setDirty(true);
};
  // 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();
	store.setDirty(true);
};
  // 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, .nameInput, .newTitleInput, .newTagInput {\n"+
	"	padding: 0.2em 0.4em; \n"+
	"	border: none;\n"+
	"	margin: 0.4em 0.4em 0;\n"+
	"	box-shadow: 1px 1px 3px #999 inset;\n"+
	"}\n"+
	".ttextInput, .nameInput {\n"+
	"	box-sizing: border-box;\n"+
	"	width: calc(100% - 0.8em);\n"+
	"}\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 > tbody > tr > td > .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: rgba(0,0,255,0.15); }\n\n"+
	".darkMode .tableList > tbody > .selected { background-color: rgba(0,0,255,0.4); }\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('keydown',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 "editable" and "adder" helpers)
		//  for now, deprecated "insert" helper is supported (same as "editable")
		var usedScript = getParam(preParsedParams,"script",""),
		    insertDefinition = "var editable = "+
		        "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||'')+'>>';"+
		    "}, insert = editable;";
		    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 // empty = default
		   || 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("title", null, true, false, false);
		var title  = getParam(params, "title",  config.macros.newTiddler.title),
		    label  = getParam(params, "label",  config.macros.newTiddler.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, null, true);
		    };

		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;
			addTidToSet(theTiddler);

			store.saveTiddler(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
			}

			// for compability with SetManagerPlugin (usage in itemMenus)
			if(cmi)
			{
				if(goOnSelected)
					cmi.ensureFocusOnCurrentlyDragged();
				else
					cmi.clearCurrentlyDragged();
			}

			// bad fix for SMP: if autoSaveChanges(); is called directly,
			// reselection doesn't work; but it's enough to set timeout
			// of 1 ms and the bug disappears
			setTimeout(autoSaveChanges, 1);
		};

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

				if(e.shiftKey)
					Popup.remove(1);
				else
					Popup.remove();

				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
}

		// "add" button
		createTiddlyButton(wrapper, "add", "add 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"),
		    selectOptions = getParam(pParams,"options","[]");
		eval('selectOptions = '+selectOptions);

		wrapper.options = {
			tiddler:	tidName ? store.fetchTiddler(tidName) : tiddler,
//# store tiddler name so that we can create it if it doesn't exist
			part:		partName,
			viewType:	getParam(pParams,"viewType","wikified"),
			selectOptions:	selectOptions,
			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")
		};

		// when "transcluding", use same stack and nest limit to prevent looping
		var stack = config.macros.tiddler.tiddlerStack,
		    title = wrapper.options.tiddler ? wrapper.options.tiddler.title : '';
		if(!config.options.transclusionsNestLimit && (stack.indexOf(title) != -1)
		   || config.options.transclusionsNestLimit &&
		     (stack.length+1 > config.options.transclusionsNestLimit))
		{
			createTiddlyError(wrapper,
				"insertEditable: maximum transcluding depth reached");
			return;
		}
		stack.push(title);
		try {

			this.turnViewMode(wrapper);

		} finally {
			stack.pop();
		}
	},
	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);
		//if(!tiddler)
			//# 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.tiddler && place.options.part[0] != "c")
			return;
//# wtf is this "c" as first character?
		//# may be add warning; also, do the 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"
				 : (place.options.viewType == "select"? "":"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 links, buttons, etc; if noedit:
			if(noedit || e.target.tagName.match(new RegExp("^(a|img|button|"+
			    "input|textarea|select|option|canvas)$", "i")))
				return true;
			// prevent editing on text selection
			var selection = "";
			if (window.getSelection) {
				selection = window.getSelection().toString();
			}
			// cross-browser enough https://stackoverflow.com/q/10478/3995261
			if(selection) 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 "select":
				var select = createTiddlyElement(container, 'select'),
				    options = place.options.selectOptions, i,
				    selectedIndex = -1;

				// populate with options
				for(i = 0; i < options.length; i++) {
					createTiddlyElement(select,'option',null,null,
					    options[i].label, {value:options[i].option});
					if(options[i].option == value)
						selectedIndex = i;
				}
				if(selectedIndex != -1)
					select.selectedIndex = selectedIndex;
				else {
					// indicate that value is not a suggested option
					jQuery(select).addClass('unsuggested');
					jQuery(select).find('option').addClass('normal');
					// but show it as a selected "option"
					var currentOption = jQuery('<option>'+ value +'</option>')
						.prependTo(select);
					select.selectedIndex = 0;
				}

				select.onchange = function(e) {

					// once wrong option is changed to an expected one
					if(jQuery(select).hasClass('unsuggested')
					   && select.selectedIndex != 0)
						jQuery(select).removeClass('unsuggested')
							.find(':first-child').remove();
					
					var newValue = e.target.value;
					config.macros.insertEditable.setData( place.options.tiddler, place.options.part, newValue, place.options.noNotify_partial);
					if(place.options.saveOnApply)
						autoSaveChanges();
				};
				break;
			case "wikified":
			default:
				wikify(value,container,null,place.options.tiddler);
		}
	},
	turnEditMode: function(place)
	{
		// add the edit area
		var initialValue = this.getData(place.options.tiddler,place.options.part),
		    rowslimit = 1,
		    size = place.options.size;
		place.innerHTML = (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>');
		if(place.options.fill) {
			place.options.initialPadding = place.style.padding;
			place.style.padding = "0";
		}
		var editarea = place.firstChild, button;
		editarea.value = initialValue;

		// define apply/cancel helpers
		var turnOffEditing = function()
		    {
			config.macros.insertEditable.turnViewMode(place);
			jQuery('html').off("click",onClick);
		    },
		    applyChanges = function()
		    {
			config.macros.insertEditable.setData(place.options.tiddler, place.options.part, editarea.value, place.options.noNotify_partial);
			turnOffEditing();
			if(place.options.saveOnApply)
				autoSaveChanges();
		    };

		if(place.options.withButton)
			button = createTiddlyButton(place,"save",null,applyChanges);
		// on click outside the edit area, switch to the view mode
		var onClick = function(e) {
			if(place.options.keepOnBlur || jQuery(e.target).parents()/*and self*/.addBack().is(place))
				return;
			turnOffEditing();
		};
		place.onclick = null;
		jQuery('html').click(onClick);
//# avoid creating a "global" handler? (to remove extra code from .onkeydown handler)
		editarea.onkeydown = function(ev)
		{
			var e = ev || window.event;
			if(e.which == $esc)
				// for now, don't check if was changed
				turnOffEditing()
			if((e.which == $enter) && (e.ctrlKey || place.options.applyOnEnter))
			{
				applyChanges();
				e.cancelBubble = true;
				if(e.stopPropagation) e.stopPropagation();
				return false;
			}
		};
		editarea.oninput = function(){
			adjustHeightToContent.apply(this);
			if(editarea.value != initialValue)
				editarea.classList.add('inline-editor_dirty');
			else
				editarea.classList.remove('inline-editor_dirty')
		}

		// set initial "state"
		editarea.focus();
		if(!window.isOpenedOnTouchScreen()) // with FF for Android, better not to
			editarea.select();
		adjustHeightToContent.apply(editarea);
	}
};

// define styles
setStylesheet(
	'.emptyViewer { color: #ddd; background-color: #ddd; }\n'+
	'.transparentEmptyViewer { color: rgba(0,0,0,0); background-color: rgba(0,0,0,0); }\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'+
	'.unsuggested { background-color: #ffdddd; }\n'+
	'option.normal { background-color: white; }',
"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;

		var newTids = [];
		if(check instanceof Function)
			store.forEachTiddler(function(tName,tiddler) {
				if(check(tiddler)) newTids.push(tiddler);
			});
		else
			// check is not a function, but an array of tiddlers
			newTids = check;

		if(setDefinition.sortField) {
			var getComparableCounter = function(tiddler)
			{
				var c = tiddler.getSCounter(setDefinition.sortField);
				return (c != 0 && !c)? -1 : c; // undefined up
			};
			newTids.sort(function(tiddlerA, tiddlerB)
			{
				return getComparableCounter(tiddlerA)
					- getComparableCounter(tiddlerB);
			});
		}

		for(var i = 0; i < newTids.length; i++)
			results.pushUnique(newTids[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);
	}
};
// if SharedTiddlersPlugin is installed, make sets "include-aware"
var stp = config.extensions.SharedTiddlersPlugin;
if(stp)
	stp.useForReallyEachTiddler(config.macros.defineSet,"getSetTiddlers");

// 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)
//}}}
/***
main common styles+ are set by the SetCommonStylesPlugin; these are microreps-only (may be eliminated in the future)
***/
//{{{
var getStylesFromSection = function(sectionName) {
	var css = store.getRecursiveTiddlerText("SetStandartMicrorepStyles##"+sectionName,"",10);
	return css.substring(4,css.length-4);
};
setStylesheet(getStylesFromSection("Styles"),"SetStandartMicrorepStyles");
//}}}
// // Remove the previous "version"
//{{{
store.removeTiddler("SetStandartSemanticStyles");
//}}}
/***
!!!Styles
{{{
#displayArea	{ margin-bottom: 30% !important; }

.inline a	{ display: inline !important; margin: 0 !important; padding: 0 !important; }
}}}
!!!
***/
(under construction) plugin to display ~LaTeX formulae in TW / $e^{i \pi} = -1, \sum\limits_{n=0}^∞ {1\over n^2} = {π^2\over 6}$
~TwFormulaePlugin
/***
|Version|0.1.0|
allows to quickly set the favicon using an (ugly) input ({{{<<faviconSymbol>>}}} macro): <<faviconSymbol>> (applies on enter)
***/
//{{{
config.macros.faviconSymbol = {
	// UI prototype:
	handler: function(place, macroName, params/*, wikifier, paramString, tiddler*/) {
		var value = this.getUsedFavicon() || ''
		var attributes = {
			placeholder: 'insert emoji and hit Enter'
		}
		var editor = createTiddlyElement(place, 'input', null, null, null, attributes)
		var setFavicon = this.setFavicon.bind(this)
		jQuery(editor)
		  .val(value)
		  .on('keyup', function(event) {
			if(event.key.toLowerCase() === 'enter') {
				// TODO: leave just 1 ~symbol, including emoji 💡
				var char = editor.value ? editor.value/*[0]*/ : ''
				setFavicon(char)
				displayMessage('set favicon to ' + char)
			}
		})
		// TODO: *add autosuggestions
	},

	getUsedFavicon: function() {
		var markupPreHead = this.getMarkupTiddlerText()
		if(!markupPreHead) return null
		var existingBit = this.findFaviconInMarkup(markupPreHead)
		return existingBit ? existingBit.iconText : null
	},
	setFavicon: function(iconChar) {
		var markupPreHead = this.getMarkupTiddlerText()
		if(markupPreHead !== null) var existingBit = this.findFaviconInMarkup(markupPreHead)

		if(markupPreHead === null || !existingBit) {
			// TODO: don't just append, insert before \n<!--}}}-->
			this.setMarkupTiddlerText(this.getMarkupText() +
				'\n' + this.getFaviconMarkup(iconChar))
		} else {
			var newMarkup = markupPreHead.substring(0, existingBit.markupStart) +
				this.getFaviconMarkup(iconChar) +
				markupPreHead.substring(existingBit.markupEnd)
			this.setMarkupTiddlerText(newMarkup)
		}
	},
	setMarkupTiddlerText: function(newText) {
		var tiddler = store.fetchTiddler('MarkupPreHead') || new Tiddler('MarkupPreHead')
		// without setting second param, .saveTiddler tries to save a tiddler with an empty title
		store.saveTiddler('MarkupPreHead', 'MarkupPreHead', newText, 'SymbolsPlugin', new Date())
	},
	getMarkupTiddlerText: function() {
		var tiddler = store.fetchTiddler('MarkupPreHead')
		return tiddler ? tiddler.text : null
	},
	// tiddler or shadow
	getMarkupText: function() {
		return store.getTiddlerText('MarkupPreHead')
	},

	faviconMarkupStart: '<link rel="icon" href="data:image/svg+xml,' +
		'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y=".9em" font-size="80">'.replace(/"/g,'%22'),
	faviconMarkupEnd: '</text></svg>">',
	// ..where's the link to the tweak source?
	// TODO: report again: doesn't work in Vivaldi desktop yet (5.7)
	getFaviconMarkup: function(iconChar) {
		return this.faviconMarkupStart +
				iconChar +
			this.faviconMarkupEnd
	},
	findFaviconInMarkup: function(markup) {
		var startIndex = markup.indexOf(this.faviconMarkupStart)
		if(startIndex === -1) return null
		var endPartIndex = markup.indexOf(this.faviconMarkupEnd, startIndex + this.faviconMarkupStart.length)
		if(endPartIndex === -1) return null
		return {
			iconText: markup.substring(startIndex + this.faviconMarkupStart.length, endPartIndex),
			markupStart: startIndex,
			markupEnd: endPartIndex + this.faviconMarkupEnd.length
		}
	}
}
//}}}
|~ViewToolbar|closeTiddler +editTiddler jump > fields permalink references closeOthers deleteTiddler|
|~EditToolbar|+saveTiddler -cancelTiddler references deleteTiddler|
/***
|Name       |TwFormulaePlugin|
|Description|Render beautiful formulas using LaTeX syntax in wrappers like {{{$$...$$}}}. Plugin supports different libraries for that (MathJax, KaTeX, jqMath, MathQuill) – the supported LaTeX subset and some features depend on the selected library|
|Version    |0.5.10|
|Source     |https://github.com/YakovL/TiddlyWiki_TwFormulaPlugin/blob/master/TwFormulaPlugin.js|
|Previous contributors|Forked from ~PluginMathJax v1.3, by an anonymous author (called themselves "[[Canada East|http://tiddlywiki.canada-east.ca/]]"); jqMath was added thanks to [[this|https://groups.google.com/forum/#!topic/tiddlywiki/PNXaylx1HRY]] thread and the prototype provied by Eric Schulman|
!!!Installation and configuring
# install the plugin as usual (copy with the {{{systemConfig}}} tag, reload)
# if you're ok with using ~KaTeX, download the latest [[release|https://github.com/KaTeX/KaTeX/releases]], unpack all the files into {{{./jsLibs/KaTeX/}}} (so that if your TW is {{{folder/TW.html}}}, the katex.min.js, for instance, is in {{{folder/jsLibs/KaTeX/katex.min.js}}}; same for {{{katex.min.css}}}, etc) {{PoGc{can we use CDN instead?}}}, and reload your TW
# if you want to use another supported library, put one of the {{{libs}}} listed in code here: <<option txtMathLib>> {{DDnc{implement a select for an option macro instead}}}, _ {{DDnc{where to get the files, where to put}}}, reload TW (this is applied on startup)

!!!Usage and examples
The plugin introduces several formatters to write math. For instance $a^2 + b^2$ is an inline formula, which can be written as {{{$ a^2 + b^2 $}}} and {{{\( a^2 + b^2 \)}}} (spaces are optional: {{{$a^2 + b^2$}}} will produce the same result). To write an ordinary {{{$}}}, write {{{\$}}} {{DDnc{make optional backward compatibility (disabling .. formatter)?}}}

In other cases, you may need a centered block formula like this:
$$ P(E) = {n \choose k} p^k (1 - p)^{n - k} $$
This can be inserted via the {{{$$ ... $$}}} and {{{\[ ... \]}}} wrappers. Note that to get \$\$ as plain text, you'll need {{{\$\$}}} (and not {{{\$$}}}).

{{PoGc{explain other formatters}}}
{{PoGc{how to add (12.2) on the right}}}
{{DDnc{startup vs reload tiddlers}}}

!!!Cross-library compatibility
Different libraries implement different subsets of ~LaTeX, so it's not like you can use any of them for any formula. ~MathJax presumably still supports a larger subset, but ~KaTeX is greatly faster, so this is the default option.

{{PoGc{describe ~WYSIWYGish libs}}}
***/
//{{{
var libs = {
//	jsMath: 1,	// not supported in this version as a deprecated solution
	MathJax: 2,
	KaTeX: 3,
	jqMath: 4,
	MathQuill: 5,
}

// config:
config.options.txtMathLib = config.options.txtMathLib || 'KaTeX'
var math_lib = libs[config.options.txtMathLib]

;(function main() {
	// install only once, notify if there's another copy of formulae plugin
	if(version.extensions.PluginMathJax || version.extensions.TwFormulaePlugin || window.jsMath) {
		alert("TwFormulaePlugin: another copy of PluginMathJax or TwFormulaePlugin is installed or jsMath is loaded")
		return
	}
	version.extensions.TwFormulaePlugin = { installed: true }

	var ie9RegExp = /^9\./;
	var UseInnerHTML = (config.browser.isOpera || config.browser.isIE && ie9RegExp.test(config.browser.ieVersion[1]));

// a helper
var loadLib = function(fullPath,config)
{
	// create the script element and add it
	var script = document.createElement("script");
	script.type = "text/javascript";
	script.src = fullPath;
	
	if(UseInnerHTML)
		script.innerHTML = config;
	script.text = config;

	document.getElementsByTagName("head")[0].appendChild(script);

	return script;
};
var loadCSS = function(fullPath)
{
	jQuery("head").append("<link rel='stylesheet' type='text/css' href='"+fullPath+"' />");
/*	var stylesheet = document.createElement('link');
	stylesheet.href = fullPath;
	stylesheet.rel = 'stylesheet';
	stylesheet.type = 'text/css';
	document.getElementsByTagName('head')[0].appendChild(stylesheet);
*/	// http://stackoverflow.com/questions/5186638/how-to-asynchronously-load-css-using-jquery
};

// =================================================================
//			     Load the library
// =================================================================

switch(math_lib)
{
	case libs.MathJax:
		// set the path to MathJax.js (this may be overwritten by the user)
		var mathJaxPath = "elder/MathJaxPlugin/" + "js/MathJax/";

		var mjconfig =
		'MathJax.Hub.Config({' +
			'jax: ["input/TeX","output/HTML-CSS"],' +
			'extensions: ["TeX/AMSmath.js", "TeX/AMSsymbols.js"],' +
			'"HTML-CSS": {' +
				'scale: 115' +
			'}' +
		'});' +
		'MathJax.Hub.Startup.onload();';

		var script = loadLib(mathJaxPath + "MathJax.js",mjconfig);
	break;
	case libs.KaTeX:
		var kaTeXpath = "jsLibs/KaTeX/";
		loadLib(kaTeXpath + "katex.min.js"); // jQuery.getScript requires xhr, so won't work locally (through file://)
		// http://stackoverflow.com/questions/7718935/load-scripts-asynchronously

		loadCSS(kaTeXpath + "katex.min.css")
	break;
	case libs.MathQuill:
		var mathQuillPath = "jsLibs/mathquill/";
		loadLib(mathQuillPath + "mathquill-0.10.0-min.js");
		var loadMQ = function(){ try{
			config.extensions.mathQuill = MathQuill.getInterface(2);
		}catch(e){ setTimeout(loadMQ,50); } };
		loadMQ();
		loadCSS(mathQuillPath + "mathquill-0.10.0-min.css");
		mathQuillCssExtras = // div = outline formulae
			"div.mq-editable-field { display: block; text-align: center; }\n"+
			"   .mq-editable-field { border: thin solid #cccccc; }";
	break;

	// for jqMath nothing is to be loaded
	// jsMath is not implemented in this version
}

// =================================================================
//	   Define helpers for wikitext editing through WYSIWYG
// =================================================================

var changeWikiText = function(sourceTiddler,startPosition,oldLatexLength,openWrapper,closeWrapper,newLatex)
{
	// prepare texts and positions
	var noTiddlerMsg = "changeWikiText: no sourceTiddler detected";
	if(!sourceTiddler)
		return console.log(noTiddlerMsg);

	var oldLenght = openWrapper.length + oldLatexLength + closeWrapper.length;

	sourceTiddler.text = sourceTiddler.text.substr(0,startPosition) +
		openWrapper + newLatex + closeWrapper +
		sourceTiddler.text.substr(startPosition + oldLenght);

	store.saveTiddler(sourceTiddler); // recalcs slices, notifies etc
};

// =================================================================
//	   Define formatters and hijack wikifying for latex
// =================================================================

config.formatterHelpers.mathFormatHelper = function(w) {

	var endRegExp = new RegExp(this.terminator, "mg");
	endRegExp.lastIndex = w.matchStart + w.matchLength;
	var matched = endRegExp.exec(w.source);

	if(matched) {
		var e = document.createElement(this.element);
		if(math_lib == libs.MathJax)
			e.type = this.inline ? "math/tex" : "math/tex; mode=display";
		var latex = w.source.substr(w.matchStart + w.matchLength,
			matched.index - w.matchStart - w.matchLength);
		if(this.keepdelim)
			latex = w.source.substr(w.matchStart, matched.index + matched[0].length - w.matchStart);

// pre-parsing can be done here
latex = latex.replace(/\\?π/mg,"\\pi").replace("×","\\times").replace("∞","\\infty");

		if(UseInnerHTML)
			e.innerHTML = latex;
		else
			e.text = latex;
		w.output.appendChild(e);
		if(math_lib == libs.jqMath)
			M.parseMath(e);
		if(math_lib == libs.KaTeX)
			try {
				katex.render(latex,e,{ displayMode: !this.inline,
					throwOnError: false, errorColor: "#ff0000" });
			} catch(e) {
				if(!(e.message == "katex is not defined"))
					console.log("katex exception:");console.log(e);
			}
		if(math_lib == libs.MathQuill)
		{ try{
			var mqEditor = config.extensions.mathQuill.MathField(e, {
				spaceBehavesLikeTab: true, // ??
				handlers: {
					edit: function() {
						// do onchange stuff here
						// use mathQuillEditor.latex()
						//  to either set or get latex
					}
				}
			});
			mqEditor.latex(latex);

			var tid = w.tiddler,
			    startPos = w.matchStart,
			    openWrapper = this.openWrapper,
			    closeWrapper = this.closeWrapper;
			jQuery(e).keydown(function(e){
				if(e.which == 13) // on press enter, apply changes
					changeWikiText(tid,startPos,latex.length, openWrapper,closeWrapper,mqEditor.latex())

			});
		}catch(e){ console.log("MathQuill formatter: "+e.message); } }

		w.nextMatch = endRegExp.lastIndex;
	}
};

var mainMathFormatters = [
	{
		name: "displayMath1",
		openWrapper: "$$", // used for editing, would be nice to generate from it:
					// closeWrapper, match, terminator and termRegExp
		closeWrapper: "$$",
		match: "\\\$\\\$",
		terminator: "\\\$\\\$\\n?",
		termRegExp: "\\\$\\\$\\n?",
		element: (math_lib == libs.MathJax ? "script" : "div"),
		inline: false,
		keepdelim: (math_lib == libs.jqMath),
		handler: config.formatterHelpers.mathFormatHelper
	},{
		name: "inlineMath1",
		openWrapper: "$", // used for editing, would be nice to generate from it:
					// closeWrapper, match, terminator and termRegExp
		closeWrapper: "$",
		match: "\\\$", 
		terminator: "\\\$",
		termRegExp: "\\\$",
		element: (math_lib == libs.MathJax ? "script" : "span"),
		inline: true,
		keepdelim: (math_lib == libs.jqMath),
		handler: config.formatterHelpers.mathFormatHelper
	}
];

var backslashFormatters = [
	{
		name: "inlineMath2",
		openWrapper: "\\(",  // used for editing, would be nice to generate from
				     // it: match
		closeWrapper: "\\)", // ~ : terminator and termRegExp
		match: "\\\\\\\(",
		terminator: "\\\\\\\)",
		termRegExp: "\\\\\\\)",
		element: (math_lib == libs.MathJax ? "script" : "span"),
		inline: true,
		keepdelim: (math_lib == libs.jqMath),
		handler: config.formatterHelpers.mathFormatHelper
	},{
		name: "displayMath2",
		openWrapper: "\\[",  // used for editing, would be nice to generate from
				     // it: match
		closeWrapper: "\\]", // ~ : terminator and termRegExp
		match: "\\\\\\\[",
		terminator: "\\\\\\\]\\n?",
		termRegExp: "\\\\\\\]\\n?",
		element: (math_lib == libs.MathJax ? "script" : "div"),
		inline: false,
		keepdelim: (math_lib == libs.jqMath),
		handler: config.formatterHelpers.mathFormatHelper
	},{
		name: "displayMath3",
		openWrapper: "\\begin{equation}", // used for editing, would be nice to
						  // generate from it: match
		closeWrapper: "\\end{equation}",  // ~ : terminator and termRegExp
		match: "\\\\begin\\{equation\\}",
		terminator: "\\\\end\\{equation\\}\\n?",
		termRegExp: "\\\\end\\{equation\\}\\n?",
		element: (math_lib == libs.MathJax ? "script" : "div"),
		inline: false,
		keepdelim: (math_lib == libs.jqMath),
		handler: config.formatterHelpers.mathFormatHelper
	},{
		// These can be nested.  e.g. \begin{equation} \begin{array}{ccc} \begin{array}{ccc} ...

		name: "displayMath4",
		openWrapper: "\\begin{eqnarray}", // used for editing, would be nice to
						  // generate from it: match
		closeWrapper: "\\end{eqnarray}",  // ~ : terminator and termRegExp
		match: "\\\\begin\\{eqnarray\\}",
		terminator: "\\\\end\\{eqnarray\\}\\n?",
		termRegExp: "\\\\end\\{eqnarray\\}\\n?",
		element: (math_lib == libs.MathJax ? "script" : "div"),
		inline: false,
		keepdelim: true,
		handler: config.formatterHelpers.mathFormatHelper
	},{
		// The escape must come between backslash formatters and regular ones.
		// So any latex-like \commands must be added to the beginning of
		// backslashformatters here.

		name: "escape",
		match: "\\\\.",
		handler: function(w) {
			var escapedSymbol = w.source.substr(w.matchStart+1,1);
			w.output.appendChild(document.createTextNode(escapedSymbol));
			w.nextMatch = w.matchStart+2;
		}
	}
];

config.formatters = config.formatters.concat(mainMathFormatters,backslashFormatters);

if(math_lib == libs.MathJax) {
	old_wikify = wikify;
	wikify = function(source,output,highlightRegExp,tiddler) {
		old_wikify.apply(this,arguments);
		if(window.MathJax)
			MathJax.Hub.Queue(["Typeset",MathJax.Hub,output]);
	};
}

// =================================================================
//			     Add the stylings
// =================================================================

if(math_lib == libs.jqMath)
	setStylesheet(store.getTiddlerText("JQMath.css"),"jqMathStyles");
if(math_lib == libs.MathQuill)
	setStylesheet(mathQuillCssExtras,"mathQuillCssExtras");
})();
//}}}
$$
\int_{-\infty}^\infty
    f(\xi) e^{2 \pi i \xi x}
    d\xi
$$