Welcome to TiddlyWiki created by Jeremy Ruston; Copyright © 2004-2007 Jeremy Ruston, Copyright © 2007-2011 UnaMesa Association
/***
|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 —
***/
//{{{
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 = "–";
};
};
//# put this after "rule" and before "characterFormat" formatters
/*config.formatters.splice(0,0,{
name: "mdash",
match: "---",
handler: function(w)
{
createTiddlyElement(w.output,"span").innerHTML = "—";
}
});*/
//}}}
/***
|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}$
/***
|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
$$