Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
12263 lines (11209 sloc) 489 KB
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "">
<html xmlns="" xml:lang="en" lang="en">
<script id="versionArea" type="text/javascript">
var version = {title: "TiddlyWiki", major: 2, minor: 6, revision: 5, date: new Date("October 6, 2011"), extensions: {}};
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta name="copyright" content="
TiddlyWiki created by Jeremy Ruston, (jeremy [at] osmosoft [dot] com)
Copyright (c) Jeremy Ruston 2004-2007
Copyright (c) UnaMesa Association 2007-2011
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
Neither the name of the UnaMesa Association nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.
" />
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' />
<title> bootStrap - info on github </title>
<style id="styleArea" type="text/css">
#saveTest {display:none;}
#messageArea {display:none;}
#copyright {display:none;}
#storeArea {display:none;}
#storeArea div {padding:0.5em; margin:1em 0em 0em 0em; border-color:#fff #666 #444 #ddd; border-style:solid; border-width:2px; overflow:auto;}
#shadowArea {display:none;}
#javascriptWarning {width:100%; text-align:center; font-weight:bold; background-color:#dd1100; color:#fff; padding:1em 0em;}
<body onload="main();" onunload="if(window.unload) unload();">
<div id="copyright">
Welcome to TiddlyWiki created by Jeremy Ruston; Copyright &copy; 2004-2007 Jeremy Ruston, Copyright &copy; 2007-2011 UnaMesa Association
<div id="javascriptWarning">
This page requires JavaScript to function properly.<br /><br />If you are using Microsoft Internet Explorer you may need to click on the yellow bar above and select 'Allow Blocked Content'. You must then click 'Yes' on the following security warning.
<div id="saveTest"></div>
<div id="backstageCloak"></div>
<div id="backstageButton"></div>
<div id="backstageArea"><div id="backstageToolbar"></div></div>
<div id="backstage">
<div id="backstagePanel"></div>
<div id="contentWrapper"></div>
<div id="contentStash"></div>
<div id="shadowArea">
<div title="MarkupPreHead">
&lt;link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' /&gt;
<div title="ColorPalette">
<pre>Background: #fff
Foreground: #000
PrimaryPale: #8cf
PrimaryLight: #18f
PrimaryMid: #04b
PrimaryDark: #014
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
<div title="StyleSheetColors">
body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
a {color:[[ColorPalette::PrimaryMid]];}
a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
a img {border:0;}
h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}
.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}
.header {background:[[ColorPalette::PrimaryMid]];}
.headerShadow {color:[[ColorPalette::Foreground]];}
.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
.headerForeground {color:[[ColorPalette::Background]];}
.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}
.tabSelected {color:[[ColorPalette::PrimaryDark]];
border-left:1px solid [[ColorPalette::TertiaryLight]];
border-top:1px solid [[ColorPalette::TertiaryLight]];
border-right:1px solid [[ColorPalette::TertiaryLight]];
.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
.tabContents .button {border:0;}
#sidebar {}
#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}
.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
border:1px solid [[ColorPalette::PrimaryMid]];}
.wizardStep.wizardStepDone {background:[[ColorPalette::TertiaryLight]];}
.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}
.wizard .notChanged {background:transparent;}
.wizard .changedLocally {background:#80ff80;}
.wizard .changedServer {background:#8080ff;}
.wizard .changedBoth {background:#ff8080;}
.wizard .notFound {background:#ffff80;}
.wizard .putToServer {background:#ff80ff;}
.wizard .gotFromServer {background:#80ffff;}
#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}
.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}
.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}
.tiddler .defaultCommand {font-weight:bold;}
.shadow .title {color:[[ColorPalette::TertiaryDark]];}
.title {color:[[ColorPalette::SecondaryDark]];}
.subtitle {color:[[ColorPalette::TertiaryDark]];}
.toolbar {color:[[ColorPalette::PrimaryMid]];}
.toolbar a {color:[[ColorPalette::TertiaryLight]];}
.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}
.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
.tagging .button, .tagged .button {border:none;}
.footer {color:[[ColorPalette::TertiaryLight]];}
.selected .footer {color:[[ColorPalette::TertiaryMid]];}
.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
.lowlight {background:[[ColorPalette::TertiaryLight]];}
.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}
.imageLink, #displayArea .imageLink {background:transparent;}
.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}
.viewer .listTitle {list-style-type:none; margin-left:-2em;}
.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}
.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}
.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
.viewer code {color:[[ColorPalette::SecondaryDark]];}
.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}
.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}
.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
.editorFooter {color:[[ColorPalette::TertiaryMid]];}
.readOnly {background:[[ColorPalette::TertiaryPale]];}
#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:alpha(opacity=60);}
<div title="StyleSheetLayout">
* html .tiddler {height:1%;}
body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}
h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
h4,h5,h6 {margin-top:1em;}
h1 {font-size:1.35em;}
h2 {font-size:1.25em;}
h3 {font-size:1.1em;}
h4 {font-size:1em;}
h5 {font-size:.9em;}
hr {height:1px;}
a {text-decoration:none;}
dt {font-weight:bold;}
ol {list-style-type:decimal;}
ol ol {list-style-type:lower-alpha;}
ol ol ol {list-style-type:lower-roman;}
ol ol ol ol {list-style-type:decimal;}
ol ol ol ol ol {list-style-type:lower-alpha;}
ol ol ol ol ol ol {list-style-type:lower-roman;}
ol ol ol ol ol ol ol {list-style-type:decimal;}
.txtOptionInput {width:11em;}
#contentWrapper .chkOptionInput {border:0;}
.externalLink {text-decoration:underline;}
.indent {margin-left:3em;}
.outdent {margin-left:3em; text-indent:-3em;}
code.escaped {white-space:nowrap;}
.tiddlyLinkExisting {font-weight:bold;}
.tiddlyLinkNonExisting {font-style:italic;}
/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
a.tiddlyLinkNonExisting.shadow {font-weight:bold;}
#mainMenu .tiddlyLinkExisting,
#mainMenu .tiddlyLinkNonExisting,
#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}
.header {position:relative;}
.header a:hover {background:transparent;}
.headerShadow {position:relative; padding:4.5em 0 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:4.5em 0 1em 1em; left:0; top:0;}
.siteTitle {font-size:3em;}
.siteSubtitle {font-size:1.2em;}
#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}
#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#sidebarOptions {padding-top:0.3em;}
#sidebarOptions a {margin:0 0.2em; padding:0.2em 0.3em; display:block;}
#sidebarOptions input {margin:0.4em 0.5em;}
#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#sidebarOptions .sliderPanel input {margin:0 0 0.3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}
.wizard {padding:0.1em 1em 0 2em;}
.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0; margin:0.4em 0 0.2em;}
.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0; margin:0.4em 0 0.2em;}
.wizardStep {padding:1em 1em 1em 1em;}
.wizard .button {margin:0.5em 0 0; font-size:1.2em;}
.wizardFooter {padding:0.8em 0.4em 0.8em 0;}
.wizardFooter .status {padding:0 0.4em; margin-left:1em;}
.wizard .button {padding:0.1em 0.2em;}
#messageArea {position:fixed; top:2em; right:0; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
.messageToolbar {display:block; text-align:right; padding:0.2em;}
#messageArea a {text-decoration:underline;}
.tiddlerPopupButton {padding:0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em; margin:0;}
.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
.popup .popupMessage {padding:0.4em;}
.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0;}
.popup li.disabled {padding:0.4em;}
.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
.listBreak {font-size:1px; line-height:1px;}
.listBreak div {margin:2px 0;}
.tabset {padding:1em 0 0 0.5em;}
.tab {margin:0 0 0 0.25em; padding:2px;}
.tabContents {padding:0.5em;}
.tabContents ul, .tabContents ol {margin:0; padding:0;}
.txtMainTab .tabContents li {list-style:none;}
.tabContents li.listLink { margin-left:.75em;}
#contentWrapper {display:block;}
#splashScreen {display:none;}
#displayArea {margin:1em 17em 0 14em;}
.toolbar {text-align:right; font-size:.9em;}
.tiddler {padding:1em 1em 0;}
.missing .viewer,.missing .title {font-style:italic;}
.title {font-size:1.6em; font-weight:bold;}
.missing .subtitle {display:none;}
.subtitle {font-size:1.1em;}
.tiddler .button {padding:0.2em 0.4em;}
.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
.isTag .tagging {display:block;}
.tagged {margin:0.5em; float:right;}
.tagging, .tagged {font-size:0.9em; padding:0.25em;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
.tagClear {clear:both;}
.footer {font-size:.9em;}
.footer li {display:inline;}
.annotation {padding:0.5em; margin:0.5em;}
* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer {line-height:1.4em; padding-top:0.5em;}
.viewer .button {margin:0 0.25em; padding:0 0.25em;}
.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}
.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
table.listView {font-size:0.85em; margin:0.8em 1.0em;}
table.listView th, table.listView td, table.listView tr {padding:0 3px 0 3px;}
.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
.viewer code {font-size:1.2em; line-height:1.4em;}
.editor {font-size:1.1em;}
.editor input, .editor textarea {display:block; width:100%; font:inherit;}
.editorFooter {padding:0.25em 0; font-size:.9em;}
.editorFooter .button {padding-top:0; padding-bottom:0;}
.fieldsetFix {border:0; padding:0; margin:1px 0px;}
.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
.zoomer div {padding:1em;}
* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0; right:0;}
#backstageButton a {padding:0.1em 0.4em; margin:0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel {display:none; z-index:100; position:absolute; width:90%; margin-left:3em; padding:1em;}
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding:0.2em 0.4em;}
#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}
.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
<div title="StyleSheetLocale">
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which need larger font sizes.
body {font-size:0.8em;}
#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}
.subtitle {font-size:0.8em;}
.viewer table.listView {font-size:0.95em;}
<div title="StyleSheetPrint">
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none !important;}
#displayArea {margin: 1em 1em 0em;}
noscript {display:none;} /* Fixes a feature in Firefox where print preview displays the noscript content */
<div title="PageTemplate">
&lt;div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'&gt;
&lt;div class='headerShadow'&gt;
&lt;span class='siteTitle' refresh='content' tiddler='SiteTitle'&gt;&lt;/span&gt;&amp;nbsp;
&lt;span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'&gt;&lt;/span&gt;
&lt;div class='headerForeground'&gt;
&lt;span class='siteTitle' refresh='content' tiddler='SiteTitle'&gt;&lt;/span&gt;&amp;nbsp;
&lt;span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'&gt;&lt;/span&gt;
&lt;div id='mainMenu' refresh='content' tiddler='MainMenu'&gt;&lt;/div&gt;
&lt;div id='sidebar'&gt;
&lt;div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'&gt;&lt;/div&gt;
&lt;div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'&gt;&lt;/div&gt;
&lt;div id='displayArea'&gt;
&lt;div id='messageArea'&gt;&lt;/div&gt;
&lt;div id='tiddlerDisplay'&gt;&lt;/div&gt;
<div title="ViewTemplate">
&lt;div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'&gt;&lt;/div&gt;
&lt;div class='title' macro='view title'&gt;&lt;/div&gt;
&lt;div class='subtitle'&gt;&lt;span macro='view modifier link'&gt;&lt;/span&gt;, &lt;span macro='view modified date'&gt;&lt;/span&gt; (&lt;span macro='message views.wikified.createdPrompt'&gt;&lt;/span&gt; &lt;span macro='view created date'&gt;&lt;/span&gt;)&lt;/div&gt;
&lt;div class='tagging' macro='tagging'&gt;&lt;/div&gt;
&lt;div class='tagged' macro='tags'&gt;&lt;/div&gt;
&lt;div class='viewer' macro='view text wikified'&gt;&lt;/div&gt;
&lt;div class='tagClear'&gt;&lt;/div&gt;
<div title="EditTemplate">
&lt;div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'&gt;&lt;/div&gt;
&lt;div class='title' macro='view title'&gt;&lt;/div&gt;
&lt;div class='editor' macro='edit title'&gt;&lt;/div&gt;
&lt;div macro='annotations'&gt;&lt;/div&gt;
&lt;div class='editor' macro='edit text'&gt;&lt;/div&gt;
&lt;div class='editor' macro='edit tags'&gt;&lt;/div&gt;&lt;div class='editorFooter'&gt;&lt;span macro='message views.editor.tagPrompt'&gt;&lt;/span&gt;&lt;span macro='tagChooser excludeLists'&gt;&lt;/span&gt;&lt;/div&gt;
<div title="GettingStarted">
<pre>To get started with this blank [[TiddlyWiki]], you'll need to modify the following tiddlers:
* [[SiteTitle]] &amp; [[SiteSubtitle]]: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* [[MainMenu]]: The menu (usually on the left)
* [[DefaultTiddlers]]: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: &lt;&lt;option txtUserName&gt;&gt;</pre>
<div title="OptionsPanel">
<pre>These [[InterfaceOptions]] for customising [[TiddlyWiki]] are saved in your browser
Your username for signing your edits. Write it as a [[WikiWord]] (eg [[JoeBloggs]])
&lt;&lt;option txtUserName&gt;&gt;
&lt;&lt;option chkSaveBackups&gt;&gt; [[SaveBackups]]
&lt;&lt;option chkAutoSave&gt;&gt; [[AutoSave]]
&lt;&lt;option chkRegExpSearch&gt;&gt; [[RegExpSearch]]
&lt;&lt;option chkCaseSensitiveSearch&gt;&gt; [[CaseSensitiveSearch]]
&lt;&lt;option chkAnimate&gt;&gt; [[EnableAnimations]]
Also see [[AdvancedOptions]]</pre>
<div title="ImportTiddlers">
<div id="storeArea">
<div title="C" creator="bootStrap" modifier="bootStrap" created="201111281519" tags="boot Decrypt(C)" changecount="1">
<div title="DefaultTiddlers" creator="bootStrap" modifier="bootStrap" created="201111281549" changecount="1">
<div title="LoadRemoteFileThroughProxy" creator="bootStrap" modifier="bootStrap" created="201112120659" tags="systemConfig" changecount="1">
|''Name:''|LoadRemoteFileThroughProxy (previous LoadRemoteFileHijack)|
|''Description:''|When the TiddlyWiki file is located on the web (view over http) the content of [[SiteProxy]] tiddler is added in front of the file url. If [[SiteProxy]] does not exist &quot;/proxy/&quot; is added. |
|''Date:''|mar 17, 2007|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license| ]]|
version.extensions.LoadRemoteFileThroughProxy = {
major: 1, minor: 1, revision: 0,
date: new Date(&quot;mar 17, 2007&quot;),
source: &quot;;};
if (!window.bidix) window.bidix = {}; // bidix namespace
if (!bidix.core) bidix.core = {};
bidix.core.loadRemoteFile = loadRemoteFile;
loadRemoteFile = function(url,callback,params)
if ((document.location.toString().substr(0,4) == &quot;http&quot;) &amp;&amp; (url.substr(0,4) == &quot;http&quot;)){
url = store.getTiddlerText(&quot;SiteProxy&quot;, &quot;/proxy/&quot;) + url;
return bidix.core.loadRemoteFile(url,callback,params);
<div title="MainMenu" creator="bootStrap" modifier="bootStrap" created="201111281504" modified="201111281514" tags="boot" changecount="10">
@@margin-left:.5em;&lt;&lt;slider chkContents bootContents &quot;/boot »&quot; &quot;Show boot&quot;&gt;&gt;@@
<div title="Note" creator="bootStrap" modifier="bootStrap" created="201111281547" tags="Decrypt(N)" changecount="1">
<div title="O" creator="bootStrap" modifier="bootStrap" created="201111281505" modified="201111290442" tags="Decrypt(O)" changecount="4">
<div title="P" creator="bootStrap" modifier="bootStrap" created="201111281505" modified="201112022140" tags="Decrypt(P)" changecount="4">
<div title="PartTiddlerPlugin" creator="bootStrap" modifier="bootStrap" created="201112022140" tags="systemConfig" changecount="1">
|&lt;html&gt;&lt;a name=&quot;Top&quot;/&gt;&lt;/html&gt;''Name:''|PartTiddlerPlugin|
|''Version:''|1.0.9 (2007-07-14)|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license]]|
|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
!Table of Content&lt;html&gt;&lt;a name=&quot;TOC&quot;/&gt;&lt;/html&gt;
* &lt;html&gt;&lt;a href=&quot;javascript:;&quot; onclick=&quot;window.scrollAnchorVisible('Description',null, event)&quot;&gt;Description, Syntax&lt;/a&gt;&lt;/html&gt;
* &lt;html&gt;&lt;a href=&quot;javascript:;&quot; onclick=&quot;window.scrollAnchorVisible('Applications',null, event)&quot;&gt;Applications&lt;/a&gt;&lt;/html&gt;
** &lt;html&gt;&lt;a href=&quot;javascript:;&quot; onclick=&quot;window.scrollAnchorVisible('LongTiddler',null, event)&quot;&gt;Refering to Paragraphs of a Longer Tiddler&lt;/a&gt;&lt;/html&gt;
** &lt;html&gt;&lt;a href=&quot;javascript:;&quot; onclick=&quot;window.scrollAnchorVisible('Citation',null, event)&quot;&gt;Citation Index&lt;/a&gt;&lt;/html&gt;
** &lt;html&gt;&lt;a href=&quot;javascript:;&quot; onclick=&quot;window.scrollAnchorVisible('TableCells',null, event)&quot;&gt;Creating &quot;multi-line&quot; Table Cells&lt;/a&gt;&lt;/html&gt;
** &lt;html&gt;&lt;a href=&quot;javascript:;&quot; onclick=&quot;window.scrollAnchorVisible('Tabs',null, event)&quot;&gt;Creating Tabs&lt;/a&gt;&lt;/html&gt;
** &lt;html&gt;&lt;a href=&quot;javascript:;&quot; onclick=&quot;window.scrollAnchorVisible('Sliders',null, event)&quot;&gt;Using Sliders&lt;/a&gt;&lt;/html&gt;
* &lt;html&gt;&lt;a href=&quot;javascript:;&quot; onclick=&quot;window.scrollAnchorVisible('Revisions',null, event)&quot;&gt;Revision History&lt;/a&gt;&lt;/html&gt;
* &lt;html&gt;&lt;a href=&quot;javascript:;&quot; onclick=&quot;window.scrollAnchorVisible('Code',null, event)&quot;&gt;Code&lt;/a&gt;&lt;/html&gt;
!Description&lt;html&gt;&lt;a name=&quot;Description&quot;/&gt;&lt;/html&gt;
With the {{{&lt;part aPartName&gt; ... &lt;/part&gt;}}} feature you can structure your tiddler text into separate (named) parts.
Each part can be referenced as a &quot;normal&quot; tiddler, using the &quot;//tiddlerName//''/''//partName//&quot; syntax (e.g. &quot;About/Features&quot;). E.g. you may create links to the parts (e.g. {{{[[Quotes/BAX95]]}}} or {{{[[Hobbies|AboutMe/Hobbies]]}}}), use it in {{{&lt;&lt;tiddler...&gt;&gt;}}} or {{{&lt;&lt;tabs...&gt;&gt;}}} macros etc.
|&gt;|''&lt;part'' //partName// [''hidden''] ''&gt;'' //any tiddler content// ''&lt;/part&gt;''|
|//partName//|The name of the part. You may reference a part tiddler with the combined tiddler name &quot;//nameOfContainerTidder//''/''//partName//. &lt;&lt;br&gt;&gt;If you use a partName containing spaces you need to quote it (e.g. {{{&quot;Major Overview&quot;}}} or {{{[[Shortcut List]]}}}).|
|''hidden''|When defined the content of the part is not displayed in the container tiddler. But when the part is explicitly referenced (e.g. in a {{{&lt;&lt;tiddler...&gt;&gt;}}} macro or in a link) the part's content is displayed.|
|&lt;html&gt;&lt;i&gt;any&amp;nbsp;tiddler&amp;nbsp;content&lt;/i&gt;&lt;/html&gt;|&lt;html&gt;The content of the part.&lt;br&gt;A part can have any content that a &quot;normal&quot; tiddler may have, e.g. you may use all the formattings and macros defined.&lt;/html&gt;|
|&gt;|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
&lt;html&gt;&lt;sub&gt;&lt;a href=&quot;javascript:;&quot; onclick=&quot;window.scrollAnchorVisible('Top',null, event)&quot;&gt;[Top]&lt;/sub&gt;&lt;/a&gt;&lt;/html&gt;
!Applications&lt;html&gt;&lt;a name=&quot;Applications&quot;/&gt;&lt;/html&gt;
!!Refering to Paragraphs of a Longer Tiddler&lt;html&gt;&lt;a name=&quot;LongTiddler&quot;/&gt;&lt;/html&gt;
Assume you have written a long description in a tiddler and now you want to refer to the content of a certain paragraph in that tiddler (e.g. some definition.) Just wrap the text with a ''part'' block, give it a nice name, create a &quot;pretty link&quot; (like {{{[[Discussion Groups|Introduction/DiscussionGroups]]}}}) and you are done.
Notice this complements the approach to first writing a lot of small tiddlers and combine these tiddlers to one larger tiddler in a second step (e.g. using the {{{&lt;&lt;tiddler...&gt;&gt;}}} macro). Using the ''part'' feature you can first write a &quot;classic&quot; (longer) text that can be read &quot;from top to bottom&quot; and later &quot;reuse&quot; parts of this text for some more &quot;non-linear&quot; reading.
&lt;html&gt;&lt;sub&gt;&lt;a href=&quot;javascript:;&quot; onclick=&quot;window.scrollAnchorVisible('Top',null, event)&quot;&gt;[Top]&lt;/sub&gt;&lt;/a&gt;&lt;/html&gt;
!!Citation Index&lt;html&gt;&lt;a name=&quot;Citation&quot;/&gt;&lt;/html&gt;
Create a tiddler &quot;Citations&quot; that contains your &quot;citations&quot;.
Wrap every citation with a part and a proper name.
&lt;part BAX98&gt;Baxter, Ira D. et al: //Clone Detection Using Abstract Syntax Trees.//
in //Proc. ICSM//, 1998.&lt;/part&gt;
&lt;part BEL02&gt;Bellon, Stefan: //Vergleich von Techniken zur Erkennung duplizierten Quellcodes.//
Thesis, Uni Stuttgart, 2002.&lt;/part&gt;
&lt;part DUC99&gt;Ducasse, Stéfane et al: //A Language Independent Approach for Detecting Duplicated Code.//
in //Proc. ICSM//, 1999.&lt;/part&gt;
You may now &quot;cite&quot; them just by using a pretty link like {{{[[Citations/BAX98]]}}} or even more pretty, like this {{{[[BAX98|Citations/BAX98]]}}}.
&lt;html&gt;&lt;sub&gt;&lt;a href=&quot;javascript:;&quot; onclick=&quot;window.scrollAnchorVisible('Top',null, event)&quot;&gt;[Top]&lt;/sub&gt;&lt;/a&gt;&lt;/html&gt;
!!Creating &quot;multi-line&quot; Table Cells&lt;html&gt;&lt;a name=&quot;TableCells&quot;/&gt;&lt;/html&gt;
You may have noticed that it is hard to create table cells with &quot;multi-line&quot; content. E.g. if you want to create a bullet list inside a table cell you cannot just write the bullet list
* Item 1
* Item 2
* Item 3
into a table cell (i.e. between the | ... | bars) because every bullet item must start in a new line but all cells of a table row must be in one line.
Using the ''part'' feature this problem can be solved. Just create a hidden part that contains the cells content and use a {{{&lt;&lt;tiddler &gt;&gt;}}} macro to include its content in the table's cell.
|subject1|&lt;&lt;tiddler ./Cell1&gt;&gt;|
|subject2|&lt;&lt;tiddler ./Cell2&gt;&gt;|
&lt;part Cell1 hidden&gt;
* Item 1
* Item 2
* Item 3
Notice that inside the {{{&lt;&lt;tiddler ...&gt;&gt;}}} macro you may refer to the &quot;current tiddler&quot; using the &quot;.&quot;.
BTW: The same approach can be used to create bullet lists with items that contain more than one line.
&lt;html&gt;&lt;sub&gt;&lt;a href=&quot;javascript:;&quot; onclick=&quot;window.scrollAnchorVisible('Top',null, event)&quot;&gt;[Top]&lt;/sub&gt;&lt;/a&gt;&lt;/html&gt;
!!Creating Tabs&lt;html&gt;&lt;a name=&quot;Tabs&quot;/&gt;&lt;/html&gt;
The build-in {{{&lt;&lt;tabs ...&gt;&gt;}}} macro requires that you defined an additional tiddler for every tab it displays. When you want to have &quot;nested&quot; tabs you need to define a tiddler for the &quot;main tab&quot; and one for every tab it contains. I.e. the definition of a set of tabs that is visually displayed at one place is distributed across multiple tiddlers.
With the ''part'' feature you can put the complete definition in one tiddler, making it easier to keep an overview and maintain the tab sets.
The standard tabs at the sidebar are defined by the following eight tiddlers:
* SideBarTabs
* TabAll
* TabMore
* TabMoreMissing
* TabMoreOrphans
* TabMoreShadowed
* TabTags
* TabTimeline
Instead of these eight tiddlers one could define the following SideBarTabs tiddler that uses the ''part'' feature:
&lt;&lt;tabs txtMainTab
Timeline Timeline SideBarTabs/Timeline
All 'All tiddlers' SideBarTabs/All
Tags 'All tags' SideBarTabs/Tags
More 'More lists' SideBarTabs/More&gt;&gt;
&lt;part Timeline hidden&gt;&lt;&lt;timeline&gt;&gt;&lt;/part&gt;
&lt;part All hidden&gt;&lt;&lt;list all&gt;&gt;&lt;/part&gt;
&lt;part Tags hidden&gt;&lt;&lt;allTags&gt;&gt;&lt;/part&gt;
&lt;part More hidden&gt;&lt;&lt;tabs txtMoreTab
Missing 'Missing tiddlers' SideBarTabs/Missing
Orphans 'Orphaned tiddlers' SideBarTabs/Orphans
Shadowed 'Shadowed tiddlers' SideBarTabs/Shadowed&gt;&gt;&lt;/part&gt;
&lt;part Missing hidden&gt;&lt;&lt;list missing&gt;&gt;&lt;/part&gt;
&lt;part Orphans hidden&gt;&lt;&lt;list orphans&gt;&gt;&lt;/part&gt;
&lt;part Shadowed hidden&gt;&lt;&lt;list shadowed&gt;&gt;&lt;/part&gt;
Notice that you can easily &quot;overwrite&quot; individual parts in separate tiddlers that have the full name of the part.
E.g. if you don't like the classic timeline tab but only want to see the 100 most recent tiddlers you could create a tiddler &quot;~SideBarTabs/Timeline&quot; with the following content:
sortBy 'tiddler.modified' descending
write '(index &lt; 100) ? &quot;* [[&quot;+tiddler.title+&quot;]]\n&quot;:&quot;&quot;'&gt;&gt;
&lt;html&gt;&lt;sub&gt;&lt;a href=&quot;javascript:;&quot; onclick=&quot;window.scrollAnchorVisible('Top',null, event)&quot;&gt;[Top]&lt;/sub&gt;&lt;/a&gt;&lt;/html&gt;
!!Using Sliders&lt;html&gt;&lt;a name=&quot;Sliders&quot;/&gt;&lt;/html&gt;
Very similar to the build-in {{{&lt;&lt;tabs ...&gt;&gt;}}} macro (see above) the {{{&lt;&lt;slider ...&gt;&gt;}}} macro requires that you defined an additional tiddler that holds the content &quot;to be slid&quot;. You can avoid creating this extra tiddler by using the ''part'' feature
In a tiddler &quot;About&quot; we may use the slider to show some details that are documented in the tiddler's &quot;Details&quot; part.
&lt;&lt;slider chkAboutDetails About/Details details &quot;Click here to see more details&quot;&gt;&gt;
&lt;part Details hidden&gt;
To give you a better overview ...
Notice that putting the content of the slider into the slider's tiddler also has an extra benefit: When you decide you need to edit the content of the slider you can just doubleclick the content, the tiddler opens for editing and you can directly start editing the content (in the part section). In the &quot;old&quot; approach you would doubleclick the tiddler, see that the slider is using tiddler X, have to look for the tiddler X and can finally open it for editing. So using the ''part'' approach results in a much short workflow.
&lt;html&gt;&lt;sub&gt;&lt;a href=&quot;javascript:;&quot; onclick=&quot;window.scrollAnchorVisible('Top',null, event)&quot;&gt;[Top]&lt;/sub&gt;&lt;/a&gt;&lt;/html&gt;
!Revision history&lt;html&gt;&lt;a name=&quot;Revisions&quot;/&gt;&lt;/html&gt;
* v1.0.9 (2007-07-14)
** Bugfix: Error when using the SideBarTabs example and switching between &quot;More&quot; and &quot;Shadow&quot;. Thanks to cmari for reporting the issue.
* v1.0.8 (2007-06-16)
** Speeding up display of tiddlers containing multiple pard definitions. Thanks to Paco Rivière for reporting the issue.
** Support &quot;./partName&quot; syntax inside &lt;&lt;tabs ...&gt;&gt; macro
* v1.0.7 (2007-03-07)
** Bugfix: &lt;&lt;tiddler &quot;./partName&quot;&gt;&gt; does not always render correctly after a refresh (e.g. like it happens when using the &quot;Include&quot; plugin). Thanks to Morris Gray for reporting the bug.
* v1.0.6 (2006-11-07)
** Bugfix: cannot edit tiddler when UploadPlugin by Bidix is installed. Thanks to José Luis González Castro for reporting the bug.
* v1.0.5 (2006-03-02)
** Bugfix: Example with multi-line table cells does not work in IE6. Thanks to Paulo Soares for reporting the bug.
* v1.0.4 (2006-02-28)
** Bugfix: Shadow tiddlers cannot be edited (in TW 2.0.6). Thanks to Torsten Vanek for reporting the bug.
* v1.0.3 (2006-02-26)
** Adapt code to newly introduced Tiddler.prototype.isReadOnly() function (in TW 2.0.6). Thanks to Paulo Soares for reporting the problem.
* v1.0.2 (2006-02-05)
** Also allow other macros than the &quot;tiddler&quot; macro use the &quot;.&quot; in the part reference (to refer to &quot;this&quot; tiddler)
* v1.0.1 (2006-01-27)
** Added Table of Content for plugin documentation. Thanks to RichCarrillo for suggesting.
** Bugfix: newReminder plugin does not work when PartTiddler is installed. Thanks to PauloSoares for reporting.
* v1.0.0 (2006-01-25)
** initial version
&lt;html&gt;&lt;sub&gt;&lt;a href=&quot;javascript:;&quot; onclick=&quot;window.scrollAnchorVisible('Top',null, event)&quot;&gt;[Top]&lt;/sub&gt;&lt;/a&gt;&lt;/html&gt;
!Code&lt;html&gt;&lt;a name=&quot;Code&quot;/&gt;&lt;/html&gt;
&lt;html&gt;&lt;sub&gt;&lt;a href=&quot;javascript:;&quot; onclick=&quot;window.scrollAnchorVisible('Top',null, event)&quot;&gt;[Top]&lt;/sub&gt;&lt;/a&gt;&lt;/html&gt;
// PartTiddlerPlugin
// Ensure that the PartTiddler Plugin is only installed once.
if (!version.extensions.PartTiddlerPlugin) {
version.extensions.PartTiddlerPlugin = {
major: 1, minor: 0, revision: 9,
date: new Date(2007, 6, 14),
type: 'plugin',
source: &quot;;
if (!window.abego) window.abego = {};
if (version.major &lt; 2) alertAndThrow(&quot;PartTiddlerPlugin requires TiddlyWiki 2.0 or newer.&quot;);
// Common Helpers
// Looks for the next newline, starting at the index-th char of text.
// If there are only whitespaces between index and the newline
// the index behind the newline is returned,
// otherwise (or when no newline is found) index is returned.
var skipEmptyEndOfLine = function(text, index) {
var re = /(\n|[^\s])/g;
re.lastIndex = index;
var result = re.exec(text);
return (result &amp;&amp; text.charAt(result.index) == '\n')
? result.index+1
: index;
// Constants
var partEndOrStartTagRE = /(&lt;\/part&gt;)|(&lt;part(?:\s+)((?:[^&gt;])+)&gt;)/mg;
var partEndTagREString = &quot;&lt;\\/part&gt;&quot;;
var partEndTagString = &quot;&lt;/part&gt;&quot;;
// Plugin Specific Helpers
// Parse the parameters inside a &lt;part ...&gt; tag and return the result.
// @return [may be null] {partName: ..., isHidden: ...}
var parseStartTagParams = function(paramText) {
var params = paramText.readMacroParams();
if (params.length == 0 || params[0].length == 0) return null;
var name = params[0];
var paramsIndex = 1;
var hidden = false;
if (paramsIndex &lt; params.length) {
hidden = params[paramsIndex] == &quot;hidden&quot;;
return {
partName: name,
isHidden: hidden
// Returns the match to the next (end or start) part tag in the text,
// starting the search at startIndex.
// When no such tag is found null is returned, otherwise a &quot;Match&quot; is returned:
// [0]: full match
// [1]: matched &quot;end&quot; tag (or null when no end tag match)
// [2]: matched &quot;start&quot; tag (or null when no start tag match)
// [3]: content of start tag (or null if no start tag match)
var findNextPartEndOrStartTagMatch = function(text, startIndex) {
var re = new RegExp(partEndOrStartTagRE);
re.lastIndex = startIndex;
var match = re.exec(text);
return match;
// Formatter
// Process the &lt;part ...&gt; ... &lt;/part&gt; starting at (w.source, w.matchStart) for formatting.
// @return true if a complete part section (including the end tag) could be processed, false otherwise.
var handlePartSection = function(w) {
var tagMatch = findNextPartEndOrStartTagMatch(w.source, w.matchStart);
if (!tagMatch) return false;
if (tagMatch.index != w.matchStart || !tagMatch[2]) return false;
// Parse the start tag parameters
var arguments = parseStartTagParams(tagMatch[3]);
if (!arguments) return false;
// Continue processing
var startTagEndIndex = skipEmptyEndOfLine(w.source, tagMatch.index + tagMatch[0].length);
var endMatch = findNextPartEndOrStartTagMatch(w.source, startTagEndIndex);
if (endMatch &amp;&amp; endMatch[1]) {
if (!arguments.isHidden) {
w.nextMatch = startTagEndIndex;
w.nextMatch = skipEmptyEndOfLine(w.source, endMatch.index + endMatch[0].length);
return true;
return false;
config.formatters.push( {
name: &quot;part&quot;,
match: &quot;&lt;part\\s+[^&gt;]+&gt;&quot;,
handler: function(w) {
if (!handlePartSection(w)) {
} )
// Extend &quot;fetchTiddler&quot; functionality to also recognize &quot;part&quot;s of tiddlers
// as tiddlers.
var currentParent = null; // used for the &quot;.&quot; parent (e.g. in the &quot;tiddler&quot; macro)
// Return the match to the first &lt;part ...&gt; tag of the text that has the
// requrest partName.
// @return [may be null]
var findPartStartTagByName = function(text, partName) {
var i = 0;
while (true) {
var tagMatch = findNextPartEndOrStartTagMatch(text, i);
if (!tagMatch) return null;
if (tagMatch[2]) {
// Is start tag
// Check the name
var arguments = parseStartTagParams(tagMatch[3]);
if (arguments &amp;&amp; arguments.partName == partName) {
return tagMatch;
i = tagMatch.index+tagMatch[0].length;
// Return the part &quot;partName&quot; of the given parentTiddler as a &quot;readOnly&quot; Tiddler
// object, using fullName as the Tiddler's title.
// All remaining properties of the new Tiddler (tags etc.) are inherited from
// the parentTiddler.
// @return [may be null]
var getPart = function(parentTiddler, partName, fullName) {
var text = parentTiddler.text;
var startTag = findPartStartTagByName(text, partName);
if (!startTag) return null;
var endIndexOfStartTag = skipEmptyEndOfLine(text, startTag.index+startTag[0].length);
var indexOfEndTag = text.indexOf(partEndTagString, endIndexOfStartTag);
if (indexOfEndTag &gt;= 0) {
var partTiddlerText = text.substring(endIndexOfStartTag,indexOfEndTag);
var partTiddler = new Tiddler();
partTiddler.abegoIsPartTiddler = true;
return partTiddler;
return null;
// Hijack the store.fetchTiddler to recognize the &quot;part&quot; addresses.
var hijackFetchTiddler = function() {
var oldFetchTiddler = store.fetchTiddler ;
store.fetchTiddler = function(title) {
var result = oldFetchTiddler.apply(this, arguments);
if (!result &amp;&amp; title) {
var i = title.lastIndexOf('/');
if (i &gt; 0) {
var parentName = title.substring(0, i);
var partName = title.substring(i+1);
var parent = (parentName == &quot;.&quot;)
? store.resolveTiddler(currentParent)
: oldFetchTiddler.apply(this, [parentName]);
if (parent) {
return getPart(parent, partName, parent.title+&quot;/&quot;+partName);
return result;
// for debugging the plugin is not loaded through the systemConfig mechanism but via a script tag.
// At that point in the &quot;store&quot; is not yet defined. In that case hijackFetchTiddler through the restart function.
// Otherwise hijack now.
if (!store) {
var oldRestartFunc = restart;
window.restart = function() {
} else
// The user must not edit a readOnly/partTiddler
config.commands.editTiddler.oldIsReadOnlyFunction = Tiddler.prototype.isReadOnly;
Tiddler.prototype.isReadOnly = function() {
// Tiddler.isReadOnly was introduced with TW 2.0.6.
// For older version we explicitly check the global readOnly flag
if (config.commands.editTiddler.oldIsReadOnlyFunction) {
if (config.commands.editTiddler.oldIsReadOnlyFunction.apply(this, arguments)) return true;
} else {
if (readOnly) return true;
return this.abegoIsPartTiddler;
config.commands.editTiddler.handler = function(event,src,title)
var t = store.getTiddler(title);
// Edit the tiddler if it either is not a tiddler (but a shadowTiddler)
// or the tiddler is not readOnly
if(!t || !t.abegoIsPartTiddler)
return false;
// To allow the &quot;./partName&quot; syntax in macros we need to hijack
// the invokeMacro to define the &quot;currentParent&quot; while it is running.
var oldInvokeMacro = window.invokeMacro;
function myInvokeMacro(place,macro,params,wikifier,tiddler) {
var oldCurrentParent = currentParent;
if (tiddler) currentParent = tiddler;
try {
oldInvokeMacro.apply(this, arguments);
} finally {
currentParent = oldCurrentParent;
window.invokeMacro = myInvokeMacro;
// To correctly support the &quot;./partName&quot; syntax while refreshing we need to hijack
// the config.refreshers.tiddlers to define the &quot;currentParent&quot; while it is running.
(function() {
var oldTiddlerRefresher= config.refreshers.tiddler;
config.refreshers.tiddler = function(e,changeList) {
var oldCurrentParent = currentParent;
try {
currentParent = e.getAttribute(&quot;tiddler&quot;);
return oldTiddlerRefresher.apply(this,arguments);
} finally {
currentParent = oldCurrentParent;
// Support &quot;./partName&quot; syntax inside &lt;&lt;tabs ...&gt;&gt; macro
(function() {
var extendRelativeNames = function(e, title) {
var nodes = e.getElementsByTagName(&quot;a&quot;);
for(var i=0; i&lt;nodes.length; i++) {
var node = nodes[i];
var s = node.getAttribute(&quot;content&quot;);
if (s &amp;&amp; s.indexOf(&quot;./&quot;) == 0)
var oldHandler = config.macros.tabs.handler;
config.macros.tabs.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var result = oldHandler.apply(this,arguments);
if (tiddler)
extendRelativeNames(place, tiddler.title);
return result;
// Scroll the anchor anchorName in the viewer of the given tiddler visible.
// When no tiddler is defined use the tiddler of the target given event is used.
window.scrollAnchorVisible = function(anchorName, tiddler, evt) {
var tiddlerElem = null;
if (tiddler) {
tiddlerElem = document.getElementById(story.idPrefix + tiddler);
if (!tiddlerElem &amp;&amp; evt) {
var target = resolveTarget(evt);
tiddlerElem = story.findContainingTiddler(target);
if (!tiddlerElem) return;
var children = tiddlerElem.getElementsByTagName(&quot;a&quot;);
for (var i = 0; i &lt; children.length; i++) {
var child = children[i];
var name = child.getAttribute(&quot;name&quot;);
if (name == anchorName) {
var y = findPosY(child);
} // of &quot;install only once&quot;
&lt;html&gt;&lt;sub&gt;&lt;a href=&quot;javascript:;&quot; onclick=&quot;scrollAnchorVisible('Top',null, event)&quot;&gt;[Top]&lt;/sub&gt;&lt;/a&gt;&lt;/html&gt;
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2006 ([[|]])
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
Neither the name of abego Software nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.
&lt;html&gt;&lt;sub&gt;&lt;a href=&quot;javascript:;&quot; onclick=&quot;scrollAnchorVisible('Top',null, event)&quot;&gt;[Top]&lt;/sub&gt;&lt;/a&gt;&lt;/html&gt;
<div title="PasswordOptionsPlugin" creator="bootStrap" modifier="bootStrap" created="201112120700" tags="systemConfig" changecount="1">
|''Description:''|Extends TiddlyWiki options with non encrypted password option.|
|''Date:''|Apr 19, 2007|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license| ]]|
|''~CoreVersion:''|2.2.0 (Beta 5)|
version.extensions.PasswordOptionPlugin = {
major: 1, minor: 0, revision: 2,
date: new Date(&quot;Apr 19, 2007&quot;),
source: '',
author: 'BidiX (BidiX (at) bidix (dot) info',
license: '[[BSD open source license|]]',
coreVersion: '2.2.0 (Beta 5)'
config.macros.option.passwordCheckboxLabel = &quot;Save this password on this computer&quot;;
config.macros.option.passwordInputType = &quot;password&quot;; // password | text
setStylesheet(&quot;.pasOptionInput {width: 11em;}\n&quot;,&quot;passwordInputTypeStyle&quot;);
merge(config.macros.option.types, {
'pas': {
elementType: &quot;input&quot;,
valueField: &quot;value&quot;,
eventName: &quot;onkeyup&quot;,
className: &quot;pasOptionInput&quot;,
typeValue: config.macros.option.passwordInputType,
create: function(place,type,opt,className,desc) {
// password field
// checkbox linked with this password &quot;save this password on this computer&quot;
// text savePasswordCheckboxLabel
onChange: config.macros.option.genericOnChange
merge(config.optionHandlers['chk'], {
get: function(name) {
// is there an option linked with this chk ?
var opt = name.substr(3);
if (config.options[opt])
return config.options[name] ? &quot;true&quot; : &quot;false&quot;;
merge(config.optionHandlers, {
'pas': {
get: function(name) {
if (config.options[&quot;chk&quot;+name]) {
return encodeCookie(config.options[name].toString());
} else {
return &quot;&quot;;
set: function(name,value) {config.options[name] = decodeCookie(value);}
// need to reload options to load passwordOptions
if (!config.options['pasPassword'])
config.options['pasPassword'] = '';
pasPassword: &quot;Test password&quot;
<div title="SiteSubtitle" creator="bootStrap" modifier="bootStrap" created="201111281503" modified="201111281548" changecount="3">
<pre>info on github</pre>
<div title="SiteTitle" creator="bootStrap" modifier="bootStrap" created="201111281503" changecount="1">
<div title="TiddlerEncryptionPlugin" creator="bootstrap" modifier="bootstrap" created="201111281502" tags="systemConfig" changecount="1">
|Author|Lyall Pearce|
|License|[[Creative Commons Attribution-Share Alike 3.0 License|]]|
|Overrides|store.getSaver().externalizeTiddler(), store.getTiddler() and store.getTiddlerText()|
|Description|Encrypt/Decrypt Tiddlers with a Password key|
* Tag a tiddler with Encrypt(prompt)
** Consider the 'prompt' something to help you remember the password with. If multiple tiddlers can be encrypted with the same 'prompt' and you will only be asked for the password once.
* Upon save, the Tiddler will be encrypted and the tag replaced with Decrypt(prompt).
** Failure to encrypt (by not entering a password) will leave the tiddler unencrypted and will leave the Encrypt(prompt) tag in place. This means that the next time you save, you will be asked for the password again.
** To have multiple tiddlers use the same password - simply use the same 'prompt'.
** Tiddlers that are encrypted may be automatically tagged 'excludeSearch' as there is no point in searching encrypted data - this is configurable by an option - you still may want to search the titles of encrypted tiddlers
** Tiddlers that are encrypted may be automatically tagged 'excludeLists', if you have them encrypted, you may also want to keep them 'hidden' - this is configurable by an option.
** Automatic removal of excludeLists and excludeSearch tags is performed, if the above two options are set, only if these two tags are the last 2 tags for a tiddler, if they are positioned somewhere else in the tags list, they will be left in place, meaning that the decrypted tiddler will not be searchable and/or will not appear in lists.
** Encrypted tiddlers are stored as displayable hex, to keep things visibly tidy, should you display an encrypted tiddler. There is nothing worse than seeing a pile of gobbledy gook on your screen. Additionally, the encrypted data is easily cut/paste/emailed if displayed in hex form.
* Tiddlers are decrypted only if you click the decrypt button or the decryptAll button, not when you load the TiddlyWiki
** If you don't display a tiddler, you won't have the option to decrypt it (unless you use the {{{&lt;&lt;EncryptionDecryptAll&gt;&gt;}}} macro)
** Tiddlers will re-encrypt automatically on save.
** Decryption of Tiddlers does not make your TiddlyWiki 'dirty' - you will not be asked to save if you leave the page.
* Errors are reported via diagnostic messages.
** Empty passwords, on save, will result in the tiddler being saved unencrypted - this should only occur with new tiddlers, decrypted tiddlers or with tiddlers who have had their 'prompt' tag changed.
** Encrypted tiddlers know if they are decrypted successfully - failure to decrypt a tiddler will ''not'' lose your data.
** Editing of an encrypted (that has not been unencrypted) tiddler will result in loss of that tiddler as the SHA1 checksums will no longer match, upon decryption. To this end, it is best that you do not check the option. You can, however edit an encrypted tiddler tag list - just do ''not'' change the tiddler contents.
** To change the password on a Tiddler, change the Encrypt('prompt') tag to a new prompt value, after decrypting the tiddler.
** You can edit the tags of an encrypted tiddler, so long as you do not edit the text.
** To change the password for all tiddlers of a particular prompt, use the {{{&lt;&lt;EncryptionChangePassword [&quot;button text&quot; [&quot;tooltip text&quot; [&quot;prompt string&quot; [&quot;accessKey&quot;]]]]&gt;&gt;}}} macro.
** To decrypt all tiddlers of a particular &quot;prompt string&quot;, use the {{{&lt;&lt;EncryptionDecryptAll [&quot;button text&quot; [&quot;tooltip text&quot; [&quot;prompt string&quot; [&quot;accessKey&quot;]]]]&gt;&gt;}}} macro - this will make tiddlers encrypted with &quot;prompt string&quot; searchable - or prompt for all 'prompt strings', if none is supplied.
Useful Buttons:
&lt;&lt;EncryptionChangePassword&gt;&gt; - Change passwords of encrypted tiddlers.
&lt;&lt;EncryptionDecryptAll&gt;&gt; - Decrypt ALL tiddlers - enables searching contents of encrypted tiddlers.
&lt;&lt;option chkExcludeEncryptedFromSearch&gt;&gt; - If set, Encrypted Tiddlers are excluded from searching by tagging with excludeSearch. If Clear, excludeSearch is not added and it is also removed from existing Encrypted Tiddlers only if it is the last Tag. Searching of Encrypted Tiddlers is only meaningful for the Title and Tags.
&lt;&lt;option chkExcludeEncryptedFromLists&gt;&gt; - If set, Encrypted Tiddlers are excluded from lists by tagging with excludeLists. If Clear, excludeLists is not added and it is also removed from existing Encrypted Tiddlers only if it is the last Tag. Preventing encrypted tiddlers from appearing in lists effectively hides them.
&lt;&lt;option chkShowDecryptButtonInContent&gt;&gt; - If set, Encrypted Tiddlers content is replaced by &lt;&lt;EncryptionDecryptThis&gt;&gt; button. This has consequences, in the current version as, if you edit the tiddler without decrypting it, you lose the contents.
!!!!!Revision History
* 3.2.1 - Returned the &lt;&lt;EncryptionDecryptThis&gt;&gt; button as an option.
* 3.2.0 - Ditched the 'Decrypt' button showing up in the tiddler contents if the tiddler is encrypted. It caused too much pain if you edit the tiddler without decrypting it - you lost your data as it was replaced by a Decrypt Macro call! Additionally, a 'decrypt' button will now appear in the toolbar, just before the edit button, if the tiddler is encrypted. This button only appears if using core TiddlyWiki version 2.4 or above.
* 3.1.1 - Obscure bug whereby if an encrypted tiddler was a certain length, it would refuse to decrypt.
* 3.1.0 - When creating a new Encrypt(prompt) tiddler and you have not previously decrypted a tiddler with the same prompt, on save, you will be prompted for the password to encrypt the tiddler. Prior to encrypting, an attempt to decrypt all other tiddlers with the same prompt, is performed. If any tiddler fails to decrypt, the save is aborted - this is so you don't accidentally have 2 (or more!) passwords for the same prompt. Either you enter the correct password, change the prompt string and try re-saving or you cancel (and the tiddler is saved unencrypted).
* 3.0.1 - Allow Enter to be used for password entry, rather than having to press the OK button.
* 3.0.0 - Major revamp internally to support entry of passwords using forms such that passwords are no longer visible on entry. Completely backward compatible with old encrypted tiddlers. No more using the javascript prompt() function.
!!!!!Additional work
version.extensions.TiddlerEncryptionPlugin = {major: 3, minor: 2, revision: 1, date: new Date(2008,10,26)};
// where I cache the passwords - for want of a better place.
config.encryptionPasswords = new Array();
config.encryptionReEnterPasswords = false;
if(config.options.chkExcludeEncryptedFromSearch == undefined) config.options.chkExcludeEncryptedFromSearch = false;
if(config.options.chkExcludeEncryptedFromLists == undefined) config.options.chkExcludeEncryptedFromLists = false;
if(config.options.chkShowDecryptButtonInContent == undefined) config.options.chkShowDecryptButtonInContent = false;
config.macros.EncryptionChangePassword = {};
config.macros.EncryptionChangePassword.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var theButton = createTiddlyButton(place,
(params[0] &amp;&amp; params[0].length &gt; 0) ? params[0] : &quot;Change Passwords&quot;,
(params[1] &amp;&amp; params[1].length &gt; 0) ? params[1] : &quot;Change Passwords&quot; + (params[2] ? &quot; for prompt &quot;+params[2] : &quot;&quot;),
if(params[2] &amp;&amp; params[2].length &gt; 0) {
theButton.setAttribute(&quot;promptString&quot;, params[2]);
config.macros.EncryptionDecryptAll = {};
config.macros.EncryptionDecryptAll.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var theButton = createTiddlyButton(place,
(params[0] &amp;&amp; params[0].length &gt; 0) ? params[0] : &quot;Decrypt All&quot;,
(params[1] &amp;&amp; params[1].length &gt; 0) ? params[1] : &quot;Decrypt All Tiddlers&quot; + ((params[2] &amp;&amp; params[2].length &gt; 0) ? &quot; for prompt &quot;+params[2] : &quot; for a given 'prompt string'&quot;),
if(params[2] &amp;&amp; params[2].length &gt; 0) {
theButton.setAttribute(&quot;promptString&quot;, params[2]);
config.macros.EncryptionDecryptThis = {};
config.macros.EncryptionDecryptThis.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var theButton = createTiddlyButton(place,
(params[0] &amp;&amp; params[0].length &gt; 0) ? params[0] : &quot;Decrypt&quot;,
(params[1] &amp;&amp; params[1].length &gt; 0) ? params[1] : &quot;Decrypt this Tiddler&quot;,
if(params[2] &amp;&amp; params[2].length &gt; 0) {
theButton.setAttribute(&quot;theTiddler&quot;, params[2]);
// toolbar button to decrypt tiddlers.
config.commands.decryptThis = {
text: &quot;decrypt&quot;,
tooltip: &quot;Decrypt this tiddler&quot;,
isEnabled : function(tiddler) {
// Only show decrypt button if tiddler is tagged as Decrypt(
if(tiddler.tags.join().indexOf('Decrypt(') == -1) {
return false;
} else {
return true;
handler: function(event, src, title) {
return false;
// core version 2.4 or above get a 'decrypt' button in the toolbar.
if(config.shadowTiddlers &amp;&amp; config.shadowTiddlers.ToolbarCommands &amp;&amp; config.shadowTiddlers.ToolbarCommands.indexOf('decryptThis') == -1) {
// put our toolbar button in before the edit button.
// won't work if editTiddler is not the default item (prefixed with plus)
config.shadowTiddlers.ToolbarCommands.replace(/\+editTiddler/,'decryptThis +editTiddler');
// Called by the EncryptionChangePassword macro/button
// Also invoked by the callback for password entry
function onClickEncryptionChangePassword(eventObject) {
var promptString;
if(!promptString &amp;&amp; this.getAttribute) {
promptString = this.getAttribute(&quot;promptString&quot;);
// I do call this function directly
if(!promptString &amp;&amp; typeof(eventObject) == &quot;string&quot;) {
promptString = eventObject;
if(!promptString) {
promptString = prompt(&quot;Enter 'prompt string' to change password for:&quot;,&quot;&quot;);
if(!promptString) {
if(! config.encryptionPasswords[promptString]) {
var changePasswordContext = {changePasswordPromptString: promptString,
callbackFunction: MyChangePasswordPromptCallback_TiddlerEncryptionPlugin};
// Callback function will re-invoke this function
// Decrypt ALL tiddlers for that prompt
// Now ditch the cached password, this will force the re-request for the new password, on save.
displayMessage(&quot;Save TiddlyWiki to set new password for '&quot;+promptString+&quot;'&quot;);
config.encryptionPasswords[promptString] = null;
// mark store as dirty so a save will be requrested.
// Called by the password entry form when the user clicks 'OK' button.
function MyChangePasswordPromptCallback_TiddlerEncryptionPlugin(context) {
config.encryptionPasswords[context.passwordPrompt] = context.password;
// Called by the EncryptionDecryptThis macro/button
function onClickEncryptionDecryptThis() {
var theTiddler = this.getAttribute(&quot;theTiddler&quot;);
if(!theTiddler) {
function encryptionGetAndDecryptTiddler(title) {
config.encryptionReEnterPasswords = true;
try {
theTiddler = store.getTiddler(title);
config.encryptionReEnterPasswords = false;
} catch (e) {
if(e == &quot;DecryptionFailed&quot;) {
displayMessage(&quot;Decryption failed&quot;);
} // catch
// called by the EncryptionDecryptAlll macro/button
// Also called by the callback after the user clicks 'OK' button on the password entry form
function onClickEncryptionDecryptAll(eventObject) {
var promptString;
if(!promptString &amp;&amp; this.getAttribute) {
promptString = this.getAttribute(&quot;promptString&quot;);
// I do call this function directly
if(!promptString &amp;&amp; typeof(eventObject) == &quot;string&quot;) {
promptString = eventObject;
if(!promptString) {
promptString = &quot;&quot;;
// Loop through all tiddlers, looking to see if there are any Decrypt(promptString) tagged tiddlers
// If there are, check to see if their password has been cached.
// If not, ask for the first one that is missing, that we find
// the call back function will store that password then invoke this function again,
// which will repeat the whole process. If we find all passwords have been cached
// then we will finally do the decryptAll functionality, which will then
// be able to decrypt all the required tiddlers, without prompting.
// We have to do this whole rigmarole because we are using a 'form' to enter the password
// rather than the 'prompt()' function - which shows the value of the password.
var tagToSearchFor=&quot;Decrypt(&quot;+promptString;
config.encryptionReEnterPasswords = true;
var promptGenerated = false;
store.forEachTiddler(function(store,tiddler) {
// Note, there is no way to stop the forEachTiddler iterations
if(!promptGenerated &amp;&amp; tiddler &amp;&amp; tiddler.tags) {
for(var ix=0; ix&lt;tiddler.tags.length &amp;&amp; !promptGenerated; ix++) {
if(tiddler.tags[ix].indexOf(tagToSearchFor) == 0) {
var tag = tiddler.tags[ix];
var lastBracket=tag.lastIndexOf(&quot;)&quot;);
if(lastBracket &gt;= 0) {
// Ok, tagged with Encrypt(passwordPrompt)
// extract the passwordPrompt name
var passwordPromptString=tag.substring(8,lastBracket);
if(!config.encryptionPasswords[passwordPromptString]) {
// no password cached, prompt and cache it, rather than decryptAll
// callback from prompting form will resume decryptAll attempt.
var decryptAllContext = {decryptAllPromptString: promptString,
callbackFunction: MyDecryptAllPromptCallback_TiddlerEncryptionPlugin};
promptGenerated = true;
} // if(!config.encryptionPasswords
} // if(lastBracket
} // if(tiddler.tags[ix]..
} // for
} // if
}); // store.forEachTiddler
// If we get here, all passwords have been cached.
if(!promptGenerated) {
config.encryptionReEnterPasswords = false;
// Now do the decrypt all functionality
try {
store.forEachTiddler(function(store,tiddler) {
// Note, there is no way to stop the forEachTiddler iterations
if(tiddler &amp;&amp; tiddler.tags) {
for(var ix=0; ix&lt;tiddler.tags.length; ix++) {
if(tiddler.tags[ix].indexOf(tagToSearchFor) == 0) {
try {
} catch (e) {
displayMessage(&quot;Decryption of '&quot;+tiddler.title+&quot;' failed.&quot;);
// throw e;
} // if(tiddler.tags
} // for
} // if
}); // store.forEachTiddler
displayMessage(&quot;All tiddlers&quot; + (promptString != &quot;&quot; ? &quot; for '&quot;+promptString+&quot;'&quot; : &quot;&quot;) + &quot; have been decrypted&quot;);
} catch (e) {
if(e == &quot;DecryptionFailed&quot;) {
} // catch
function MyDecryptAllPromptCallback_TiddlerEncryptionPlugin(context) {
config.encryptionPasswords[context.passwordPrompt] = context.password;
// restart the decryptAll process again after the user has entered a password.
saveChanges_TiddlerEncryptionPlugin = saveChanges;
saveChanges = function(onlyIfDirty,tiddlers) {
// Loop through all tiddlers, looking to see if there are any Encrypt(string) tagged tiddlers
// If there are, check to see if their password has been cached.
// If not, ask for the first one that is missing, that we find
// the call back function will store that password then invoke this function again,
// which will repeat the whole process. If we find all passwords have been cached
// then we will finally call the original saveChanges() function, which will then
// be able to save the tiddlers.
// We have to do this whole rigmarole because we are using a 'form' to enter the password
// rather than the 'prompt()' function - which shows the value of the password.
config.encryptionReEnterPasswords = true;
var promptGenerated = false;
store.forEachTiddler(function(store,tiddler) {
if(!promptGenerated &amp;&amp; tiddler &amp;&amp; tiddler.tags) {
for(var ix=0; ix&lt;tiddler.tags.length &amp;&amp; !promptGenerated; ix++) {
if(tiddler.tags[ix].indexOf(&quot;Encrypt(&quot;) == 0) {
var tag = tiddler.tags[ix];
var lastBracket=tag.lastIndexOf(&quot;)&quot;);
if(lastBracket &gt;= 0) {
// Ok, tagged with Encrypt(passwordPrompt)
// extract the passwordPrompt name
var passwordPrompt=tag.substring(8,lastBracket);
if(!config.encryptionPasswords[passwordPrompt]) {
// no password cached, prompt and cache it, rather than save
var saveContext = {onlyIfDirty: onlyIfDirty,
tiddlers: tiddlers,
callbackFunction: MySavePromptCallback_TiddlerEncryptionPlugin};
promptGenerated = true;
} // if(!config.encryptionPasswords
} // if(lastBracket
} // if(tiddler.tags[ix]..
} // for
} // if
}); // store.forEachTiddler
// If we get here, all passwords have been cached.
if(!promptGenerated) {
config.encryptionReEnterPasswords = false;
function MySavePromptCallback_TiddlerEncryptionPlugin(context) {
config.encryptionPasswords[context.passwordPrompt] = context.password;
// validate the password entered by attempting to decrypt all tiddlers
// with the same encryption prompt string.
// restart the save process again
saveChanges(context.onlyIfDirty, context.tiddlers);
store.getSaver().externalizeTiddler_TiddlerEncryptionPlugin = store.getSaver().externalizeTiddler;
store.getSaver().externalizeTiddler = function(store, tiddler) {
// Ok, got the tiddler, track down the passwordPrompt in the tags.
// track down the Encrypt(passwordPrompt) tag
if(tiddler &amp;&amp; tiddler.tags) {
for(var g=0; g&lt;tiddler.tags.length; g++) {
var tag = tiddler.tags[g];
if(tag.indexOf(&quot;Encrypt(&quot;) == 0) {
var lastBracket=tag.lastIndexOf(&quot;)&quot;);
if(lastBracket &gt;= 0) {
// Ok, tagged with Encrypt(passwordPrompt)
// extract the passwordPrompt name
var passwordPrompt=tag.substring(8,lastBracket);
// Ok, Encrypt this tiddler!
var decryptedSHA1 = Crypto.hexSha1Str(tiddler.text);
var password = GetAndSetPasswordForPrompt_TiddlerEncryptionPlugin(passwordPrompt);
if(password) {
var encryptedText = TEAencrypt(tiddler.text, password);
encryptedText = StringToHext_TiddlerEncryptionPlugin(encryptedText);
tiddler.text = &quot;Encrypted(&quot;+decryptedSHA1+&quot;)\n&quot;+encryptedText;
// Replace the Tag with the Decrypt() tag
// let the store know it's dirty
store.setDirty(tiddler.title, true);
// prevent searches on encrypted tiddlers, still nice to search on title though.
if(config.options.chkExcludeEncryptedFromSearch == true) {
// prevent lists of encrypted tiddlers
if(config.options.chkExcludeEncryptedFromLists == true) {
} else {
// do not encrypt - no password entered
} // if (lastBracket...
} // if(tag.indexOf(...
} // for(var g=0;...
} // if(tiddler.tags...
// Then, finally, do the save by calling the function we override.
return store.getSaver().externalizeTiddler_TiddlerEncryptionPlugin(store, tiddler);
function CheckTiddlerForDecryption_TiddlerEncryptionPlugin(tiddler) {
if(tiddler &amp;&amp; tiddler.tags) {
for(var g=0; g&lt;tiddler.tags.length; g++) {
var tag = tiddler.tags[g];
if(tag.indexOf(&quot;Decrypt(&quot;) == 0) {
var lastBracket=tag.lastIndexOf(&quot;)&quot;);
if(lastBracket &gt;= 0) {
if(tiddler.text.substr(0,10) == &quot;Encrypted(&quot;) {
var closingSHA1Bracket = tiddler.text.indexOf(&quot;)&quot;);
var decryptedSHA1 = tiddler.text.substring(10, closingSHA1Bracket);
// Ok, tagged with Decrypt(passwordPrompt)
// extract the passwordPrompt name
var passwordPrompt=tag.substring(8,lastBracket);
// Ok, Decrypt this tiddler!
var decryptedText = tiddler.text.substr(closingSHA1Bracket+2);
decryptedText = HexToString_TiddlerEncryptionPlugin(decryptedText);
// prompt(&quot;Decryption request for Tiddler '&quot;+tiddler.title+&quot;'&quot;);
var password = GetAndSetPasswordForPromptToDecrypt_TiddlerEncryptionPlugin(passwordPrompt);
if(password) {
decryptedText = TEAdecrypt(decryptedText, password );
var thisDecryptedSHA1 = Crypto.hexSha1Str(decryptedText);
if(decryptedSHA1 == thisDecryptedSHA1) {
tiddler.text = decryptedText;
// Replace the Tag with the Encrypt() tag
if(tiddler.tags[tiddler.tags.length-1] == 'excludeLists') {
// Remove exclude lists only if it's the last entry
// as it's automatically put there by encryption
if(tiddler.tags[tiddler.tags.length-1] == 'excludeSearch') {
// Remove exclude search only if it's the last entry
// as it's automatically put there by encryption
} else {
// Did not decrypt, discard the password from the cache
config.encryptionPasswords[passwordPrompt] = null;
config.encryptionReEnterPasswords = false;
throw &quot;DecryptionFailed&quot;;
} else {
// no password supplied, dont bother trying to decrypt
config.encryptionReEnterPasswords = false;
throw &quot;DecryptionFailed&quot;;
} else {
// Tagged as encrypted but not expected format, just leave it unchanged
break; // out of for loop
} // if (lastBracket...
} // if(tag.indexOf(...
} // for(var g=0;...
} // if (tiddler &amp;&amp; tags)
return tiddler;
store.getTiddler_TiddlerEncryptionPlugin = store.getTiddler;
store.getTiddler = function(title) {
var tiddler = store.getTiddler_TiddlerEncryptionPlugin(title);
if(tiddler) { // shadow tiddlers are not expected to be encrypted.
try {
return CheckTiddlerForDecryption_TiddlerEncryptionPlugin(tiddler);
} catch (e) {
if (config.options.chkShowDecryptButtonInContent == true) {
if(e == &quot;DecryptionFailed&quot;) {
var tiddler = store.getTiddler(&quot;DecryptionFailed&quot;);
if(!tiddler) {
tiddler = new Tiddler();
&quot;&lt;&lt;EncryptionDecryptThis \&quot;Decrypt\&quot; \&quot;Decrypt this tiddler\&quot; \&quot;&quot;+title+&quot;\&quot;&gt;&gt;&quot;,
return tiddler;
} // if(e)
} // catch
} // if(tiddler) {
return null;
store.getTiddlerText_TiddlerEncryptionPlugin = store.getTiddlerText;
store.getTiddlerText = function(title,defaultText) {
// Simply retrieve the tiddler, normally, if it requires decryption, it will be decrypted
var decryptedTiddler = store.getTiddler(title);
if(decryptedTiddler) {
return decryptedTiddler.text;
//Ok, rather than duplicate all the core code, the above code should fail if we reach here
// let the core code take over.
return store.getTiddlerText_TiddlerEncryptionPlugin(title,defaultText);
// Given a prompt, search our cache to see if we have already entered the password.
// Can return null if the user enters nothing.
function MyPrompt_TiddlerEncryptionPlugin(promptString,defaultValue,context) {
if(!context) {
context = {};
context.passwordPrompt = promptString;
PasswordPrompt.prompt(MyPromptCallback_TiddlerEncryptionPlugin, context);
function MyPromptCallback_TiddlerEncryptionPlugin(context) {
if(context.callbackFunction) {
} else {
config.encryptionPasswords[context.passwordPrompt] = context.password;
function GetAndSetPasswordForPrompt_TiddlerEncryptionPlugin(promptString) {
if(!config.encryptionPasswords[promptString]) {
config.encryptionPasswords[promptString] = MyPrompt_TiddlerEncryptionPlugin(promptString, &quot;&quot;);
return config.encryptionPasswords[promptString]; // may be null, prompt can be cancelled.
function GetAndSetPasswordForPromptToDecrypt_TiddlerEncryptionPlugin(promptString) {
if(config.encryptionReEnterPasswords) {
return GetAndSetPasswordForPrompt_TiddlerEncryptionPlugin(promptString);
} else {
return config.encryptionPasswords[promptString];
// Make the encrypted tiddlies look a little more presentable.
function StringToHext_TiddlerEncryptionPlugin(theString) {
var theResult = &quot;&quot;;
for(var i=0; i&lt;theString.length; i++) {
var theHex = theString.charCodeAt(i).toString(16);
if(theHex.length&lt;2) {
theResult += &quot;0&quot;+theHex;
} else {
theResult += theHex;
if(i &amp;&amp; i % 32 == 0)
theResult += &quot;\n&quot;;
return theResult;
function HexToString_TiddlerEncryptionPlugin(theString) {
var theResult = &quot;&quot;;
for(var i=0; i&lt;theString.length; i+=2) {
if(theString.charAt(i) == &quot;\n&quot;) {
i--; // cause us to skip over the newline and resume
theResult += String.fromCharCode(parseInt(theString.substr(i, 2),16));
return theResult;
// Heavily leveraged from Revision 5635
PasswordPrompt ={
prompt : function(callback,context){
if (!context) {
context = {};
var box = createTiddlyElement(document.getElementById(&quot;contentWrapper&quot;),'div','passwordPromptBox');
box.innerHTML = store.getTiddlerText('PasswordPromptTemplate'); = 'absolute';;
document.getElementById('promptDisplayField').value = context.passwordPrompt;
var passwordInputField = document.getElementById('passwordInputField');
passwordInputField.onkeyup = function(ev) {
var e = ev || window.event;
if(e.keyCode == 10 || e.keyCode == 13) { // Enter
PasswordPrompt.submit(callback, context);
document.getElementById('passwordPromptSubmitBtn').onclick = function(){PasswordPrompt.submit(callback,context);};
document.getElementById('passwordPromptCancelBtn').onclick = function(){PasswordPrompt.cancel(callback,context);};
center : function(el){
var size = this.getsize(el); = (Math.round(findWindowWidth()/2) - (size.width /2) + findScrollX())+'px'; = (Math.round(findWindowHeight()/2) - (size.height /2) + findScrollY())+'px';
getsize : function (el){
var x = {};
x.width = el.offsetWidth ||;
x.height = el.offsetHeight ||;
return x;
submit : function(cb,context){
context.passwordPrompt = document.getElementById('promptDisplayField').value;
context.password = document.getElementById('passwordInputField').value;
var box = document.getElementById('passwordPromptBox');
return false;
cancel : function(cb,context){
var box = document.getElementById('passwordPromptBox');
return false;
setStyles : function(){
&quot;#passwordPromptBox dd.submit {margin-left:0; font-weight: bold; margin-top:1em;}\n&quot;+
&quot;#passwordPromptBox dd.submit .button {padding:0.5em 1em; border:1px solid #ccc;}\n&quot;+
&quot;#passwordPromptBox dt.heading {margin-bottom:0.5em; font-size:1.2em;}\n&quot;+
&quot;#passwordPromptBox {border:1px solid #ccc;background-color: #eee;padding:1em 2em;}&quot;,'passwordPromptStyles');
template : '&lt;form action=&quot;&quot; onsubmit=&quot;return false;&quot; id=&quot;passwordPromptForm&quot;&gt;\n'+
' &lt;dl&gt;\n'+
' &lt;dt class=&quot;heading&quot;&gt;Please enter the password:&lt;/dt&gt;\n'+
' &lt;dt&gt;Prompt:&lt;/dt&gt;\n'+
' &lt;dd&gt;&lt;input type=&quot;text&quot; readonly id=&quot;promptDisplayField&quot; class=&quot;display&quot;/&gt;&lt;/dd&gt;\n'+
' &lt;dt&gt;Password:&lt;/dt&gt;\n'+
' &lt;dd&gt;&lt;input type=&quot;password&quot; tabindex=&quot;1&quot; class=&quot;input&quot; id=&quot;passwordInputField&quot;/&gt;&lt;/dd&gt;\n'+
' &lt;dd class=&quot;submit&quot;&gt;\n'+
' &lt;a tabindex=&quot;2&quot; href=&quot;javascript:;&quot; class=&quot;button&quot; id=&quot;passwordPromptSubmitBtn&quot;&gt;OK&lt;/a&gt;\n'+
' &lt;a tabindex=&quot;3&quot; href=&quot;javascript:;&quot; class=&quot;button&quot; id=&quot;passwordPromptCancelBtn&quot;&gt;Cancel&lt;/a&gt;\n'+
' &lt;/dd&gt;\n'+
' &lt;/dl&gt;\n'+
init : function(){
config.shadowTiddlers.PasswordPromptTemplate = this.template;
// TEAencrypt: Use Corrected Block TEA to encrypt plaintext using password
// (note plaintext &amp; password must be strings not string objects)
// Return encrypted text as string
function TEAencrypt(plaintext, password)
if (plaintext.length == 0) return(''); // nothing to encrypt
// 'escape' plaintext so chars outside ISO-8859-1 work in single-byte packing, but keep
// spaces as spaces (not '%20') so encrypted text doesn't grow too long (quick &amp; dirty)
var asciitext = escape(plaintext).replace(/%20/g,' ');
var v = strToLongs(asciitext); // convert string to array of longs
if (v.length &lt;= 1) v[1] = 0; // algorithm doesn't work for n&lt;2 so fudge by adding a null
var k = strToLongs(password.slice(0,16)); // simply convert first 16 chars of password as key
var n = v.length;
var z = v[n-1], y = v[0], delta = 0x9E3779B9;
var mx, e, q = Math.floor(6 + 52/n), sum = 0;
while (q-- &gt; 0) { // 6 + 52/n operations gives between 6 &amp; 32 mixes on each word
sum += delta;
e = sum&gt;&gt;&gt;2 &amp; 3;
for (var p = 0; p &lt; n; p++) {
y = v[(p+1)%n];
mx = (z&gt;&gt;&gt;5 ^ y&lt;&lt;2) + (y&gt;&gt;&gt;3 ^ z&lt;&lt;4) ^ (sum^y) + (k[p&amp;3 ^ e] ^ z);
z = v[p] += mx;
var ciphertext = longsToStr(v);
return escCtrlCh(ciphertext);
// TEAdecrypt: Use Corrected Block TEA to decrypt ciphertext using password
function TEAdecrypt(ciphertext, password)
if (ciphertext.length == 0) return('');
var v = strToLongs(unescCtrlCh(ciphertext));
var k = strToLongs(password.slice(0,16));
var n = v.length;
var z = v[n-1], y = v[0], delta = 0x9E3779B9;
var mx, e, q = Math.floor(6 + 52/n), sum = q*delta;
while (sum != 0) {
e = sum&gt;&gt;&gt;2 &amp; 3;
for (var p = n-1; p &gt;= 0; p--) {
z = v[p&gt;0 ? p-1 : n-1];
mx = (z&gt;&gt;&gt;5 ^ y&lt;&lt;2) + (y&gt;&gt;&gt;3 ^ z&lt;&lt;4) ^ (sum^y) + (k[p&amp;3 ^ e] ^ z);
y = v[p] -= mx;
sum -= delta;
var plaintext = longsToStr(v);
// strip trailing null chars resulting from filling 4-char blocks:
plaintext = plaintext.replace(/\0+$/,'');
return unescape(plaintext);
// supporting functions
function strToLongs(s) { // convert string to array of longs, each containing 4 chars
// note chars must be within ISO-8859-1 (with Unicode code-point &lt; 256) to fit 4/long
var l = new Array(Math.ceil(s.length/4));
for (var i=0; i&lt;l.length; i++) {
// note little-endian encoding - endianness is irrelevant as long as
// it is the same in longsToStr()
l[i] = s.charCodeAt(i*4) + (s.charCodeAt(i*4+1)&lt;&lt;8) +
(s.charCodeAt(i*4+2)&lt;&lt;16) + (s.charCodeAt(i*4+3)&lt;&lt;24);
return l; // note running off the end of the string generates nulls since
} // bitwise operators treat NaN as 0
function longsToStr(l) { // convert array of longs back to string
var a = new Array(l.length);
for (var i=0; i&lt;l.length; i++) {
a[i] = String.fromCharCode(l[i] &amp; 0xFF, l[i]&gt;&gt;&gt;8 &amp; 0xFF,
l[i]&gt;&gt;&gt;16 &amp; 0xFF, l[i]&gt;&gt;&gt;24 &amp; 0xFF);
return a.join(''); // use Array.join() rather than repeated string appends for efficiency
function escCtrlCh(str) { // escape control chars etc which might cause problems with encrypted texts
return str.replace(/[\0\t\n\v\f\r\xa0'&quot;!]/g, function(c) { return '!' + c.charCodeAt(0) + '!'; });
function unescCtrlCh(str) { // unescape potentially problematic nulls and control characters
return str.replace(/!\d\d?\d?!/g, function(c) { return String.fromCharCode(c.slice(1,-1)); });
<div title="TspotSetupPlugin" creator="bootStrap" modifier="bootStrap" created="201112120700" tags="systemConfig" changecount="1">
Description: Contains the stuff you need to use Tiddlyspot
Note, you also need UploadPlugin, PasswordOptionPlugin and LoadRemoteFileThroughProxy
from for a complete working Tiddlyspot site.
// edit this if you are migrating sites or retrofitting an existing TW
config.tiddlyspotSiteId = 'bootstrap';
// make it so you can by default see edit controls via http
config.options.chkHttpReadOnly = false;
window.readOnly = false; // make sure of it (for tw 2.2)
window.showBackstage = true; // show backstage too
// disable autosave in d3
if (window.location.protocol != &quot;file:&quot;)
config.options.chkGTDLazyAutoSave = false;
// tweak shadow tiddlers to add upload button, password entry box etc
with (config.shadowTiddlers) {
SiteUrl = 'http://'+config.tiddlyspotSiteId+'';
SideBarOptions = SideBarOptions.replace(/(&lt;&lt;saveChanges&gt;&gt;)/,&quot;$1&lt;&lt;tiddler TspotSidebar&gt;&gt;&quot;);
OptionsPanel = OptionsPanel.replace(/^/,&quot;&lt;&lt;tiddler TspotOptions&gt;&gt;&quot;);
DefaultTiddlers = DefaultTiddlers.replace(/^/,&quot;[[WelcomeToTiddlyspot]] &quot;);
MainMenu = MainMenu.replace(/^/,&quot;[[WelcomeToTiddlyspot]] &quot;);
// create some shadow tiddler content
&quot;tiddlyspot password:&quot;,
&quot;&lt;&lt;option pasUploadPassword&gt;&gt;&quot;,
&quot;| tiddlyspot password:|&lt;&lt;option pasUploadPassword&gt;&gt;|&quot;,
&quot;| site management:|&lt;&lt;upload http://&quot; + config.tiddlyspotSiteId + &quot; index.html . . &quot; + config.tiddlyspotSiteId + &quot;&gt;&gt;//(requires tiddlyspot password)//&lt;br&gt;[[control panel|http://&quot; + config.tiddlyspotSiteId + &quot;]], [[download (go offline)|http://&quot; + config.tiddlyspotSiteId + &quot;]]|&quot;,
&quot;| links:|[[|]], [[FAQs|]], [[blog|]], email [[support|]] &amp; [[feedback|]], [[donate|]]|&quot;
&quot;This document is a ~TiddlyWiki from A ~TiddlyWiki is an electronic notebook that is great for managing todo lists, personal information, and all sorts of things.&quot;,
&quot;@@font-weight:bold;font-size:1.3em;color:#444; //What now?// &amp;nbsp;&amp;nbsp;@@ Before you can save any changes, you need to enter your password in the form below. Then configure privacy and other site settings at your [[control panel|http://&quot; + config.tiddlyspotSiteId + &quot;]] (your control panel username is //&quot; + config.tiddlyspotSiteId + &quot;//).&quot;,
&quot;&lt;&lt;tiddler TspotControls&gt;&gt;&quot;,
&quot;See also GettingStarted.&quot;,
&quot;@@font-weight:bold;font-size:1.3em;color:#444; //Working online// &amp;nbsp;&amp;nbsp;@@ You can edit this ~TiddlyWiki right now, and save your changes using the \&quot;save to web\&quot; button in the column on the right.&quot;,
&quot;@@font-weight:bold;font-size:1.3em;color:#444; //Working offline// &amp;nbsp;&amp;nbsp;@@ A fully functioning copy of this ~TiddlyWiki can be saved onto your hard drive or USB stick. You can make changes and save them locally without being connected to the Internet. When you're ready to sync up again, just click \&quot;upload\&quot; and your ~TiddlyWiki will be saved back to;,
&quot;@@font-weight:bold;font-size:1.3em;color:#444; //Help!// &amp;nbsp;&amp;nbsp;@@ Find out more about ~TiddlyWiki at [[|]]. Also visit [[|]] for documentation on learning and using ~TiddlyWiki. New users are especially welcome on the [[TiddlyWiki mailing list|]], which is an excellent place to ask questions and get help. If you have a tiddlyspot related problem email [[tiddlyspot support|]].&quot;,
&quot;@@font-weight:bold;font-size:1.3em;color:#444; //Enjoy :)// &amp;nbsp;&amp;nbsp;@@ We hope you like using your site. Please email [[|]] with any comments or suggestions.&quot;
&quot;&lt;&lt;upload http://&quot; + config.tiddlyspotSiteId + &quot; index.html . . &quot; + config.tiddlyspotSiteId + &quot;&gt;&gt;&lt;html&gt;&lt;a href='http://&quot; + config.tiddlyspotSiteId + &quot;' class='button'&gt;download&lt;/a&gt;&lt;/html&gt;&quot;
<div title="UploadLog" modifier="bootStrap" created="201112120712" modified="201112120712">
<pre>| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |
| 11/12/2011 23:12:34 | bootStrap | [[bootstrap.html|file:///home/x/views/git/Hello-World/bootstrap.html]] | [[store.cgi|]] | . | [[index.html |]] | . |</pre>
<div title="UploadPlugin" creator="bootStrap" modifier="bootStrap" created="201112120701" tags="systemConfig" changecount="1">
|''Description:''|Save to web a TiddlyWiki|
|''Date:''|Feb 24, 2008|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license| ]]|
version.extensions.UploadPlugin = {
major: 4, minor: 1, revision: 3,
date: new Date(&quot;Feb 24, 2008&quot;),
source: '',
author: 'BidiX (BidiX (at) bidix (dot) info',
coreVersion: '2.2.0'
// Environment
if (!window.bidix) window.bidix = {}; // bidix namespace
bidix.debugMode = false; // true to activate both in Plugin and UploadService
// Upload Macro
config.macros.upload = {
// default values
defaultBackupDir: '', //no backup
defaultStoreScript: &quot;store.php&quot;,
defaultToFilename: &quot;index.html&quot;,
defaultUploadDir: &quot;.&quot;,
authenticateUser: true // UploadService Authenticate User
config.macros.upload.label = {
promptOption: &quot;Save and Upload this TiddlyWiki with UploadOptions&quot;,
promptParamMacro: &quot;Save and Upload this TiddlyWiki in %0&quot;,
saveLabel: &quot;save to web&quot;,
saveToDisk: &quot;save to disk&quot;,
uploadLabel: &quot;upload&quot;
config.macros.upload.messages = {
noStoreUrl: &quot;No store URL in parmeters or options&quot;,
usernameOrPasswordMissing: &quot;Username or password missing&quot;
config.macros.upload.handler = function(place,macroName,params) {
if (readOnly)
var label;
if (document.location.toString().substr(0,4) == &quot;http&quot;)
label = this.label.saveLabel;
label = this.label.uploadLabel;
var prompt;
if (params[0]) {
prompt = this.label.promptParamMacro.toString().format([this.destFile(params[0],
(params[1] ? params[1]:bidix.basename(window.location.toString())), params[3])]);
} else {
prompt = this.label.promptOption;
createTiddlyButton(place, label, prompt, function() {config.macros.upload.action(params);}, null, null, this.accessKey);
config.macros.upload.action = function(params)
// for missing macro parameter set value from options
if (!params) params = {};
var storeUrl = params[0] ? params[0] : config.options.txtUploadStoreUrl;
var toFilename = params[1] ? params[1] : config.options.txtUploadFilename;
var backupDir = params[2] ? params[2] : config.options.txtUploadBackupDir;
var uploadDir = params[3] ? params[3] : config.options.txtUploadDir;
var username = params[4] ? params[4] : config.options.txtUploadUserName;
var password = config.options.pasUploadPassword; // for security reason no password as macro parameter
// for still missing parameter set default value
if ((!storeUrl) &amp;&amp; (document.location.toString().substr(0,4) == &quot;http&quot;))
storeUrl = bidix.dirname(document.location.toString())+'/'+config.macros.upload.defaultStoreScript;
if (storeUrl.substr(0,4) != &quot;http&quot;)
storeUrl = bidix.dirname(document.location.toString()) +'/'+ storeUrl;
if (!toFilename)
toFilename = bidix.basename(window.location.toString());
if (!toFilename)
toFilename = config.macros.upload.defaultToFilename;
if (!uploadDir)
uploadDir = config.macros.upload.defaultUploadDir;
if (!backupDir)
backupDir = config.macros.upload.defaultBackupDir;
// report error if still missing
if (!storeUrl) {
return false;
if (config.macros.upload.authenticateUser &amp;&amp; (!username || !password)) {
return false;
bidix.upload.uploadChanges(false,null,storeUrl, toFilename, uploadDir, backupDir, username, password);
return false;
config.macros.upload.destFile = function(storeUrl, toFilename, uploadDir)
if (!storeUrl)
return null;
var dest = bidix.dirname(storeUrl);
if (uploadDir &amp;&amp; uploadDir != '.')
dest = dest + '/' + uploadDir;
dest = dest + '/' + toFilename;
return dest;
// uploadOptions Macro
config.macros.uploadOptions = {
handler: function(place,macroName,params) {
var wizard = new Wizard();
var markList = wizard.getElement(&quot;markList&quot;);
var listWrapper = document.createElement(&quot;div&quot;);
var uploadCaption;
if (document.location.toString().substr(0,4) == &quot;http&quot;)
uploadCaption = config.macros.upload.label.saveLabel;
uploadCaption = config.macros.upload.label.uploadLabel;
{caption: uploadCaption, tooltip: config.macros.upload.label.promptOption,
onClick: config.macros.upload.action},
{caption: this.cancelButton, tooltip: this.cancelButtonPrompt, onClick: this.onCancel}
options: [
refreshOptions: function(listWrapper) {
var opts = [];
for(i=0; i&lt;this.options.length; i++) {
var opt = {};
opt.option = &quot;&quot;;
n = this.options[i]; = n;
opt.lowlight = !config.optionsDesc[n];
opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
for(n=0; n&lt;opts.length; n++) {
var type = opts[n].name.substr(0,3);
var h = config.macros.option.types[type];
if (h &amp;&amp; h.create) {
onCancel: function(e)
return false;
wizardTitle: &quot;Upload with options&quot;,
step1Title: &quot;These options are saved in cookies in your browser&quot;,
step1Html: &quot;&lt;input type='hidden' name='markList'&gt;&lt;/input&gt;&lt;br&gt;&quot;,
cancelButton: &quot;Cancel&quot;,
cancelButtonPrompt: &quot;Cancel prompt&quot;,
listViewTemplate: {
columns: [
{name: 'Description', field: 'description', title: &quot;Description&quot;, type: 'WikiText'},
{name: 'Option', field: 'option', title: &quot;Option&quot;, type: 'String'},
{name: 'Name', field: 'name', title: &quot;Name&quot;, type: 'String'}
rowClasses: [
{className: 'lowlight', field: 'lowlight'}
// upload functions
if (!bidix.upload) bidix.upload = {};
if (!bidix.upload.messages) bidix.upload.messages = {
//from saving
invalidFileError: &quot;The original file '%0' does not appear to be a valid TiddlyWiki&quot;,
backupSaved: &quot;Backup saved&quot;,
backupFailed: &quot;Failed to upload backup file&quot;,
rssSaved: &quot;RSS feed uploaded&quot;,
rssFailed: &quot;Failed to upload RSS feed file&quot;,
emptySaved: &quot;Empty template uploaded&quot;,
emptyFailed: &quot;Failed to upload empty template file&quot;,
mainSaved: &quot;Main TiddlyWiki file uploaded&quot;,
mainFailed: &quot;Failed to upload main TiddlyWiki file. Your changes have not been saved&quot;,
//specific upload
loadOriginalHttpPostError: &quot;Can't get original file&quot;,
aboutToSaveOnHttpPost: 'About to upload on %0 ...',
storePhpNotFound: &quot;The store script '%0' was not found.&quot;
bidix.upload.uploadChanges = function(onlyIfDirty,tiddlers,storeUrl,toFilename,uploadDir,backupDir,username,password)
var callback = function(status,uploadParams,original,url,xhr) {
if (!status) {
if (bidix.debugMode)
// Locate the storeArea div's
var posDiv = locateStoreArea(original);
if((posDiv[0] == -1) || (posDiv[1] == -1)) {
if(onlyIfDirty &amp;&amp; !store.isDirty())
// save on localdisk ?
if (document.location.toString().substr(0,4) == &quot;file&quot;) {
var path = document.location.toString();
var localPath = getLocalPath(path);
// get original
var uploadParams = new Array(storeUrl,toFilename,uploadDir,backupDir,username,password);
var originalPath = document.location.toString();
// If url is a directory : add index.html
if (originalPath.charAt(originalPath.length-1) == &quot;/&quot;)
originalPath = originalPath + &quot;index.html&quot;;
var dest = config.macros.upload.destFile(storeUrl,toFilename,uploadDir);
var log = new bidix.UploadLog();
log.startUpload(storeUrl, dest, uploadDir, backupDir);
if (bidix.debugMode)
alert(&quot;about to execute Http - GET on &quot;+originalPath);
var r = doHttp(&quot;GET&quot;,originalPath,null,null,username,password,callback,uploadParams,null);
if (typeof r == &quot;string&quot;)
return r;
bidix.upload.uploadRss = function(uploadParams,original,posDiv)
var callback = function(status,params,responseText,url,xhr) {
if(status) {
var destfile = responseText.substring(responseText.indexOf(&quot;destfile:&quot;)+9,responseText.indexOf(&quot;\n&quot;, responseText.indexOf(&quot;destfile:&quot;)));
} else {
// do uploadRss
if(config.options.chkGenerateAnRssFeed) {
var rssPath = uploadParams[1].substr(0,uploadParams[1].lastIndexOf(&quot;.&quot;)) + &quot;.xml&quot;;
var rssUploadParams = new Array(uploadParams[0],rssPath,uploadParams[2],'',uploadParams[4],uploadParams[5]);
var rssString = generateRss();
// no UnicodeToUTF8 conversion needed when location is &quot;file&quot; !!!
if (document.location.toString().substr(0,4) != &quot;file&quot;)
rssString = convertUnicodeToUTF8(rssString);
} else {
bidix.upload.uploadMain = function(uploadParams,original,posDiv)
var callback = function(status,params,responseText,url,xhr) {
var log = new bidix.UploadLog();
if(status) {
// if backupDir specified
if ((params[3]) &amp;&amp; (responseText.indexOf(&quot;backupfile:&quot;) &gt; -1)) {
var backupfile = responseText.substring(responseText.indexOf(&quot;backupfile:&quot;)+11,responseText.indexOf(&quot;\n&quot;, responseText.indexOf(&quot;backupfile:&quot;)));
var destfile = responseText.substring(responseText.indexOf(&quot;destfile:&quot;)+9,responseText.indexOf(&quot;\n&quot;, responseText.indexOf(&quot;destfile:&quot;)));
} else {
// do uploadMain
var revised = bidix.upload.updateOriginal(original,posDiv);
bidix.upload.httpUpload = function(uploadParams,data,callback,params)
var localCallback = function(status,params,responseText,url,xhr) {
url = (url.indexOf(&quot;nocache=&quot;) &lt; 0 ? url : url.substring(0,url.indexOf(&quot;nocache=&quot;)-1));
if (xhr.status == 404)
if ((bidix.debugMode) || (responseText.indexOf(&quot;Debug mode&quot;) &gt;= 0 )) {
if (responseText.indexOf(&quot;Debug mode&quot;) &gt;= 0 )
responseText = responseText.substring(responseText.indexOf(&quot;\n\n&quot;)+2);
} else if (responseText.charAt(0) != '0')
if (responseText.charAt(0) != '0')
status = null;
// do httpUpload
var boundary = &quot;---------------------------&quot;+&quot;AaB03x&quot;;
var uploadFormName = &quot;UploadPlugin&quot;;
// compose headers data
var sheader = &quot;&quot;;
sheader += &quot;--&quot; + boundary + &quot;\r\nContent-disposition: form-data; name=\&quot;&quot;;
sheader += uploadFormName +&quot;\&quot;\r\n\r\n&quot;;
sheader += &quot;backupDir=&quot;+uploadParams[3] +
&quot;;user=&quot; + uploadParams[4] +
&quot;;password=&quot; + uploadParams[5] +
&quot;;uploaddir=&quot; + uploadParams[2];
if (bidix.debugMode)
sheader += &quot;;debug=1&quot;;
sheader += &quot;;;\r\n&quot;;
sheader += &quot;\r\n&quot; + &quot;--&quot; + boundary + &quot;\r\n&quot;;
sheader += &quot;Content-disposition: form-data; name=\&quot;userfile\&quot;; filename=\&quot;&quot;+uploadParams[1]+&quot;\&quot;\r\n&quot;;
sheader += &quot;Content-Type: text/html;charset=UTF-8&quot; + &quot;\r\n&quot;;
sheader += &quot;Content-Length: &quot; + data.length + &quot;\r\n\r\n&quot;;
// compose trailer data
var strailer = new String();
strailer = &quot;\r\n--&quot; + boundary + &quot;--\r\n&quot;;
data = sheader + data + strailer;
if (bidix.debugMode) alert(&quot;about to execute Http - POST on &quot;+uploadParams[0]+&quot;\n with \n&quot;+data.substr(0,500)+ &quot; ... &quot;);
var r = doHttp(&quot;POST&quot;,uploadParams[0],data,&quot;multipart/form-data; ;charset=UTF-8; boundary=&quot;+boundary,uploadParams[4],uploadParams[5],localCallback,params,null);
if (typeof r == &quot;string&quot;)
return r;
// same as Saving's updateOriginal but without convertUnicodeToUTF8 calls
bidix.upload.updateOriginal = function(original, posDiv)
if (!posDiv)
posDiv = locateStoreArea(original);
if((posDiv[0] == -1) || (posDiv[1] == -1)) {
var revised = original.substr(0,posDiv[0] + startSaveArea.length) + &quot;\n&quot; +
store.allTiddlersAsHtml() + &quot;\n&quot; +
var newSiteTitle = getPageTitle().htmlEncode();
revised = revised.replaceChunk(&quot;&lt;title&quot;+&quot;&gt;&quot;,&quot;&lt;/title&quot;+&quot;&gt;&quot;,&quot; &quot; + newSiteTitle + &quot; &quot;);
revised = updateMarkupBlock(revised,&quot;PRE-HEAD&quot;,&quot;MarkupPreHead&quot;);
revised = updateMarkupBlock(revised,&quot;POST-HEAD&quot;,&quot;MarkupPostHead&quot;);
revised = updateMarkupBlock(revised,&quot;PRE-BODY&quot;,&quot;MarkupPreBody&quot;);
revised = updateMarkupBlock(revised,&quot;POST-SCRIPT&quot;,&quot;MarkupPostBody&quot;);
return revised;
// UploadLog
// config.options.chkUploadLog :
// false : no logging
// true : logging
// config.options.txtUploadLogMaxLine :
// -1 : no limit
// 0 : no Log lines but UploadLog is still in place
// n : the last n lines are only kept
// NaN : no limit (-1)
bidix.UploadLog = function() {
if (!config.options.chkUploadLog)
return; // this.tiddler = null
this.tiddler = store.getTiddler(&quot;UploadLog&quot;);
if (!this.tiddler) {
this.tiddler = new Tiddler();
this.tiddler.title = &quot;UploadLog&quot;;
this.tiddler.text = &quot;| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |&quot;;
this.tiddler.created = new Date();
this.tiddler.modifier = config.options.txtUserName;
this.tiddler.modified = new Date();
return this;
bidix.UploadLog.prototype.addText = function(text) {
if (!this.tiddler)
// retrieve maxLine when we need it
var maxLine = parseInt(config.options.txtUploadLogMaxLine,10);
if (isNaN(maxLine))
maxLine = -1;
// add text
if (maxLine != 0)
this.tiddler.text = this.tiddler.text + text;
// Trunck to maxLine
if (maxLine &gt;= 0) {
var textArray = this.tiddler.text.split('\n');
if (textArray.length &gt; maxLine + 1)
this.tiddler.text = textArray.join('\n');
// update tiddler fields
this.tiddler.modifier = config.options.txtUserName;
this.tiddler.modified = new Date();
// refresh and notifiy for immediate update
store.notify(this.tiddler.title, true);
bidix.UploadLog.prototype.startUpload = function(storeUrl, toFilename, uploadDir, backupDir) {
if (!this.tiddler)
var now = new Date();
var text = &quot;\n| &quot;;
var filename = bidix.basename(document.location.toString());
if (!filename) filename = '/';
text += now.formatString(&quot;0DD/0MM/YYYY 0hh:0mm:0ss&quot;) +&quot; | &quot;;
text += config.options.txtUserName + &quot; | &quot;;
text += &quot;[[&quot;+filename+&quot;|&quot;+location + &quot;]] |&quot;;
text += &quot; [[&quot; + bidix.basename(storeUrl) + &quot;|&quot; + storeUrl + &quot;]] | &quot;;
text += uploadDir + &quot; | &quot;;
text += &quot;[[&quot; + bidix.basename(toFilename) + &quot; | &quot; +toFilename + &quot;]] | &quot;;
text += backupDir + &quot; |&quot;;
bidix.UploadLog.prototype.endUpload = function(status) {
if (!this.tiddler)
this.addText(&quot; &quot;+status+&quot; |&quot;);
// Utilities
bidix.checkPlugin = function(plugin, major, minor, revision) {
var ext = version.extensions[plugin];
if (!
(ext &amp;&amp;
((ext.major &gt; major) ||
((ext.major == major) &amp;&amp; (ext.minor &gt; minor)) ||
((ext.major == major) &amp;&amp; (ext.minor == minor) &amp;&amp; (ext.revision &gt;= revision))))) {
// write error in PluginManager
if (pluginInfo)
pluginInfo.log.push(&quot;Requires &quot; + plugin + &quot; &quot; + major + &quot;.&quot; + minor + &quot;.&quot; + revision);
eval(plugin); // generate an error : &quot;Error: ReferenceError: xxxx is not defined&quot;
bidix.dirname = function(filePath) {
if (!filePath)
var lastpos;
if ((lastpos = filePath.lastIndexOf(&quot;/&quot;)) != -1) {
return filePath.substring(0, lastpos);
} else {
return filePath.substring(0, filePath.lastIndexOf(&quot;\\&quot;));
bidix.basename = function(filePath) {
if (!filePath)
var lastpos;
if ((lastpos = filePath.lastIndexOf(&quot;#&quot;)) != -1)
filePath = filePath.substring(0, lastpos);
if ((lastpos = filePath.lastIndexOf(&quot;/&quot;)) != -1) {
return filePath.substring(lastpos + 1);
} else
return filePath.substring(filePath.lastIndexOf(&quot;\\&quot;)+1);
bidix.initOption = function(name,value) {
if (!config.options[name])
config.options[name] = value;
// Initializations
// require PasswordOptionPlugin 1.0.1 or better
bidix.checkPlugin(&quot;PasswordOptionPlugin&quot;, 1, 0, 1);
// styleSheet
setStylesheet('.txtUploadStoreUrl, .txtUploadBackupDir, .txtUploadDir {width: 22em;}',&quot;uploadPluginStyles&quot;);
txtUploadStoreUrl: &quot;Url of the UploadService script (default: store.php)&quot;,
txtUploadFilename: &quot;Filename of the uploaded file (default: in index.html)&quot;,
txtUploadDir: &quot;Relative Directory where to store the file (default: . (downloadService directory))&quot;,
txtUploadBackupDir: &quot;Relative Directory where to backup the file. If empty no backup. (default: ''(empty))&quot;,
txtUploadUserName: &quot;Upload Username&quot;,
pasUploadPassword: &quot;Upload Password&quot;,
chkUploadLog: &quot;do Logging in UploadLog (default: true)&quot;,
txtUploadLogMaxLine: &quot;Maximum of lines in UploadLog (default: 10)&quot;
// Options Initializations
// Backstage
uploadOptions: {text: &quot;upload&quot;, tooltip: &quot;Change UploadOptions and Upload&quot;, content: '&lt;&lt;uploadOptions&gt;&gt;'}
<div title="bootContents" creator="bootStrap" modifier="bootStrap" created="201111281512" modified="201111281518" tags="boot" changecount="5">
<pre> [[O]]
<script id="jsArea" type="text/javascript">
// Please note:
// * This code is designed to be readable but for compactness it only includes brief comments. You can see fuller comments
// in the project repository at
// * You should never need to modify this source code directly. TiddlyWiki is carefully designed to allow deep customisation
// without changing the core code. Please consult the development group at
// JSLint directives
/*global jQuery:false, version:false */
/*jslint bitwise:true, browser:true, confusion:true, eqeq:true, evil:true, forin:true, maxerr:100, plusplus:true, regexp:true, sloppy:true, sub:true, undef:true, unparam:true, vars:true, white:true */
//-- Configuration repository
// Miscellaneous options
var config = {
numRssItems: 20, // Number of items in the RSS feed
animDuration: 400, // Duration of UI animations in milliseconds
cascadeFast: 20, // Speed for cascade animations (higher == slower)
cascadeSlow: 60, // Speed for EasterEgg cascade animations
cascadeDepth: 5, // Depth of cascade animation
locale: "en" // W3C language tag
// Hashmap of alternative parsers for the wikifier
config.parsers = {};
// Adaptors
config.adaptors = {};
config.defaultAdaptor = null;
// Backstage tasks
config.tasks = {};
// Annotations
config.annotations = {};
// Custom fields to be automatically added to new tiddlers
config.defaultCustomFields = {};
// Messages
config.messages = {
messageClose: {},
dates: {},
tiddlerPopup: {}
// Options that can be set in the options panel and/or cookies
config.options = {
chkRegExpSearch: false,
chkCaseSensitiveSearch: false,
chkIncrementalSearch: true,
chkAnimate: true,
chkSaveBackups: true,
chkAutoSave: false,
chkGenerateAnRssFeed: false,
chkSaveEmptyTemplate: false,
chkOpenInNewWindow: true,
chkToggleLinks: false,
chkHttpReadOnly: true,
chkForceMinorUpdate: false,
chkConfirmDelete: true,
chkInsertTabs: false,
chkUsePreForStorage: true, // Whether to use <pre> format for storage
chkDisplayInstrumentation: false,
txtBackupFolder: "",
txtEditorFocus: "text",
txtMainTab: "tabTimeline",
txtMoreTab: "moreTabAll",
txtMaxEditRows: "30",
txtFileSystemCharSet: "UTF-8",
txtTheme: ""
config.optionsDesc = {};
config.optionsSource = {};
// Default tiddler templates
config.tiddlerTemplates = {
1: "ViewTemplate",
2: "EditTemplate"
// More messages (rather a legacy layout that should not really be like this)
config.views = {
wikified: {
tag: {}
editor: {
tagChooser: {}
// Backstage tasks
config.backstageTasks = ["save","sync","importTask","tweak","upgrade","plugins"];
// Extensions
config.extensions = {};
// Macros; each has a 'handler' member that is inserted later
config.macros = {
today: {},
version: {},
search: {sizeTextbox: 15},
tiddler: {},
tag: {},
tags: {},
tagging: {},
timeline: {},
allTags: {},
list: {
all: {},
missing: {},
orphans: {},
shadowed: {},
touched: {},
filter: {}
closeAll: {},
permaview: {},
saveChanges: {},
slider: {},
option: {},
options: {},
newTiddler: {},
newJournal: {},
tabs: {},
gradient: {},
message: {},
view: {defaultView: "text"},
edit: {},
tagChooser: {},
toolbar: {},
plugins: {},
refreshDisplay: {},
importTiddlers: {},
upgrade: {
source: "",
backupExtension: "pre.core.upgrade"
sync: {},
annotations: {}
// Commands supported by the toolbar macro
config.commands = {
closeTiddler: {},
closeOthers: {},
editTiddler: {},
saveTiddler: {hideReadOnly: true},
cancelTiddler: {},
deleteTiddler: {hideReadOnly: true},
permalink: {},
references: {type: "popup"},
jump: {type: "popup"},
syncing: {type: "popup"},
fields: {type: "popup"}
// Control of macro parameter evaluation
config.evaluateMacroParameters = "all";
// Basic regular expressions
config.textPrimitives = {
upperLetter: "[A-Z\u00c0-\u00de\u0150\u0170]",
lowerLetter: "[a-z0-9_\\-\u00df-\u00ff\u0151\u0171]",
anyLetter: "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]",
anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]"
if(!((new RegExp("[\u0150\u0170]","g")).test("\u0150"))) {
config.textPrimitives = {
upperLetter: "[A-Z\u00c0-\u00de]",
lowerLetter: "[a-z0-9_\\-\u00df-\u00ff]",
anyLetter: "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff]",
anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff]"
config.textPrimitives.sliceSeparator = "::";
config.textPrimitives.sectionSeparator = "##";
config.textPrimitives.urlPattern = "(?:file|http|https|mailto|ftp|irc|news|data):[^\\s'\"]+(?:/|\\b)";
config.textPrimitives.unWikiLink = "~";
config.textPrimitives.wikiLink = "(?:(?:" + config.textPrimitives.upperLetter + "+" +
config.textPrimitives.lowerLetter + "+" +
config.textPrimitives.upperLetter +
config.textPrimitives.anyLetter + "*)|(?:" +
config.textPrimitives.upperLetter + "{2,}" +
config.textPrimitives.lowerLetter + "+))";
config.textPrimitives.cssLookahead = "(?:(" + config.textPrimitives.anyLetter + "+)\\(([^\\)\\|\\n]+)(?:\\):))|(?:(" + config.textPrimitives.anyLetter + "+):([^;\\|\\n]+);)";
config.textPrimitives.cssLookaheadRegExp = new RegExp(config.textPrimitives.cssLookahead,"mg");
config.textPrimitives.brackettedLink = "\\[\\[([^\\]]+)\\]\\]";
config.textPrimitives.titledBrackettedLink = "\\[\\[([^\\[\\]\\|]+)\\|([^\\[\\]\\|]+)\\]\\]";
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");
config.glyphs = {
currBrowser: null,
browsers: [],
codes: {}
//-- Shadow tiddlers
config.shadowTiddlers = {
StyleSheet: "",
MarkupPreHead: "",
MarkupPostHead: "",
MarkupPreBody: "",
MarkupPostBody: "",
TabTimeline: '<<timeline>>',
TabAll: '<<list all>>',
TabTags: '<<allTags excludeLists>>',
TabMoreMissing: '<<list missing>>',
TabMoreOrphans: '<<list orphans>>',
TabMoreShadowed: '<<list shadowed>>',
AdvancedOptions: '<<options>>',
PluginManager: '<<plugins>>',
SystemSettings: '',
ToolbarCommands: '|~ViewToolbar|closeTiddler closeOthers +editTiddler > fields syncing permalink references jump|\n|~EditToolbar|+saveTiddler -cancelTiddler deleteTiddler|',
WindowTitle: '<<tiddler SiteTitle>> - <<tiddler SiteSubtitle>>'
// Browser detection... In a very few places, there's nothing else for it but to know what browser we're using.
config.userAgent = navigator.userAgent.toLowerCase();
config.browser = {
isIE: config.userAgent.indexOf("msie") != -1 && config.userAgent.indexOf("opera") == -1,
isGecko: navigator.product == "Gecko" && config.userAgent.indexOf("WebKit") == -1,
ieVersion: /MSIE (\d.\d)/i.exec(config.userAgent), // config.browser.ieVersion[1], if it exists, will be the IE version string, eg "6.0"
isSafari: config.userAgent.indexOf("applewebkit") != -1,
isBadSafari: !((new RegExp("[\u0150\u0170]","g")).test("\u0150")),
firefoxDate: /gecko\/(\d{8})/i.exec(config.userAgent), // config.browser.firefoxDate[1], if it exists, will be Firefox release date as "YYYYMMDD"
isOpera: config.userAgent.indexOf("opera") != -1,
isChrome: config.userAgent.indexOf('chrome') > -1,
isLinux: config.userAgent.indexOf("linux") != -1,
isUnix: config.userAgent.indexOf("x11") != -1,
isMac: config.userAgent.indexOf("mac") != -1,
isWindows: config.userAgent.indexOf("win") != -1
browsers: [
function() {return config.browser.isIE;},
function() {return true;}
codes: {
downTriangle: ["\u25BC","\u25BE"],
downArrow: ["\u2193","\u2193"],
bentArrowLeft: ["\u2190","\u21A9"],
bentArrowRight: ["\u2192","\u21AA"]
//-- Translateable strings
// Strings in "double quotes" should be translated; strings in 'single quotes' should be left alone
txtUserName: "YourName"});
save: {text: "save", tooltip: "Save your changes to this TiddlyWiki"},
sync: {text: "sync", tooltip: "Synchronise changes with other TiddlyWiki files and servers", content: '<<sync>>'},
importTask: {text: "import", tooltip: "Import tiddlers and plugins from other TiddlyWiki files and servers", content: '<<importTiddlers>>'},
tweak: {text: "tweak", tooltip: "Tweak the appearance and behaviour of TiddlyWiki", content: '<<options>>'},
upgrade: {text: "upgrade", tooltip: "Upgrade TiddlyWiki core code", content: '<<upgrade>>'},
plugins: {text: "plugins", tooltip: "Manage installed plugins", content: '<<plugins>>'}
// Options that can be set in the options panel and/or cookies
txtUserName: "Username for signing your edits",
chkRegExpSearch: "Enable regular expressions for searches",
chkCaseSensitiveSearch: "Case-sensitive searching",
chkIncrementalSearch: "Incremental key-by-key searching",
chkAnimate: "Enable animations",
chkSaveBackups: "Keep backup file when saving changes",
chkAutoSave: "Automatically save changes",
chkGenerateAnRssFeed: "Generate an RSS feed when saving changes",
chkSaveEmptyTemplate: "Generate an empty template when saving changes",
chkOpenInNewWindow: "Open external links in a new window",
chkToggleLinks: "Clicking on links to open tiddlers causes them to close",
chkHttpReadOnly: "Hide editing features when viewed over HTTP",
chkForceMinorUpdate: "Don't update modifier username and date when editing tiddlers",
chkConfirmDelete: "Require confirmation before deleting tiddlers",
chkInsertTabs: "Use the tab key to insert tab characters instead of moving between fields",
txtBackupFolder: "Name of folder to use for backups",
txtMaxEditRows: "Maximum number of rows in edit boxes",
txtTheme: "Name of the theme to use",
txtFileSystemCharSet: "Default character set for saving changes (Firefox/Mozilla only)"});
customConfigError: "Problems were encountered loading plugins. See PluginManager for details",
pluginError: "Error: %0",
pluginDisabled: "Not executed because disabled via 'systemConfigDisable' tag",
pluginForced: "Executed because forced via 'systemConfigForce' tag",
pluginVersionError: "Not executed because this plugin needs a newer version of TiddlyWiki",
nothingSelected: "Nothing is selected. You must select one or more items first",
savedSnapshotError: "It appears that this TiddlyWiki has been incorrectly saved. Please see for details",
subtitleUnknown: "(unknown)",
undefinedTiddlerToolTip: "The tiddler '%0' doesn't yet exist",
shadowedTiddlerToolTip: "The tiddler '%0' doesn't yet exist, but has a pre-defined shadow value",
tiddlerLinkTooltip: "%0 - %1, %2",
externalLinkTooltip: "External link to %0",
noTags: "There are no tagged tiddlers",
notFileUrlError: "You need to save this TiddlyWiki to a file before you can save changes",
cantSaveError: "It's not possible to save changes. Possible reasons include:\n- your browser doesn't support saving (Firefox, Internet Explorer, Safari and Opera all work if properly configured)\n- the pathname to your TiddlyWiki file contains illegal characters\n- the TiddlyWiki HTML file has been moved or renamed",
invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
backupSaved: "Backup saved",
backupFailed: "Failed to save backup file",
rssSaved: "RSS feed saved",
rssFailed: "Failed to save RSS feed file",
emptySaved: "Empty template saved",
emptyFailed: "Failed to save empty template file",
mainSaved: "Main TiddlyWiki file saved",
mainFailed: "Failed to save main TiddlyWiki file. Your changes have not been saved",
macroError: "Error in macro <<%0>>",
macroErrorDetails: "Error while executing macro <<%0>>:\n%1",
missingMacro: "No such macro",
overwriteWarning: "A tiddler named '%0' already exists. Choose OK to overwrite it",
unsavedChangesWarning: "WARNING! There are unsaved changes in TiddlyWiki\n\nChoose OK to save\nChoose CANCEL to discard",
confirmExit: "--------------------------------\n\nThere are unsaved changes in TiddlyWiki. If you continue you will lose those changes\n\n--------------------------------",
saveInstructions: "SaveChanges",
unsupportedTWFormat: "Unsupported TiddlyWiki format '%0'",
tiddlerSaveError: "Error when saving tiddler '%0'",
tiddlerLoadError: "Error when loading tiddler '%0'",
wrongSaveFormat: "Cannot save with storage format '%0'. Using standard format for save.",
invalidFieldName: "Invalid field name %0",
fieldCannotBeChanged: "Field '%0' cannot be changed",
loadingMissingTiddler: "Attempting to retrieve the tiddler '%0' from the '%1' server at:\n\n'%2' in the workspace '%3'",
upgradeDone: "The upgrade to version %0 is now complete\n\nClick 'OK' to reload the newly upgraded TiddlyWiki",
invalidCookie: "Invalid cookie '%0'"});
text: "close",
tooltip: "close this message area"});
config.messages.backstage = {
open: {text: "backstage", tooltip: "Open the backstage area to perform authoring and editing tasks"},
close: {text: "close", tooltip: "Close the backstage area"},
prompt: "backstage: ",
decal: {
edit: {text: "edit", tooltip: "Edit the tiddler '%0'"}
config.messages.listView = {
tiddlerTooltip: "Click for the full text of this tiddler",
previewUnavailable: "(preview not available)"
config.messages.dates.months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November","December"];
config.messages.dates.days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
config.messages.dates.shortMonths = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
config.messages.dates.shortDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
// suffixes for dates, eg "1st","2nd","3rd"..."30th","31st"
config.messages.dates.daySuffixes = ["st","nd","rd","th","th","th","th","th","th","th",
"st"]; = "am"; = "pm";
labelNoTags: "no tags",
labelTags: "tags: ",
openTag: "Open tag '%0'",
tooltip: "Show tiddlers tagged with '%0'",
openAllText: "Open all",
openAllTooltip: "Open all of these tiddlers",
popupNone: "No other tiddlers tagged with '%0'"});
defaultText: "The tiddler '%0' doesn't yet exist. Double-click to create it",
defaultModifier: "(missing)",
shadowModifier: "(built-in shadow tiddler)",
dateFormat: "DD MMM YYYY",
createdPrompt: "created"});
tagPrompt: "Type tags separated with spaces, [[use double square brackets]] if necessary, or add existing",
defaultText: "Type the text for '%0'"});
text: "tags",
tooltip: "Choose existing tags to add to this tiddler",
popupNone: "There are no tags defined",
tagTooltip: "Add the tag '%0'"});
{unit: 1024*1024*1024, template: "%0\u00a0GB"},
{unit: 1024*1024, template: "%0\u00a0MB"},
{unit: 1024, template: "%0\u00a0KB"},
{unit: 1, template: "%0\u00a0B"}
label: "search",
prompt: "Search this TiddlyWiki",
placeholder: "",
accessKey: "F",
successMsg: "%0 tiddlers found matching %1",
failureMsg: "No tiddlers found matching %0"});
label: "tagging: ",
labelNotTag: "not tagging",
tooltip: "List of tiddlers tagged with '%0'"});
dateFormat: "DD MMM YYYY"});
tooltip: "Show tiddlers tagged with '%0'",
noTags: "There are no tagged tiddlers"});
config.macros.list.all.prompt = "All tiddlers in alphabetical order";
config.macros.list.missing.prompt = "Tiddlers that have links to them but are not defined";
config.macros.list.orphans.prompt = "Tiddlers that are not linked to from any other tiddlers";
config.macros.list.shadowed.prompt = "Tiddlers shadowed with default contents";
config.macros.list.touched.prompt = "Tiddlers that have been modified locally";
label: "close all",
prompt: "Close all displayed tiddlers (except any that are being edited)"});
label: "permaview",
prompt: "Link to an URL that retrieves all the currently displayed tiddlers"});
label: "save changes",
prompt: "Save all tiddlers to create a new TiddlyWiki",
accessKey: "S"});
label: "new tiddler",
prompt: "Create a new tiddler",
title: "New Tiddler",
accessKey: "N"});
label: "new journal",
prompt: "Create a new tiddler from the current date and time",
accessKey: "J"});
wizardTitle: "Tweak advanced options",
step1Title: "These options are saved in cookies in your browser",
step1Html: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='false' name='chkUnknown'>Show unknown options</input>",
unknownDescription: "//(unknown)//",
listViewTemplate: {
columns: [
{name: 'Option', field: 'option', title: "Option", type: 'String'},
{name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
{name: 'Name', field: 'name', title: "Name", type: 'String'}
rowClasses: [
{className: 'lowlight', field: 'lowlight'}
wizardTitle: "Manage plugins",
step1Title: "Currently loaded plugins",
step1Html: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
skippedText: "(This plugin has not been executed because it was added since startup)",
noPluginText: "There are no plugins installed",
confirmDeleteText: "Are you sure you want to delete these plugins:\n\n%0",
removeLabel: "remove systemConfig tag",
removePrompt: "Remove systemConfig tag",
deleteLabel: "delete",
deletePrompt: "Delete these tiddlers forever",
listViewTemplate: {
columns: [
{name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
{name: 'Description', field: 'Description', title: "Description", type: 'String'},
{name: 'Version', field: 'Version', title: "Version", type: 'String'},
{name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'},
{name: 'Forced', field: 'forced', title: "Forced", tag: 'systemConfigForce', type: 'TagCheckbox'},
{name: 'Disabled', field: 'disabled', title: "Disabled", tag: 'systemConfigDisable', type: 'TagCheckbox'},
{name: 'Executed', field: 'executed', title: "Loaded", type: 'Boolean', trueText: "Yes", falseText: "No"},
{name: 'Startup Time', field: 'startupTime', title: "Startup Time", type: 'String'},
{name: 'Error', field: 'error', title: "Status", type: 'Boolean', trueText: "Error", falseText: "OK"},
{name: 'Log', field: 'log', title: "Log", type: 'StringList'}
rowClasses: [
{className: 'error', field: 'error'},
{className: 'warning', field: 'warning'}
listViewTemplateReadOnly: {
columns: [
{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
{name: 'Description', field: 'Description', title: "Description", type: 'String'},
{name: 'Version', field: 'Version', title: "Version", type: 'String'},
{name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'},
{name: 'Executed', field: 'executed', title: "Loaded", type: 'Boolean', trueText: "Yes", falseText: "No"},
{name: 'Startup Time', field: 'startupTime', title: "Startup Time", type: 'String'},
{name: 'Error', field: 'error', title: "Status", type: 'Boolean', trueText: "Error", falseText: "OK"},
{name: 'Log', field: 'log', title: "Log", type: 'StringList'}
rowClasses: [
{className: 'error', field: 'error'},
{className: 'warning', field: 'warning'}
moreLabel: "more",
morePrompt: "Show additional commands",
lessLabel: "less",
lessPrompt: "Hide additional commands",
separator: "|"
label: "refresh",
prompt: "Redraw the entire TiddlyWiki display"
readOnlyWarning: "You cannot import into a read-only TiddlyWiki file. Try opening it from a file:// URL",
wizardTitle: "Import tiddlers from another file or server",
step1Title: "Step 1: Locate the server or TiddlyWiki file",
step1Html: "Specify the type of the server: <select name='selTypes'><option value=''>Choose...</option></select><br>Enter the URL or pathname here: <input type='text' size=50 name='txtPath'><br>...or browse for a file: <input type='file' size=50 name='txtBrowse'><br><hr>...or select a pre-defined feed: <select name='selFeeds'><option value=''>Choose...</option></select>",
openLabel: "open",
openPrompt: "Open the connection to this file or server",
statusOpenHost: "Opening the host",
statusGetWorkspaceList: "Getting the list of available workspaces",
step2Title: "Step 2: Choose the workspace",
step2Html: "Enter a workspace name: <input type='text' size=50 name='txtWorkspace'><br>...or select a workspace: <select name='selWorkspace'><option value=''>Choose...</option></select>",
cancelLabel: "cancel",
cancelPrompt: "Cancel this import",
statusOpenWorkspace: "Opening the workspace",
statusGetTiddlerList: "Getting the list of available tiddlers",
errorGettingTiddlerList: "Error getting list of tiddlers, click Cancel to try again",
errorGettingTiddlerListHttp404: "Error retrieving tiddlers from url, please ensure the url exists. Click Cancel to try again.",
errorGettingTiddlerListHttp: "Error retrieving tiddlers from url, please ensure this url exists and is <a href=''>CORS</a> enabled",
errorGettingTiddlerListFile: "Error retrieving tiddlers from local file, please make sure the file is in the same directory as your TiddlyWiki. Click Cancel to try again.",
step3Title: "Step 3: Choose the tiddlers to import",
step3Html: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='true' name='chkSync'>Keep these tiddlers linked to this server so that you can synchronise subsequent changes</input><br><input type='checkbox' name='chkSave'>Save the details of this server in a 'systemServer' tiddler called:</input> <input type='text' size=25 name='txtSaveTiddler'>",
importLabel: "import",
importPrompt: "Import these tiddlers",
confirmOverwriteText: "Are you sure you want to overwrite these tiddlers:\n\n%0",
step4Title: "Step 4: Importing %0 tiddler(s)",
step4Html: "<input type='hidden' name='markReport'></input>", // DO NOT TRANSLATE
doneLabel: "done",
donePrompt: "Close this wizard",
statusDoingImport: "Importing tiddlers",
statusDoneImport: "All tiddlers imported",
systemServerNamePattern: "%2 on %1",
systemServerNamePatternNoWorkspace: "%1",
confirmOverwriteSaveTiddler: "The tiddler '%0' already exists. Click 'OK' to overwrite it with the details of this server, or 'Cancel' to leave it unchanged",
serverSaveTemplate: "|''Type:''|%0|\n|''URL:''|%1|\n|''Workspace:''|%2|\n\nThis tiddler was automatically created to record the details of this server",
serverSaveModifier: "(System)",
listViewTemplate: {
columns: [
{name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
{name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'},
{name: 'Tags', field: 'tags', title: "Tags", type: 'Tags'}
rowClasses: [
wizardTitle: "Upgrade TiddlyWiki core code",
step1Title: "Update or repair this TiddlyWiki to the latest release",
step1Html: "You are about to upgrade to the latest release of the TiddlyWiki core code (from <a href='%0' class='externalLink' target='_blank'>%1</a>). Your content will be preserved across the upgrade.<br><br>Note that core upgrades have been known to interfere with older plugins. If you run into problems with the upgraded file, see <a href='' class='externalLink' target='_blank'></a>",
errorCantUpgrade: "Unable to upgrade this TiddlyWiki. You can only perform upgrades on TiddlyWiki files stored locally",
errorNotSaved: "You must save changes before you can perform an upgrade",
step2Title: "Confirm the upgrade details",
step2Html_downgrade: "You are about to downgrade to TiddlyWiki version %0 from %1.<br><br>Downgrading to an earlier version of the core code is not recommended",
step2Html_restore: "This TiddlyWiki appears to be already using the latest version of the core code (%0).<br><br>You can continue to upgrade anyway to ensure that the core code hasn't been corrupted or damaged",
step2Html_upgrade: "You are about to upgrade to TiddlyWiki version %0 from %1",
upgradeLabel: "upgrade",
upgradePrompt: "Prepare for the upgrade process",
statusPreparingBackup: "Preparing backup",
statusSavingBackup: "Saving backup file",
errorSavingBackup: "There was a problem saving the backup file",
statusLoadingCore: "Loading core code",
errorLoadingCore: "Error loading the core code",
errorCoreFormat: "Error with the new core code",
statusSavingCore: "Saving the new core code",
statusReloadingCore: "Reloading the new core code",
startLabel: "start",
startPrompt: "Start the upgrade process",
cancelLabel: "cancel",
cancelPrompt: "Cancel the upgrade process",
step3Title: "Upgrade cancelled",
step3Html: "You have cancelled the upgrade process"
listViewTemplate: {
columns: [
{name: 'Selected', field: 'selected', rowName: 'title', type: 'Selector'},
{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
{name: 'Server Type', field: 'serverType', title: "Server type", type: 'String'},
{name: 'Server Host', field: 'serverHost', title: "Server host", type: 'String'},
{name: 'Server Workspace', field: 'serverWorkspace', title: "Server workspace", type: 'String'},
{name: 'Status', field: 'status', title: "Synchronisation status", type: 'String'},
{name: 'Server URL', field: 'serverUrl', title: "Server URL", text: "View", type: 'Link'}
rowClasses: [
buttons: [
{caption: "Sync these tiddlers", name: 'sync'}
wizardTitle: "Synchronize with external servers and files",
step1Title: "Choose the tiddlers you want to synchronize",
step1Html: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
syncLabel: "sync",
syncPrompt: "Sync these tiddlers",
hasChanged: "Changed while unplugged",
hasNotChanged: "Unchanged while unplugged",
syncStatusList: {
none: {text: "...", display:'none', className:'notChanged'},
changedServer: {text: "Changed on server", display:null, className:'changedServer'},
changedLocally: {text: "Changed while unplugged", display:null, className:'changedLocally'},
changedBoth: {text: "Changed while unplugged and on server", display:null, className:'changedBoth'},
notFound: {text: "Not found on server", display:null, className:'notFound'},
putToServer: {text: "Saved update on server", display:null, className:'putToServer'},
gotFromServer: {text: "Retrieved update from server", display:null, className:'gotFromServer'}
text: "close",
tooltip: "Close this tiddler"});
text: "close others",
tooltip: "Close all other tiddlers"});
text: "edit",
tooltip: "Edit this tiddler",
readOnlyText: "view",
readOnlyTooltip: "View the source of this tiddler"});
text: "done",
tooltip: "Save changes to this tiddler"});
text: "cancel",
tooltip: "Undo changes to this tiddler",
warning: "Are you sure you want to abandon your changes to '%0'?",
readOnlyText: "done",
readOnlyTooltip: "View this tiddler normally"});
text: "delete",
tooltip: "Delete this tiddler",
warning: "Are you sure you want to delete '%0'?"});
text: "permalink",
tooltip: "Permalink for this tiddler"});
text: "references",
tooltip: "Show tiddlers that link to this one",
popupNone: "No references"});
text: "jump",
tooltip: "Jump to another open tiddler"});
text: "syncing",
tooltip: "Control synchronisation of this tiddler with a server or external file",
currentlySyncing: "<div>Currently syncing via <span class='popupHighlight'>'%0'</span> to:</"+"div><div>host: <span class='popupHighlight'>%1</span></"+"div><div>workspace: <span class='popupHighlight'>%2</span></"+"div>", // Note escaping of closing <div> tag
notCurrentlySyncing: "Not currently syncing",
captionUnSync: "Stop synchronising this tiddler",
chooseServer: "Synchronise this tiddler with another server:",
currServerMarker: "\u25cf ",
notCurrServerMarker: " "});
text: "fields",
tooltip: "Show the extended fields of this tiddler",
emptyText: "There are no extended fields for this tiddler",
listViewTemplate: {
columns: [
{name: 'Field', field: 'field', title: "Field", type: 'String'},
{name: 'Value', field: 'value', title: "Value", type: 'String'}
rowClasses: [
buttons: [
DefaultTiddlers: "[[GettingStarted]]",
MainMenu: "[[GettingStarted]]",
SiteTitle: "My TiddlyWiki",
SiteSubtitle: "a reusable non-linear personal web notebook",
SiteUrl: "",
SideBarOptions: '<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal "DD MMM YYYY" "journal">><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel "options \u00bb" "Change TiddlyWiki advanced options">>',
SideBarTabs: '<<tabs txtMainTab "Timeline" "Timeline" TabTimeline "All" "All tiddlers" TabAll "Tags" "All tags" TabTags "More" "More lists" TabMore>>',
TabMore: '<<tabs txtMoreTab "Missing" "Missing tiddlers" TabMoreMissing "Orphans" "Orphaned tiddlers" TabMoreOrphans "Shadowed" "Shadowed tiddlers" TabMoreShadowed>>'
AdvancedOptions: "This shadow tiddler provides access to several advanced options",
ColorPalette: "These values in this shadow tiddler determine the colour scheme of the ~TiddlyWiki user interface",
DefaultTiddlers: "The tiddlers listed in this shadow tiddler will be automatically displayed when ~TiddlyWiki starts up",
EditTemplate: "The HTML template in this shadow tiddler determines how tiddlers look while they are being edited",
GettingStarted: "This shadow tiddler provides basic usage instructions",
ImportTiddlers: "This shadow tiddler provides access to importing tiddlers",
MainMenu: "This shadow tiddler is used as the contents of the main menu in the left-hand column of the screen",
MarkupPreHead: "This tiddler is inserted at the top of the <head> section of the TiddlyWiki HTML file",
MarkupPostHead: "This tiddler is inserted at the bottom of the <head> section of the TiddlyWiki HTML file",
MarkupPreBody: "This tiddler is inserted at the top of the <body> section of the TiddlyWiki HTML file",
MarkupPostBody: "This tiddler is inserted at the end of the <body> section of the TiddlyWiki HTML file immediately after the script block",
OptionsPanel: "This shadow tiddler is used as the contents of the options panel slider in the right-hand sidebar",
PageTemplate: "The HTML template in this shadow tiddler determines the overall ~TiddlyWiki layout",
PluginManager: "This shadow tiddler provides access to the plugin manager",
SideBarOptions: "This shadow tiddler is used as the contents of the option panel in the right-hand sidebar",
SideBarTabs: "This shadow tiddler is used as the contents of the tabs panel in the right-hand sidebar",
SiteSubtitle: "This shadow tiddler is used as the second part of the page title",
SiteTitle: "This shadow tiddler is used as the first part of the page title",
SiteUrl: "This shadow tiddler should be set to the full target URL for publication",
StyleSheetColors: "This shadow tiddler contains CSS definitions related to the color of page elements. ''DO NOT EDIT THIS TIDDLER'', instead make your changes in the StyleSheet shadow tiddler",
StyleSheet: "This tiddler can contain custom CSS definitions",
StyleSheetLayout: "This shadow tiddler contains CSS definitions related to the layout of page elements. ''DO NOT EDIT THIS TIDDLER'', instead make your changes in the StyleSheet shadow tiddler",
StyleSheetLocale: "This shadow tiddler contains CSS definitions related to the translation locale",
StyleSheetPrint: "This shadow tiddler contains CSS definitions for printing",
SystemSettings: "This tiddler is used to store configuration options for this TiddlyWiki document",
TabAll: "This shadow tiddler contains the contents of the 'All' tab in the right-hand sidebar",
TabMore: "This shadow tiddler contains the contents of the 'More' tab in the right-hand sidebar",
TabMoreMissing: "This shadow tiddler contains the contents of the 'Missing' tab in the right-hand sidebar",
TabMoreOrphans: "This shadow tiddler contains the contents of the 'Orphans' tab in the right-hand sidebar",
TabMoreShadowed: "This shadow tiddler contains the contents of the 'Shadowed' tab in the right-hand sidebar",
TabTags: "This shadow tiddler contains the contents of the 'Tags' tab in the right-hand sidebar",
TabTimeline: "This shadow tiddler contains the contents of the 'Timeline' tab in the right-hand sidebar",
ToolbarCommands: "This shadow tiddler determines which commands are shown in tiddler toolbars",
ViewTemplate: "The HTML template in this shadow tiddler determines how tiddlers look"
//-- Main
var params = null; // Command line parameters
var store = null; // TiddlyWiki storage
var story = null; // Main story
var formatter = null; // Default formatters for the wikifier
var anim = typeof Animator == "function" ? new Animator() : null; // Animation engine
var readOnly = false; // Whether we're in readonly mode
var highlightHack = null; // Embarrassing hack department...
var hadConfirmExit = false; // Don't warn more than once
var safeMode = false; // Disable all plugins and cookies
var showBackstage; // Whether to include the backstage area
var installedPlugins = []; // Information filled in when plugins are executed
var startingUp = false; // Whether we're in the process of starting up
var pluginInfo,tiddler; // Used to pass information to plugins in loadPlugins()
// Whether to use the JavaSaver applet
var useJavaSaver = (config.browser.isSafari || config.browser.isOpera) && (document.location.toString().substr(0,4) != "http");
if(!window || !window.console) {
console = {tiddlywiki:true,log:function(message) {displayMessage(message);}};
// Starting up
function main()
var t10,t9,t8,t7,t6,t5,t4,t3,t2,t1,t0 = new Date();
startingUp = true;
var doc = jQuery(document);
window.onbeforeunload = function(e) {if(window.confirmExit) return confirmExit();};
params = getParameters();
params = params.parseParams("open",null,false);
store = new TiddlyWiki({config:config});
story = new Story("tiddlerDisplay","tiddler");
var s;
for(s=0; s<config.notifyTiddlers.length; s++)
t1 = new Date();
t2 = new Date();
t3 = new Date();
t4 = new Date();
readOnly = (window.location.protocol == "file:") ? false : config.options.chkHttpReadOnly;
var pluginProblem = loadPlugins("systemConfig");
t5 = new Date();
formatter = new Formatter(config.formatters);
showBackstage = showBackstage !== undefined ? showBackstage : !readOnly;
t6 = new Date();
var m;
for(m in config.macros) {
t7 = new Date();
t8 = new Date();
t9 = new Date();
if(pluginProblem) {
t10 = new Date();
if(config.options.chkDisplayInstrumentation) {
displayMessage("LoadShadows " + (t2-t1) + " ms");
displayMessage("LoadFromDiv " + (t3-t2) + " ms");
displayMessage("LoadPlugins " + (t5-t4) + " ms");
displayMessage("Macro init " + (t7-t6) + " ms");
displayMessage("Notify " + (t8-t7) + " ms");
displayMessage("Restart " + (t9-t8) + " ms");
displayMessage("Total: " + (t10-t0) + " ms");
startingUp = false;
// Called on unload. All functions called conditionally since they themselves may have been unloaded.
function unload()
// Restarting
function restart()
if(story.isEmpty()) {
function saveTest()
var s = document.getElementById("saveTest");
function loadShadowTiddlers()
var shadows = new TiddlyWiki();
shadows.forEachTiddler(function(title,tiddler){config.shadowTiddlers[title] = tiddler.text;});
function loadPlugins(tag)
return false;
var tiddlers = store.getTaggedTiddlers(tag);
tiddlers.sort(function(a,b) {return a.title < b.title ? -1 : (a.title