1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4 <script type="text/javascript">
6 var version = {major: 2, minor: 1, revision: 3, date: new Date("Nov 3, 2006"), extensions: {}};
10 TiddlyWiki 2.1.3 by Jeremy Ruston, (jeremy [at] osmosoft [dot] com)
12 Copyright (c) Osmosoft Limited 2004-2006
14 Redistribution and use in source and binary forms, with or without modification,
15 are permitted provided that the following conditions are met:
17 Redistributions of source code must retain the above copyright notice, this
18 list of conditions and the following disclaimer.
20 Redistributions in binary form must reproduce the above copyright notice, this
21 list of conditions and the following disclaimer in the documentation and/or other
22 materials provided with the distribution.
24 Neither the name of the Osmosoft Limited nor the names of its contributors may be
25 used to endorse or promote products derived from this software without specific
26 prior written permission.
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
29 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
30 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
31 SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
32 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
33 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
34 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
36 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
39 <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
42 <link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>
45 <title> axTLS Embedded SSL - changes, notes and errata </title>
46 <script type="text/javascript">
48 // ---------------------------------------------------------------------------------
49 // Configuration repository
50 // ---------------------------------------------------------------------------------
52 // Miscellaneous options
54 numRssItems: 20, // Number of items in the RSS feed
55 animFast: 0.12, // Speed for animations (lower == slower)
56 animSlow: 0.01, // Speed for EasterEgg animations
57 cascadeFast: 20, // Speed for cascade animations (higher == slower)
58 cascadeSlow: 60, // Speed for EasterEgg cascade animations
59 cascadeDepth: 5, // Depth of cascade animation
60 displayStartupTime: false // Whether to display startup time
69 // Options that can be set in the options panel and/or cookies
71 chkRegExpSearch: false,
72 chkCaseSensitiveSearch: false,
76 chkGenerateAnRssFeed: false,
77 chkSaveEmptyTemplate: false,
78 chkOpenInNewWindow: true,
79 chkToggleLinks: false,
80 chkHttpReadOnly: true,
81 chkForceMinorUpdate: false,
82 chkConfirmDelete: true,
85 txtMainTab: "tabTimeline",
86 txtMoreTab: "moreTabAll",
90 // List of notification functions to be called when certain tiddlers are changed or deleted
91 config.notifyTiddlers = [
92 {name: "StyleSheetLayout", notify: refreshStyles},
93 {name: "StyleSheetColors", notify: refreshStyles},
94 {name: "StyleSheet", notify: refreshStyles},
95 {name: "StyleSheetPrint", notify: refreshStyles},
96 {name: "PageTemplate", notify: refreshPageTemplate},
97 {name: "SiteTitle", notify: refreshPageTitle},
98 {name: "SiteSubtitle", notify: refreshPageTitle},
99 {name: "ColorPalette", notify: refreshColorPalette},
100 {name: null, notify: refreshDisplay}
103 // Default tiddler templates
104 var DEFAULT_VIEW_TEMPLATE = 1;
105 var DEFAULT_EDIT_TEMPLATE = 2;
106 config.tiddlerTemplates = {
111 // More messages (rather a legacy layout that shouldn't really be like this)
121 // Macros; each has a 'handler' member that is inserted later
125 search: {sizeTextbox: 15},
159 // Commands supported by the toolbar macro
164 saveTiddler: {hideReadOnly: true},
166 deleteTiddler: {hideReadOnly: true},
172 // Browser detection... In a very few places, there's nothing else for it but to
173 // know what browser we're using.
174 config.userAgent = navigator.userAgent.toLowerCase();
176 isIE: config.userAgent.indexOf("msie") != -1 && config.userAgent.indexOf("opera") == -1,
177 ieVersion: /MSIE (\d.\d)/i.exec(config.userAgent), // config.browser.ieVersion[1], if it exists, will be the IE version string, eg "6.0"
178 isSafari: config.userAgent.indexOf("applewebkit") != -1,
179 isBadSafari: !((new RegExp("[\u0150\u0170]","g")).test("\u0150")),
180 firefoxDate: /Gecko\/(\d{8})/i.exec(config.userAgent), // config.browser.firefoxDate[1], if it exists, will be Firefox release date as "YYYYMMDD"
181 isOpera: config.userAgent.indexOf("opera") != -1,
182 isLinux: config.userAgent.indexOf("linux") != -1,
183 isUnix: config.userAgent.indexOf("x11") != -1,
184 isMac: config.userAgent.indexOf("mac") != -1,
185 isWindows: config.userAgent.indexOf("win") != -1
188 // Basic regular expressions
189 config.textPrimitives = {
190 upperLetter: "[A-Z\u00c0-\u00de\u0150\u0170]",
191 lowerLetter: "[a-z0-9_\\-\u00df-\u00ff\u0151\u0171]",
192 anyLetter: "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]",
193 anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]"
195 if(config.browser.isBadSafari)
196 config.textPrimitives = {
197 upperLetter: "[A-Z\u00c0-\u00de]",
198 lowerLetter: "[a-z0-9_\\-\u00df-\u00ff]",
199 anyLetter: "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff]",
200 anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff]"
202 config.textPrimitives.sliceSeparator = "::";
203 config.textPrimitives.urlPattern = "[a-z]{3,8}:[^\\s:'\"][^\\s'\"]*(?:/|\\b)";
204 config.textPrimitives.unWikiLink = "~";
205 config.textPrimitives.wikiLink = "(?:(?:" + config.textPrimitives.upperLetter + "+" +
206 config.textPrimitives.lowerLetter + "+" +
207 config.textPrimitives.upperLetter +
208 config.textPrimitives.anyLetter + "*)|(?:" +
209 config.textPrimitives.upperLetter + "{2,}" +
210 config.textPrimitives.lowerLetter + "+))";
212 config.textPrimitives.cssLookahead = "(?:(" + config.textPrimitives.anyLetter + "+)\\(([^\\)\\|\\n]+)(?:\\):))|(?:(" + config.textPrimitives.anyLetter + "+):([^;\\|\\n]+);)";
213 config.textPrimitives.cssLookaheadRegExp = new RegExp(config.textPrimitives.cssLookahead,"mg");
215 config.textPrimitives.brackettedLink = "\\[\\[([^\\]]+)\\]\\]";
216 config.textPrimitives.titledBrackettedLink = "\\[\\[([^\\[\\]\\|]+)\\|([^\\[\\]\\|]+)\\]\\]";
217 config.textPrimitives.tiddlerForcedLinkRegExp = new RegExp("(?:" + config.textPrimitives.titledBrackettedLink + ")|(?:" +
218 config.textPrimitives.brackettedLink + ")|(?:" +
219 config.textPrimitives.urlPattern + ")","mg");
220 config.textPrimitives.tiddlerAnyLinkRegExp = new RegExp("("+ config.textPrimitives.wikiLink + ")|(?:" +
221 config.textPrimitives.titledBrackettedLink + ")|(?:" +
222 config.textPrimitives.brackettedLink + ")|(?:" +
223 config.textPrimitives.urlPattern + ")","mg");
225 // ---------------------------------------------------------------------------------
227 // ---------------------------------------------------------------------------------
229 config.shadowTiddlers = {
230 ColorPalette: "Background: #fff\n" +
231 "Foreground: #000\n" +
232 "PrimaryPale: #8cf\n" +
233 "PrimaryLight: #18f\n" +
234 "PrimaryMid: #04b\n" +
235 "PrimaryDark: #014\n" +
236 "SecondaryPale: #ffc\n" +
237 "SecondaryLight: #fe8\n" +
238 "SecondaryMid: #db4\n" +
239 "SecondaryDark: #841\n" +
240 "TertiaryPale: #eee\n" +
241 "TertiaryLight: #ccc\n" +
242 "TertiaryMid: #999\n" +
243 "TertiaryDark: #666\n" +
246 StyleSheetColors: "/*{{{*/\nbody {\n background: [[ColorPalette::Background]];\n color: [[ColorPalette::Foreground]];\n}\n\na{\n color: [[ColorPalette::PrimaryMid]];\n}\n\na:hover{\n background: [[ColorPalette::PrimaryMid]];\n color: [[ColorPalette::Background]];\n}\n\na img{\n border: 0;\n}\n\nh1,h2,h3,h4,h5 {\n color: [[ColorPalette::SecondaryDark]];\n background: [[ColorPalette::PrimaryPale]];\n}\n\n.button {\n color: [[ColorPalette::PrimaryDark]];\n border: 1px solid [[ColorPalette::Background]];\n}\n\n.button:hover {\n color: [[ColorPalette::PrimaryDark]];\n background: [[ColorPalette::SecondaryLight]];\n border-color: [[ColorPalette::SecondaryMid]];\n}\n\n.button:active {\n color: [[ColorPalette::Background]];\n background: [[ColorPalette::SecondaryMid]];\n border: 1px solid [[ColorPalette::SecondaryDark]];\n}\n\n.header {\n background: [[ColorPalette::PrimaryMid]];\n}\n\n.headerShadow {\n color: [[ColorPalette::Foreground]];\n}\n\n.headerShadow a {\n font-weight: normal;\n color: [[ColorPalette::Foreground]];\n}\n\n.headerForeground {\n color: [[ColorPalette::Background]];\n}\n\n.headerForeground a {\n font-weight: normal;\n color: [[ColorPalette::PrimaryPale]];\n}\n\n.tabSelected{\n color: [[ColorPalette::PrimaryDark]];\n background: [[ColorPalette::TertiaryPale]];\n border-left: 1px solid [[ColorPalette::TertiaryLight]];\n border-top: 1px solid [[ColorPalette::TertiaryLight]];\n border-right: 1px solid [[ColorPalette::TertiaryLight]];\n}\n\n.tabUnselected {\n color: [[ColorPalette::Background]];\n background: [[ColorPalette::TertiaryMid]];\n}\n\n.tabContents {\n color: [[ColorPalette::PrimaryDark]];\n background: [[ColorPalette::TertiaryPale]];\n border: 1px solid [[ColorPalette::TertiaryLight]];\n}\n\n.tabContents .button {\n border: 0;}\n\n#sidebar {\n}\n\n#sidebarOptions input {\n border: 1px solid [[ColorPalette::PrimaryMid]];\n}\n\n#sidebarOptions .sliderPanel {\n background: [[ColorPalette::PrimaryPale]];\n}\n\n#sidebarOptions .sliderPanel a {\n border: none;\n color: [[ColorPalette::PrimaryMid]];\n}\n\n#sidebarOptions .sliderPanel a:hover {\n color: [[ColorPalette::Background]];\n background: [[ColorPalette::PrimaryMid]];\n}\n\n#sidebarOptions .sliderPanel a:active {\n color: [[ColorPalette::PrimaryMid]];\n background: [[ColorPalette::Background]];\n}\n\n.wizard {\n background: [[ColorPalette::SecondaryLight]];\n border-top: 1px solid [[ColorPalette::SecondaryMid]];\n border-left: 1px solid [[ColorPalette::SecondaryMid]];\n}\n\n.wizard h1 {\n color: [[ColorPalette::SecondaryDark]];\n}\n\n.wizard h2 {\n color: [[ColorPalette::Foreground]];\n}\n\n.wizardStep {\n background: [[ColorPalette::Background]];\n border-top: 1px solid [[ColorPalette::SecondaryMid]];\n border-bottom: 1px solid [[ColorPalette::SecondaryMid]];\n border-left: 1px solid [[ColorPalette::SecondaryMid]];\n}\n\n.wizard .button {\n color: [[ColorPalette::Background]];\n background: [[ColorPalette::PrimaryMid]];\n border-top: 1px solid [[ColorPalette::PrimaryLight]];\n border-right: 1px solid [[ColorPalette::PrimaryDark]];\n border-bottom: 1px solid [[ColorPalette::PrimaryDark]];\n border-left: 1px solid [[ColorPalette::PrimaryLight]];\n}\n\n.wizard .button:hover {\n color: [[ColorPalette::PrimaryLight]];\n background: [[ColorPalette::PrimaryDark]];\n border-color: [[ColorPalette::PrimaryLight]];\n}\n\n.wizard .button:active {\n color: [[ColorPalette::Background]];\n background: [[ColorPalette::PrimaryMid]];\n border-top: 1px solid [[ColorPalette::PrimaryLight]];\n border-right: 1px solid [[ColorPalette::PrimaryDark]];\n border-bottom: 1px solid [[ColorPalette::PrimaryDark]];\n border-left: 1px solid [[ColorPalette::PrimaryLight]];\n}\n\n#messageArea {\n border: 1px solid [[ColorPalette::SecondaryDark]];\n background: [[ColorPalette::SecondaryMid]];\n color: [[ColorPalette::PrimaryDark]];\n}\n\n#messageArea .button {\n padding: 0.2em 0.2em 0.2em 0.2em;\n color: [[ColorPalette::PrimaryDark]];\n background: [[ColorPalette::Background]];\n}\n\n.popup {\n background: [[ColorPalette::PrimaryLight]];\n border: 1px solid [[ColorPalette::PrimaryMid]];\n}\n\n.popup hr {\n color: [[ColorPalette::PrimaryDark]];\n background: [[ColorPalette::PrimaryDark]];\n border-bottom: 1px;\n}\n\n.listBreak div{\n border-bottom: 1px solid [[ColorPalette::PrimaryDark]];\n}\n\n.popup li.disabled {\n color: [[ColorPalette::PrimaryMid]];\n}\n\n.popup li a, .popup li a:visited {\n color: [[ColorPalette::TertiaryPale]];\n border: none;\n}\n\n.popup li a:hover {\n background: [[ColorPalette::PrimaryDark]];\n color: [[ColorPalette::Background]];\n border: none;\n}\n\n.tiddler .defaultCommand {\n font-weight: bold;\n}\n\n.shadow .title {\n color: [[ColorPalette::TertiaryDark]];\n}\n\n.title {\n color: [[ColorPalette::SecondaryDark]];\n}\n\n.subtitle {\n color: [[ColorPalette::TertiaryDark]];\n}\n\n.toolbar {\n color: [[ColorPalette::PrimaryMid]];\n}\n\n.tagging, .tagged {\n border: 1px solid [[ColorPalette::TertiaryPale]];\n background-color: [[ColorPalette::TertiaryPale]];\n}\n\n.selected .tagging, .selected .tagged {\n background-color: [[ColorPalette::TertiaryLight]];\n border: 1px solid [[ColorPalette::TertiaryMid]];\n}\n\n.tagging .listTitle, .tagged .listTitle {\n color: [[ColorPalette::PrimaryDark]];\n}\n\n.tagging .button, .tagged .button {\n border: none;\n}\n\n.footer {\n color: [[ColorPalette::TertiaryLight]];\n}\n\n.selected .footer {\n color: [[ColorPalette::TertiaryMid]];\n}\n\n.sparkline {\n background: [[ColorPalette::PrimaryPale]];\n border: 0;\n}\n\n.sparktick {\n background: [[ColorPalette::PrimaryDark]];\n}\n\n.error, .errorButton {\n color: [[ColorPalette::Foreground]];\n background: [[ColorPalette::Error]];\n}\n\n.warning {\n color: [[ColorPalette::Foreground]];\n background: [[ColorPalette::SecondaryPale]];\n}\n\n.cascade {\n background: [[ColorPalette::TertiaryPale]];\n color: [[ColorPalette::TertiaryMid]];\n border: 1px solid [[ColorPalette::TertiaryMid]];\n}\n\n.imageLink, #displayArea .imageLink {\n background: transparent;\n}\n\n.viewer .listTitle {list-style-type: none; margin-left: -2em;}\n\n.viewer .button {\n border: 1px solid [[ColorPalette::SecondaryMid]];\n}\n\n.viewer blockquote {\n border-left: 3px solid [[ColorPalette::TertiaryDark]];\n}\n\n.viewer table {\n border: 2px solid [[ColorPalette::TertiaryDark]];\n}\n\n.viewer th, thead td {\n background: [[ColorPalette::SecondaryMid]];\n border: 1px solid [[ColorPalette::TertiaryDark]];\n color: [[ColorPalette::Background]];\n}\n\n.viewer td, .viewer tr {\n border: 1px solid [[ColorPalette::TertiaryDark]];\n}\n\n.viewer pre {\n border: 1px solid [[ColorPalette::SecondaryLight]];\n background: [[ColorPalette::SecondaryPale]];\n}\n\n.viewer code {\n color: [[ColorPalette::SecondaryDark]];\n}\n\n.viewer hr {\n border: 0;\n border-top: dashed 1px [[ColorPalette::TertiaryDark]];\n color: [[ColorPalette::TertiaryDark]];\n}\n\n.highlight, .marked {\n background: [[ColorPalette::SecondaryLight]];\n}\n\n.editor input {\n border: 1px solid [[ColorPalette::PrimaryMid]];\n}\n\n.editor textarea {\n border: 1px solid [[ColorPalette::PrimaryMid]];\n width: 100%;\n}\n\n.editorFooter {\n color: [[ColorPalette::TertiaryMid]];\n}\n\n/*}}}*/",
247 StyleSheetLayout: "/*{{{*/\n* html .tiddler {\n height: 1%;\n}\n\nbody {\n font-size: .75em;\n font-family: arial,helvetica;\n margin: 0;\n padding: 0;\n}\n\nh1,h2,h3,h4,h5 {\n font-weight: bold;\n text-decoration: none;\n padding-left: 0.4em;\n}\n\nh1 {font-size: 1.35em;}\nh2 {font-size: 1.25em;}\nh3 {font-size: 1.1em;}\nh4 {font-size: 1em;}\nh5 {font-size: .9em;}\n\nhr {\n height: 1px;\n}\n\na{\n text-decoration: none;\n}\n\ndt {font-weight: bold;}\n\nol { list-style-type: decimal }\nol ol { list-style-type: lower-alpha }\nol ol ol { list-style-type: lower-roman }\nol ol ol ol { list-style-type: decimal }\nol ol ol ol ol { list-style-type: lower-alpha }\nol ol ol ol ol ol { list-style-type: lower-roman }\nol ol ol ol ol ol ol { list-style-type: decimal }\n\n.txtOptionInput {\n width: 11em;\n}\n\n#contentWrapper .chkOptionInput {\n border: 0;\n}\n\n.externalLink {\n text-decoration: underline;\n}\n\n.indent {margin-left:3em;}\n.outdent {margin-left:3em; text-indent:-3em;}\ncode.escaped {white-space:nowrap;}\n\n.tiddlyLinkExisting {\n font-weight: bold;\n}\n\n.tiddlyLinkNonExisting {\n font-style: italic;\n}\n\n/* the 'a' is required for IE, otherwise it renders the whole tiddler a bold */\na.tiddlyLinkNonExisting.shadow {\n font-weight: bold;\n}\n\n#mainMenu .tiddlyLinkExisting, \n#mainMenu .tiddlyLinkNonExisting,\n#sidebarTabs .tiddlyLinkNonExisting{\n font-weight: normal;\n font-style: normal;\n}\n\n#sidebarTabs .tiddlyLinkExisting {\n font-weight: bold;\n font-style: normal;\n}\n\n.header {\n position: relative;\n}\n\n.header a:hover {\n background: transparent;\n}\n\n.headerShadow {\n position: relative;\n padding: 4.5em 0em 1em 1em;\n left: -1px;\n top: -1px;\n}\n\n.headerForeground {\n position: absolute;\n padding: 4.5em 0em 1em 1em;\n left: 0px;\n top: 0px;\n}\n\n.siteTitle {\n font-size: 3em;\n}\n\n.siteSubtitle {\n font-size: 1.2em;\n}\n\n#mainMenu {\n position: absolute;\n left: 0;\n width: 10em;\n text-align: right;\n line-height: 1.6em;\n padding: 1.5em 0.5em 0.5em 0.5em;\n font-size: 1.1em;\n}\n\n#sidebar {\n position: absolute;\n right: 3px;\n width: 16em;\n font-size: .9em;\n}\n\n#sidebarOptions {\n padding-top: 0.3em;\n}\n\n#sidebarOptions a {\n margin: 0em 0.2em;\n padding: 0.2em 0.3em;\n display: block;\n}\n\n#sidebarOptions input {\n margin: 0.4em 0.5em;\n}\n\n#sidebarOptions .sliderPanel {\n margin-left: 1em;\n padding: 0.5em;\n font-size: .85em;\n}\n\n#sidebarOptions .sliderPanel a {\n font-weight: bold;\n display: inline;\n padding: 0;\n}\n\n#sidebarOptions .sliderPanel input {\n margin: 0 0 .3em 0;\n}\n\n#sidebarTabs .tabContents {\n width: 15em;\n overflow: hidden;\n}\n\n.wizard {\n padding: 0.1em 0em 0em 2em;\n}\n\n.wizard h1 {\n font-size: 2em;\n font-weight: bold;\n background: none;\n padding: 0em 0em 0em 0em;\n margin: 0.4em 0em 0.2em 0em;\n}\n\n.wizard h2 {\n font-size: 1.2em;\n font-weight: bold;\n background: none;\n padding: 0em 0em 0em 0em;\n margin: 0.2em 0em 0.2em 0em;\n}\n\n.wizardStep {\n padding: 1em 1em 1em 1em;\n}\n\n.wizard .button {\n margin: 0.5em 0em 0em 0em;\n font-size: 1.2em;\n}\n\n#messageArea {\nposition:absolute; top:0; right:0; margin: 0.5em; padding: 0.5em;\n}\n\n*[id='messageArea'] {\nposition:fixed !important; z-index:99;}\n\n.messageToolbar {\ndisplay: block;\ntext-align: right;\n}\n\n#messageArea a{\n text-decoration: underline;\n}\n\n.popup {\n font-size: .9em;\n padding: 0.2em;\n list-style: none;\n margin: 0;\n}\n\n.popup hr {\n display: block;\n height: 1px;\n width: auto;\n padding: 0;\n margin: 0.2em 0em;\n}\n\n.listBreak {\n font-size: 1px;\n line-height: 1px;\n}\n\n.listBreak div {\n margin: 2px 0;\n}\n\n.popup li.disabled {\n padding: 0.2em;\n}\n\n.popup li a{\n display: block;\n padding: 0.2em;\n}\n\n.tabset {\n padding: 1em 0em 0em 0.5em;\n}\n\n.tab {\n margin: 0em 0em 0em 0.25em;\n padding: 2px;\n}\n\n.tabContents {\n padding: 0.5em;\n}\n\n.tabContents ul, .tabContents ol {\n margin: 0;\n padding: 0;\n}\n\n.txtMainTab .tabContents li {\n list-style: none;\n}\n\n.tabContents li.listLink {\n margin-left: .75em;\n}\n\n#displayArea {\n margin: 1em 17em 0em 14em;\n}\n\n\n.toolbar {\n text-align: right;\n font-size: .9em;\n visibility: hidden;\n}\n\n.selected .toolbar {\n visibility: visible;\n}\n\n.tiddler {\n padding: 1em 1em 0em 1em;\n}\n\n.missing .viewer,.missing .title {\n font-style: italic;\n}\n\n.title {\n font-size: 1.6em;\n font-weight: bold;\n}\n\n.missing .subtitle {\n display: none;\n}\n\n.subtitle {\n font-size: 1.1em;\n}\n\n.tiddler .button {\n padding: 0.2em 0.4em;\n}\n\n.tagging {\nmargin: 0.5em 0.5em 0.5em 0;\nfloat: left;\ndisplay: none;\n}\n\n.isTag .tagging {\ndisplay: block;\n}\n\n.tagged {\nmargin: 0.5em;\nfloat: right;\n}\n\n.tagging, .tagged {\nfont-size: 0.9em;\npadding: 0.25em;\n}\n\n.tagging ul, .tagged ul {\nlist-style: none;margin: 0.25em;\npadding: 0;\n}\n\n.tagClear {\nclear: both;\n}\n\n.footer {\n font-size: .9em;\n}\n\n.footer li {\ndisplay: inline;\n}\n\n* html .viewer pre {\n width: 99%;\n padding: 0 0 1em 0;\n}\n\n.viewer {\n line-height: 1.4em;\n padding-top: 0.5em;\n}\n\n.viewer .button {\n margin: 0em 0.25em;\n padding: 0em 0.25em;\n}\n\n.viewer blockquote {\n line-height: 1.5em;\n padding-left: 0.8em;\n margin-left: 2.5em;\n}\n\n.viewer ul, .viewer ol{\n margin-left: 0.5em;\n padding-left: 1.5em;\n}\n\n.viewer table {\n border-collapse: collapse;\n margin: 0.8em 1.0em;\n}\n\n.viewer th, .viewer td, .viewer tr,.viewer caption{\n padding: 3px;\n}\n\n.viewer table.listView {\n font-size: 0.85em;\n margin: 0.8em 1.0em;\n}\n\n.viewer table.listView th, .viewer table.listView td, .viewer table.listView tr {\n padding: 0px 3px 0px 3px;\n}\n\n.viewer pre {\n padding: 0.5em;\n margin-left: 0.5em;\n font-size: 1.2em;\n line-height: 1.4em;\n overflow: auto;\n}\n\n.viewer code {\n font-size: 1.2em;\n line-height: 1.4em;\n}\n\n.editor {\nfont-size: 1.1em;\n}\n\n.editor input, .editor textarea {\n display: block;\n width: 100%;\n font: inherit;\n}\n\n.editorFooter {\n padding: 0.25em 0em;\n font-size: .9em;\n}\n\n.editorFooter .button {\npadding-top: 0px; padding-bottom: 0px;}\n\n.fieldsetFix {border: 0;\npadding: 0;\nmargin: 1px 0px 1px 0px;\n}\n\n.sparkline {\n line-height: 1em;\n}\n\n.sparktick {\n outline: 0;\n}\n\n.zoomer {\n font-size: 1.1em;\n position: absolute;\n padding: 1em;\n}\n\n.cascade {\n font-size: 1.1em;\n position: absolute;\n overflow: hidden;\n}\n/*}}}*/",
248 StyleSheetPrint: "/*{{{*/\n@media print {\n#mainMenu, #sidebar, #messageArea, .toolbar {display: none ! important;}\n#displayArea {margin: 1em 1em 0em 1em;}\n/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */\nnoscript {display:none;}\n}\n/*}}}*/",
249 PageTemplate: "<!--{{{-->\n<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>\n<div class='headerShadow'>\n<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span> \n<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>\n</div>\n<div class='headerForeground'>\n<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span> \n<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>\n</div>\n</div>\n<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>\n<div id='sidebar'>\n<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>\n<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>\n</div>\n<div id='displayArea'>\n<div id='messageArea'></div>\n<div id='tiddlerDisplay'></div>\n</div>\n<!--}}}-->",
250 ViewTemplate: "<!--{{{-->\n<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler permalink references jump'></div>\n<div class='title' macro='view title'></div>\n<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date [[DD MMM YYYY]]'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date [[DD MMM YYYY]]'></span>)</div>\n<div class='tagging' macro='tagging'></div>\n<div class='tagged' macro='tags'></div>\n<div class='viewer' macro='view text wikified'></div>\n<div class='tagClear'></div>\n<!--}}}-->",
251 EditTemplate: "<!--{{{-->\n<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>\n<div class='title' macro='view title'></div>\n<div class='editor' macro='edit title'></div>\n<div class='editor' macro='edit text'></div>\n<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>\n<!--}}}-->",
252 MarkupPreHead: "<!--{{{-->\n<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>\n<!--}}}-->",
258 // ---------------------------------------------------------------------------------
259 // Translateable strings
260 // ---------------------------------------------------------------------------------
262 // Strings in "double quotes" should be translated; strings in 'single quotes' should be left alone
264 merge(config.options,{
265 txtUserName: "YourName"});
267 merge(config.messages,{
268 customConfigError: "Problems were encountered loading plugins. See PluginManager for details",
269 pluginError: "Error: %0",
270 pluginDisabled: "Not executed because disabled via 'systemConfigDisable' tag",
271 pluginForced: "Executed because forced via 'systemConfigForce' tag",
272 pluginVersionError: "Not executed because this plugin needs a newer version of TiddlyWiki",
273 nothingSelected: "Nothing is selected. You must select one or more items first",
274 savedSnapshotError: "It appears that this TiddlyWiki has been incorrectly saved. Please see http://www.tiddlywiki.com/#DownloadSoftware for details",
275 subtitleUnknown: "(unknown)",
276 undefinedTiddlerToolTip: "The tiddler '%0' doesn't yet exist",
277 shadowedTiddlerToolTip: "The tiddler '%0' doesn't yet exist, but has a pre-defined shadow value",
278 tiddlerLinkTooltip: "%0 - %1, %2",
279 externalLinkTooltip: "External link to %0",
280 noTags: "There are no tagged tiddlers",
281 notFileUrlError: "You need to save this TiddlyWiki to a file before you can save changes",
282 cantSaveError: "It's not possible to save changes. This could be because your browser doesn't support saving (instead, use FireFox if you can), or because the pathname to your TiddlyWiki file contains illegal characters",
283 invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
284 backupSaved: "Backup saved",
285 backupFailed: "Failed to save backup file",
286 rssSaved: "RSS feed saved",
287 rssFailed: "Failed to save RSS feed file",
288 emptySaved: "Empty template saved",
289 emptyFailed: "Failed to save empty template file",
290 mainSaved: "Main TiddlyWiki file saved",
291 mainFailed: "Failed to save main TiddlyWiki file. Your changes have not been saved",
292 macroError: "Error in macro <<%0>>",
293 macroErrorDetails: "Error while executing macro <<%0>>:\n%1",
294 missingMacro: "No such macro",
295 overwriteWarning: "A tiddler named '%0' already exists. Choose OK to overwrite it",
296 unsavedChangesWarning: "WARNING! There are unsaved changes in TiddlyWiki\n\nChoose OK to save\nChoose CANCEL to discard",
297 confirmExit: "--------------------------------\n\nThere are unsaved changes in TiddlyWiki. If you continue you will lose those changes\n\n--------------------------------",
298 saveInstructions: "SaveChanges",
299 unsupportedTWFormat: "Unsupported TiddlyWiki format '%0'",
300 tiddlerSaveError: "Error when saving tiddler '%0'",
301 tiddlerLoadError: "Error when loading tiddler '%0'",
302 wrongSaveFormat: "Cannot save with storage format '%0'. Using standard format for save.",
303 invalidFieldName: "Invalid field name %0",
304 fieldCannotBeChanged: "Field '%0' cannot be changed"});
306 merge(config.messages.messageClose,{
308 tooltip: "close this message area"});
310 config.messages.dates.months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November","December"];
311 config.messages.dates.days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
312 config.messages.dates.shortMonths = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
313 config.messages.dates.shortDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
315 merge(config.views.wikified.tag,{
316 labelNoTags: "no tags",
318 openTag: "Open tag '%0'",
319 tooltip: "Show tiddlers tagged with '%0'",
320 openAllText: "Open all",
321 openAllTooltip: "Open all of these tiddlers",
322 popupNone: "No other tiddlers tagged with '%0'"});
324 merge(config.views.wikified,{
325 defaultText: "The tiddler '%0' doesn't yet exist. Double-click to create it",
326 defaultModifier: "(missing)",
327 shadowModifier: "(built-in shadow tiddler)",
328 createdPrompt: "created"});
330 merge(config.views.editor,{
331 tagPrompt: "Type tags separated with spaces, [[use double square brackets]] if necessary, or add existing",
332 defaultText: "Type the text for '%0'"});
334 merge(config.views.editor.tagChooser,{
336 tooltip: "Choose existing tags to add to this tiddler",
337 popupNone: "There are no tags defined",
338 tagTooltip: "Add the tag '%0'"});
340 merge(config.macros.search,{
342 prompt: "Search this TiddlyWiki",
344 successMsg: "%0 tiddlers found matching %1",
345 failureMsg: "No tiddlers found matching %0"});
347 merge(config.macros.tagging,{
349 labelNotTag: "not tagging",
350 tooltip: "List of tiddlers tagged with '%0'"});
352 merge(config.macros.timeline,{
353 dateFormat: "DD MMM YYYY"});
355 merge(config.macros.allTags,{
356 tooltip: "Show tiddlers tagged with '%0'",
357 noTags: "There are no tagged tiddlers"});
359 config.macros.list.all.prompt = "All tiddlers in alphabetical order";
360 config.macros.list.missing.prompt = "Tiddlers that have links to them but are not defined";
361 config.macros.list.orphans.prompt = "Tiddlers that are not linked to from any other tiddlers";
362 config.macros.list.shadowed.prompt = "Tiddlers shadowed with default contents";
364 merge(config.macros.closeAll,{
366 prompt: "Close all displayed tiddlers (except any that are being edited)"});
368 merge(config.macros.permaview,{
370 prompt: "Link to an URL that retrieves all the currently displayed tiddlers"});
372 merge(config.macros.saveChanges,{
373 label: "save changes",
374 prompt: "Save all tiddlers to create a new TiddlyWiki",
377 merge(config.macros.newTiddler,{
378 label: "new tiddler",
379 prompt: "Create a new tiddler",
380 title: "New Tiddler",
383 merge(config.macros.newJournal,{
384 label: "new journal",
385 prompt: "Create a new tiddler from the current date and time",
388 merge(config.macros.plugins,{
389 skippedText: "(This plugin has not been executed because it was added since startup)",
390 noPluginText: "There are no plugins installed",
391 confirmDeleteText: "Are you sure you want to delete these tiddlers:\n\n%0",
394 {name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
395 {name: 'Title', field: 'title', tiddlerLink: 'title', title: "Title", type: 'TiddlerLink'},
396 {name: 'Forced', field: 'forced', title: "Forced", tag: 'systemConfigForce', type: 'TagCheckbox'},
397 {name: 'Disabled', field: 'disabled', title: "Disabled", tag: 'systemConfigDisable', type: 'TagCheckbox'},
398 {name: 'Executed', field: 'executed', title: "Loaded", type: 'Boolean', trueText: "Yes", falseText: "No"},
399 {name: 'Error', field: 'error', title: "Status", type: 'Boolean', trueText: "Error", falseText: "OK"},
400 {name: 'Log', field: 'log', title: "Log", type: 'StringList'}
403 {className: 'error', field: 'error'},
404 {className: 'warning', field: 'warning'}
407 {caption: "More actions...", name: ''},
408 {caption: "Remove systemConfig tag", name: 'remove'},
409 {caption: "Delete these tiddlers forever", name: 'delete'}
413 merge(config.macros.refreshDisplay,{
415 prompt: "Redraw the entire TiddlyWiki display"
418 merge(config.macros.importTiddlers,{
419 readOnlyWarning: "You cannot import tiddlers into a read-only TiddlyWiki. Try opening the TiddlyWiki file from a file:// URL",
420 defaultPath: "http://www.tiddlywiki.com/index.html",
422 fetchPrompt: "Fetch the tiddlywiki file",
423 fetchError: "There were problems fetching the tiddlywiki file",
424 confirmOverwriteText: "Are you sure you want to overwrite these tiddlers:\n\n%0",
425 wizardTitle: "Import tiddlers from another TiddlyWiki file",
426 step1: "Step 1: Locate the TiddlyWiki file",
427 step1prompt: "Enter the URL or pathname here: ",
428 step1promptFile: "...or browse for a file: ",
429 step1promptFeeds: "...or select a pre-defined feed: ",
430 step1feedPrompt: "Choose...",
431 step2: "Step 2: Loading TiddlyWiki file",
432 step2Text: "Please wait while the file is loaded from: %0",
433 step3: "Step 3: Choose the tiddlers to import",
434 step4: "%0 tiddler(s) imported",
438 {name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
439 {name: 'Title', field: 'title', title: "Title", type: 'String'},
440 {name: 'Snippet', field: 'text', title: "Snippet", type: 'String'},
441 {name: 'Tags', field: 'tags', title: "Tags", type: 'Tags'}
446 {caption: "More actions...", name: ''},
447 {caption: "Import these tiddlers", name: 'import'}
451 merge(config.commands.closeTiddler,{
453 tooltip: "Close this tiddler"});
455 merge(config.commands.closeOthers,{
456 text: "close others",
457 tooltip: "Close all other tiddlers"});
459 merge(config.commands.editTiddler,{
461 tooltip: "Edit this tiddler",
462 readOnlyText: "view",
463 readOnlyTooltip: "View the source of this tiddler"});
465 merge(config.commands.saveTiddler,{
467 tooltip: "Save changes to this tiddler"});
469 merge(config.commands.cancelTiddler,{
471 tooltip: "Undo changes to this tiddler",
472 warning: "Are you sure you want to abandon your changes to '%0'?",
473 readOnlyText: "done",
474 readOnlyTooltip: "View this tiddler normally"});
476 merge(config.commands.deleteTiddler,{
478 tooltip: "Delete this tiddler",
479 warning: "Are you sure you want to delete '%0'?"});
481 merge(config.commands.permalink,{
483 tooltip: "Permalink for this tiddler"});
485 merge(config.commands.references,{
487 tooltip: "Show tiddlers that link to this one",
488 popupNone: "No references"});
490 merge(config.commands.jump,{
492 tooltip: "Jump to another open tiddler"});
494 merge(config.shadowTiddlers,{
495 DefaultTiddlers: "GettingStarted",
496 MainMenu: "GettingStarted",
497 SiteTitle: "My TiddlyWiki",
498 SiteSubtitle: "a reusable non-linear personal web notebook",
499 SiteUrl: "http://www.tiddlywiki.com/",
500 GettingStarted: "To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:\n* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)\n* MainMenu: The menu (usually on the left)\n* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened\nYou'll also need to enter your username for signing your edits: <<option txtUserName>>",
501 SideBarOptions: "<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal 'DD MMM YYYY'>><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel 'options »' 'Change TiddlyWiki advanced options'>>",
502 OptionsPanel: "These InterfaceOptions for customising TiddlyWiki are saved in your browser\n\nYour username for signing your edits. Write it as a WikiWord (eg JoeBloggs)\n\n<<option txtUserName>>\n<<option chkSaveBackups>> SaveBackups\n<<option chkAutoSave>> AutoSave\n<<option chkRegExpSearch>> RegExpSearch\n<<option chkCaseSensitiveSearch>> CaseSensitiveSearch\n<<option chkAnimate>> EnableAnimations\n\n----\nAdvancedOptions\nPluginManager\nImportTiddlers",
503 AdvancedOptions: "<<option chkGenerateAnRssFeed>> GenerateAnRssFeed\n<<option chkOpenInNewWindow>> OpenLinksInNewWindow\n<<option chkSaveEmptyTemplate>> SaveEmptyTemplate\n<<option chkToggleLinks>> Clicking on links to tiddlers that are already open causes them to close\n^^(override with Control or other modifier key)^^\n<<option chkHttpReadOnly>> HideEditingFeatures when viewed over HTTP\n<<option chkForceMinorUpdate>> Treat edits as MinorChanges by preserving date and time\n^^(override with Shift key when clicking 'done' or by pressing Ctrl-Shift-Enter^^\n<<option chkConfirmDelete>> ConfirmBeforeDeleting\nMaximum number of lines in a tiddler edit box: <<option txtMaxEditRows>>\nFolder name for backup files: <<option txtBackupFolder>>\n<<option chkInsertTabs>> Use tab key to insert tab characters instead of jumping to next field",
504 SideBarTabs: "<<tabs txtMainTab Timeline Timeline TabTimeline All 'All tiddlers' TabAll Tags 'All tags' TabTags More 'More lists' TabMore>>",
505 TabTimeline: "<<timeline>>",
506 TabAll: "<<list all>>",
507 TabTags: "<<allTags>>",
508 TabMore: "<<tabs txtMoreTab Missing 'Missing tiddlers' TabMoreMissing Orphans 'Orphaned tiddlers' TabMoreOrphans Shadowed 'Shadowed tiddlers' TabMoreShadowed>>",
509 TabMoreMissing: "<<list missing>>",
510 TabMoreOrphans: "<<list orphans>>",
511 TabMoreShadowed: "<<list shadowed>>",
512 PluginManager: "<<plugins>>",
513 ImportTiddlers: "<<importTiddlers>>"});
515 // ---------------------------------------------------------------------------------
517 // ---------------------------------------------------------------------------------
519 var params = null; // Command line parameters
520 var store = null; // TiddlyWiki storage
521 var story = null; // Main story
522 var formatter = null; // Default formatters for the wikifier
523 config.parsers = {}; // Hashmap of alternative parsers for the wikifier
524 var anim = new Animator(); // Animation engine
525 var readOnly = false; // Whether we're in readonly mode
526 var highlightHack = null; // Embarrassing hack department...
527 var hadConfirmExit = false; // Don't warn more than once
528 var safeMode = false; // Disable all plugins and cookies
529 var installedPlugins = []; // Information filled in when plugins are executed
530 var startingUp = false; // Whether we're in the process of starting up
531 var pluginInfo,tiddler; // Used to pass information to plugins in loadPlugins()
533 // Whether to use the JavaSaver applet
534 var useJavaSaver = config.browser.isSafari || config.browser.isOpera;
539 var now, then = new Date();
541 window.onbeforeunload = function(e) {if(window.confirmExit) return confirmExit();};
542 params = getParameters();
544 params = params.parseParams("open",null,false);
545 store = new TiddlyWiki();
546 invokeParamifier(params,"oninit");
547 story = new Story("tiddlerDisplay","tiddler");
548 addEvent(document,"click",Popup.onDocumentClick);
551 for(var s=0; s<config.notifyTiddlers.length; s++)
552 store.addNotification(config.notifyTiddlers[s].name,config.notifyTiddlers[s].notify);
553 store.loadFromDiv("storeArea","store",true);
554 invokeParamifier(params,"onload");
555 var pluginProblem = loadPlugins();
556 formatter = new Formatter(config.formatters);
557 readOnly = (window.location.protocol == "file:") ? false : config.options.chkHttpReadOnly;
558 invokeParamifier(params,"onconfig");
563 story.displayTiddler(null,"PluginManager");
564 displayMessage(config.messages.customConfigError);
567 if(config.displayStartupTime)
568 displayMessage("TiddlyWiki startup in " + (now-then)/1000 + " seconds");
575 invokeParamifier(params,"onstart");
578 var defaultParams = store.getTiddlerText("DefaultTiddlers").parseParams("open",null,false);
579 invokeParamifier(defaultParams,"onstart");
581 window.scrollTo(0,0);
586 var saveTest = document.getElementById("saveTest");
587 if(saveTest.hasChildNodes())
588 alert(config.messages.savedSnapshotError);
589 saveTest.appendChild(document.createTextNode("savetest"));
592 function loadPlugins()
596 var configTiddlers = store.getTaggedTiddlers("systemConfig");
597 installedPlugins = [];
598 var hadProblem = false;
599 for(var t=0; t<configTiddlers.length; t++)
601 tiddler = configTiddlers[t];
602 pluginInfo = getPluginInfo(tiddler);
603 if(isPluginExecutable(pluginInfo))
605 pluginInfo.executed = true;
606 pluginInfo.error = false;
609 if(tiddler.text && tiddler.text != "")
610 window.eval(tiddler.text);
614 pluginInfo.log.push(config.messages.pluginError.format([exceptionText(e)]));
615 pluginInfo.error = true;
620 pluginInfo.warning = true;
621 installedPlugins.push(pluginInfo);
626 function getPluginInfo(tiddler)
628 var p = store.getTiddlerSlices(tiddler.title,["Name","Description","Version","CoreVersion","Date","Source","Author","License","Browsers"]);
630 p.title = tiddler.title;
635 // Check that a particular plugin is valid for execution
636 function isPluginExecutable(plugin)
638 if(plugin.tiddler.isTagged("systemConfigDisable"))
639 return verifyTail(plugin,false,config.messages.pluginDisabled);
640 if(plugin.tiddler.isTagged("systemConfigForce"))
641 return verifyTail(plugin,true,config.messages.pluginForced);
642 if(plugin["CoreVersion"])
644 var coreVersion = plugin["CoreVersion"].split(".");
645 var w = parseInt(coreVersion[0]) - version.major;
646 if(w == 0 && coreVersion[1])
647 w = parseInt(coreVersion[1]) - version.minor;
648 if(w == 0 && coreVersion[2])
649 w = parseInt(coreVersion[2]) - version.revision;
651 return verifyTail(plugin,false,config.messages.pluginVersionError);
656 function verifyTail(plugin,result,message)
658 plugin.log.push(message);
662 function invokeMacro(place,macro,params,wikifier,tiddler)
666 var m = config.macros[macro];
668 m.handler(place,macro,params.readMacroParams(),wikifier,params,tiddler);
670 createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,config.messages.missingMacro]));
674 createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,ex.toString()]));
678 // ---------------------------------------------------------------------------------
680 // ---------------------------------------------------------------------------------
682 function getParameters()
685 if(window.location.hash)
687 p = decodeURI(window.location.hash.substr(1));
688 if(config.browser.firefoxDate != null && config.browser.firefoxDate[1] < "20051111")
689 p = convertUTF8ToUnicode(p);
694 function invokeParamifier(params,handler)
696 if(!params || params.length == undefined || params.length <= 1)
698 for(var t=1; t<params.length; t++)
700 var p = config.paramifiers[params[t].name];
701 if(p && p[handler] instanceof Function)
702 p[handler](params[t].value);
706 config.paramifiers = {};
708 config.paramifiers.start = {
709 oninit: function(v) {
710 safeMode = v.toLowerCase() == "safe";
714 config.paramifiers.open = {
715 onstart: function(v) {
716 story.displayTiddler("bottom",v,null,false,false);
720 config.paramifiers.story = {
721 onstart: function(v) {
722 var list = store.getTiddlerText(v,"").parseParams("open",null,false);
723 invokeParamifier(list,"onstart");
727 config.paramifiers.search = {
728 onstart: function(v) {
729 story.search(v,false,false);
733 config.paramifiers.searchRegExp = {
734 onstart: function(v) {
735 story.prototype.search(v,false,true);
739 config.paramifiers.tag = {
740 onstart: function(v) {
741 var tagged = store.getTaggedTiddlers(v,"title");
742 for(var t=0; t<tagged.length; t++)
743 story.displayTiddler("bottom",tagged[t].title,null,false,false);
747 config.paramifiers.newTiddler = {
748 onstart: function(v) {
751 story.displayTiddler(null,v,DEFAULT_EDIT_TEMPLATE);
752 story.focusTiddler(v,"text");
757 config.paramifiers.newJournal = {
758 onstart: function(v) {
761 var now = new Date();
762 var title = now.formatString(v.trim());
763 story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
764 story.focusTiddler(title,"text");
769 // ---------------------------------------------------------------------------------
771 // ---------------------------------------------------------------------------------
773 function Formatter(formatters)
775 this.formatters = [];
777 for(var n=0; n<formatters.length; n++)
779 pattern.push("(" + formatters[n].match + ")");
780 this.formatters.push(formatters[n]);
782 this.formatterRegExp = new RegExp(pattern.join("|"),"mg");
785 config.formatterHelpers = {
787 createElementAndWikify: function(w)
789 w.subWikifyTerm(createTiddlyElement(w.output,this.element),this.termRegExp);
792 inlineCssHelper: function(w)
795 config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
796 var lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
797 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch)
800 if(lookaheadMatch[1])
802 s = lookaheadMatch[1].unDash();
803 v = lookaheadMatch[2];
807 s = lookaheadMatch[3].unDash();
808 v = lookaheadMatch[4];
811 s = "backgroundColor";
812 styles.push({style: s, value: v});
813 w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
814 config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
815 lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
820 applyCssHelper: function(e,styles)
822 for(var t=0; t< styles.length; t++)
826 e.style[styles[t].style] = styles[t].value;
834 enclosedTextHelper: function(w)
836 this.lookaheadRegExp.lastIndex = w.matchStart;
837 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
838 if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
840 var text = lookaheadMatch[1];
841 if(config.browser.isIE)
842 text = text.replace(/\n/g,"\r");
843 createTiddlyElement(w.output,this.element,null,null,text);
844 w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
848 isExternalLink: function(link)
850 if(store.tiddlerExists(link) || store.isShadowTiddler(link))
852 //# Definitely not an external link
855 var urlRegExp = new RegExp(config.textPrimitives.urlPattern,"mg");
856 if(urlRegExp.exec(link))
858 // Definitely an external link
861 if (link.indexOf(".")!=-1 || link.indexOf("\\")!=-1 || link.indexOf("/")!=-1)
863 //# Link contains . / or \ so is probably an external link
866 //# Otherwise assume it is not an external link
872 // ---------------------------------------------------------------------------------
873 // Standard formatters
874 // ---------------------------------------------------------------------------------
876 config.formatters = [
879 match: "^\\|(?:[^\\n]*)\\|(?:[fhck]?)$",
880 lookaheadRegExp: /^\|([^\n]*)\|([fhck]?)$/mg,
881 rowTermRegExp: /(\|(?:[fhck]?)$\n?)/mg,
882 cellRegExp: /(?:\|([^\n\|]*)\|)|(\|[fhck]?$\n?)/mg,
883 cellTermRegExp: /((?:\x20*)\|)/mg,
884 rowTypes: {"c":"caption", "h":"thead", "":"tbody", "f":"tfoot"},
888 var table = createTiddlyElement(w.output,"table");
889 var prevColumns = [];
890 var currRowType = null;
893 w.nextMatch = w.matchStart;
894 this.lookaheadRegExp.lastIndex = w.nextMatch;
895 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
896 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch)
898 var nextRowType = lookaheadMatch[2];
899 if(nextRowType == "k")
901 table.className = lookaheadMatch[1];
902 w.nextMatch += lookaheadMatch[0].length+1;
906 if(nextRowType != currRowType)
908 rowContainer = createTiddlyElement(table,this.rowTypes[nextRowType]);
909 currRowType = nextRowType;
911 if(currRowType == "c")
915 if(rowContainer != table.firstChild)
916 table.insertBefore(rowContainer,table.firstChild);
917 rowContainer.setAttribute("align",rowCount == 0?"top":"bottom");
918 w.subWikifyTerm(rowContainer,this.rowTermRegExp);
922 this.rowHandler(w,createTiddlyElement(rowContainer,"tr",null,(rowCount&1)?"oddRow":"evenRow"),prevColumns);
926 this.lookaheadRegExp.lastIndex = w.nextMatch;
927 lookaheadMatch = this.lookaheadRegExp.exec(w.source);
930 rowHandler: function(w,e,prevColumns)
933 var colSpanCount = 1;
935 this.cellRegExp.lastIndex = w.nextMatch;
936 var cellMatch = this.cellRegExp.exec(w.source);
937 while(cellMatch && cellMatch.index == w.nextMatch)
939 if(cellMatch[1] == "~")
942 var last = prevColumns[col];
946 last.element.setAttribute("rowspan",last.rowSpanCount);
947 last.element.setAttribute("rowSpan",last.rowSpanCount); // Needed for IE
948 last.element.valign = "center";
950 w.nextMatch = this.cellRegExp.lastIndex-1;
952 else if(cellMatch[1] == ">")
956 w.nextMatch = this.cellRegExp.lastIndex-1;
958 else if(cellMatch[2])
961 if(prevCell && colSpanCount > 1)
963 prevCell.setAttribute("colspan",colSpanCount);
964 prevCell.setAttribute("colSpan",colSpanCount); // Needed for IE
966 w.nextMatch = this.cellRegExp.lastIndex;
973 var styles = config.formatterHelpers.inlineCssHelper(w);
974 var spaceLeft = false;
975 var chr = w.source.substr(w.nextMatch,1);
980 chr = w.source.substr(w.nextMatch,1);
985 cell = createTiddlyElement(e,"th");
989 cell = createTiddlyElement(e,"td");
991 prevColumns[col] = {rowSpanCount:1, element:cell};
994 cell.setAttribute("colspan",colSpanCount);
995 cell.setAttribute("colSpan",colSpanCount); // Needed for IE
998 config.formatterHelpers.applyCssHelper(cell,styles);
999 w.subWikifyTerm(cell,this.cellTermRegExp);
1000 if(w.matchText.substr(w.matchText.length-2,1) == " ") // spaceRight
1001 cell.align = spaceLeft ? "center" : "left";
1003 cell.align = "right";
1007 this.cellRegExp.lastIndex = w.nextMatch;
1008 cellMatch = this.cellRegExp.exec(w.source);
1016 termRegExp: /(\n)/mg,
1017 handler: function(w)
1019 w.subWikifyTerm(createTiddlyElement(w.output,"h" + w.matchLength),this.termRegExp);
1025 match: "^(?:(?:(?:\\*)|(?:#)|(?:;)|(?::))+)",
1026 lookaheadRegExp: /^(?:(?:(\*)|(#)|(;)|(:))+)/mg,
1027 termRegExp: /(\n)/mg,
1028 handler: function(w)
1030 var placeStack = [w.output];
1031 var currLevel = 0, currType = null;
1032 var listLevel, listType, itemType;
1033 w.nextMatch = w.matchStart;
1034 this.lookaheadRegExp.lastIndex = w.nextMatch;
1035 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
1036 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch)
1038 if(lookaheadMatch[1])
1043 else if(lookaheadMatch[2])
1048 else if(lookaheadMatch[3])
1053 else if(lookaheadMatch[4])
1058 listLevel = lookaheadMatch[0].length;
1059 w.nextMatch += lookaheadMatch[0].length;
1060 if(listLevel > currLevel)
1062 for(var t=currLevel; t<listLevel; t++)
1063 placeStack.push(createTiddlyElement(placeStack[placeStack.length-1],listType));
1065 else if(listLevel < currLevel)
1067 for(var t=currLevel; t>listLevel; t--)
1070 else if(listLevel == currLevel && listType != currType)
1073 placeStack.push(createTiddlyElement(placeStack[placeStack.length-1],listType));
1075 currLevel = listLevel;
1076 currType = listType;
1077 var e = createTiddlyElement(placeStack[placeStack.length-1],itemType);
1078 w.subWikifyTerm(e,this.termRegExp);
1079 this.lookaheadRegExp.lastIndex = w.nextMatch;
1080 lookaheadMatch = this.lookaheadRegExp.exec(w.source);
1086 name: "quoteByBlock",
1088 termRegExp: /(^<<<(\n|$))/mg,
1089 element: "blockquote",
1090 handler: config.formatterHelpers.createElementAndWikify
1094 name: "quoteByLine",
1096 lookaheadRegExp: /^>+/mg,
1097 termRegExp: /(\n)/mg,
1098 element: "blockquote",
1099 handler: function(w)
1101 var placeStack = [w.output];
1103 var newLevel = w.matchLength;
1106 if(newLevel > currLevel)
1108 for(t=currLevel; t<newLevel; t++)
1109 placeStack.push(createTiddlyElement(placeStack[placeStack.length-1],this.element));
1111 else if(newLevel < currLevel)
1113 for(t=currLevel; t>newLevel; t--)
1116 currLevel = newLevel;
1117 w.subWikifyTerm(placeStack[placeStack.length-1],this.termRegExp);
1118 createTiddlyElement(placeStack[placeStack.length-1],"br");
1119 this.lookaheadRegExp.lastIndex = w.nextMatch;
1120 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
1121 var matched = lookaheadMatch && lookaheadMatch.index == w.nextMatch;
1124 newLevel = lookaheadMatch[0].length;
1125 w.nextMatch += lookaheadMatch[0].length;
1133 match: "^----+$\\n?",
1134 handler: function(w)
1136 createTiddlyElement(w.output,"hr");
1141 name: "monospacedByLine",
1142 match: "^\\{\\{\\{\\n",
1143 lookaheadRegExp: /^\{\{\{\n((?:^[^\n]*\n)+?)(^\}\}\}$\n?)/mg,
1145 handler: config.formatterHelpers.enclosedTextHelper
1149 name: "monospacedByLineForCSS",
1150 match: "^/\\*[\\{]{3}\\*/\\n",
1151 lookaheadRegExp: /\/\*[\{]{3}\*\/\n*((?:^[^\n]*\n)+?)(\n*^\/\*[\}]{3}\*\/$\n?)/mg,
1153 handler: config.formatterHelpers.enclosedTextHelper
1157 name: "monospacedByLineForPlugin",
1158 match: "^//\\{\\{\\{\\n",
1159 lookaheadRegExp: /^\/\/\{\{\{\n\n*((?:^[^\n]*\n)+?)(\n*^\/\/\}\}\}$\n?)/mg,
1161 handler: config.formatterHelpers.enclosedTextHelper
1165 name: "monospacedByLineForTemplate",
1166 match: "^<!--[\\{]{3}-->\\n",
1167 lookaheadRegExp: /<!--[\{]{3}-->\n*((?:^[^\n]*\n)+?)(\n*^<!--[\}]{3}-->$\n?)/mg,
1169 handler: config.formatterHelpers.enclosedTextHelper
1173 name: "wikifyCommentForPlugin",
1174 match: "^/\\*\\*\\*\\n",
1175 termRegExp: /(^\*\*\*\/\n)/mg,
1176 handler: function(w)
1178 w.subWikifyTerm(w.output,this.termRegExp);
1183 name: "wikifyCommentForTemplate",
1185 termRegExp: /(^--->\n)/mg,
1186 handler: function(w)
1188 w.subWikifyTerm(w.output,this.termRegExp);
1195 lookaheadRegExp: /<<([^>\s]+)(?:\s*)((?:[^>]|(?:>(?!>)))*)>>/mg,
1196 handler: function(w)
1198 this.lookaheadRegExp.lastIndex = w.matchStart;
1199 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
1200 if(lookaheadMatch && lookaheadMatch.index == w.matchStart && lookaheadMatch[1])
1202 w.nextMatch = this.lookaheadRegExp.lastIndex;
1203 invokeMacro(w.output,lookaheadMatch[1],lookaheadMatch[2],w,w.tiddler);
1211 lookaheadRegExp: /\[\[(.*?)(?:\|(~)?(.*?))?\]\]/mg,
1212 handler: function(w)
1214 this.lookaheadRegExp.lastIndex = w.matchStart;
1215 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
1216 if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
1219 var text = lookaheadMatch[1];
1220 if(lookaheadMatch[3])
1222 // Pretty bracketted link
1223 var link = lookaheadMatch[3];
1224 e = (!lookaheadMatch[2] && config.formatterHelpers.isExternalLink(link))
1225 ? createExternalLink(w.output,link)
1226 : createTiddlyLink(w.output,link,false,null,w.isStatic);
1230 // Simple bracketted link
1231 e = createTiddlyLink(w.output,text,false,null,w.isStatic);
1233 createTiddlyText(e,text);
1234 w.nextMatch = this.lookaheadRegExp.lastIndex;
1241 match: config.textPrimitives.unWikiLink+config.textPrimitives.wikiLink,
1242 handler: function(w)
1244 w.outputText(w.output,w.matchStart+1,w.nextMatch);
1250 match: config.textPrimitives.wikiLink,
1251 handler: function(w)
1253 if(w.matchStart > 0)
1255 var preRegExp = new RegExp(config.textPrimitives.anyLetterStrict,"mg");
1256 preRegExp.lastIndex = w.matchStart-1;
1257 var preMatch = preRegExp.exec(w.source);
1258 if(preMatch.index == w.matchStart-1)
1260 w.outputText(w.output,w.matchStart,w.nextMatch);
1264 if(w.autoLinkWikiWords == true || store.isShadowTiddler(w.matchText))
1266 var link = createTiddlyLink(w.output,w.matchText,false,null,w.isStatic);
1267 w.outputText(link,w.matchStart,w.nextMatch);
1271 w.outputText(w.output,w.matchStart,w.nextMatch);
1278 match: config.textPrimitives.urlPattern,
1279 handler: function(w)
1281 w.outputText(createExternalLink(w.output,w.matchText),w.matchStart,w.nextMatch);
1287 match: "\\[[<>]?[Ii][Mm][Gg]\\[",
1288 lookaheadRegExp: /\[(<?)(>?)[Ii][Mm][Gg]\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg,
1289 handler: function(w)
1291 this.lookaheadRegExp.lastIndex = w.matchStart;
1292 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
1293 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) // Simple bracketted link
1296 if(lookaheadMatch[5])
1298 var link = lookaheadMatch[5];
1299 e = config.formatterHelpers.isExternalLink(link) ? createExternalLink(w.output,link) : createTiddlyLink(w.output,link,false,null,w.isStatic);
1300 addClass(e,"imageLink");
1302 var img = createTiddlyElement(e,"img");
1303 if(lookaheadMatch[1])
1305 else if(lookaheadMatch[2])
1306 img.align = "right";
1307 if(lookaheadMatch[3])
1308 img.title = lookaheadMatch[3];
1309 img.src = lookaheadMatch[4];
1310 w.nextMatch = this.lookaheadRegExp.lastIndex;
1317 match: "<[Hh][Tt][Mm][Ll]>",
1318 lookaheadRegExp: /<[Hh][Tt][Mm][Ll]>((?:.|\n)*?)<\/[Hh][Tt][Mm][Ll]>/mg,
1319 handler: function(w)
1321 this.lookaheadRegExp.lastIndex = w.matchStart;
1322 var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
1323 if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
1325 createTiddlyElement(w.output,"span").innerHTML = lookaheadMatch[1];
1326 w.nextMatch = this.lookaheadRegExp.lastIndex;
1332 name: "commentByBlock",
1334 lookaheadRegExp: /\/%((?:.|\n)*?)%\//mg,
1335 handler: function(w)
1337 this.lookaheadRegExp.lastIndex = w.matchStart;
1338 var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
1339 if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
1340 w.nextMatch = this.lookaheadRegExp.lastIndex;
1347 termRegExp: /('')/mg,
1349 handler: config.formatterHelpers.createElementAndWikify
1353 name: "italicByChar",
1355 termRegExp: /(\/\/)/mg,
1357 handler: config.formatterHelpers.createElementAndWikify
1361 name: "underlineByChar",
1363 termRegExp: /(__)/mg,
1365 handler: config.formatterHelpers.createElementAndWikify
1369 name: "strikeByChar",
1370 match: "--(?!\\s|$)",
1371 termRegExp: /((?!\s)--|(?=\n\n))/mg,
1373 handler: config.formatterHelpers.createElementAndWikify
1377 name: "superscriptByChar",
1379 termRegExp: /(\^\^)/mg,
1381 handler: config.formatterHelpers.createElementAndWikify
1385 name: "subscriptByChar",
1387 termRegExp: /(~~)/mg,
1389 handler: config.formatterHelpers.createElementAndWikify
1393 name: "monospacedByChar",
1395 lookaheadRegExp: /\{\{\{((?:.|\n)*?)\}\}\}/mg,
1396 handler: function(w)
1398 this.lookaheadRegExp.lastIndex = w.matchStart;
1399 var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
1400 if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
1402 createTiddlyElement(w.output,"code",null,null,lookaheadMatch[1]);
1403 w.nextMatch = this.lookaheadRegExp.lastIndex;
1409 name: "styleByChar",
1411 termRegExp: /(@@)/mg,
1412 handler: function(w)
1414 var e = createTiddlyElement(w.output,"span");
1415 var styles = config.formatterHelpers.inlineCssHelper(w);
1416 if(styles.length == 0)
1417 e.className = "marked";
1419 config.formatterHelpers.applyCssHelper(e,styles);
1420 w.subWikifyTerm(e,this.termRegExp);
1426 match: "\\n|<br ?/?>",
1427 handler: function(w)
1429 createTiddlyElement(w.output,"br");
1435 match: "\\\"{3}|<nowiki>",
1436 lookaheadRegExp: /(?:\"{3}|<nowiki>)((?:.|\n)*?)(?:\"{3}|<\/nowiki>)/mg,
1437 handler: function(w)
1439 this.lookaheadRegExp.lastIndex = w.matchStart;
1440 var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
1441 if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
1443 createTiddlyElement(w.output,"span",null,null,lookaheadMatch[1]);
1444 w.nextMatch = this.lookaheadRegExp.lastIndex;
1452 handler: function(w)
1454 createTiddlyElement(w.output,"span").innerHTML = "—";
1459 name: "htmlEntitiesEncoding",
1460 match: "(?:(?:&#?[a-zA-Z0-9]{2,8};|.)(?:&#?(?:x0*(?:3[0-6][0-9a-fA-F]|1D[c-fC-F][0-9a-fA-F]|20[d-fD-F][0-9a-fA-F]|FE2[0-9a-fA-F])|0*(?:76[89]|7[7-9][0-9]|8[0-7][0-9]|761[6-9]|76[2-7][0-9]|84[0-3][0-9]|844[0-7]|6505[6-9]|6506[0-9]|6507[0-1]));)+|&#?[a-zA-Z0-9]{2,8};)",
1461 handler: function(w)
1463 createTiddlyElement(w.output,"span").innerHTML = w.matchText;
1468 name: "customClasses",
1470 termRegExp: /(\}\}\})/mg,
1471 lookaheadRegExp: /\{\{[\s]*([\w]+[\s\w]*)[\s]*\{(\n?)/mg,
1472 handler: function(w)
1474 this.lookaheadRegExp.lastIndex = w.matchStart;
1475 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
1478 var e = createTiddlyElement(w.output,lookaheadMatch[2] == "\n" ? "div" : "span",null,lookaheadMatch[1]);
1479 w.nextMatch = this.lookaheadRegExp.lastIndex;
1480 w.subWikifyTerm(e,this.termRegExp);
1487 // ---------------------------------------------------------------------------------
1489 // ---------------------------------------------------------------------------------
1491 function getParser(tiddler)
1496 for(var i in config.parsers)
1498 if(tiddler.isTagged(config.parsers[i].formatTag))
1500 f = config.parsers[i];
1508 function wikify(source,output,highlightRegExp,tiddler)
1510 if(source && source != "")
1512 var wikifier = new Wikifier(source,getParser(tiddler),highlightRegExp,tiddler);
1513 wikifier.subWikifyUnterm(output);
1517 function wikifyStatic(source,highlightRegExp,tiddler)
1519 var e = createTiddlyElement(document.body,"div");
1520 e.style.display = "none";
1522 if(source && source != "")
1524 var wikifier = new Wikifier(source,getParser(tiddler),highlightRegExp,tiddler);
1525 wikifier.isStatic = true;
1526 wikifier.subWikifyUnterm(e);
1528 e.parentNode.removeChild(e);
1533 // Wikify a named tiddler to plain text
1534 function wikifyPlain(title)
1536 if(store.tiddlerExists(title) || store.isShadowTiddler(title))
1538 var wikifier = new Wikifier(store.getTiddlerText(title),formatter,null,store.getTiddler(title));
1539 return wikifier.wikifyPlain();
1545 // Highlight plain text into an element
1546 function highlightify(source,output,highlightRegExp)
1548 if(source && source != "")
1550 var wikifier = new Wikifier(source,formatter,highlightRegExp);
1551 wikifier.outputText(output,0,source.length);
1555 // Construct a wikifier object
1556 // source - source string that's going to be wikified
1557 // formatter - Formatter() object containing the list of formatters to be used
1558 // highlightRegExp - regular expression of the text string to highlight
1559 // tiddler - reference to the tiddler that's taken to be the container for this wikification
1560 function Wikifier(source,formatter,highlightRegExp,tiddler)
1562 this.source = source;
1564 this.formatter = formatter;
1566 this.autoLinkWikiWords = tiddler && tiddler.autoLinkWikiWords() == false ? false : true;
1567 this.highlightRegExp = highlightRegExp;
1568 this.highlightMatch = null;
1569 this.isStatic = false;
1572 highlightRegExp.lastIndex = 0;
1573 this.highlightMatch = highlightRegExp.exec(source);
1575 this.tiddler = tiddler;
1578 Wikifier.prototype.wikifyPlain = function()
1580 var e = createTiddlyElement(document.body,"div");
1581 e.style.display = "none";
1583 var text = getPlainText(e);
1584 e.parentNode.removeChild(e);
1588 Wikifier.prototype.subWikify = function(output,terminator)
1590 // Handle the terminated and unterminated cases separately
1592 this.subWikifyTerm(output,new RegExp("(" + terminator + ")","mg"));
1594 this.subWikifyUnterm(output);
1597 Wikifier.prototype.subWikifyUnterm = function(output)
1599 // subWikify() can be indirectly recursive, so we need to save the old output pointer
1600 var oldOutput = this.output;
1601 this.output = output;
1602 // Get the first match
1603 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
1604 var formatterMatch = this.formatter.formatterRegExp.exec(this.source);
1605 while(formatterMatch)
1607 // Output any text before the match
1608 if(formatterMatch.index > this.nextMatch)
1609 this.outputText(this.output,this.nextMatch,formatterMatch.index);
1610 // Set the match parameters for the handler
1611 this.matchStart = formatterMatch.index;
1612 this.matchLength = formatterMatch[0].length;
1613 this.matchText = formatterMatch[0];
1614 this.nextMatch = this.formatter.formatterRegExp.lastIndex;
1615 // Figure out which formatter matched and call its handler
1616 for(var t=1; t<formatterMatch.length; t++)
1618 if(formatterMatch[t])
1620 this.formatter.formatters[t-1].handler(this);
1621 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
1625 // Get the next match
1626 formatterMatch = this.formatter.formatterRegExp.exec(this.source);
1628 // Output any text after the last match
1629 if(this.nextMatch < this.source.length)
1631 this.outputText(this.output,this.nextMatch,this.source.length);
1632 this.nextMatch = this.source.length;
1634 // Restore the output pointer
1635 this.output = oldOutput;
1638 Wikifier.prototype.subWikifyTerm = function(output,terminatorRegExp)
1640 // subWikify() can be indirectly recursive, so we need to save the old output pointer
1641 var oldOutput = this.output;
1642 this.output = output;
1643 // Get the first matches for the formatter and terminator RegExps
1644 terminatorRegExp.lastIndex = this.nextMatch;
1645 var terminatorMatch = terminatorRegExp.exec(this.source);
1646 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
1647 var formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
1648 while(terminatorMatch || formatterMatch)
1650 // Check for a terminator match before the next formatter match
1651 if(terminatorMatch && (!formatterMatch || terminatorMatch.index <= formatterMatch.index))
1653 // Output any text before the match
1654 if(terminatorMatch.index > this.nextMatch)
1655 this.outputText(this.output,this.nextMatch,terminatorMatch.index);
1656 // Set the match parameters
1657 this.matchText = terminatorMatch[1];
1658 this.matchLength = terminatorMatch[1].length;
1659 this.matchStart = terminatorMatch.index;
1660 this.nextMatch = this.matchStart + this.matchLength;
1661 // Restore the output pointer
1662 this.output = oldOutput;
1665 // It must be a formatter match; output any text before the match
1666 if(formatterMatch.index > this.nextMatch)
1667 this.outputText(this.output,this.nextMatch,formatterMatch.index);
1668 // Set the match parameters
1669 this.matchStart = formatterMatch.index;
1670 this.matchLength = formatterMatch[0].length;
1671 this.matchText = formatterMatch[0];
1672 this.nextMatch = this.formatter.formatterRegExp.lastIndex;
1673 // Figure out which formatter matched and call its handler
1674 for(var t=1; t<formatterMatch.length; t++)
1676 if(formatterMatch[t])
1678 this.formatter.formatters[t-1].handler(this);
1679 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
1683 // Get the next match
1684 terminatorRegExp.lastIndex = this.nextMatch;
1685 terminatorMatch = terminatorRegExp.exec(this.source);
1686 formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
1688 // Output any text after the last match
1689 if(this.nextMatch < this.source.length)
1691 this.outputText(this.output,this.nextMatch,this.source.length);
1692 this.nextMatch = this.source.length;
1694 // Restore the output pointer
1695 this.output = oldOutput;
1698 Wikifier.prototype.outputText = function(place,startPos,endPos)
1700 // Check for highlights
1701 while(this.highlightMatch && (this.highlightRegExp.lastIndex > startPos) && (this.highlightMatch.index < endPos) && (startPos < endPos))
1703 // Deal with any plain text before the highlight
1704 if(this.highlightMatch.index > startPos)
1706 createTiddlyText(place,this.source.substring(startPos,this.highlightMatch.index));
1707 startPos = this.highlightMatch.index;
1709 // Deal with the highlight
1710 var highlightEnd = Math.min(this.highlightRegExp.lastIndex,endPos);
1711 var theHighlight = createTiddlyElement(place,"span",null,"highlight",this.source.substring(startPos,highlightEnd));
1712 startPos = highlightEnd;
1713 // Nudge along to the next highlight if we're done with this one
1714 if(startPos >= this.highlightRegExp.lastIndex)
1715 this.highlightMatch = this.highlightRegExp.exec(this.source);
1717 // Do the unhighlighted text left over
1718 if(startPos < endPos)
1720 createTiddlyText(place,this.source.substring(startPos,endPos));
1724 // ---------------------------------------------------------------------------------
1725 // Macro definitions
1726 // ---------------------------------------------------------------------------------
1728 config.macros.today.handler = function(place,macroName,params)
1730 var now = new Date();
1733 text = now.formatString(params[0].trim());
1735 text = now.toLocaleString();
1736 createTiddlyElement(place,"span",null,null,text);
1739 config.macros.version.handler = function(place)
1741 createTiddlyElement(place,"span",null,null,version.major + "." + version.minor + "." + version.revision + (version.beta ? " (beta " + version.beta + ")" : ""));
1744 config.macros.list.handler = function(place,macroName,params)
1746 var type = params[0] ? params[0] : "all";
1747 var theList = document.createElement("ul");
1748 place.appendChild(theList);
1749 if(this[type].prompt)
1750 createTiddlyElement(theList,"li",null,"listTitle",this[type].prompt);
1752 if(this[type].handler)
1753 results = this[type].handler(params);
1754 for(var t = 0; t < results.length; t++)
1756 var theListItem = document.createElement("li")
1757 theList.appendChild(theListItem);
1758 if(typeof results[t] == "string")
1759 createTiddlyLink(theListItem,results[t],true);
1761 createTiddlyLink(theListItem,results[t].title,true);
1765 config.macros.list.all.handler = function(params)
1767 return store.reverseLookup("tags","excludeLists",false,"title");
1770 config.macros.list.missing.handler = function(params)
1772 return store.getMissingLinks();
1775 config.macros.list.orphans.handler = function(params)
1777 return store.getOrphans();
1780 config.macros.list.shadowed.handler = function(params)
1782 return store.getShadowed();
1785 config.macros.allTags.handler = function(place,macroName,params)
1787 var tags = store.getTags();
1788 var theDateList = createTiddlyElement(place,"ul");
1789 if(tags.length == 0)
1790 createTiddlyElement(theDateList,"li",null,"listTitle",this.noTags);
1791 for(var t=0; t<tags.length; t++)
1793 var theListItem =createTiddlyElement(theDateList,"li");
1794 var theTag = createTiddlyButton(theListItem,tags[t][0] + " (" + tags[t][1] + ")",this.tooltip.format([tags[t][0]]),onClickTag);
1795 theTag.setAttribute("tag",tags[t][0]);
1799 config.macros.timeline.handler = function(place,macroName,params)
1801 var field = params[0] ? params[0] : "modified";
1802 var tiddlers = store.reverseLookup("tags","excludeLists",false,field);
1804 var last = params[1] ? tiddlers.length-Math.min(tiddlers.length,parseInt(params[1])) : 0;
1805 for(var t=tiddlers.length-1; t>=last; t--)
1807 var tiddler = tiddlers[t];
1808 var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
1809 if(theDay != lastDay)
1811 var theDateList = document.createElement("ul");
1812 place.appendChild(theDateList);
1813 createTiddlyElement(theDateList,"li",null,"listTitle",tiddler[field].formatString(this.dateFormat));
1816 var theDateListItem = createTiddlyElement(theDateList,"li",null,"listLink");
1817 theDateListItem.appendChild(createTiddlyLink(place,tiddler.title,true));
1821 config.macros.search.handler = function(place,macroName,params)
1823 var searchTimeout = null;
1824 var btn = createTiddlyButton(place,this.label,this.prompt,this.onClick);
1825 var txt = createTiddlyElement(place,"input",null,"txtOptionInput");
1827 txt.value = params[0];
1828 txt.onkeyup = this.onKeyPress;
1829 txt.onfocus = this.onFocus;
1830 txt.setAttribute("size",this.sizeTextbox);
1831 txt.setAttribute("accessKey",this.accessKey);
1832 txt.setAttribute("autocomplete","off");
1833 txt.setAttribute("lastSearchText","");
1834 if(config.browser.isSafari)
1836 txt.setAttribute("type","search");
1837 txt.setAttribute("results","5");
1840 txt.setAttribute("type","text");
1843 // Global because there's only ever one outstanding incremental search timer
1844 config.macros.search.timeout = null;
1846 config.macros.search.doSearch = function(txt)
1848 if(txt.value.length > 0)
1850 story.search(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch);
1851 txt.setAttribute("lastSearchText",txt.value);
1855 config.macros.search.onClick = function(e)
1857 config.macros.search.doSearch(this.nextSibling);
1861 config.macros.search.onKeyPress = function(e)
1863 if(!e) var e = window.event;
1866 case 13: // Ctrl-Enter
1867 case 10: // Ctrl-Enter on IE PC
1868 config.macros.search.doSearch(this);
1875 if(this.value.length > 2)
1877 if(this.value != this.getAttribute("lastSearchText"))
1879 if(config.macros.search.timeout)
1880 clearTimeout(config.macros.search.timeout);
1882 config.macros.search.timeout = setTimeout(function() {config.macros.search.doSearch(txt);},500);
1887 if(config.macros.search.timeout)
1888 clearTimeout(config.macros.search.timeout);
1892 config.macros.search.onFocus = function(e)
1897 config.macros.tiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
1899 params = paramString.parseParams("name",null,true,false,true);
1900 var names = params[0]["name"];
1901 var tiddlerName = names[0];
1902 var className = names[1] ? names[1] : null;
1903 var args = params[0]["with"];
1904 var wrapper = createTiddlyElement(place,"span",null,className);
1907 wrapper.setAttribute("refresh","content");
1908 wrapper.setAttribute("tiddler",tiddlerName);
1910 var text = store.getTiddlerText(tiddlerName);
1913 var stack = config.macros.tiddler.tiddlerStack;
1914 if(stack.indexOf(tiddlerName) !== -1)
1916 stack.push(tiddlerName);
1919 var n = args ? Math.min(args.length,9) : 0;
1920 for(var i=0; i<n; i++)
1922 var placeholderRE = new RegExp("\\$" + (i + 1),"mg");
1923 text = text.replace(placeholderRE,args[i]);
1925 config.macros.tiddler.renderText(wrapper,text,tiddlerName,params);
1934 config.macros.tiddler.renderText = function(place,text,tiddlerName,params)
1936 wikify(text,place,null,store.getTiddler(tiddlerName));
1939 config.macros.tiddler.tiddlerStack = [];
1941 config.macros.tag.handler = function(place,macroName,params)
1943 createTagButton(place,params[0]);
1946 config.macros.tags.handler = function(place,macroName,params,wikifier,paramString,tiddler)
1948 params = paramString.parseParams("anon",null,true,false,false);
1949 var theList = createTiddlyElement(place,"ul");
1950 var title = getParam(params,"anon","");
1951 if(title && store.tiddlerExists(title))
1952 tiddler = store.getTiddler(title);
1953 var sep = getParam(params,"sep"," ");
1954 var lingo = config.views.wikified.tag;
1955 var prompt = tiddler.tags.length == 0 ? lingo.labelNoTags : lingo.labelTags;
1956 createTiddlyElement(theList,"li",null,"listTitle",prompt.format([tiddler.title]));
1957 for(var t=0; t<tiddler.tags.length; t++)
1959 createTagButton(createTiddlyElement(theList,"li"),tiddler.tags[t],tiddler.title);
1960 if(t<tiddler.tags.length-1)
1961 createTiddlyText(theList,sep);
1965 config.macros.tagging.handler = function(place,macroName,params,wikifier,paramString,tiddler)
1967 params = paramString.parseParams("anon",null,true,false,false);
1968 var theList = createTiddlyElement(place,"ul");
1969 var title = getParam(params,"anon","");
1970 if(title == "" && tiddler instanceof Tiddler)
1971 title = tiddler.title;
1972 var sep = getParam(params,"sep"," ");
1973 theList.setAttribute("title",this.tooltip.format([title]));
1974 var tagged = store.getTaggedTiddlers(title);
1975 var prompt = tagged.length == 0 ? this.labelNotTag : this.label;
1976 createTiddlyElement(theList,"li",null,"listTitle",prompt.format([title,tagged.length]));
1977 for(var t=0; t<tagged.length; t++)
1979 createTiddlyLink(createTiddlyElement(theList,"li"),tagged[t].title,true);
1980 if(t<tagged.length-1)
1981 createTiddlyText(theList,sep);
1985 config.macros.closeAll.handler = function(place)
1987 createTiddlyButton(place,this.label,this.prompt,this.onClick);
1990 config.macros.closeAll.onClick = function(e)
1992 story.closeAllTiddlers();
1996 config.macros.permaview.handler = function(place)
1998 createTiddlyButton(place,this.label,this.prompt,this.onClick);
2001 config.macros.permaview.onClick = function(e)
2007 config.macros.saveChanges.handler = function(place)
2010 createTiddlyButton(place,this.label,this.prompt,this.onClick,null,null,this.accessKey);
2013 config.macros.saveChanges.onClick = function(e)
2019 config.macros.slider.onClickSlider = function(e)
2021 if(!e) var e = window.event;
2022 var n = this.nextSibling;
2023 var cookie = n.getAttribute("cookie");
2024 var isOpen = n.style.display != "none";
2025 if(anim && config.options.chkAnimate)
2026 anim.startAnimating(new Slider(n,!isOpen,e.shiftKey || e.altKey,"none"));
2028 n.style.display = isOpen ? "none" : "block";
2029 config.options[cookie] = !isOpen;
2030 saveOptionCookie(cookie);
2034 config.macros.slider.createSlider = function(place,cookie,title,tooltip)
2036 var cookie = cookie ? cookie : "";
2037 var btn = createTiddlyButton(place,title,tooltip,this.onClickSlider);
2038 var panel = createTiddlyElement(null,"div",null,"sliderPanel");
2039 panel.setAttribute("cookie",cookie);
2040 panel.style.display = config.options[cookie] ? "block" : "none";
2041 place.appendChild(panel);
2045 config.macros.slider.handler = function(place,macroName,params)
2047 var panel = this.createSlider(place,params[0],params[2],params[3]);
2048 var text = store.getTiddlerText(params[1]);
2049 panel.setAttribute("refresh", "content");
2050 panel.setAttribute("tiddler", params[1]);
2052 wikify(text,panel,null,store.getTiddler(params[1]));
2055 config.macros.option.onChangeOption = function(e)
2057 var opt = this.getAttribute("option");
2058 var elementType,valueField;
2061 switch(opt.substr(0,3))
2064 elementType = "input";
2065 valueField = "value";
2068 elementType = "input";
2069 valueField = "checked";
2072 config.options[opt] = this[valueField];
2073 saveOptionCookie(opt);
2074 var nodes = document.getElementsByTagName(elementType);
2075 for(var t=0; t<nodes.length; t++)
2077 var optNode = nodes[t].getAttribute("option");
2079 nodes[t][valueField] = this[valueField];
2085 config.macros.option.handler = function(place,macroName,params)
2087 var opt = params[0];
2088 if(config.options[opt] == undefined)
2091 switch(opt.substr(0,3))
2094 c = document.createElement("input");
2095 c.onkeyup = this.onChangeOption;
2096 c.setAttribute("option",opt);
2097 c.className = "txtOptionInput";
2098 place.appendChild(c);
2099 c.value = config.options[opt];
2102 c = document.createElement("input");
2103 c.setAttribute("type","checkbox");
2104 c.onclick = this.onChangeOption;
2105 c.setAttribute("option",opt);
2106 c.className = "chkOptionInput";
2107 place.appendChild(c);
2108 c.checked = config.options[opt];
2115 config.macros.newTiddler.createNewTiddlerButton = function(place,title,params,label,prompt,accessKey,newFocus,isJournal)
2118 for(var t=1; t<params.length; t++)
2119 if((params[t].name == "anon" && t != 1) || (params[t].name == "tag"))
2120 tags.push(params[t].value);
2121 label = getParam(params,"label",label);
2122 prompt = getParam(params,"prompt",prompt);
2123 accessKey = getParam(params,"accessKey",accessKey);
2124 newFocus = getParam(params,"focus",newFocus);
2125 var btn = createTiddlyButton(place,label,prompt,this.onClickNewTiddler,null,null,accessKey);
2126 btn.setAttribute("newTitle",title);
2127 btn.setAttribute("isJournal",isJournal);
2128 btn.setAttribute("params",tags.join("|"));
2129 btn.setAttribute("newFocus",newFocus);
2130 btn.setAttribute("newTemplate",getParam(params,"template",DEFAULT_EDIT_TEMPLATE));
2131 var text = getParam(params,"text");
2132 if(text !== undefined)
2133 btn.setAttribute("newText",text);
2137 config.macros.newTiddler.onClickNewTiddler = function()
2139 var title = this.getAttribute("newTitle");
2140 if(this.getAttribute("isJournal"))
2142 var now = new Date();
2143 title = now.formatString(title.trim());
2145 var params = this.getAttribute("params").split("|");
2146 var focus = this.getAttribute("newFocus");
2147 var template = this.getAttribute("newTemplate");
2148 story.displayTiddler(null,title,template);
2149 var text = this.getAttribute("newText");
2150 if(typeof text == "string")
2151 story.getTiddlerField(title,"text").value = text.format([title]);
2152 for(var t=0;t<params.length;t++)
2153 story.setTiddlerTag(title,params[t],+1);
2154 story.focusTiddler(title,focus);
2158 config.macros.newTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
2162 params = paramString.parseParams("anon",null,true,false,false);
2163 var title = params[1] && params[1].name == "anon" ? params[1].value : this.title;
2164 title = getParam(params,"title",title);
2165 this.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"title",false);
2169 config.macros.newJournal.handler = function(place,macroName,params,wikifier,paramString,tiddler)
2173 params = paramString.parseParams("anon",null,true,false,false);
2174 var title = params[1] && params[1].name == "anon" ? params[1].value : "";
2175 title = getParam(params,"title",title);
2176 config.macros.newTiddler.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"text",true);
2180 config.macros.sparkline.handler = function(place,macroName,params)
2185 for(var t=0; t<params.length; t++)
2187 var v = parseInt(params[t]);
2196 var box = createTiddlyElement(place,"span",null,"sparkline",String.fromCharCode(160));
2197 box.title = data.join(",");
2198 var w = box.offsetWidth;
2199 var h = box.offsetHeight;
2200 box.style.paddingRight = (data.length * 2 - w) + "px";
2201 box.style.position = "relative";
2202 for(var d=0; d<data.length; d++)
2204 var tick = document.createElement("img");
2206 tick.className = "sparktick";
2207 tick.style.position = "absolute";
2208 tick.src = "data:image/gif,GIF89a%01%00%01%00%91%FF%00%FF%FF%FF%00%00%00%C0%C0%C0%00%00%00!%F9%04%01%00%00%02%00%2C%00%00%00%00%01%00%01%00%40%02%02T%01%00%3B";
2209 tick.style.left = d*2 + "px";
2210 tick.style.width = "2px";
2211 var v = Math.floor(((data[d] - min)/(max-min)) * h);
2212 tick.style.top = (h-v) + "px";
2213 tick.style.height = v + "px";
2214 box.appendChild(tick);
2218 config.macros.tabs.handler = function(place,macroName,params)
2220 var cookie = params[0];
2221 var numTabs = (params.length-1)/3;
2222 var wrapper = createTiddlyElement(null,"div",null,cookie);
2223 var tabset = createTiddlyElement(wrapper,"div",null,"tabset");
2224 tabset.setAttribute("cookie",cookie);
2225 var validTab = false;
2226 for(var t=0; t<numTabs; t++)
2228 var label = params[t*3+1];
2229 var prompt = params[t*3+2];
2230 var content = params[t*3+3];
2231 var tab = createTiddlyButton(tabset,label,prompt,this.onClickTab,"tab tabUnselected");
2232 tab.setAttribute("tab",label);
2233 tab.setAttribute("content",content);
2235 if(config.options[cookie] == label)
2239 config.options[cookie] = params[1];
2240 place.appendChild(wrapper);
2241 this.switchTab(tabset,config.options[cookie]);
2244 config.macros.tabs.onClickTab = function(e)
2246 config.macros.tabs.switchTab(this.parentNode,this.getAttribute("tab"));
2250 config.macros.tabs.switchTab = function(tabset,tab)
2252 var cookie = tabset.getAttribute("cookie");
2254 var nodes = tabset.childNodes;
2255 for(var t=0; t<nodes.length; t++)
2256 if(nodes[t].getAttribute && nodes[t].getAttribute("tab") == tab)
2259 theTab.className = "tab tabSelected";
2262 nodes[t].className = "tab tabUnselected"
2265 if(tabset.nextSibling && tabset.nextSibling.className == "tabContents")
2266 tabset.parentNode.removeChild(tabset.nextSibling);
2267 var tabContent = createTiddlyElement(null,"div",null,"tabContents");
2268 tabset.parentNode.insertBefore(tabContent,tabset.nextSibling);
2269 var contentTitle = theTab.getAttribute("content");
2270 wikify(store.getTiddlerText(contentTitle),tabContent,null,store.getTiddler(contentTitle));
2273 config.options[cookie] = tab;
2274 saveOptionCookie(cookie);
2279 // <<gradient [[tiddler name]] vert|horiz rgb rgb rgb rgb... >>
2280 config.macros.gradient.handler = function(place,macroName,params,wikifier)
2282 var terminator = ">>";
2285 panel = createTiddlyElement(place,"div",null,"gradient");
2288 panel.style.position = "relative";
2289 panel.style.overflow = "hidden";
2290 panel.style.zIndex = "0";
2294 var styles = config.formatterHelpers.inlineCssHelper(wikifier);
2295 config.formatterHelpers.applyCssHelper(panel,styles);
2298 for(t=1; t<params.length; t++)
2300 var c = new RGB(params[t]);
2304 drawGradient(panel,params[0] != "vert",colours);
2306 wikifier.subWikify(panel,terminator);
2309 panel.style.height = "100%";
2310 panel.style.width = "100%";
2314 config.macros.message.handler = function(place,macroName,params)
2319 var p = params[0].split(".");
2320 for(var t=0; t<p.length; t++)
2327 createTiddlyText(place,m.toString().format(params.splice(1)));
2331 config.macros.view.handler = function(place,macroName,params,wikifier,paramString,tiddler)
2333 if((tiddler instanceof Tiddler) && params[0])
2335 var value = store.getValue(tiddler,params[0]);
2336 if(value != undefined)
2340 highlightify(value,place,highlightHack);
2343 createTiddlyLink(place,value,true);
2346 wikify(value,place,highlightHack,tiddler);
2349 value = Date.convertFromYYYYMMDDHHMM(value);
2351 createTiddlyText(place,value.formatString(params[2]));
2353 createTiddlyText(place,value);
2359 config.macros.edit.handler = function(place,macroName,params,wikifier,paramString,tiddler)
2361 var field = params[0];
2362 if((tiddler instanceof Tiddler) && field)
2364 story.setDirty(tiddler.title,true);
2367 var e = createTiddlyElement(null,"input");
2368 if(tiddler.isReadOnly())
2369 e.setAttribute("readOnly","readOnly");
2370 e.setAttribute("edit",field);
2371 e.setAttribute("type","text");
2372 var v = store.getValue(tiddler,field);
2376 e.setAttribute("size","40");
2377 e.setAttribute("autocomplete","off");
2378 place.appendChild(e);
2382 var wrapper1 = createTiddlyElement(null,"fieldset",null,"fieldsetFix");
2383 var wrapper2 = createTiddlyElement(wrapper1,"div");
2384 var e = createTiddlyElement(wrapper2,"textarea");
2385 if(tiddler.isReadOnly())
2386 e.setAttribute("readOnly","readOnly");
2387 var v = store.getValue(tiddler,field);
2392 var lines = v.match(/\n/mg);
2393 var maxLines = Math.max(parseInt(config.options.txtMaxEditRows),5);
2394 if(lines != null && lines.length > rows)
2395 rows = lines.length + 5;
2396 rows = Math.min(rows,maxLines);
2397 e.setAttribute("rows",rows);
2398 e.setAttribute("edit",field);
2399 place.appendChild(wrapper1);
2404 config.macros.tagChooser.onClick = function(e)
2406 if(!e) var e = window.event;
2407 var lingo = config.views.editor.tagChooser;
2408 var popup = Popup.create(this);
2409 var tags = store.getTags();
2410 if(tags.length == 0)
2411 createTiddlyText(createTiddlyElement(popup,"li"),lingo.popupNone);
2412 for(var t=0; t<tags.length; t++)
2414 var theTag = createTiddlyButton(createTiddlyElement(popup,"li"),tags[t][0],lingo.tagTooltip.format([tags[t][0]]),config.macros.tagChooser.onTagClick);
2415 theTag.setAttribute("tag",tags[t][0]);
2416 theTag.setAttribute("tiddler", this.getAttribute("tiddler"));
2418 Popup.show(popup,false);
2419 e.cancelBubble = true;
2420 if(e.stopPropagation) e.stopPropagation();
2424 config.macros.tagChooser.onTagClick = function(e)
2426 if(!e) var e = window.event;
2427 var tag = this.getAttribute("tag");
2428 var title = this.getAttribute("tiddler");
2430 story.setTiddlerTag(title,tag,0);
2434 config.macros.tagChooser.handler = function(place,macroName,params,wikifier,paramString,tiddler)
2436 if(tiddler instanceof Tiddler)
2438 var title = tiddler.title;
2439 var lingo = config.views.editor.tagChooser;
2440 var btn = createTiddlyButton(place,lingo.text,lingo.tooltip,this.onClick);
2441 btn.setAttribute("tiddler", title);
2445 // Create a toolbar command button
2446 // place - parent DOM element
2447 // command - reference to config.commands[] member -or- name of member
2448 // tiddler - reference to tiddler that toolbar applies to
2449 // theClass - the class to give the button
2450 config.macros.toolbar.createCommand = function(place,commandName,tiddler,theClass)
2452 if(typeof commandName != "string")
2455 for(var t in config.commands)
2456 if(config.commands[t] == commandName)
2460 if((tiddler instanceof Tiddler) && (typeof commandName == "string"))
2462 var title = tiddler.title;
2463 var command = config.commands[commandName];
2464 var ro = tiddler.isReadOnly();
2465 var shadow = store.isShadowTiddler(title) && !store.tiddlerExists(title);
2466 var text = ro && command.readOnlyText ? command.readOnlyText : command.text;
2467 var tooltip = ro && command.readOnlyTooltip ? command.readOnlyTooltip : command.tooltip;
2468 if((!ro || (ro && !command.hideReadOnly)) && !(shadow && command.hideShadow))
2471 var btn = createTiddlyButton(null,text,tooltip,this.onClickCommand);
2472 btn.setAttribute("commandName", commandName);
2473 btn.setAttribute("tiddler", title);
2475 addClass(btn,theClass);
2476 place.appendChild(btn);
2481 config.macros.toolbar.onClickCommand = function(e)
2483 if(!e) var e = window.event;
2484 var command = config.commands[this.getAttribute("commandName")];
2485 return command.handler(e,this,this.getAttribute("tiddler"));
2488 // Invoke the first command encountered from a given place that is tagged with a specified class
2489 config.macros.toolbar.invokeCommand = function(place,theClass,event)
2491 var children = place.getElementsByTagName("a")
2492 for(var t=0; t<children.length; t++)
2494 var c = children[t];
2495 if(hasClass(c,theClass) && c.getAttribute && c.getAttribute("commandName"))
2497 if(c.onclick instanceof Function)
2498 c.onclick.call(c,event);
2504 config.macros.toolbar.handler = function(place,macroName,params,wikifier,paramString,tiddler)
2506 for(var t=0; t<params.length; t++)
2510 switch(c.substr(0,1))
2513 theClass = "defaultCommand";
2517 theClass = "cancelCommand";
2521 if(c in config.commands)
2522 this.createCommand(place,c,tiddler,theClass);
2526 config.macros.plugins.handler = function(place,macroName,params,wikifier,paramString,tiddler)
2528 var e = createTiddlyElement(place,"div");
2529 e.setAttribute("refresh","macro");
2530 e.setAttribute("macroName","plugins");
2531 e.setAttribute("params",paramString);
2532 this.refresh(e,paramString);
2535 config.macros.plugins.refresh = function(place,params)
2537 var selectedRows = [];
2538 ListView.forEachSelector(place,function(e,rowName) {
2540 selectedRows.push(e.getAttribute("rowName"));
2542 removeChildren(place);
2543 params = params.parseParams("anon");
2544 var plugins = installedPlugins.slice(0);
2546 var configTiddlers = store.getTaggedTiddlers("systemConfig");
2547 for(t=0; t<configTiddlers.length; t++)
2549 tiddler = configTiddlers[t];
2550 if(plugins.findByField("title",tiddler.title) == null)
2552 p = getPluginInfo(tiddler);
2554 p.log.splice(0,0,this.skippedText);
2558 for(t=0; t<plugins.length; t++)
2561 p.forced = p.tiddler.isTagged("systemConfigForce");
2562 p.disabled = p.tiddler.isTagged("systemConfigDisable");
2563 p.Selected = selectedRows.indexOf(plugins[t].title) != -1;
2565 if(plugins.length == 0)
2566 createTiddlyElement(place,"em",null,null,this.noPluginText);
2568 ListView.create(place,plugins,this.listViewTemplate,this.onSelectCommand);
2571 config.macros.plugins.onSelectCommand = function(command,rowNames)
2577 for(t=0; t<rowNames.length; t++)
2578 store.setTiddlerTag(rowNames[t],false,"systemConfig");
2581 if(rowNames.length > 0 && confirm(config.macros.plugins.confirmDeleteText.format([rowNames.join(", ")])))
2583 for(t=0; t<rowNames.length; t++)
2585 store.removeTiddler(rowNames[t]);
2586 story.closeTiddler(rowNames[t],true,false);
2591 if(config.options.chkAutoSave)
2595 config.macros.refreshDisplay.handler = function(place)
2597 createTiddlyButton(place,this.label,this.prompt,this.onClick);
2600 config.macros.refreshDisplay.onClick = function(e)
2606 config.macros.importTiddlers.handler = function(place,macroName,params,wikifier,paramString,tiddler)
2610 createTiddlyElement(place,"div",null,"marked",this.readOnlyWarning);
2613 var importer = createTiddlyElement(null,"div",null,"importTiddler wizard");
2614 createTiddlyElement(importer,"h1",null,null,this.wizardTitle);
2615 createTiddlyElement(importer,"h2",null,"step1",this.step1);
2616 var step = createTiddlyElement(importer,"div",null,"wizardStep");
2617 createTiddlyText(step,this.step1prompt);
2618 var input = createTiddlyElement(null,"input",null,"txtOptionInput");
2619 input.type = "text";
2621 step.appendChild(input);
2622 importer.inputBox = input;
2623 createTiddlyElement(step,"br");
2624 createTiddlyText(step,this.step1promptFile);
2625 var fileInput = createTiddlyElement(null,"input",null,"txtOptionInput");
2626 fileInput.type = "file";
2627 fileInput.size = 50;
2628 fileInput.onchange = this.onBrowseChange;
2629 fileInput.onkeyup = this.onBrowseChange;
2630 step.appendChild(fileInput);
2631 createTiddlyElement(step,"br");
2632 createTiddlyText(step,this.step1promptFeeds);
2633 var feeds = this.getFeeds([{caption: this.step1feedPrompt, name: ""}]);
2634 createTiddlyDropDown(step,this.onFeedChange,feeds);
2635 createTiddlyElement(step,"br");
2636 createTiddlyButton(step,this.fetchLabel,this.fetchPrompt,this.onFetch,null,null,null);
2637 place.appendChild(importer);
2640 config.macros.importTiddlers.getFeeds = function(feeds)
2642 var tagged = store.getTaggedTiddlers("contentPublisher","title");
2643 for(var t=0; t<tagged.length; t++)
2644 feeds.push({caption: tagged[t].title, name: store.getTiddlerSlice(tagged[t].title,"URL")});
2648 config.macros.importTiddlers.onFeedChange = function(e)
2650 var importer = findRelated(this,"importTiddler","className","parentNode");
2651 importer.inputBox.value = this.value;
2652 this.selectedIndex = 0;
2655 config.macros.importTiddlers.onBrowseChange = function(e)
2657 var importer = findRelated(this,"importTiddler","className","parentNode");
2658 importer.inputBox.value = "file://" + this.value;
2661 config.macros.importTiddlers.onFetch = function(e)
2663 var importer = findRelated(this,"importTiddler","className","parentNode");
2664 var url = importer.inputBox.value;
2665 var cutoff = findRelated(importer.firstChild,"step2","className","nextSibling");
2668 var temp = cutoff.nextSibling;
2669 cutoff.parentNode.removeChild(cutoff);
2672 createTiddlyElement(importer,"h2",null,"step2",config.macros.importTiddlers.step2);
2673 var step = createTiddlyElement(importer,"div",null,"wizardStep",config.macros.importTiddlers.step2Text.format([url]));
2674 loadRemoteFile(url,config.macros.importTiddlers.onLoad,importer);
2677 config.macros.importTiddlers.onLoad = function(status,params,responseText,url,xhr)
2681 displayMessage(this.fetchError);
2684 var importer = params;
2685 // Check that the tiddler we're in hasn't been closed - doesn't work on IE
2686 // var p = importer;
2687 // while(p.parentNode)
2688 // p = p.parentNode;
2689 // if(!(p instanceof HTMLDocument))
2691 // Crack out the content - (should be refactored)
2692 var posOpeningDiv = responseText.indexOf(startSaveArea);
2693 var limitClosingDiv = responseText.indexOf("<!--POST-BODY-START--"+">");
2694 var posClosingDiv = responseText.lastIndexOf(endSaveArea,limitClosingDiv == -1 ? responseText.length : limitClosingDiv);
2695 if((posOpeningDiv == -1) || (posClosingDiv == -1))
2697 alert(config.messages.invalidFileError.format([url]));
2700 var content = "<html><body>" + responseText.substring(posOpeningDiv,posClosingDiv + endSaveArea.length) + "</body></html>";
2701 // Create the iframe
2702 var iframe = document.createElement("iframe");
2703 iframe.style.display = "none";
2704 importer.insertBefore(iframe,importer.firstChild);
2705 var doc = iframe.document;
2706 if(iframe.contentDocument)
2707 doc = iframe.contentDocument; // For NS6
2708 else if(iframe.contentWindow)
2709 doc = iframe.contentWindow.document; // For IE5.5 and IE6
2710 // Put the content in the iframe
2712 doc.writeln(content);
2714 // Load the content into a TiddlyWiki() object
2715 var storeArea = doc.getElementById("storeArea");
2716 var importStore = new TiddlyWiki();
2717 importStore.loadFromDiv(storeArea,"store");
2718 // Get rid of the iframe
2719 iframe.parentNode.removeChild(iframe);
2720 // Extract data for the listview
2722 importStore.forEachTiddler(function(title,tiddler)
2726 t.modified = tiddler.modified;
2727 t.modifier = tiddler.modifier;
2728 t.text = tiddler.text.substr(0,50);
2729 t.tags = tiddler.tags;
2732 // Display the listview
2733 createTiddlyElement(importer,"h2",null,"step3",config.macros.importTiddlers.step3);
2734 var step = createTiddlyElement(importer,"div",null,"wizardStep");
2735 ListView.create(step,tiddlers,config.macros.importTiddlers.listViewTemplate,config.macros.importTiddlers.onSelectCommand);
2736 // Save the importer
2737 importer.store = importStore;
2740 config.macros.importTiddlers.onSelectCommand = function(listView,command,rowNames)
2742 var importer = findRelated(listView,"importTiddler","className","parentNode");
2746 config.macros.importTiddlers.doImport(importer,rowNames);
2749 if(config.options.chkAutoSave)
2753 config.macros.importTiddlers.doImport = function(importer,rowNames)
2755 var theStore = importer.store;
2756 var overwrite = new Array();
2758 for(t=0; t<rowNames.length; t++)
2760 if(store.tiddlerExists(rowNames[t]))
2761 overwrite.push(rowNames[t]);
2763 if(overwrite.length > 0)
2764 if(!confirm(this.confirmOverwriteText.format([overwrite.join(", ")])))
2766 for(t=0; t<rowNames.length; t++)
2768 var inbound = theStore.fetchTiddler(rowNames[t]);
2769 store.saveTiddler(inbound.title, inbound.title, inbound.text, inbound.modifier, inbound.modified, inbound.tags);
2770 store.fetchTiddler(inbound.title).created = inbound.created;
2771 store.notify(rowNames[t],false);
2774 store.setDirty(true);
2775 createTiddlyElement(importer,"h2",null,"step4",this.step4.format([rowNames.length]));
2776 var step = createTiddlyElement(importer,"div",null,"wizardStep");
2777 for(t=0; t<rowNames.length; t++)
2779 createTiddlyLink(step,rowNames[t],true);
2780 createTiddlyElement(step,"br");
2782 createTiddlyElement(importer,"h2",null,"step5",this.step5);
2784 // ---------------------------------------------------------------------------------
2785 // Menu and toolbar commands
2786 // ---------------------------------------------------------------------------------
2788 config.commands.closeTiddler.handler = function(event,src,title)
2790 story.closeTiddler(title,true,event.shiftKey || event.altKey);
2794 config.commands.closeOthers.handler = function(event,src,title)
2796 story.closeAllTiddlers(title);
2800 config.commands.editTiddler.handler = function(event,src,title)
2803 story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
2804 story.focusTiddler(title,"text");
2808 config.commands.saveTiddler.handler = function(event,src,title)
2810 var newTitle = story.saveTiddler(title,event.shiftKey);
2812 story.displayTiddler(null,newTitle);
2816 config.commands.cancelTiddler.handler = function(event,src,title)
2818 if(story.hasChanges(title) && !readOnly)
2819 if(!confirm(this.warning.format([title])))
2821 story.setDirty(title,false);
2822 story.displayTiddler(null,title);
2826 config.commands.deleteTiddler.handler = function(event,src,title)
2828 var deleteIt = true;
2829 if (config.options.chkConfirmDelete)
2830 deleteIt = confirm(this.warning.format([title]));
2833 store.removeTiddler(title);
2834 story.closeTiddler(title,true,event.shiftKey || event.altKey);
2835 if(config.options.chkAutoSave)
2841 config.commands.permalink.handler = function(event,src,title)
2843 var t = encodeURIComponent(String.encodeTiddlyLink(title));
2844 if(window.location.hash != t)
2845 window.location.hash = t;
2849 config.commands.references.handler = function(event,src,title)
2851 var popup = Popup.create(src);
2854 var references = store.getReferringTiddlers(title);
2856 for(var r=0; r<references.length; r++)
2857 if(references[r].title != title && !references[r].isTagged("excludeLists"))
2859 createTiddlyLink(createTiddlyElement(popup,"li"),references[r].title,true);
2863 createTiddlyText(createTiddlyElement(popup,"li",null,"disabled"),this.popupNone);
2865 Popup.show(popup,false);
2866 event.cancelBubble = true;
2867 if (event.stopPropagation) event.stopPropagation();
2871 config.commands.jump.handler = function(event,src,title)
2873 var popup = Popup.create(src);
2876 story.forEachTiddler(function(title,element) {
2877 createTiddlyLink(createTiddlyElement(popup,"li"),title,true);
2880 Popup.show(popup,false);
2881 event.cancelBubble = true;
2882 if (event.stopPropagation) event.stopPropagation();
2886 // ---------------------------------------------------------------------------------
2888 // ---------------------------------------------------------------------------------
2894 this.modifier = null;
2895 this.modified = new Date();
2896 this.created = new Date();
2898 this.linksUpdated = false;
2903 Tiddler.prototype.getLinks = function()
2905 if(this.linksUpdated==false)
2910 // Format the text for storage in an RSS item
2911 Tiddler.prototype.saveToRss = function(url)
2915 s.push("<title" + ">" + this.title.htmlEncode() + "</title" + ">");
2916 s.push("<description>" + wikifyStatic(this.text,null,this).htmlEncode() + "</description>");
2917 for(var t=0; t<this.tags.length; t++)
2918 s.push("<category>" + this.tags[t] + "</category>");
2919 s.push("<link>" + url + "#" + encodeURIComponent(String.encodeTiddlyLink(this.title)) + "</link>");
2920 s.push("<pubDate>" + this.modified.toGMTString() + "</pubDate>");
2922 return(s.join("\n"));
2925 // Change the text and other attributes of a tiddler
2926 Tiddler.prototype.set = function(title,text,modifier,modified,tags,created,fields)
2928 this.assign(title,text,modifier,modified,tags,created,fields);
2933 // Change the text and other attributes of a tiddler without triggered a tiddler.changed() call
2934 Tiddler.prototype.assign = function(title,text,modifier,modified,tags,created,fields)
2936 if(title != undefined)
2938 if(text != undefined)
2940 if(modifier != undefined)
2941 this.modifier = modifier;
2942 if(modified != undefined)
2943 this.modified = modified;
2944 if(created != undefined)
2945 this.created = created;
2946 if(fields != undefined)
2947 this.fields = fields;
2948 if(tags != undefined)
2949 this.tags = (typeof tags == "string") ? tags.readBracketedList() : tags;
2950 else if(this.tags == undefined)
2955 // Get the tags for a tiddler as a string (space delimited, using [[brackets]] for tags containing spaces)
2956 Tiddler.prototype.getTags = function()
2958 return String.encodeTiddlyLinkList(this.tags);
2961 // Test if a tiddler carries a tag
2962 Tiddler.prototype.isTagged = function(tag)
2964 return this.tags.indexOf(tag) != -1;
2967 // Static method to convert "\n" to newlines, "\s" to "\"
2968 Tiddler.unescapeLineBreaks = function(text)
2970 return text ? text.unescapeLineBreaks() : "";
2973 // Convert newlines to "\n", "\" to "\s"
2974 Tiddler.prototype.escapeLineBreaks = function()
2976 return this.text.escapeLineBreaks();
2979 // Updates the secondary information (like links[] array) after a change to a tiddler
2980 Tiddler.prototype.changed = function()
2983 var t = this.autoLinkWikiWords() ? 0 : 1;
2984 var tiddlerLinkRegExp = t==0 ? config.textPrimitives.tiddlerAnyLinkRegExp : config.textPrimitives.tiddlerForcedLinkRegExp;
2985 tiddlerLinkRegExp.lastIndex = 0;
2986 var formatMatch = tiddlerLinkRegExp.exec(this.text);
2989 if(t==0 && formatMatch[1] && formatMatch[1] != this.title) // wikiWordLink
2991 if(formatMatch.index > 0)
2993 var preRegExp = new RegExp(config.textPrimitives.unWikiLink+"|"+config.textPrimitives.anyLetter,"mg");
2994 preRegExp.lastIndex = formatMatch.index-1;
2995 var preMatch = preRegExp.exec(this.text);
2996 if(preMatch.index != formatMatch.index-1)
2997 this.links.pushUnique(formatMatch[1]);
3000 this.links.pushUnique(formatMatch[1]);
3002 else if(formatMatch[2-t] && (store.tiddlerExists(formatMatch[3-t]) || store.isShadowTiddler(formatMatch[3-t]))) // titledBrackettedLink
3003 this.links.pushUnique(formatMatch[3-t]);
3004 else if(formatMatch[4-t] && formatMatch[4-t] != this.title) // brackettedLink
3005 this.links.pushUnique(formatMatch[4-t]);
3006 // Do not add link if match urlPattern (formatMatch[5-t])
3007 formatMatch = tiddlerLinkRegExp.exec(this.text);
3009 this.linksUpdated = true;
3013 Tiddler.prototype.getSubtitle = function()
3015 var theModifier = this.modifier;
3017 theModifier = config.messages.subtitleUnknown;
3018 var theModified = this.modified;
3020 theModified = theModified.toLocaleString();
3022 theModified = config.messages.subtitleUnknown;
3023 return(config.messages.tiddlerLinkTooltip.format([this.title,theModifier,theModified]));
3026 Tiddler.prototype.isReadOnly = function()
3031 Tiddler.prototype.autoLinkWikiWords = function()
3033 return !(this.isTagged("systemConfig") || this.isTagged("excludeMissing"));
3036 Tiddler.prototype.generateFingerprint = function()
3038 return "0x" + Crypto.hexSha1Str(this.text);
3041 // ---------------------------------------------------------------------------------
3042 // TiddlyWiki() object contains Tiddler()s
3043 // ---------------------------------------------------------------------------------
3045 function TiddlyWiki()
3047 var tiddlers = {}; // Hashmap by name of tiddlers
3048 this.tiddlersUpdated = false;
3049 this.namedNotifications = []; // Array of {name:,notify:} of notification functions
3050 this.notificationLevel = 0;
3051 this.slices = {}; // map tiddlerName->(map sliceName->sliceValue). Lazy.
3052 this.clear = function() {
3054 this.setDirty(false);
3056 this.fetchTiddler = function(title) {
3057 return tiddlers[title];
3059 this.deleteTiddler = function(title) {
3060 delete this.slices[title];
3061 delete tiddlers[title];
3063 this.addTiddler = function(tiddler) {
3064 delete this.slices[tiddler.title];
3065 tiddlers[tiddler.title] = tiddler;
3067 this.forEachTiddler = function(callback) {
3068 for(var t in tiddlers)
3070 var tiddler = tiddlers[t];
3071 if(tiddler instanceof Tiddler)
3072 callback.call(this,t,tiddler);
3077 // Set the dirty flag
3078 TiddlyWiki.prototype.setDirty = function(dirty)
3083 TiddlyWiki.prototype.isDirty = function()
3088 TiddlyWiki.prototype.suspendNotifications = function()
3090 this.notificationLevel--;
3093 TiddlyWiki.prototype.resumeNotifications = function()
3095 this.notificationLevel++;
3098 // Invoke the notification handlers for a particular tiddler
3099 TiddlyWiki.prototype.notify = function(title,doBlanket)
3101 if(!this.notificationLevel)
3102 for(var t=0; t<this.namedNotifications.length; t++)
3104 var n = this.namedNotifications[t];
3105 if((n.name == null && doBlanket) || (n.name == title))
3110 // Invoke the notification handlers for all tiddlers
3111 TiddlyWiki.prototype.notifyAll = function()
3113 if(!this.notificationLevel)
3114 for(var t=0; t<this.namedNotifications.length; t++)
3116 var n = this.namedNotifications[t];
3122 // Add a notification handler to a tiddler
3123 TiddlyWiki.prototype.addNotification = function(title,fn)
3125 for (var i=0; i<this.namedNotifications.length; i++)
3126 if((this.namedNotifications[i].name == title) && (this.namedNotifications[i].notify == fn))
3128 this.namedNotifications.push({name: title, notify: fn});
3132 TiddlyWiki.prototype.removeTiddler = function(title)
3134 var tiddler = this.fetchTiddler(title);
3137 this.deleteTiddler(title);
3138 this.notify(title,true);
3139 this.setDirty(true);
3143 TiddlyWiki.prototype.tiddlerExists = function(title)
3145 var t = this.fetchTiddler(title);
3146 return (t != undefined);
3149 TiddlyWiki.prototype.isShadowTiddler = function(title)
3151 return typeof config.shadowTiddlers[title] == "string";
3154 TiddlyWiki.prototype.getTiddler = function(title)
3156 var t = this.fetchTiddler(title);
3163 TiddlyWiki.prototype.getTiddlerText = function(title,defaultText)
3165 var tiddler = this.fetchTiddler(title);
3167 return tiddler.text;
3170 var pos = title.indexOf(config.textPrimitives.sliceSeparator);
3173 var slice = this.getTiddlerSlice(title.substr(0,pos),title.substr(pos + config.textPrimitives.sliceSeparator.length));
3177 if(this.isShadowTiddler(title))
3178 return config.shadowTiddlers[title];
3179 if(defaultText != undefined)
3184 TiddlyWiki.prototype.slicesRE = /(?:[\'\/]*~?(\w+)[\'\/]*\:[\'\/]*\s*(.*?)\s*$)|(?:\|[\'\/]*~?(\w+)\:?[\'\/]*\|\s*(.*?)\s*\|)/gm
3187 TiddlyWiki.prototype.calcAllSlices = function(title)
3190 var text = this.getTiddlerText(title,"");
3191 this.slicesRE.lastIndex = 0;
3194 var m = this.slicesRE.exec(text);
3198 slices[m[1]] = m[2];
3200 slices[m[3]] = m[4];
3207 // Returns the slice of text of the given name
3209 //# A text slice is a substring in the tiddler's text that is defined
3210 //# either like this
3211 //# aName: textSlice
3213 //# |aName:| textSlice |
3215 //# |aName| textSlice |
3217 //# In the text the name (or name:) may be decorated with '' or //. I.e.
3218 //# this would also a possible text slice:
3220 //# |''aName:''| textSlice |
3222 //# @param name should only contain "word characters" (i.e. "a-ZA-Z_0-9")
3223 //# @return [may be undefined] the (trimmed) text of the specified slice.
3224 TiddlyWiki.prototype.getTiddlerSlice = function(title,sliceName)
3226 var slices = this.slices[title];
3228 slices = this.calcAllSlices(title);
3229 this.slices[title] = slices;
3231 return slices[sliceName];
3234 // Build an hashmap of the specified named slices of a tiddler
3235 TiddlyWiki.prototype.getTiddlerSlices = function(title,sliceNames)
3238 for(var t=0; t<sliceNames.length; t++)
3240 var slice = this.getTiddlerSlice(title,sliceNames[t]);
3242 r[sliceNames[t]] = slice;
3247 TiddlyWiki.prototype.getRecursiveTiddlerText = function(title,defaultText,depth)
3249 var bracketRegExp = new RegExp("(?:\\[\\[([^\\]]+)\\]\\])","mg");
3250 var text = this.getTiddlerText(title,null);
3256 var match = bracketRegExp.exec(text);
3259 textOut.push(text.substr(lastPos,match.index-lastPos));
3263 textOut.push(match[1]);
3265 textOut.push(this.getRecursiveTiddlerText(match[1],"[[" + match[1] + "]]",depth-1));
3267 lastPos = match.index + match[0].length;
3270 textOut.push(text.substr(lastPos));
3272 return(textOut.join(""));
3275 TiddlyWiki.prototype.setTiddlerTag = function(title,status,tag)
3277 var tiddler = this.fetchTiddler(title);
3280 var t = tiddler.tags.indexOf(tag);
3282 tiddler.tags.splice(t,1);
3284 tiddler.tags.push(tag);
3286 this.notify(title,true);
3287 this.setDirty(true);
3291 TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags,fields)
3293 var tiddler = this.fetchTiddler(title);
3297 created = tiddler.created; // Preserve created date
3298 this.deleteTiddler(title);
3302 tiddler = new Tiddler();
3305 tiddler.set(newTitle,newBody,modifier,modified,tags,created,fields);
3306 this.addTiddler(tiddler);
3307 if(title != newTitle)
3308 this.notify(title,true);
3309 this.notify(newTitle,true);
3310 this.setDirty(true);
3314 TiddlyWiki.prototype.createTiddler = function(title)
3316 var tiddler = this.fetchTiddler(title);
3319 tiddler = new Tiddler();
3320 tiddler.title = title;
3321 this.addTiddler(tiddler);
3322 this.setDirty(true);
3327 // Load contents of a tiddlywiki from an HTML DIV
3328 TiddlyWiki.prototype.loadFromDiv = function(src,idPrefix,noUpdate)
3330 this.idPrefix = idPrefix;
3331 var storeElem = (typeof src == "string") ? document.getElementById(src) : src;
3332 var tiddlers = this.getLoader().loadTiddlers(this,storeElem.childNodes);
3333 this.setDirty(false);
3336 for(var i = 0;i<tiddlers.length; i++)
3337 tiddlers[i].changed();
3341 TiddlyWiki.prototype.updateTiddlers = function()
3343 this.tiddlersUpdated = true;
3344 this.forEachTiddler(function(title,tiddler) {
3349 // Return all tiddlers formatted as an HTML string
3350 TiddlyWiki.prototype.allTiddlersAsHtml = function()
3352 return store.getSaver().externalize(store);
3355 // Return an array of tiddlers matching a search regular expression
3356 TiddlyWiki.prototype.search = function(searchRegExp,sortField,excludeTag)
3358 var candidates = this.reverseLookup("tags",excludeTag,false);
3360 for(var t=0; t<candidates.length; t++)
3362 if((candidates[t].title.search(searchRegExp) != -1) || (candidates[t].text.search(searchRegExp) != -1))
3363 results.push(candidates[t]);
3366 sortField = "title";
3367 results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
3371 // Return an array of all the tags in use. Each member of the array is another array where [0] is the name of the tag and [1] is the number of occurances
3372 TiddlyWiki.prototype.getTags = function()
3375 this.forEachTiddler(function(title,tiddler) {
3376 for(var g=0; g<tiddler.tags.length; g++)
3378 var tag = tiddler.tags[g];
3380 for(var c=0; c<results.length; c++)
3381 if(results[c][0] == tag)
3387 results.push([tag,1]);
3390 results.sort(function(a,b) {return a[0].toLowerCase() < b[0].toLowerCase() ? -1 : (a[0].toLowerCase() == b[0].toLowerCase() ? 0 : +1);});
3394 // Return an array of the tiddlers that are tagged with a given tag
3395 TiddlyWiki.prototype.getTaggedTiddlers = function(tag,sortField)
3397 return this.reverseLookup("tags",tag,true,sortField);
3400 // Return an array of the tiddlers that link to a given tiddler
3401 TiddlyWiki.prototype.getReferringTiddlers = function(title,unusedParameter,sortField)
3403 if(!this.tiddlersUpdated)
3404 this.updateTiddlers();
3405 return this.reverseLookup("links",title,true,sortField);
3408 // Return an array of the tiddlers that do or do not have a specified entry in the specified storage array (ie, "links" or "tags")
3409 // lookupMatch == true to match tiddlers, false to exclude tiddlers
3410 TiddlyWiki.prototype.reverseLookup = function(lookupField,lookupValue,lookupMatch,sortField)
3413 this.forEachTiddler(function(title,tiddler) {
3414 var f = !lookupMatch;
3415 for(var lookup=0; lookup<tiddler[lookupField].length; lookup++)
3416 if(tiddler[lookupField][lookup] == lookupValue)
3419 results.push(tiddler);
3422 sortField = "title";
3423 results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
3427 // Return the tiddlers as a sorted array
3428 TiddlyWiki.prototype.getTiddlers = function(field,excludeTag)
3431 this.forEachTiddler(function(title,tiddler) {
3432 if(excludeTag == undefined || !tiddler.isTagged(excludeTag))
3433 results.push(tiddler);
3436 results.sort(function(a,b) {return a[field] < b[field] ? -1 : (a[field] == b[field] ? 0 : +1);});
3440 // Return array of names of tiddlers that are referred to but not defined
3441 TiddlyWiki.prototype.getMissingLinks = function(sortField)
3443 if(!this.tiddlersUpdated)
3444 this.updateTiddlers();
3446 this.forEachTiddler(function (title,tiddler) {
3447 for(var n=0; n<tiddler.links.length;n++)
3449 var link = tiddler.links[n];
3450 if(this.fetchTiddler(link) == null && !this.isShadowTiddler(link))
3451 results.pushUnique(link);
3458 // Return an array of names of tiddlers that are defined but not referred to
3459 TiddlyWiki.prototype.getOrphans = function()
3462 this.forEachTiddler(function (title,tiddler) {
3463 if(this.getReferringTiddlers(title).length == 0 && !tiddler.isTagged("excludeLists"))
3464 results.push(title);
3470 // Return an array of names of all the shadow tiddlers
3471 TiddlyWiki.prototype.getShadowed = function()
3474 for(var t in config.shadowTiddlers)
3475 if(typeof config.shadowTiddlers[t] == "string")
3481 // Resolves a Tiddler reference or tiddler title into a Tiddler object, or null if it doesn't exist
3482 TiddlyWiki.prototype.resolveTiddler = function(tiddler)
3484 var t = (typeof tiddler == 'string') ? this.getTiddler(tiddler) : tiddler;
3485 return t instanceof Tiddler ? t : null;
3488 TiddlyWiki.prototype.getLoader = function()
3491 this.loader = new TW21Loader();
3495 TiddlyWiki.prototype.getSaver = function()
3498 this.saver = new TW21Saver();
3502 // Returns true if path is a valid field name (path),
3503 // i.e. a sequence of identifiers, separated by '.'
3504 TiddlyWiki.isValidFieldName = function (name) {
3505 var match = /[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*/.exec(name);
3506 return match && (match[0] == name);
3509 // Throws an exception when name is not a valid field name.
3510 TiddlyWiki.checkFieldName = function(name) {
3511 if (!TiddlyWiki.isValidFieldName(name))
3512 throw config.messages.invalidFieldName.format([name]);
3515 function StringFieldAccess(n, readOnly) {
3517 ? function(t,v) {if (v != t[n]) throw config.messages.fieldCannotBeChanged.format([n]);}
3518 : function(t,v) {if (v != t[n]) {t[n] = v; return true;}};
3519 this.get = function(t) {return t[n];};
3522 function DateFieldAccess(n) {
3523 this.set = function(t,v) {
3524 var d = v instanceof Date ? v : Date.convertFromYYYYMMDDHHMM(v);
3526 t[n] = d; return true;
3529 this.get = function(t) {return t[n].convertToYYYYMMDDHHMM();}
3532 function LinksFieldAccess(n) {
3533 this.set = function(t,v) {
3534 var s = (typeof v == "string") ? v.readBracketedList() : v;
3535 if (s.toString() != t[n].toString()) {
3536 t[n] = s; return true;
3539 this.get = function(t) {return String.encodeTiddlyLinkList(t[n]);}
3542 TiddlyWiki.standardFieldAccess = {
3543 // The set functions return true when setting the data has changed the value.
3545 "title": new StringFieldAccess("title", true),
3546 // Handle the "tiddler" field name as the title
3547 "tiddler": new StringFieldAccess("title", true),
3549 "text": new StringFieldAccess("text"),
3550 "modifier": new StringFieldAccess("modifier"),
3551 "modified": new DateFieldAccess("modified"),
3552 "created": new DateFieldAccess("created"),
3553 "tags": new LinksFieldAccess("tags")
3556 TiddlyWiki.isStandardField = function(name) {
3557 return TiddlyWiki.standardFieldAccess[name] != undefined;
3560 // Sets the value of the given field of the tiddler to the value.
3561 // Setting an ExtendedField's value to null or undefined removes the field.
3562 // Setting a namespace to undefined removes all fields of that namespace.
3563 // The fieldName is case-insensitive.
3564 // All values will be converted to a string value.
3565 TiddlyWiki.prototype.setValue = function(tiddler, fieldName, value) {
3566 TiddlyWiki.checkFieldName(fieldName);
3567 var t = this.resolveTiddler(tiddler);
3571 fieldName = fieldName.toLowerCase();
3573 var isRemove = (value === undefined) || (value === null);
3578 var accessor = TiddlyWiki.standardFieldAccess[fieldName];
3581 // don't remove StandardFields
3583 var h = TiddlyWiki.standardFieldAccess[fieldName];
3584 if (!h.set(t, value))
3588 var oldValue = t.fields[fieldName];
3591 if (oldValue !== undefined) {
3592 // deletes a single field
3593 delete t.fields[fieldName];
3595 // no concrete value is defined for the fieldName
3596 // so we guess this is a namespace path.
3598 // delete all fields in a namespace
3599 var re = new RegExp('^'+fieldName+'\\.');
3601 for (var n in t.fields) {
3612 // the "normal" set case. value is defined (not null/undefined)
3613 // For convenience provide a nicer conversion Date->String
3614 value = value instanceof Date
3615 ? value.convertToYYYYMMDDHHMMSSMMM()
3617 if (oldValue == value)
3619 t.fields[fieldName] = value;
3623 // When we are here the tiddler/store really was changed.
3624 this.notify(t.title,true);
3625 if (!fieldName.match(/^temp\./))
3626 this.setDirty(true);
3629 // Returns the value of the given field of the tiddler.
3630 // The fieldName is case-insensitive.
3631 // Will only return String values (or undefined).
3632 TiddlyWiki.prototype.getValue = function(tiddler, fieldName) {
3633 var t = this.resolveTiddler(tiddler);
3637 fieldName = fieldName.toLowerCase();
3639 var accessor = TiddlyWiki.standardFieldAccess[fieldName];
3641 return accessor.get(t);
3644 return t.fields ? t.fields[fieldName] : undefined;
3647 // Calls the callback function for every field in the tiddler.
3649 // When callback function returns a non-false value the iteration stops
3650 // and that value is returned.
3652 // The order of the fields is not defined.
3654 // @param callback a function(tiddler, fieldName, value).
3656 TiddlyWiki.prototype.forEachField = function(tiddler, callback, onlyExtendedFields) {
3657 var t = this.resolveTiddler(tiddler);
3662 for (var n in t.fields) {
3663 var result = callback(t, n, t.fields[n]);
3669 if (onlyExtendedFields)
3672 for (var n in TiddlyWiki.standardFieldAccess) {
3674 // even though the "title" field can also be referenced through the name "tiddler"
3675 // we only visit this field once.
3678 var result = callback(t, n, TiddlyWiki.standardFieldAccess[n].get(t));
3685 // ---------------------------------------------------------------------------------
3687 // ---------------------------------------------------------------------------------
3689 // A story is a HTML div containing a sequence of tiddlers that can be manipulated
3690 // container - id of containing element
3691 // idPrefix - string prefix prepended to title to make ids for tiddlers in this story
3692 function Story(container,idPrefix)
3694 this.container = container;
3695 this.idPrefix = idPrefix;
3696 this.highlightRegExp = null;
3699 // Iterate through all the tiddlers in a story
3700 // fn - callback function to be called for each tiddler. Arguments are:
3701 // tiddler - reference to Tiddler object
3702 // element - reference to tiddler display element
3703 Story.prototype.forEachTiddler = function(fn)
3705 var place = document.getElementById(this.container);
3708 var e = place.firstChild;
3711 var n = e.nextSibling;
3712 var title = e.getAttribute("tiddler");
3713 fn.call(this,title,e);
3718 // Display several tiddlers given their titles in an array. Parameters same as displayTiddler(), except:
3719 // titles - array of string titles
3720 Story.prototype.displayTiddlers = function(srcElement,titles,template,animate,slowly)
3722 for(var t = titles.length-1;t>=0;t--)
3723 this.displayTiddler(srcElement,titles[t],template,animate,slowly);
3726 // Display a given tiddler with a given template. If the tiddler is already displayed but with a different
3727 // template, it is switched to the specified template
3728 // srcElement - reference to element from which this one is being opened -or-
3729 // special positions "top", "bottom"
3730 // title - title of tiddler to display
3731 // template - the name of the tiddler containing the template -or-
3732 // one of the constants DEFAULT_VIEW_TEMPLATE and DEFAULT_EDIT_TEMPLATE -or-
3733 // null or undefined to indicate the current template if there is one, DEFAULT_VIEW_TEMPLATE if not
3734 // animate - whether to perform animations
3735 // slowly - whether to perform animations in slomo
3736 Story.prototype.displayTiddler = function(srcElement,title,template,animate,slowly)
3738 var place = document.getElementById(this.container);
3739 var tiddlerElem = document.getElementById(this.idPrefix + title);
3741 this.refreshTiddler(title,template);
3744 var before = this.positionTiddler(srcElement);
3745 tiddlerElem = this.createTiddler(place,before,title,template);
3747 if(srcElement && typeof srcElement !== "string")
3749 if(anim && config.options.chkAnimate && (animate == undefined || animate == true))
3750 anim.startAnimating(new Cascade(title,srcElement,tiddlerElem,slowly),new Scroller(tiddlerElem,slowly));
3752 window.scrollTo(0,ensureVisible(tiddlerElem));
3756 // Figure out the appropriate position for a newly opened tiddler
3757 // srcElement - reference to the element containing the link to the tiddler -or-
3758 // special positions "top", "bottom"
3759 // returns - reference to the tiddler that the new one should appear before (null for the bottom of the story)
3760 Story.prototype.positionTiddler = function(srcElement)
3762 var place = document.getElementById(this.container);
3764 if(typeof srcElement == "string")
3769 before = place.firstChild;
3778 var after = this.findContainingTiddler(srcElement);
3780 before = place.firstChild;
3781 else if(after.nextSibling)
3782 before = after.nextSibling;
3789 // Create a tiddler frame at the appropriate place in a story column
3790 // place - reference to parent element
3791 // before - null, or reference to element before which to insert new tiddler
3792 // title - title of new tiddler
3793 // template - the name of the tiddler containing the template or one of the constants DEFAULT_VIEW_TEMPLATE and DEFAULT_EDIT_TEMPLATE
3794 Story.prototype.createTiddler = function(place,before,title,template)
3796 var tiddlerElem = createTiddlyElement(null,"div",this.idPrefix + title,"tiddler");
3797 tiddlerElem.setAttribute("refresh","tiddler");
3798 place.insertBefore(tiddlerElem,before);
3799 this.refreshTiddler(title,template);
3803 // Overridable for choosing the name of the template to apply for a tiddler
3804 Story.prototype.chooseTemplateForTiddler = function(title,template)
3807 template = DEFAULT_VIEW_TEMPLATE;
3808 if(template == DEFAULT_VIEW_TEMPLATE || template == DEFAULT_EDIT_TEMPLATE)
3809 template = config.tiddlerTemplates[template];
3813 // Overridable for extracting the text of a template from a tiddler
3814 Story.prototype.getTemplateForTiddler = function(title,template,tiddler)
3816 return store.getRecursiveTiddlerText(template,null,10);
3819 // Apply a template to an existing tiddler if it is not already displayed using that template
3820 // title - title of tiddler to update
3821 // template - the name of the tiddler containing the template or one of the constants DEFAULT_VIEW_TEMPLATE and DEFAULT_EDIT_TEMPLATE
3822 // force - if true, forces the refresh even if the template hasn't changedd
3823 Story.prototype.refreshTiddler = function(title,template,force)
3825 var tiddlerElem = document.getElementById(this.idPrefix + title);
3828 if(tiddlerElem.getAttribute("dirty") == "true" && !force)
3830 template = this.chooseTemplateForTiddler(title,template);
3831 var currTemplate = tiddlerElem.getAttribute("template");
3832 if((template != currTemplate) || force)
3834 var tiddler = store.getTiddler(title);
3837 tiddler = new Tiddler();
3838 if(store.isShadowTiddler(title))
3839 tiddler.set(title,store.getTiddlerText(title),config.views.wikified.shadowModifier,version.date,[],version.date);
3842 var text = template=="EditTemplate"
3843 ? config.views.editor.defaultText.format([title])
3844 : config.views.wikified.defaultText.format([title]);
3845 tiddler.set(title,text,config.views.wikified.defaultModifier,version.date,[],version.date);
3848 tiddlerElem.setAttribute("tags",tiddler.tags.join(" "));
3849 tiddlerElem.setAttribute("tiddler",title);
3850 tiddlerElem.setAttribute("template",template);
3852 tiddlerElem.onmouseover = this.onTiddlerMouseOver;
3853 tiddlerElem.onmouseout = this.onTiddlerMouseOut;
3854 tiddlerElem.ondblclick = this.onTiddlerDblClick;
3855 tiddlerElem[window.event?"onkeydown":"onkeypress"] = this.onTiddlerKeyPress;
3856 var html = this.getTemplateForTiddler(title,template,tiddler);
3857 tiddlerElem.innerHTML = html;
3858 applyHtmlMacros(tiddlerElem,tiddler);
3859 if(store.getTaggedTiddlers(title).length > 0)
3860 addClass(tiddlerElem,"isTag");
3862 removeClass(tiddlerElem,"isTag");
3863 if(!store.tiddlerExists(title))
3865 if(store.isShadowTiddler(title))
3866 addClass(tiddlerElem,"shadow");
3868 addClass(tiddlerElem,"missing");
3872 removeClass(tiddlerElem,"shadow");
3873 removeClass(tiddlerElem,"missing");
3880 // Refresh all tiddlers in the Story
3881 Story.prototype.refreshAllTiddlers = function()
3883 var place = document.getElementById(this.container);
3884 var e = place.firstChild;
3887 this.refreshTiddler(e.getAttribute("tiddler"),e.getAttribute("template"),true);
3888 while((e = e.nextSibling) != null)
3889 this.refreshTiddler(e.getAttribute("tiddler"),e.getAttribute("template"),true);
3892 // Default tiddler onmouseover/out event handlers
3893 Story.prototype.onTiddlerMouseOver = function(e)
3895 if(window.addClass instanceof Function)
3896 addClass(this,"selected");
3899 Story.prototype.onTiddlerMouseOut = function(e)
3901 if(window.removeClass instanceof Function)
3902 removeClass(this,"selected");
3905 // Default tiddler ondblclick event handler
3906 Story.prototype.onTiddlerDblClick = function(e)
3908 if(!e) var e = window.event;
3909 var theTarget = resolveTarget(e);
3910 if(theTarget && theTarget.nodeName.toLowerCase() != "input" && theTarget.nodeName.toLowerCase() != "textarea")
3912 if(document.selection && document.selection.empty)
3913 document.selection.empty();
3914 config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
3915 e.cancelBubble = true;
3916 if (e.stopPropagation) e.stopPropagation();
3923 Story.prototype.onTiddlerKeyPress = function(e)
3925 if(!e) var e = window.event;
3927 var consume = false;
3928 var title = this.getAttribute("tiddler");
3929 var target = resolveTarget(e);
3933 if(config.options.chkInsertTabs && target.tagName.toLowerCase() == "textarea")
3935 replaceSelection(target,String.fromCharCode(9));
3940 target.onblur = function()
3947 case 13: // Ctrl-Enter
3948 case 10: // Ctrl-Enter on IE PC
3949 case 77: // Ctrl-Enter is "M" on some platforms
3953 config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
3959 config.macros.toolbar.invokeCommand(this,"cancelCommand",e);
3963 e.cancelBubble = consume;
3966 if(e.stopPropagation) e.stopPropagation(); // Stop Propagation
3967 e.returnValue = true; // Cancel The Event in IE
3968 if(e.preventDefault ) e.preventDefault(); // Cancel The Event in Moz
3973 // Returns the specified field (input or textarea element) in a tiddler, otherwise the first edit field it finds
3974 // or null if it found no edit field at all
3975 Story.prototype.getTiddlerField = function(title,field)
3977 var tiddlerElem = document.getElementById(this.idPrefix + title);
3979 if(tiddlerElem != null)
3981 var children = tiddlerElem.getElementsByTagName("*");
3982 for (var t=0; t<children.length; t++)
3984 var c = children[t];
3985 if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
3989 if(c.getAttribute("edit") == field)
3997 // Focus a specified tiddler. Attempts to focus the specified field, otherwise the first edit field it finds
3998 Story.prototype.focusTiddler = function(title,field)
4000 var e = this.getTiddlerField(title,field);
4008 // Ensures that a specified tiddler does not have the focus
4009 Story.prototype.blurTiddler = function(title)
4011 var tiddlerElem = document.getElementById(this.idPrefix + title);
4012 if(tiddlerElem != null && tiddlerElem.focus && tiddlerElem.blur)
4014 tiddlerElem.focus();
4019 // Adds a specified value to the edit controls (if any) of a particular
4020 // array-formatted field of a particular tiddler (eg "tags")
4021 // title - name of tiddler
4022 // tag - value of field, without any [[brackets]]
4023 // mode - +1 to add the tag, -1 to remove it, 0 to toggle it
4024 // field - name of field (eg "tags")
4025 Story.prototype.setTiddlerField = function(title,tag,mode,field)
4027 var c = story.getTiddlerField(title,field);
4029 var tags = c.value.readBracketedList();
4030 tags.setItem(tag,mode);
4031 c.value = String.encodeTiddlyLinkList(tags);
4034 // The same as setTiddlerField but preset to the "tags" field
4035 Story.prototype.setTiddlerTag = function(title,tag,mode)
4037 Story.prototype.setTiddlerField(title,tag,mode,"tags");
4040 // Close a specified tiddler
4041 // title - name of tiddler to close
4042 // animate - whether to perform animations
4043 // slowly - whether to perform animations in slomo
4044 Story.prototype.closeTiddler = function(title,animate,slowly)
4046 var tiddlerElem = document.getElementById(this.idPrefix + title);
4047 if(tiddlerElem != null)
4050 this.scrubTiddler(tiddlerElem);
4051 if(anim && config.options.chkAnimate && animate)
4052 anim.startAnimating(new Slider(tiddlerElem,false,slowly,"all"));
4054 tiddlerElem.parentNode.removeChild(tiddlerElem);
4058 // Scrub IDs from a tiddler. This is so that the 'ghost' of a tiddler while it is being closed
4059 // does not interfere with things
4060 // tiddler - reference to the tiddler element
4061 Story.prototype.scrubTiddler = function(tiddlerElem)
4063 tiddlerElem.id = null;
4066 // Set the 'dirty' flag of a tiddler
4067 // title - title of tiddler to change
4068 // dirty - new boolean status of flag
4069 Story.prototype.setDirty = function(title,dirty)
4071 var tiddlerElem = document.getElementById(this.idPrefix + title);
4072 if(tiddlerElem != null)
4073 tiddlerElem.setAttribute("dirty",dirty ? "true" : "false");
4076 // Is a particular tiddler dirty (with unsaved changes)?
4077 Story.prototype.isDirty = function(title)
4079 var tiddlerElem = document.getElementById(this.idPrefix + title);
4080 if(tiddlerElem != null)
4081 return tiddlerElem.getAttribute("dirty") == "true";
4085 // Determine whether any open tiddler are dirty
4086 Story.prototype.areAnyDirty = function()
4089 this.forEachTiddler(function(title,element) {
4090 if(this.isDirty(title))
4096 // Close all tiddlers in the story
4097 Story.prototype.closeAllTiddlers = function(exclude)
4100 this.forEachTiddler(function(title,element) {
4101 if((title != exclude) && element.getAttribute("dirty") != "true")
4102 this.closeTiddler(title);
4104 window.scrollTo(0,0);
4107 // Check if there are any tiddlers in the story
4108 Story.prototype.isEmpty = function()
4110 var place = document.getElementById(this.container);
4111 return(place && place.firstChild == null);
4114 // Perform a search and display the result
4115 // text - text to search for
4116 // useCaseSensitive - true for case sensitive matching
4117 // useRegExp - true to interpret text as a RegExp
4118 Story.prototype.search = function(text,useCaseSensitive,useRegExp)
4120 this.closeAllTiddlers();
4121 highlightHack = new RegExp(useRegExp ? text : text.escapeRegExp(),useCaseSensitive ? "mg" : "img");
4122 var matches = store.search(highlightHack,"title","excludeSearch");
4124 for(var t=matches.length-1; t>=0; t--)
4125 titles.push(matches[t].title);
4126 this.displayTiddlers(null,titles);
4127 highlightHack = null;
4128 var q = useRegExp ? "/" : "'";
4129 if(matches.length > 0)
4130 displayMessage(config.macros.search.successMsg.format([titles.length.toString(),q + text + q]));
4132 displayMessage(config.macros.search.failureMsg.format([q + text + q]));
4135 // Determine if the specified element is within a tiddler in this story
4136 // e - reference to an element
4137 // returns: reference to a tiddler element or null if none
4138 Story.prototype.findContainingTiddler = function(e)
4140 while(e && !hasClass(e,"tiddler"))
4145 // Gather any saveable fields from a tiddler element
4146 // e - reference to an element to scan recursively
4147 // fields - object to contain gathered field values
4148 Story.prototype.gatherSaveFields = function(e,fields)
4150 if(e && e.getAttribute)
4152 var f = e.getAttribute("edit");
4154 fields[f] = e.value.replace(/\r/mg,"");;
4155 if(e.hasChildNodes())
4157 var c = e.childNodes;
4158 for(var t=0; t<c.length; t++)
4159 this.gatherSaveFields(c[t],fields)
4164 // Determine whether a tiddler has any edit fields, and if so if their values have been changed
4165 // title - name of tiddler
4166 Story.prototype.hasChanges = function(title)
4168 var e = document.getElementById(this.idPrefix + title);
4172 this.gatherSaveFields(e,fields);
4173 var tiddler = store.fetchTiddler(title);
4176 for(var n in fields)
4177 if (store.getValue(title,n) != fields[n])
4183 // Save any open edit fields of a tiddler and updates the display as necessary
4184 // title - name of tiddler
4185 // minorUpdate - true if the modified date shouldn't be updated
4186 // returns: title of saved tiddler, or null if not saved
4187 Story.prototype.saveTiddler = function(title,minorUpdate)
4189 var tiddlerElem = document.getElementById(this.idPrefix + title);
4190 if(tiddlerElem != null)
4193 this.gatherSaveFields(tiddlerElem,fields);
4194 var newTitle = fields.title ? fields.title : title;
4195 if(store.tiddlerExists(newTitle) && newTitle != title)
4197 if(confirm(config.messages.overwriteWarning.format([newTitle.toString()])))
4198 this.closeTiddler(newTitle,false,false);
4202 tiddlerElem.id = this.idPrefix + newTitle;
4203 tiddlerElem.setAttribute("tiddler",newTitle);
4204 tiddlerElem.setAttribute("template",DEFAULT_VIEW_TEMPLATE);
4205 tiddlerElem.setAttribute("dirty","false");
4206 if(config.options.chkForceMinorUpdate)
4207 minorUpdate = !minorUpdate;
4208 var newDate = new Date();
4209 store.saveTiddler(title,newTitle,fields.text,config.options.txtUserName,minorUpdate ? undefined : newDate,fields.tags);
4210 for (var n in fields)
4211 if (!TiddlyWiki.isStandardField(n))
4212 store.setValue(newTitle,n,fields[n]);
4213 if(config.options.chkAutoSave)
4220 Story.prototype.permaView = function()
4223 this.forEachTiddler(function(title,element) {
4224 links.push(String.encodeTiddlyLink(title));
4226 var t = encodeURIComponent(links.join(" "));
4229 if(window.location.hash != t)
4230 window.location.hash = t;
4233 // ---------------------------------------------------------------------------------
4235 // ---------------------------------------------------------------------------------
4237 function getMessageDiv()
4239 var msgArea = document.getElementById("messageArea");
4242 if(!msgArea.hasChildNodes())
4243 createTiddlyButton(createTiddlyElement(msgArea,"div",null,"messageToolbar"),
4244 config.messages.messageClose.text,
4245 config.messages.messageClose.tooltip,
4247 msgArea.style.display = "block";
4248 return createTiddlyElement(msgArea,"div");
4251 function displayMessage(text,linkText)
4253 var e = getMessageDiv();
4261 var link = createTiddlyElement(e,"a",null,null,text);
4262 link.href = linkText;
4263 link.target = "_blank";
4266 e.appendChild(document.createTextNode(text));
4269 function clearMessage()
4271 var msgArea = document.getElementById("messageArea");
4274 removeChildren(msgArea);
4275 msgArea.style.display = "none";
4280 // ---------------------------------------------------------------------------------
4281 // Refresh mechanism
4282 // ---------------------------------------------------------------------------------
4284 config.refreshers = {
4285 link: function(e,changeList)
4287 var title = e.getAttribute("tiddlyLink");
4288 refreshTiddlyLink(e,title);
4292 tiddler: function(e,changeList)
4294 var title = e.getAttribute("tiddler");
4295 var template = e.getAttribute("template");
4296 if(changeList && changeList.indexOf(title) != -1 && !story.isDirty(title))
4297 story.refreshTiddler(title,template,true);
4299 refreshElements(e,changeList);
4303 content: function(e,changeList)
4305 var title = e.getAttribute("tiddler");
4306 var force = e.getAttribute("force");
4307 if(force != null || changeList == null || changeList.indexOf(title) != -1)
4310 wikify(store.getTiddlerText(title,title),e);
4317 macro: function(e,changeList)
4319 var macro = e.getAttribute("macroName");
4320 var params = e.getAttribute("params");
4322 macro = config.macros[macro];
4323 if(macro && macro.refresh)
4324 macro.refresh(e,params);
4329 function refreshElements(root,changeList)
4331 var nodes = root.childNodes;
4332 for(var c=0; c<nodes.length; c++)
4334 var e = nodes[c],type;
4336 type = e.getAttribute("refresh");
4339 var refresher = config.refreshers[type];
4340 var refreshed = false;
4341 if(refresher != undefined)
4342 refreshed = refresher(e,changeList);
4343 if(e.hasChildNodes() && !refreshed)
4344 refreshElements(e,changeList);
4348 function applyHtmlMacros(root,tiddler)
4350 var e = root.firstChild;
4353 var nextChild = e.nextSibling;
4356 var macro = e.getAttribute("macro");
4360 var p = macro.indexOf(" ");
4363 params = macro.substr(p+1);
4364 macro = macro.substr(0,p);
4366 invokeMacro(e,macro,params,null,tiddler);
4369 if(e.hasChildNodes())
4370 applyHtmlMacros(e,tiddler);
4375 function refreshPageTemplate(title)
4377 var stash = createTiddlyElement(document.body,"div");
4378 stash.style.display = "none";
4379 var display = document.getElementById("tiddlerDisplay");
4383 nodes = display.childNodes;
4384 for(t=nodes.length-1; t>=0; t--)
4385 stash.appendChild(nodes[t]);
4387 var wrapper = document.getElementById("contentWrapper");
4389 title = "PageTemplate";
4390 var html = store.getRecursiveTiddlerText(title,null,10);
4391 wrapper.innerHTML = html;
4392 applyHtmlMacros(wrapper);
4393 refreshElements(wrapper);
4394 display = document.getElementById("tiddlerDisplay");
4395 removeChildren(display);
4397 display = createTiddlyElement(wrapper,"div","tiddlerDisplay");
4398 nodes = stash.childNodes;
4399 for(t=nodes.length-1; t>=0; t--)
4400 display.appendChild(nodes[t]);
4401 stash.parentNode.removeChild(stash);
4404 function refreshDisplay(hint)
4406 var e = document.getElementById("contentWrapper");
4407 if(typeof hint == "string")
4409 refreshElements(e,hint);
4412 function refreshPageTitle()
4414 document.title = wikifyPlain("SiteTitle") + " - " + wikifyPlain("SiteSubtitle");
4417 function refreshStyles(title)
4419 setStylesheet(title == null ? "" : store.getRecursiveTiddlerText(title,"",10),title);
4422 function refreshColorPalette(title)
4428 function refreshAll()
4430 refreshPageTemplate();
4432 refreshStyles("StyleSheetLayout");
4433 refreshStyles("StyleSheetColors");
4434 refreshStyles("StyleSheet");
4435 refreshStyles("StyleSheetPrint");
4438 // ---------------------------------------------------------------------------------
4439 // Options cookie stuff
4440 // ---------------------------------------------------------------------------------
4442 function loadOptionsCookie()
4446 var cookies = document.cookie.split(";");
4447 for(var c=0; c<cookies.length; c++)
4449 var p = cookies[c].indexOf("=");
4452 var name = cookies[c].substr(0,p).trim();
4453 var value = cookies[c].substr(p+1).trim();
4454 switch(name.substr(0,3))
4457 config.options[name] = unescape(value);
4460 config.options[name] = value == "true";
4467 function saveOptionCookie(name)
4472 switch(name.substr(0,3))
4475 c += escape(config.options[name].toString());
4478 c += config.options[name] ? "true" : "false";
4481 c += "; expires=Fri, 1 Jan 2038 12:00:00 UTC; path=/";
4482 document.cookie = c;
4485 // ---------------------------------------------------------------------------------
4487 // ---------------------------------------------------------------------------------
4489 var saveUsingSafari = false;
4491 var startSaveArea = '<div id="' + 'storeArea">'; // Split up into two so that indexOf() of this source doesn't find it
4492 var endSaveArea = '</d' + 'iv>';
4494 // If there are unsaved changes, force the user to confirm before exitting
4495 function confirmExit()
4497 hadConfirmExit = true;
4498 if((store && store.isDirty && store.isDirty()) || (story && story.areAnyDirty && story.areAnyDirty()))
4499 return config.messages.confirmExit;
4502 // Give the user a chance to save changes before exitting
4503 function checkUnsavedChanges()
4505 if(store && store.isDirty && store.isDirty() && window.hadConfirmExit === false)
4507 if(confirm(config.messages.unsavedChangesWarning))
4512 function updateMarkupBlock(s,blockName,tiddlerName)
4514 return s.replaceChunk(
4515 "<!--%0-START-->".format([blockName]),
4516 "<!--%0-END-->".format([blockName]),
4517 "\n" + store.getRecursiveTiddlerText(tiddlerName,"") + "\n");
4520 // Save this tiddlywiki with the pending changes
4521 function saveChanges(onlyIfDirty)
4523 if(onlyIfDirty && !store.isDirty())
4526 // Get the URL of the document
4527 var originalPath = document.location.toString();
4528 // Check we were loaded from a file URL
4529 if(originalPath.substr(0,5) != "file:")
4531 alert(config.messages.notFileUrlError);
4532 if(store.tiddlerExists(config.messages.saveInstructions))
4533 story.displayTiddler(null,config.messages.saveInstructions);
4536 var localPath = getLocalPath(originalPath);
4537 // Load the original file
4538 var original = loadFile(localPath);
4539 if(original == null)
4541 alert(config.messages.cantSaveError);
4542 if(store.tiddlerExists(config.messages.saveInstructions))
4543 story.displayTiddler(null,config.messages.saveInstructions);
4546 // Locate the storeArea div's
4547 var posOpeningDiv = original.indexOf(startSaveArea);
4548 var limitClosingDiv = original.indexOf("<!--POST-BODY-START--"+">");
4549 var posClosingDiv = original.lastIndexOf(endSaveArea,limitClosingDiv == -1 ? original.length : limitClosingDiv);
4550 if((posOpeningDiv == -1) || (posClosingDiv == -1))
4552 alert(config.messages.invalidFileError.format([localPath]));
4556 if(config.options.chkSaveBackups)
4558 var backupPath = getBackupPath(localPath);
4559 var backup = saveFile(backupPath,original);
4561 displayMessage(config.messages.backupSaved,"file://" + backupPath);
4563 alert(config.messages.backupFailed);
4566 if(config.options.chkGenerateAnRssFeed)
4568 var rssPath = localPath.substr(0,localPath.lastIndexOf(".")) + ".xml";
4569 var rssSave = saveFile(rssPath,convertUnicodeToUTF8(generateRss()));
4571 displayMessage(config.messages.rssSaved,"file://" + rssPath);
4573 alert(config.messages.rssFailed);
4575 // Save empty template
4576 if(config.options.chkSaveEmptyTemplate)
4579 if((p = localPath.lastIndexOf("/")) != -1)
4580 emptyPath = localPath.substr(0,p) + "/empty.html";
4581 else if((p = localPath.lastIndexOf("\\")) != -1)
4582 emptyPath = localPath.substr(0,p) + "\\empty.html";
4584 emptyPath = localPath + ".empty.html";
4585 var empty = original.substr(0,posOpeningDiv + startSaveArea.length) + original.substr(posClosingDiv);
4586 var emptySave = saveFile(emptyPath,empty);
4588 displayMessage(config.messages.emptySaved,"file://" + emptyPath);
4590 alert(config.messages.emptyFailed);
4596 var revised = original.substr(0,posOpeningDiv + startSaveArea.length) + "\n" +
4597 convertUnicodeToUTF8(store.allTiddlersAsHtml()) + "\n" +
4598 original.substr(posClosingDiv);
4599 var newSiteTitle = convertUnicodeToUTF8((wikifyPlain("SiteTitle") + " - " + wikifyPlain("SiteSubtitle")).htmlEncode());
4600 revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
4601 revised = updateMarkupBlock(revised,"PRE-HEAD","MarkupPreHead");
4602 revised = updateMarkupBlock(revised,"POST-HEAD","MarkupPostHead");
4603 revised = updateMarkupBlock(revised,"PRE-BODY","MarkupPreBody");
4604 revised = updateMarkupBlock(revised,"POST-BODY","MarkupPostBody");
4605 save = saveFile(localPath,revised);
4613 displayMessage(config.messages.mainSaved,"file://" + localPath);
4614 store.setDirty(false);
4617 alert(config.messages.mainFailed);
4620 function getLocalPath(originalPath)
4622 // Remove any location or query part of the URL
4623 var argPos = originalPath.indexOf("?");
4625 originalPath = originalPath.substr(0,argPos);
4626 var hashPos = originalPath.indexOf("#");
4628 originalPath = originalPath.substr(0,hashPos);
4629 // Convert file://localhost/ to file:///
4630 if(originalPath.indexOf("file://localhost/") == 0)
4631 originalPath = "file://" + originalPath.substr(16);
4632 // Convert to a native file format assuming
4633 // "file:///x:/path/path/path..." - pc local file --> "x:\path\path\path..."
4634 // "file://///server/share/path/path/path..." - FireFox pc network file --> "\\server\share\path\path\path..."
4635 // "file:///path/path/path..." - mac/unix local file --> "/path/path/path..."
4636 // "file://server/share/path/path/path..." - pc network file --> "\\server\share\path\path\path..."
4638 if(originalPath.charAt(9) == ":") // pc local file
4639 localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\");
4640 else if(originalPath.indexOf("file://///") == 0) // FireFox pc network file
4641 localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\");
4642 else if(originalPath.indexOf("file:///") == 0) // mac/unix local file
4643 localPath = unescape(originalPath.substr(7));
4644 else if(originalPath.indexOf("file:/") == 0) // mac/unix local file
4645 localPath = unescape(originalPath.substr(5));
4646 else // pc network file
4647 localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\");
4651 function getBackupPath(localPath)
4653 var backSlash = true;
4654 var dirPathPos = localPath.lastIndexOf("\\");
4655 if(dirPathPos == -1)
4657 dirPathPos = localPath.lastIndexOf("/");
4660 var backupFolder = config.options.txtBackupFolder;
4661 if(!backupFolder || backupFolder == "")
4663 var backupPath = localPath.substr(0,dirPathPos) + (backSlash ? "\\" : "/") + backupFolder + localPath.substr(dirPathPos);
4664 backupPath = backupPath.substr(0,backupPath.lastIndexOf(".")) + "." + (new Date()).convertToYYYYMMDDHHMMSSMMM() + ".html";
4668 function generateRss()
4672 var u = store.getTiddlerText("SiteUrl");
4673 // Assemble the header
4674 s.push("<" + "?xml version=\"1.0\"?" + ">");
4675 s.push("<rss version=\"2.0\">");
4676 s.push("<channel>");
4677 s.push("<title" + ">" + wikifyPlain("SiteTitle").htmlEncode() + "</title" + ">");
4679 s.push("<link>" + u.htmlEncode() + "</link>");
4680 s.push("<description>" + wikifyPlain("SiteSubtitle").htmlEncode() + "</description>");
4681 s.push("<language>en-us</language>");
4682 s.push("<copyright>Copyright " + d.getFullYear() + " " + config.options.txtUserName.htmlEncode() + "</copyright>");
4683 s.push("<pubDate>" + d.toGMTString() + "</pubDate>");
4684 s.push("<lastBuildDate>" + d.toGMTString() + "</lastBuildDate>");
4685 s.push("<docs>http://blogs.law.harvard.edu/tech/rss</docs>");
4686 s.push("<generator>TiddlyWiki " + version.major + "." + version.minor + "." + version.revision + "</generator>");
4688 var tiddlers = store.getTiddlers("modified","excludeLists");
4689 var n = config.numRssItems > tiddlers.length ? 0 : tiddlers.length-config.numRssItems;
4690 for (var t=tiddlers.length-1; t>=n; t--)
4691 s.push(tiddlers[t].saveToRss(u));
4693 s.push("</channel>");
4696 return s.join("\n");
4700 // UTF-8 encoding rules:
4701 // 0x0000 - 0x007F: 0xxxxxxx
4702 // 0x0080 - 0x07FF: 110xxxxx 10xxxxxx
4703 // 0x0800 - 0xFFFF: 1110xxxx 10xxxxxx 10xxxxxx
4705 function convertUTF8ToUnicode(u)
4707 if(window.netscape == undefined)
4708 return manualConvertUTF8ToUnicode(u);
4710 return mozConvertUTF8ToUnicode(u);
4713 function manualConvertUTF8ToUnicode(utf)
4720 while(src < utf.length)
4722 b1 = utf.charCodeAt(src++);
4727 b2 = utf.charCodeAt(src++);
4728 c = String.fromCharCode(((b1 & 0x1F) << 6) | (b2 & 0x3F));
4729 uni = uni.substring(0,dst++).concat(c,utf.substr(src));
4733 b2 = utf.charCodeAt(src++);
4734 b3 = utf.charCodeAt(src++);
4735 c = String.fromCharCode(((b1 & 0xF) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F));
4736 uni = uni.substring(0,dst++).concat(c,utf.substr(src));
4742 function mozConvertUTF8ToUnicode(u)
4746 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
4747 var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
4748 converter.charset = "UTF-8";
4752 return manualConvertUTF8ToUnicode(u);
4754 var s = converter.ConvertToUnicode(u);
4755 var fin = converter.Finish();
4756 return (fin.length > 0) ? s+fin : s;
4759 function convertUnicodeToUTF8(s)
4761 if(window.netscape == undefined)
4762 return manualConvertUnicodeToUTF8(s);
4764 return mozConvertUnicodeToUTF8(s);
4767 function manualConvertUnicodeToUTF8(s)
4769 var re = /[^\u0000-\u007F]/g ;
4770 return s.replace(re, function($0) {return("&#" + $0.charCodeAt(0).toString() + ";");})
4773 function mozConvertUnicodeToUTF8(s)
4777 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
4778 var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
4779 converter.charset = "UTF-8";
4783 return manualConvertUnicodeToUTF8(s);
4785 var u = converter.ConvertFromUnicode(s);
4786 var fin = converter.Finish();
4793 function saveFile(fileUrl, content)
4796 if((r == null) || (r == false))
4797 r = mozillaSaveFile(fileUrl, content);
4798 if((r == null) || (r == false))
4799 r = ieSaveFile(fileUrl, content);
4800 if((r == null) || (r == false))
4801 r = javaSaveFile(fileUrl, content);
4805 function loadFile(fileUrl)
4808 if((r == null) || (r == false))
4809 r = mozillaLoadFile(fileUrl);
4810 if((r == null) || (r == false))
4811 r = ieLoadFile(fileUrl);
4812 if((r == null) || (r == false))
4813 r = javaLoadFile(fileUrl);
4817 // Returns null if it can't do it, false if there's an error, true if it saved OK
4818 function ieSaveFile(filePath, content)
4822 var fso = new ActiveXObject("Scripting.FileSystemObject");
4826 //alert("Exception while attempting to save\n\n" + e.toString());
4829 var file = fso.OpenTextFile(filePath,2,-1,0);
4830 file.Write(content);
4835 // Returns null if it can't do it, false if there's an error, or a string of the content if successful
4836 function ieLoadFile(filePath)
4840 var fso = new ActiveXObject("Scripting.FileSystemObject");
4841 var file = fso.OpenTextFile(filePath,1);
4842 var content = file.ReadAll();
4847 //alert("Exception while attempting to load\n\n" + e.toString());
4853 // Returns null if it can't do it, false if there's an error, true if it saved OK
4854 function mozillaSaveFile(filePath, content)
4856 if(window.Components)
4859 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
4860 var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
4861 file.initWithPath(filePath);
4863 file.create(0, 0664);
4864 var out = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
4865 out.init(file, 0x20 | 0x02, 00004,null);
4866 out.write(content, content.length);
4873 //alert("Exception while attempting to save\n\n" + e);
4879 // Returns null if it can't do it, false if there's an error, or a string of the content if successful
4880 function mozillaLoadFile(filePath)
4882 if(window.Components)
4885 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
4886 var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
4887 file.initWithPath(filePath);
4890 var inputStream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
4891 inputStream.init(file, 0x01, 00004, null);
4892 var sInputStream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
4893 sInputStream.init(inputStream);
4894 return(sInputStream.read(sInputStream.available()));
4898 //alert("Exception while attempting to load\n\n" + e);
4904 function javaUrlToFilename(url)
4906 var f = "//localhost";
4907 if(url.indexOf(f) == 0)
4908 return url.substring(f.length);
4909 var i = url.indexOf(":");
4911 return url.substring(i-1);
4915 function javaSaveFile(filePath, content)
4919 if(document.applets["TiddlySaver"])
4920 return document.applets["TiddlySaver"].saveFile(javaUrlToFilename(filePath),"UTF-8",content);
4927 var s = new java.io.PrintStream(new java.io.FileOutputStream(javaUrlToFilename(filePath)));
4938 function javaLoadFile(filePath)
4942 if(document.applets["TiddlySaver"])
4943 return String(document.applets["TiddlySaver"].loadFile(javaUrlToFilename(filePath),"UTF-8"));
4951 var r = new java.io.BufferedReader(new java.io.FileReader(javaUrlToFilename(filePath)));
4953 while ((line = r.readLine()) != null)
4954 content.push(new String(line));
4961 return content.join("\n");
4965 // ---------------------------------------------------------------------------------
4966 // Remote HTTP requests
4967 // ---------------------------------------------------------------------------------
4969 // Load a file over http
4970 // url - the source url
4971 // callback - function to call when there's a response
4972 // params - parameter object that gets passed to the callback for storing it's state
4973 // Return value is the underlying XMLHttpRequest object, or 'null' if there was an error
4974 // Callback function is called like this:
4975 // callback(status,params,responseText,xhr)
4976 // status - true if OK, false if error
4977 // params - the parameter object provided to loadRemoteFile()
4978 // responseText - the text of the file
4979 // xhr - the underlying XMLHttpRequest object
4980 function loadRemoteFile(url,callback,params)
4982 // Get an xhr object
4986 x = new XMLHttpRequest(); // Modern
4992 x = new ActiveXObject("Msxml2.XMLHTTP"); // IE 6
5000 x.onreadystatechange = function()
5002 if (x.readyState == 4)
5004 if ((x.status == 0 || x.status == 200) && callback)
5006 callback(true,params,x.responseText,url,x);
5009 callback(false,params,null,url,x);
5013 if(window.netscape && window.netscape.security && document.location.protocol.indexOf("http") == -1)
5014 window.netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
5017 url = url + (url.indexOf("?") < 0 ? "?" : "&") + "nocache=" + Math.random();
5018 x.open("GET",url,true);
5019 if (x.overrideMimeType)
5020 x.overrideMimeType("text/html");
5025 alert("Error in send " + e);
5030 // ---------------------------------------------------------------------------------
5031 // TiddlyWiki-specific utility functions
5032 // ---------------------------------------------------------------------------------
5034 function createTiddlyButton(theParent,theText,theTooltip,theAction,theClass,theId,theAccessKey)
5036 var theButton = document.createElement("a");
5039 theButton.onclick = theAction;
5040 theButton.setAttribute("href","javascript:;");
5043 theButton.setAttribute("title",theTooltip);
5045 theButton.appendChild(document.createTextNode(theText));
5047 theButton.className = theClass;
5049 theButton.className = "button";
5051 theButton.id = theId;
5053 theParent.appendChild(theButton);
5055 theButton.setAttribute("accessKey",theAccessKey);
5059 function createTiddlyLink(place,title,includeText,theClass,isStatic)
5061 var text = includeText ? title : null;
5062 var i = getTiddlyLinkInfo(title,theClass)
5065 btn = createExternalLink(place,"#" + title);
5067 btn = createTiddlyButton(place,text,i.subTitle,onClickTiddlerLink,i.classes);
5068 btn.setAttribute("refresh","link");
5069 btn.setAttribute("tiddlyLink",title);
5073 function refreshTiddlyLink(e,title)
5075 var i = getTiddlyLinkInfo(title,e.className);
5076 e.className = i.classes;
5077 e.title = i.subTitle;
5080 function getTiddlyLinkInfo(title,currClasses)
5082 var classes = currClasses ? currClasses.split(" ") : [];
5083 classes.pushUnique("tiddlyLink");
5084 var tiddler = store.fetchTiddler(title);
5088 subTitle = tiddler.getSubtitle();
5089 classes.pushUnique("tiddlyLinkExisting");
5090 classes.remove("tiddlyLinkNonExisting");
5091 classes.remove("shadow");
5095 classes.remove("tiddlyLinkExisting");
5096 classes.pushUnique("tiddlyLinkNonExisting");
5097 if(store.isShadowTiddler(title))
5099 subTitle = config.messages.shadowedTiddlerToolTip.format([title]);
5100 classes.pushUnique("shadow");
5104 subTitle = config.messages.undefinedTiddlerToolTip.format([title]);
5105 classes.remove("shadow");
5108 return {classes: classes.join(" "), subTitle: subTitle};
5111 function createExternalLink(place,url)
5113 var theLink = document.createElement("a");
5114 theLink.className = "externalLink";
5116 theLink.title = config.messages.externalLinkTooltip.format([url]);
5117 if(config.options.chkOpenInNewWindow)
5118 theLink.target = "_blank";
5119 place.appendChild(theLink);
5123 // Event handler for clicking on a tiddly link
5124 function onClickTiddlerLink(e)
5126 if (!e) var e = window.event;
5127 var theTarget = resolveTarget(e);
5128 var theLink = theTarget;
5131 title = theLink.getAttribute("tiddlyLink");
5132 theLink = theLink.parentNode;
5133 } while(title == null && theLink != null);
5136 var toggling = e.metaKey || e.ctrlKey;
5137 if(config.options.chkToggleLinks)
5138 toggling = !toggling;
5140 if(toggling && document.getElementById("tiddler" + title))
5141 story.closeTiddler(title,true,e.shiftKey || e.altKey);
5143 story.displayTiddler(theTarget,title,null,true,e.shiftKey || e.altKey);
5149 // Create a button for a tag with a popup listing all the tiddlers that it tags
5150 function createTagButton(place,tag,excludeTiddler)
5152 var theTag = createTiddlyButton(place,tag,config.views.wikified.tag.tooltip.format([tag]),onClickTag);
5153 theTag.setAttribute("tag",tag);
5155 theTag.setAttribute("tiddler",excludeTiddler);
5159 // Event handler for clicking on a tiddler tag
5160 function onClickTag(e)
5162 if (!e) var e = window.event;
5163 var theTarget = resolveTarget(e);
5164 var popup = Popup.create(this);
5165 var tag = this.getAttribute("tag");
5166 var title = this.getAttribute("tiddler");
5169 var tagged = store.getTaggedTiddlers(tag);
5172 for(r=0;r<tagged.length;r++)
5173 if(tagged[r].title != title)
5174 titles.push(tagged[r].title);
5175 var lingo = config.views.wikified.tag;
5176 if(titles.length > 0)
5178 var openAll = createTiddlyButton(createTiddlyElement(popup,"li"),lingo.openAllText.format([tag]),lingo.openAllTooltip,onClickTagOpenAll);
5179 openAll.setAttribute("tag",tag);
5180 createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
5181 for(r=0; r<titles.length; r++)
5183 createTiddlyLink(createTiddlyElement(popup,"li"),titles[r],true);
5187 createTiddlyText(createTiddlyElement(popup,"li",null,"disabled"),lingo.popupNone.format([tag]));
5188 createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
5189 var h = createTiddlyLink(createTiddlyElement(popup,"li"),tag,false);
5190 createTiddlyText(h,lingo.openTag.format([tag]));
5192 Popup.show(popup,false);
5193 e.cancelBubble = true;
5194 if (e.stopPropagation) e.stopPropagation();
5198 // Event handler for 'open all' on a tiddler popup
5199 function onClickTagOpenAll(e)
5201 if (!e) var e = window.event;
5202 var tag = this.getAttribute("tag");
5203 var tagged = store.getTaggedTiddlers(tag);
5205 for(var t=0; t<tagged.length; t++)
5206 titles.push(tagged[t].title);
5207 story.displayTiddlers(this,titles);
5211 function onClickError(e)
5213 if (!e) var e = window.event;
5214 var popup = Popup.create(this);
5215 var lines = this.getAttribute("errorText").split("\n");
5216 for(var t=0; t<lines.length; t++)
5217 createTiddlyElement(popup,"li",null,null,lines[t]);
5218 Popup.show(popup,false);
5219 e.cancelBubble = true;
5220 if (e.stopPropagation) e.stopPropagation();
5224 function createTiddlyDropDown(place,onchange,options)
5226 var sel = createTiddlyElement(place,"select");
5227 sel.onchange = onchange;
5228 for(var t=0; t<options.length; t++)
5230 var e = createTiddlyElement(sel,"option",null,null,options[t].caption);
5231 e.value = options[t].name;
5235 function createTiddlyError(place,title,text)
5237 var btn = createTiddlyButton(place,title,null,onClickError,"errorButton");
5238 if (text) btn.setAttribute("errorText",text);
5241 function merge(dst,src,preserveExisting)
5244 if (!preserveExisting || dst[p] === undefined)
5249 // Returns a string containing the description of an exception, optionally prepended by a message
5250 function exceptionText(e, message)
5252 var s = e.description ? e.description : e.toString();
5253 return message ? "%0:\n%1".format([message, s]) : s;
5256 // Displays an alert of an exception description with optional message
5257 function showException(e, message)
5259 alert(exceptionText(e, message));
5262 // ---------------------------------------------------------------------------------
5264 // ---------------------------------------------------------------------------------
5268 this.running = 0; // Incremented at start of each animation, decremented afterwards. If zero, the interval timer is disabled
5269 this.timerID = 0; // ID of the timer used for animating
5270 this.animations = []; // List of animations in progress
5274 // Start animation engine
5275 Animator.prototype.startAnimating = function() // Variable number of arguments
5277 for(var t=0; t<arguments.length; t++)
5278 this.animations.push(arguments[t]);
5279 if(this.running == 0)
5282 this.timerID = window.setInterval(function() {me.doAnimate(me);},5);
5284 this.running += arguments.length;
5287 // Perform an animation engine tick, calling each of the known animation modules
5288 Animator.prototype.doAnimate = function(me)
5291 while(a < me.animations.length)
5293 var animation = me.animations[a];
5294 if(animation.tick())
5298 me.animations.splice(a,1);
5299 if(--me.running == 0)
5300 window.clearInterval(me.timerID);
5305 // Map a 0..1 value to 0..1, but slow down at the start and end
5306 Animator.slowInSlowOut = function(progress)
5308 return(1-((Math.cos(progress * Math.PI)+1)/2));
5311 // ---------------------------------------------------------------------------------
5312 // Cascade animation
5313 // ---------------------------------------------------------------------------------
5315 function Cascade(text,startElement,targetElement,slowly)
5317 var winWidth = findWindowWidth();
5318 var winHeight = findWindowHeight();
5320 this.startElement = startElement;
5321 this.startLeft = findPosX(this.startElement);
5322 this.startTop = findPosY(this.startElement);
5323 this.startWidth = Math.min(this.startElement.offsetWidth,winWidth);
5324 this.startHeight = Math.min(this.startElement.offsetHeight,winHeight);
5325 this.targetElement = targetElement;
5326 targetElement.style.position = "relative";
5327 targetElement.style.zIndex = 2;
5328 this.targetLeft = findPosX(this.targetElement);
5329 this.targetTop = findPosY(this.targetElement);
5330 this.targetWidth = Math.min(this.targetElement.offsetWidth,winWidth);
5331 this.targetHeight = Math.min(this.targetElement.offsetHeight,winHeight);
5333 this.steps = slowly ? config.cascadeSlow : config.cascadeFast;
5339 Cascade.prototype.tick = function()
5342 if(this.progress >= this.steps)
5344 while(this.elements.length > 0)
5346 this.targetElement.style.position = "static";
5347 this.targetElement.style.zIndex = "";
5352 if(this.elements.length > 0 && this.progress > config.cascadeDepth)
5354 if(this.progress < (this.steps - config.cascadeDepth))
5356 var f = Animator.slowInSlowOut(this.progress/(this.steps - config.cascadeDepth - 1));
5357 var e = createTiddlyElement(document.body,"div",null,"cascade",this.text);
5359 e.style.left = this.startLeft + (this.targetLeft-this.startLeft) * f + "px";
5360 e.style.top = this.startTop + (this.targetTop-this.startTop) * f + "px";
5361 e.style.width = this.startWidth + (this.targetWidth-this.startWidth) * f + "px";
5362 e.style.height = this.startHeight + (this.targetHeight-this.startHeight) * f + "px";
5363 e.style.display = "block";
5364 this.elements.push(e);
5370 Cascade.prototype.removeTail = function()
5372 var e = this.elements[0];
5373 e.parentNode.removeChild(e);
5374 this.elements.shift();
5377 // ---------------------------------------------------------------------------------
5378 // Scroller animation
5379 // ---------------------------------------------------------------------------------
5381 function Scroller(targetElement,slowly)
5383 this.targetElement = targetElement;
5384 this.startScroll = findScrollY();
5385 this.targetScroll = ensureVisible(targetElement);
5387 this.step = slowly ? config.animSlow : config.animFast;
5391 Scroller.prototype.tick = function()
5393 this.progress += this.step;
5394 if(this.progress > 1)
5396 window.scrollTo(0,this.targetScroll);
5401 var f = Animator.slowInSlowOut(this.progress);
5402 window.scrollTo(0,this.startScroll + (this.targetScroll-this.startScroll) * f);
5407 // ---------------------------------------------------------------------------------
5409 // ---------------------------------------------------------------------------------
5411 // deleteMode - "none", "all" [delete target element and it's children], [only] "children" [but not the target element]
5412 function Slider(element,opening,slowly,deleteMode)
5414 this.element = element;
5415 element.style.display = "block";
5416 this.deleteMode = deleteMode;
5417 this.element.style.height = "auto";
5418 this.realHeight = element.offsetHeight;
5419 this.opening = opening;
5420 this.step = slowly ? config.animSlow : config.animFast;
5424 element.style.height = "0px";
5425 element.style.display = "block";
5430 this.step = -this.step;
5432 element.style.overflow = "hidden";
5436 Slider.prototype.stop = function()
5440 this.element.style.height = "auto";
5441 this.element.style.opacity = 1;
5442 this.element.style.filter = "alpha(opacity:100)";
5446 switch(this.deleteMode)
5449 this.element.style.display = "none";
5452 this.element.parentNode.removeChild(this.element);
5455 removeChildren(this.element);
5461 Slider.prototype.tick = function()
5463 this.progress += this.step;
5464 if(this.progress < 0 || this.progress > 1)
5471 var f = Animator.slowInSlowOut(this.progress);
5472 var h = this.realHeight * f;
5473 this.element.style.height = h + "px";
5474 this.element.style.opacity = f;
5475 this.element.style.filter = "alpha(opacity:" + f * 100 +")";
5480 // ---------------------------------------------------------------------------------
5482 // ---------------------------------------------------------------------------------
5485 stack: [] // Array of objects with members root: and popup:
5488 Popup.create = function(root)
5491 var popup = createTiddlyElement(document.body,"ol","popup","popup");
5492 Popup.stack.push({root: root, popup: popup});
5496 Popup.onDocumentClick = function(e)
5498 if (!e) var e = window.event;
5499 var target = resolveTarget(e);
5500 if(e.eventPhase == undefined)
5502 else if(e.eventPhase == Event.BUBBLING_PHASE || e.eventPhase == Event.AT_TARGET)
5507 Popup.show = function(unused,slowly)
5509 var curr = Popup.stack[Popup.stack.length-1];
5510 var rootLeft = findPosX(curr.root);
5511 var rootTop = findPosY(curr.root);
5512 var rootHeight = curr.root.offsetHeight;
5513 var popupLeft = rootLeft;
5514 var popupTop = rootTop + rootHeight;
5515 var popupWidth = curr.popup.offsetWidth;
5516 var winWidth = findWindowWidth();
5517 if(popupLeft + popupWidth > winWidth)
5518 popupLeft = winWidth - popupWidth;
5519 curr.popup.style.left = popupLeft + "px";
5520 curr.popup.style.top = popupTop + "px";
5521 curr.popup.style.display = "block";
5522 addClass(curr.root,"highlight");
5523 if(anim && config.options.chkAnimate)
5524 anim.startAnimating(new Scroller(curr.popup,slowly));
5526 window.scrollTo(0,ensureVisible(curr.popup));
5529 Popup.remove = function()
5531 if(Popup.stack.length > 0)
5533 Popup.removeFrom(0);
5537 Popup.removeFrom = function(from)
5539 for(var t=Popup.stack.length-1; t>=from; t--)
5541 var p = Popup.stack[t];
5542 removeClass(p.root,"highlight");
5543 p.popup.parentNode.removeChild(p.popup);
5545 Popup.stack = Popup.stack.slice(0,from);
5548 // ---------------------------------------------------------------------------------
5550 // ---------------------------------------------------------------------------------
5554 // Create a listview
5555 // place - where in the DOM tree to insert the listview
5556 // listObject - array of objects to be included in the listview
5557 // listTemplate - template for the listview
5558 // callback - callback for a command being selected
5559 // className - optional classname for the <table> element
5560 ListView.create = function(place,listObject,listTemplate,callback,className)
5562 var table = createTiddlyElement(place,"table",null,className ? className : "listView");
5563 var thead = createTiddlyElement(table,"thead");
5564 var r = createTiddlyElement(thead,"tr");
5565 for(var t=0; t<listTemplate.columns.length; t++)
5567 var columnTemplate = listTemplate.columns[t];
5568 var c = createTiddlyElement(r,"th");
5569 var colType = ListView.columnTypes[columnTemplate.type];
5570 if(colType && colType.createHeader)
5571 colType.createHeader(c,columnTemplate,t);
5573 var tbody = createTiddlyElement(table,"tbody");
5574 for(var rc=0; rc<listObject.length; rc++)
5576 rowObject = listObject[rc];
5577 r = createTiddlyElement(tbody,"tr");
5578 for(var c=0; c<listTemplate.rowClasses.length; c++)
5580 if(rowObject[listTemplate.rowClasses[c].field])
5581 addClass(r,listTemplate.rowClasses[c].className);
5583 rowObject.rowElement = rowObject;
5584 rowObject.colElements = {};
5585 for(var cc=0; cc<listTemplate.columns.length; cc++)
5587 var c = createTiddlyElement(r,"td");
5588 var columnTemplate = listTemplate.columns[cc];
5589 var field = columnTemplate.field;
5590 var colType = ListView.columnTypes[columnTemplate.type];
5591 if(colType && colType.createItem)
5592 colType.createItem(c,rowObject,field,columnTemplate,cc,rc);
5593 rowObject.colElements[field] = c;
5596 if(callback && listTemplate.actions)
5597 createTiddlyDropDown(place,ListView.getCommandHandler(callback),listTemplate.actions);
5598 if(callback && listTemplate.buttons)
5600 for(t=0; t<listTemplate.buttons.length; t++)
5602 var a = listTemplate.buttons[t];
5603 if(a && a.name != "")
5604 createTiddlyButton(place,a.caption,null,ListView.getCommandHandler(callback,a.name,a.allowEmptySelection));
5610 ListView.getCommandHandler = function(callback,name,allowEmptySelection)
5614 var view = findRelated(this,"TABLE",null,"previousSibling");
5616 ListView.forEachSelector(view,function(e,rowName) {
5618 tiddlers.push(rowName);
5620 if(tiddlers.length == 0 && !allowEmptySelection)
5621 alert(config.messages.nothingSelected);
5624 if(this.nodeName.toLowerCase() == "select")
5626 callback(view,this.value,tiddlers);
5627 this.selectedIndex = 0;
5630 callback(view,name,tiddlers);
5635 // Invoke a callback for each selector checkbox in the listview
5636 // view - <table> element of listView
5637 // callback(checkboxElement,rowName)
5639 // checkboxElement - DOM element of checkbox
5640 // rowName - name of this row as assigned by the column template
5641 // result: true if at least one selector was checked
5642 ListView.forEachSelector = function(view,callback)
5644 var checkboxes = view.getElementsByTagName("input");
5646 for(var t=0; t<checkboxes.length; t++)
5648 var cb = checkboxes[t];
5649 if(cb.getAttribute("type") == "checkbox")
5651 var rn = cb.getAttribute("rowName");
5662 ListView.columnTypes = {};
5664 ListView.columnTypes.String = {
5665 createHeader: function(place,columnTemplate,col)
5667 createTiddlyText(place,columnTemplate.title);
5669 createItem: function(place,listObject,field,columnTemplate,col,row)
5671 var v = listObject[field];
5673 createTiddlyText(place,v);
5677 ListView.columnTypes.Date = {
5678 createHeader: ListView.columnTypes.String.createHeader,
5679 createItem: function(place,listObject,field,columnTemplate,col,row)
5681 var v = listObject[field];
5683 createTiddlyText(place,v.formatString(columnTemplate.dateFormat));
5687 ListView.columnTypes.StringList = {
5688 createHeader: ListView.columnTypes.String.createHeader,
5689 createItem: function(place,listObject,field,columnTemplate,col,row)
5691 var v = listObject[field];
5694 for(var t=0; t<v.length; t++)
5696 createTiddlyText(place,v[t]);
5697 createTiddlyElement(place,"br");
5703 ListView.columnTypes.Selector = {
5704 createHeader: function(place,columnTemplate,col)
5706 createTiddlyCheckbox(place,null,false,this.onHeaderChange);
5708 createItem: function(place,listObject,field,columnTemplate,col,row)
5710 var e = createTiddlyCheckbox(place,null,listObject[field],null);
5711 e.setAttribute("rowName",listObject[columnTemplate.rowName]);
5713 onHeaderChange: function(e)
5715 var state = this.checked;
5716 var view = findRelated(this,"TABLE");
5719 ListView.forEachSelector(view,function(e,rowName) {
5725 ListView.columnTypes.Tags = {
5726 createHeader: ListView.columnTypes.String.createHeader,
5727 createItem: function(place,listObject,field,columnTemplate,col,row)
5729 var tags = listObject[field];
5730 createTiddlyText(place,String.encodeTiddlyLinkList(tags));
5734 ListView.columnTypes.Boolean = {
5735 createHeader: ListView.columnTypes.String.createHeader,
5736 createItem: function(place,listObject,field,columnTemplate,col,row)
5738 if(listObject[field] == true)
5739 createTiddlyText(place,columnTemplate.trueText);
5740 if(listObject[field] == false)
5741 createTiddlyText(place,columnTemplate.falseText);
5745 ListView.columnTypes.TagCheckbox = {
5746 createHeader: ListView.columnTypes.String.createHeader,
5747 createItem: function(place,listObject,field,columnTemplate,col,row)
5749 var e = createTiddlyCheckbox(place,null,listObject[field],this.onChange);
5750 e.setAttribute("tiddler",listObject.title);
5751 e.setAttribute("tag",columnTemplate.tag);
5753 onChange : function(e)
5755 var tag = this.getAttribute("tag");
5756 var tiddler = this.getAttribute("tiddler");
5757 store.setTiddlerTag(tiddler,this.checked,tag);
5761 ListView.columnTypes.TiddlerLink = {
5762 createHeader: ListView.columnTypes.String.createHeader,
5763 createItem: function(place,listObject,field,columnTemplate,col,row)
5765 var v = listObject[field];
5768 var link = createTiddlyLink(place,listObject[columnTemplate.tiddlerLink],false,null);
5769 createTiddlyText(link,listObject[field]);
5773 // ---------------------------------------------------------------------------------
5774 // Augmented methods for the JavaScript Number(), Array(), String() and Date() objects
5775 // ---------------------------------------------------------------------------------
5777 // Clamp a number to a range
5778 Number.prototype.clamp = function(min,max)
5788 // Add indexOf function if browser does not support it
5789 if(!Array.indexOf) {
5790 Array.prototype.indexOf = function(item,from)
5794 for(var i=from; i<this.length; i++)
5795 if(this[i] === item)
5800 // Find an entry in a given field of the members of an array
5801 Array.prototype.findByField = function(field,value)
5803 for(var t=0; t<this.length; t++)
5804 if(this[t][field] == value)
5809 // Return whether an entry exists in an array
5810 Array.prototype.contains = function(item)
5812 return this.indexOf(item) != -1;
5815 // Adds, removes or toggles a particular value within an array
5816 // value - value to add
5817 // mode - +1 to add value, -1 to remove value, 0 to toggle it
5818 Array.prototype.setItem = function(value,mode)
5820 var p = this.indexOf(value);
5822 mode = (p == -1) ? +1 : -1;
5835 // Return whether one of a list of values exists in an array
5836 Array.prototype.containsAny = function(items)
5838 for(var i=0; i<items.length; i++)
5839 if (this.indexOf(items[i]) != -1)
5844 // Return whether all of a list of values exists in an array
5845 Array.prototype.containsAll = function(items)
5847 for (var i = 0; i<items.length; i++)
5848 if (this.indexOf(items[i]) == -1)
5853 // Push a new value into an array only if it is not already present in the array. If the optional unique parameter is false, it reverts to a normal push
5854 Array.prototype.pushUnique = function(item,unique)
5856 if(unique != undefined && unique == false)
5860 if(this.indexOf(item) == -1)
5865 Array.prototype.remove = function(item)
5867 var p = this.indexOf(item);
5872 // Get characters from the right end of a string
5873 String.prototype.right = function(n)
5876 return this.slice(this.length-n);
5881 // Trim whitespace from both ends of a string
5882 String.prototype.trim = function()
5884 return this.replace(/^\s*|\s*$/g,"");
5887 // Convert a string from a CSS style property name to a JavaScript style name ("background-color" -> "backgroundColor")
5888 String.prototype.unDash = function()
5890 var s = this.split("-");
5892 for(var t=1; t<s.length; t++)
5893 s[t] = s[t].substr(0,1).toUpperCase() + s[t].substr(1);
5897 // Substitute substrings from an array into a format string that includes '%1'-type specifiers
5898 String.prototype.format = function(substrings)
5900 var subRegExp = /(?:%(\d+))/mg;
5904 var match = subRegExp.exec(this);
5905 if(match && match[1])
5907 if(match.index > currPos)
5908 r.push(this.substring(currPos,match.index));
5909 r.push(substrings[parseInt(match[1])]);
5910 currPos = subRegExp.lastIndex;
5913 if(currPos < this.length)
5914 r.push(this.substring(currPos,this.length));
5918 // Escape any special RegExp characters with that character preceded by a backslash
5919 String.prototype.escapeRegExp = function()
5921 var s = "\\^$*+?()=!|,{}[].";
5923 for(var t=0; t<s.length; t++)
5924 c = c.replace(new RegExp("\\" + s.substr(t,1),"g"),"\\" + s.substr(t,1));
5928 // Convert "\" to "\s", newlines to "\n" (and remove carriage returns)
5929 String.prototype.escapeLineBreaks = function()
5931 return this.replace(/\\/mg,"\\s").replace(/\n/mg,"\\n").replace(/\r/mg,"");
5934 // Convert "\n" to newlines, "\b" to " ", "\s" to "\" (and remove carriage returns)
5935 String.prototype.unescapeLineBreaks = function()
5937 return this.replace(/\\n/mg,"\n").replace(/\\b/mg," ").replace(/\\s/mg,"\\").replace(/\r/mg,"");
5940 // Convert & to "&", < to "<", > to ">" and " to """
5941 String.prototype.htmlEncode = function()
5943 return(this.replace(/&/mg,"&").replace(/</mg,"<").replace(/>/mg,">").replace(/\"/mg,"""));
5946 // Convert "&" to &, "<" to <, ">" to > and """ to "
5947 String.prototype.htmlDecode = function()
5949 return(this.replace(/&/mg,"&").replace(/</mg,"<").replace(/>/mg,">").replace(/"/mg,"\""));
5952 // Parse a space-separated string of name:value parameters where:
5953 // - the name or the value can be optional (in which case separate defaults are used instead)
5954 // - in case of ambiguity, a lone word is taken to be a value
5955 // - if 'cascadeDefaults' is set to true, then the defaults are modified by updated by each specified name or value
5956 // - name prefixes are not allowed if the 'noNames' parameter is true
5957 // - if both the name and value are present they must be separated by a colon
5958 // - the name and the value may both be quoted with single- or double-quotes, double-square brackets
5959 // - names or values quoted with {{double-curly braces}} are evaluated as a JavaScript expression
5960 // - as long as the 'allowEval' parameter is true
5961 // The result is an array of objects:
5962 // result[0] = object with a member for each parameter name, value of that member being an array of values
5963 // result[1..n] = one object for each parameter, with 'name' and 'value' members
5964 String.prototype.parseParams = function(defaultName,defaultValue,allowEval,noNames,cascadeDefaults)
5966 var parseToken = function(match,p)
5969 if(match[p]) // Double quoted
5971 else if(match[p+1]) // Single quoted
5973 else if(match[p+2]) // Double-square-bracket quoted
5975 else if(match[p+3]) // Double-brace quoted
5984 throw "Unable to evaluate {{" + match[p+3] + "}}: " + exceptionText(e);
5986 else if(match[p+4]) // Unquoted
5988 else if(match[p+5]) // empty quote
5993 var dblQuote = "(?:\"((?:(?:\\\\\")|[^\"])+)\")";
5994 var sngQuote = "(?:'((?:(?:\\\\\')|[^'])+)')";
5995 var dblSquare = "(?:\\[\\[((?:\\s|\\S)*?)\\]\\])";
5996 var dblBrace = "(?:\\{\\{((?:\\s|\\S)*?)\\}\\})";
5997 var unQuoted = noNames ? "([^\"'\\s]\\S*)" : "([^\"':\\s][^\\s:]*)";
5998 var emptyQuote = "((?:\"\")|(?:''))";
5999 var skipSpace = "(?:\\s*)";
6000 var token = "(?:" + dblQuote + "|" + sngQuote + "|" + dblSquare + "|" + dblBrace + "|" + unQuoted + "|" + emptyQuote + ")";
6002 ? new RegExp(token,"mg")
6003 : new RegExp(skipSpace + token + skipSpace + "(?:(\\:)" + skipSpace + token + ")?","mg");
6006 var match = re.exec(this);
6009 var n = parseToken(match,1);
6011 r.push({name: "", value: n});
6014 var v = parseToken(match,8);
6015 if(v == null && defaultName)
6020 else if(v == null && defaultValue)
6022 r.push({name: n, value: v});
6031 // Summarise parameters into first element
6032 for(var t=1; t<r.length; t++)
6035 r[0][r[t].name].push(r[t].value);
6037 r[0][r[t].name] = [r[t].value];
6042 // Process a string list of macro parameters into an array. Parameters can be quoted with "", '',
6043 // [[]], {{ }} or left unquoted (and therefore space-separated). Double-braces {{}} results in
6044 // an *evaluated* parameter: e.g. {{config.options.txtUserName}} results in the current user's name.
6045 String.prototype.readMacroParams = function()
6047 var p = this.parseParams("list",null,true,true);
6049 for(var t=1; t<p.length; t++)
6054 // Process a string list of unique tiddler names into an array. Tiddler names that have spaces in them must be [[bracketed]]
6055 String.prototype.readBracketedList = function(unique)
6057 var p = this.parseParams("list",null,false,true);
6059 for(var t=1; t<p.length; t++)
6060 n.pushUnique(p[t].value,unique);
6064 // Returns array with start and end index of chunk between given start and end marker, or undefined.
6065 String.prototype.getChunkRange = function(start,end)
6067 var s = this.indexOf(start);
6071 var e = this.indexOf(end,s);
6077 // Replace a chunk of a string given start and end markers
6078 String.prototype.replaceChunk = function(start,end,sub)
6080 var r = this.getChunkRange(start,end);
6082 ? this.substring(0,r[0]) + sub + this.substring(r[1])
6086 // Returns a chunk of a string between start and end markers, or undefined
6087 String.prototype.getChunk = function(start,end)
6089 var r = this.getChunkRange(start,end);
6091 return this.substring(r[0],r[1]);
6095 // Static method to bracket a string with double square brackets if it contains a space
6096 String.encodeTiddlyLink = function(title)
6098 if(title.indexOf(" ") == -1)
6101 return("[[" + title + "]]");
6104 // Static method to encodeTiddlyLink for every item in an array and join them with spaces
6105 String.encodeTiddlyLinkList = function(list)
6110 for(var t=0; t<list.length; t++)
6111 results.push(String.encodeTiddlyLink(list[t]));
6112 return results.join(" ");
6118 // Static method to left-pad a string with 0s to a certain width
6119 String.zeroPad = function(n,d)
6121 var s = n.toString();
6123 s = "000000000000000000000000000".substr(0,d-s.length) + s;
6127 String.prototype.startsWith = function(prefix)
6129 return !prefix || this.substring(0,prefix.length) == prefix;
6132 // Returns the first value of the given named parameter.
6135 //# as returned by parseParams or null/undefined
6136 //# @return [may be null/undefined]
6138 function getParam(params, name, defaultValue) {
6140 return defaultValue;
6141 var p = params[0][name];
6142 return p ? p[0] : defaultValue;
6145 // Returns the first value of the given boolean named parameter.
6148 //# as returned by parseParams or null/undefined
6150 function getFlag(params, name, defaultValue) {
6151 return !!getParam(params, name, defaultValue);
6154 // Substitute date components into a string
6155 Date.prototype.formatString = function(template)
6157 var t = template.replace(/0hh12/g,String.zeroPad(this.getHours12(),2));
6158 t = t.replace(/hh12/g,this.getHours12());
6159 t = t.replace(/0hh/g,String.zeroPad(this.getHours(),2));
6160 t = t.replace(/hh/g,this.getHours());
6161 t = t.replace(/0mm/g,String.zeroPad(this.getMinutes(),2));
6162 t = t.replace(/mm/g,this.getMinutes());
6163 t = t.replace(/0ss/g,String.zeroPad(this.getSeconds(),2));
6164 t = t.replace(/ss/g,this.getSeconds());
6165 t = t.replace(/[ap]m/g,this.getAmPm().toLowerCase());
6166 t = t.replace(/[AP]M/g,this.getAmPm().toUpperCase());
6167 t = t.replace(/wYYYY/g,this.getYearForWeekNo());
6168 t = t.replace(/wYY/g,String.zeroPad(this.getYearForWeekNo()-2000,2));
6169 t = t.replace(/YYYY/g,this.getFullYear());
6170 t = t.replace(/YY/g,String.zeroPad(this.getFullYear()-2000,2));
6171 t = t.replace(/MMM/g,config.messages.dates.months[this.getMonth()]);
6172 t = t.replace(/mmm/g,config.messages.dates.shortMonths[this.getMonth()]);
6173 t = t.replace(/0MM/g,String.zeroPad(this.getMonth()+1,2));
6174 t = t.replace(/MM/g,this.getMonth()+1);
6175 t = t.replace(/0WW/g,String.zeroPad(this.getWeek(),2));
6176 t = t.replace(/WW/g,this.getWeek());
6177 t = t.replace(/DDD/g,config.messages.dates.days[this.getDay()]);
6178 t = t.replace(/ddd/g,config.messages.dates.shortDays[this.getDay()]);
6179 t = t.replace(/0DD/g,String.zeroPad(this.getDate(),2));
6180 t = t.replace(/DDth/g,this.getDate()+this.daySuffix());
6181 t = t.replace(/DD/g,this.getDate());
6185 Date.prototype.getWeek = function()
6187 var dt = new Date(this.getTime());
6188 var d = dt.getDay();
6189 if (d==0) d=7;// JavaScript Sun=0, ISO Sun=7
6190 dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week to calculate weekNo
6191 var n = Math.floor((dt.getTime()-new Date(dt.getFullYear(),0,1)+3600000)/86400000);
6192 return Math.floor(n/7)+1;
6195 Date.prototype.getYearForWeekNo = function()
6197 var dt = new Date(this.getTime());
6198 var d = dt.getDay();
6199 if (d==0) d=7;// JavaScript Sun=0, ISO Sun=7
6200 dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week
6201 return dt.getFullYear();
6204 Date.prototype.getHours12 = function()
6206 var h = this.getHours();
6207 return h > 12 ? h-12 : ( h > 0 ? h : 12 );
6210 Date.prototype.getAmPm = function()
6212 return this.getHours() >= 12 ? "pm" : "am";
6215 Date.prototype.daySuffix = function()
6217 var num = this.getDate();
6218 if (num >= 11 && num <= 13) return "th";
6219 else if (num.toString().substr(-1)=="1") return "st";
6220 else if (num.toString().substr(-1)=="2") return "nd";
6221 else if (num.toString().substr(-1)=="3") return "rd";
6225 // Convert a date to local YYYYMMDDHHMM string format
6226 Date.prototype.convertToLocalYYYYMMDDHHMM = function()
6228 return(String.zeroPad(this.getFullYear(),4) + String.zeroPad(this.getMonth()+1,2) + String.zeroPad(this.getDate(),2) + String.zeroPad(this.getHours(),2) + String.zeroPad(this.getMinutes(),2));
6231 // Convert a date to UTC YYYYMMDDHHMM string format
6232 Date.prototype.convertToYYYYMMDDHHMM = function()
6234 return(String.zeroPad(this.getUTCFullYear(),4) + String.zeroPad(this.getUTCMonth()+1,2) + String.zeroPad(this.getUTCDate(),2) + String.zeroPad(this.getUTCHours(),2) + String.zeroPad(this.getUTCMinutes(),2));
6237 // Convert a date to UTC YYYYMMDD.HHMMSSMMM string format
6238 Date.prototype.convertToYYYYMMDDHHMMSSMMM = function()
6240 return(String.zeroPad(this.getUTCFullYear(),4) + String.zeroPad(this.getUTCMonth()+1,2) + String.zeroPad(this.getUTCDate(),2) + "." + String.zeroPad(this.getUTCHours(),2) + String.zeroPad(this.getUTCMinutes(),2) + String.zeroPad(this.getUTCSeconds(),2) + String.zeroPad(this.getUTCMilliseconds(),4));
6243 // Static method to create a date from a UTC YYYYMMDDHHMM format string
6244 Date.convertFromYYYYMMDDHHMM = function(d)
6246 var theDate = new Date(Date.UTC(parseInt(d.substr(0,4),10),
6247 parseInt(d.substr(4,2),10)-1,
6248 parseInt(d.substr(6,2),10),
6249 parseInt(d.substr(8,2),10),
6250 parseInt(d.substr(10,2),10),0,0));
6254 // ---------------------------------------------------------------------------------
6255 // Crypto functions and associated conversion routines
6256 // ---------------------------------------------------------------------------------
6258 // Crypto "namespace"
6259 function Crypto() {}
6261 // Convert a string to an array of big-endian 32-bit words
6262 Crypto.strToBe32s = function(str)
6265 var len = Math.floor(str.length/4);
6267 for(i=0, j=0; i<len; i++, j+=4)
6269 be[i] = ((str.charCodeAt(j)&0xff) << 24)|((str.charCodeAt(j+1)&0xff) << 16)|((str.charCodeAt(j+2)&0xff) << 8)|(str.charCodeAt(j+3)&0xff);
6271 while (j<str.length)
6273 be[j>>2] |= (str.charCodeAt(j)&0xff)<<(24-(j*8)%32);
6279 // Convert an array of big-endian 32-bit words to a string
6280 Crypto.be32sToStr = function(be)
6283 for(var i=0;i<be.length*32;i+=8)
6284 str += String.fromCharCode((be[i>>5]>>>(24-i%32)) & 0xff);
6288 // Convert an array of big-endian 32-bit words to a hex string
6289 Crypto.be32sToHex = function(be)
6291 var hex = "0123456789ABCDEF";
6293 for(var i=0;i<be.length*4;i++)
6294 str += hex.charAt((be[i>>2]>>((3-i%4)*8+4))&0xF) + hex.charAt((be[i>>2]>>((3-i%4)*8))&0xF);
6298 // Return, in hex, the SHA-1 hash of a string
6299 Crypto.hexSha1Str = function(str)
6301 return Crypto.be32sToHex(Crypto.sha1Str(str));
6304 // Return the SHA-1 hash of a string
6305 Crypto.sha1Str = function(str)
6307 return Crypto.sha1(Crypto.strToBe32s(str),str.length);
6310 // Calculate the SHA-1 hash of an array of blen bytes of big-endian 32-bit words
6311 Crypto.sha1 = function(x,blen)
6313 // Add 32-bit integers, wrapping at 32 bits
6314 //# Uses 16-bit operations internally to work around bugs in some JavaScript interpreters.
6315 add32 = function(a,b)
6317 var lsw = (a&0xFFFF)+(b&0xFFFF);
6318 var msw = (a>>16)+(b>>16)+(lsw>>16);
6319 return (msw<<16)|(lsw&0xFFFF);
6321 // Add five 32-bit integers, wrapping at 32 bits
6322 //# Uses 16-bit operations internally to work around bugs in some JavaScript interpreters.
6323 add32x5 = function(a,b,c,d,e)
6325 var lsw = (a&0xFFFF)+(b&0xFFFF)+(c&0xFFFF)+(d&0xFFFF)+(e&0xFFFF);
6326 var msw = (a>>16)+(b>>16)+(c>>16)+(d>>16)+(e>>16)+(lsw>>16);
6327 return (msw<<16)|(lsw&0xFFFF);
6329 // Bitwise rotate left a 32-bit integer by 1 bit
6332 return (n>>>31)|(n<<1);
6336 // Append padding so length in bits is 448 mod 512
6337 x[len>>5] |= 0x80 << (24-len%32);
6339 x[((len+64>>9)<<4)+15] = len;
6342 var k1 = 0x5A827999;
6343 var k2 = 0x6ED9EBA1;
6344 var k3 = 0x8F1BBCDC;
6345 var k4 = 0xCA62C1D6;
6347 var h0 = 0x67452301;
6348 var h1 = 0xEFCDAB89;
6349 var h2 = 0x98BADCFE;
6350 var h3 = 0x10325476;
6351 var h4 = 0xC3D2E1F0;
6353 for(var i=0;i<x.length;i+=16)
6364 t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
6365 e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
6369 w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
6370 t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
6371 e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
6375 w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
6376 t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k2);
6377 e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
6381 w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
6382 t = add32x5(e,(a>>>27)|(a<<5),(b&c)|(d&(b|c)),w[j],k3);
6383 e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
6387 w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
6388 t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k4);
6389 e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
6398 return Array(h0,h1,h2,h3,h4);
6401 // ---------------------------------------------------------------------------------
6402 // RGB colour object
6403 // ---------------------------------------------------------------------------------
6405 // Construct an RGB colour object from a '#rrggbb', '#rgb' or 'rgb(n,n,n)' string or from separate r,g,b values
6411 if(typeof r == "string")
6413 if(r.substr(0,1) == "#")
6417 this.r = parseInt(r.substr(1,2),16)/255;
6418 this.g = parseInt(r.substr(3,2),16)/255;
6419 this.b = parseInt(r.substr(5,2),16)/255;
6423 this.r = parseInt(r.substr(1,1),16)/15;
6424 this.g = parseInt(r.substr(2,1),16)/15;
6425 this.b = parseInt(r.substr(3,1),16)/15;
6430 var rgbPattern = /rgb\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/ ;
6431 var c = r.match(rgbPattern);
6434 this.r = parseInt(c[1],10)/255;
6435 this.g = parseInt(c[2],10)/255;
6436 this.b = parseInt(c[3],10)/255;
6449 // Mixes this colour with another in a specified proportion
6450 // c = other colour to mix
6451 // f = 0..1 where 0 is this colour and 1 is the new colour
6452 // Returns an RGB object
6453 RGB.prototype.mix = function(c,f)
6455 return new RGB(this.r + (c.r-this.r) * f,this.g + (c.g-this.g) * f,this.b + (c.b-this.b) * f);
6458 // Return an rgb colour as a #rrggbb format hex string
6459 RGB.prototype.toString = function()
6461 var r = this.r.clamp(0,1);
6462 var g = this.g.clamp(0,1);
6463 var b = this.b.clamp(0,1);
6464 return("#" + ("0" + Math.floor(r * 255).toString(16)).right(2) +
6465 ("0" + Math.floor(g * 255).toString(16)).right(2) +
6466 ("0" + Math.floor(b * 255).toString(16)).right(2));
6469 // ---------------------------------------------------------------------------------
6470 // DOM utilities - many derived from www.quirksmode.org
6471 // ---------------------------------------------------------------------------------
6473 function drawGradient(place,horiz,colours)
6475 for(var t=0; t<= 100; t+=2)
6477 var bar = document.createElement("div");
6478 place.appendChild(bar);
6479 bar.style.position = "absolute";
6480 bar.style.left = horiz ? t + "%" : 0;
6481 bar.style.top = horiz ? 0 : t + "%";
6482 bar.style.width = horiz ? (101-t) + "%" : "100%";
6483 bar.style.height = horiz ? "100%" : (101-t) + "%";
6484 bar.style.zIndex = -1;
6486 var p = f*(colours.length-1);
6487 bar.style.backgroundColor = colours[Math.floor(p)].mix(colours[Math.ceil(p)],p-Math.floor(p)).toString();
6491 function createTiddlyText(theParent,theText)
6493 return theParent.appendChild(document.createTextNode(theText));
6496 function createTiddlyCheckbox(theParent,caption,checked,onChange)
6498 var cb = document.createElement("input");
6499 cb.setAttribute("type","checkbox");
6500 cb.onclick = onChange;
6501 theParent.appendChild(cb);
6502 cb.checked = checked;
6503 cb.className = "chkOptionInput";
6505 wikify(caption,theParent);
6509 function createTiddlyElement(theParent,theElement,theID,theClass,theText)
6511 var e = document.createElement(theElement);
6512 if(theClass != null)
6513 e.className = theClass;
6515 e.setAttribute("id",theID);
6517 e.appendChild(document.createTextNode(theText));
6518 if(theParent != null)
6519 theParent.appendChild(e);
6523 // Add an event handler
6524 // Thanks to John Resig, via QuirksMode
6525 function addEvent(obj,type,fn)
6529 obj['e'+type+fn] = fn;
6530 obj[type+fn] = function(){obj['e'+type+fn](window.event);}
6531 obj.attachEvent('on'+type,obj[type+fn]);
6534 obj.addEventListener(type,fn,false);
6537 // Remove an event handler
6538 // Thanks to John Resig, via QuirksMode
6539 function removeEvent(obj,type,fn)
6543 obj.detachEvent('on'+type,obj[type+fn]);
6544 obj[type+fn] = null;
6547 obj.removeEventListener(type,fn,false);
6550 function addClass(e,theClass)
6552 var currClass = e.className.split(" ");
6553 if(currClass.indexOf(theClass) == -1)
6554 e.className += " " + theClass;
6557 function removeClass(e,theClass)
6559 var currClass = e.className.split(" ");
6560 var i = currClass.indexOf(theClass);
6563 currClass.splice(i,1);
6564 i = currClass.indexOf(theClass);
6566 e.className = currClass.join(" ");
6569 function hasClass(e,theClass)
6573 if(e.className.split(" ").indexOf(theClass) != -1)
6579 // Find the closest relative with a given property value (property defaults to tagName, relative defaults to parentNode)
6580 function findRelated(e,value,name,relative)
6582 name = name ? name : "tagName";
6583 relative = relative ? relative : "parentNode";
6584 if(name == "className")
6586 while(e && !hasClass(e,value))
6593 while(e && e[name] != value)
6601 // Resolve the target object of an event
6602 function resolveTarget(e)
6607 else if (e.srcElement)
6609 if (obj.nodeType == 3) // defeat Safari bug
6610 obj = obj.parentNode;
6614 // Return the content of an element as plain text with no formatting
6615 function getPlainText(e)
6620 else if(e.textContent)
6621 text = e.textContent;
6625 // Get the scroll position for window.scrollTo necessary to scroll a given element into view
6626 function ensureVisible(e)
6628 var posTop = findPosY(e);
6629 var posBot = posTop + e.offsetHeight;
6630 var winTop = findScrollY();
6631 var winHeight = findWindowHeight();
6632 var winBot = winTop + winHeight;
6635 else if(posBot > winBot)
6637 if(e.offsetHeight < winHeight)
6638 return(posTop - (winHeight - e.offsetHeight));
6646 // Get the current width of the display window
6647 function findWindowWidth()
6649 return(window.innerWidth ? window.innerWidth : document.documentElement.clientWidth);
6652 // Get the current height of the display window
6653 function findWindowHeight()
6655 return(window.innerHeight ? window.innerHeight : document.documentElement.clientHeight);
6658 // Get the current horizontal page scroll position
6659 function findScrollX()
6661 return(window.scrollX ? window.scrollX : document.documentElement.scrollLeft);
6664 // Get the current vertical page scroll position
6665 function findScrollY()
6667 return(window.scrollY ? window.scrollY : document.documentElement.scrollTop);
6670 function findPosX(obj)
6673 while (obj.offsetParent)
6675 curleft += obj.offsetLeft;
6676 obj = obj.offsetParent;
6681 function findPosY(obj)
6684 while (obj.offsetParent)
6686 curtop += obj.offsetTop;
6687 obj = obj.offsetParent;
6692 // Blur a particular element
6693 function blurElement(e)
6695 if(e != null && e.focus && e.blur)
6702 // Create a non-breaking space
6703 function insertSpacer(place)
6705 var e = document.createTextNode(String.fromCharCode(160));
6707 place.appendChild(e);
6711 // Remove all children of a node
6712 function removeChildren(e)
6714 while(e.hasChildNodes())
6715 e.removeChild(e.firstChild);
6718 // Add a stylesheet, replacing any previous custom stylesheet
6719 function setStylesheet(s,id)
6722 id = "customStyleSheet";
6723 var n = document.getElementById(id);
6724 if(document.createStyleSheet) // Test for IE's non-standard createStyleSheet method
6727 n.parentNode.removeChild(n);
6728 // This failed without the
6729 document.getElementsByTagName("head")[0].insertAdjacentHTML("beforeEnd"," <style id='" + id + "'>" + s + "</style>");
6734 n.replaceChild(document.createTextNode(s),n.firstChild);
6737 var n = document.createElement("style");
6738 n.type = "text/css";
6740 n.appendChild(document.createTextNode(s));
6741 document.getElementsByTagName("head")[0].appendChild(n);
6746 // Replace the current selection of a textarea or text input and scroll it into view
6748 function replaceSelection(e,text)
6750 if (e.setSelectionRange)
6752 var oldpos = e.selectionStart + 1;
6753 e.value = e.value.substr(0,e.selectionStart) + text + e.value.substr(e.selectionStart);
6754 e.setSelectionRange( oldpos, oldpos);
6755 var linecount = e.value.split('\n').length;
6756 var thisline = e.value.substr(0,e.selectionStart).split('\n').length-1;
6757 e.scrollTop = Math.floor((thisline-e.rows/2)*e.scrollHeight/linecount);
6759 else if (document.selection)
6761 var range = document.selection.createRange();
6762 if (range.parentElement() == e)
6764 var isCollapsed = range.text == "";
6768 range.moveStart('character', -text.length);
6775 // Returns the text of the given (text) node, possibly merging subsequent text nodes
6776 function getNodeText(e)
6779 while (e && e.nodeName == "#text")
6786 //# -------------------------
6787 //# LoaderBase: A (abstract) storage loader that loads the tiddlers from a list of HTML elements.
6788 //# The format of the elements is defined by subclasses of this loader through the internalizeTiddler implementation.
6789 //# Subclasses must implement:
6790 //# function getTitle(store, e)
6792 //# store must implement:
6793 //# function createTiddler(title).
6796 function LoaderBase()
6800 LoaderBase.prototype.loadTiddler = function(store,e,tiddlers)
6802 var title = this.getTitle(store, e);
6805 var tiddler = store.createTiddler(title);
6806 this.internalizeTiddler(store, tiddler, title, e);
6807 tiddlers.push(tiddler);
6811 LoaderBase.prototype.loadTiddlers = function(store,nodes)
6814 for (var t = 0; t < nodes.length; t++)
6818 this.loadTiddler(store, nodes[t], tiddlers);
6822 showException(e, config.messages.tiddlerLoadError.format([this.getTitle(store, nodes[t])]));
6828 //# -------------------------
6829 //# SaverBase: a (abstract) storage saver that externalizes all tiddlers into a string,
6830 //# with every tiddler individually externalized (using this.externalizeTiddler) and joined with newlines
6831 //# Subclasses must implement:
6832 //# function externalizeTiddler(store, tiddler)
6834 //# store must implement:
6835 //# function getTiddlers(sortByFieldName)
6838 function SaverBase()
6842 SaverBase.prototype.externalize = function(store)
6845 var tiddlers = store.getTiddlers("title");
6846 for (var t = 0; t < tiddlers.length; t++)
6847 results.push(this.externalizeTiddler(store, tiddlers[t]));
6848 return results.join("\n");
6850 //--------------------------------
6851 // TW21Loader (inherits from LoaderBase)
6853 function TW21Loader() {};
6855 TW21Loader.prototype = new LoaderBase();
6857 TW21Loader.prototype.getTitle = function(store, e) {
6860 title = e.getAttribute("tiddler");
6861 if(!title && e.id) {
6862 var lenPrefix = store.idPrefix.length;
6863 if (e.id.substr(0,lenPrefix) == store.idPrefix)
6864 title = e.id.substr(lenPrefix);
6869 TW21Loader.prototype.internalizeTiddler = function(store, tiddler, title, data) {
6870 var text = getNodeText(data.firstChild).unescapeLineBreaks();
6871 var modifier = data.getAttribute("modifier");
6872 var modified = Date.convertFromYYYYMMDDHHMM(data.getAttribute("modified"));
6873 var c = data.getAttribute("created");
6874 var created = c ? Date.convertFromYYYYMMDDHHMM(c) : modified;
6875 var tags = data.getAttribute("tags");
6877 var attrs = data.attributes;
6878 for(var i = attrs.length-1; i >= 0; i--) {
6879 var name = attrs[i].name;
6880 if (attrs[i].specified && !TiddlyWiki.isStandardField(name)) {
6881 fields[name] = attrs[i].value.unescapeLineBreaks();
6884 tiddler.assign(title,text,modifier,modified,tags,created, fields);
6888 //--------------------------------
6889 // TW21Saver (inherits from SaverBase)
6891 function TW21Saver() {};
6893 TW21Saver.prototype = new SaverBase();
6895 TW21Saver.prototype.externalizeTiddler = function(store, tiddler)
6898 var extendedFieldAttributes = "";
6899 store.forEachField(tiddler,
6900 function(tiddler, fieldName, value) {
6901 // don't store stuff from the temp namespace
6902 if (!fieldName.match(/^temp\./))
6903 extendedFieldAttributes += ' %0="%1"'.format([fieldName, value.escapeLineBreaks().htmlEncode()]);
6905 return '<div tiddler="%0" modifier="%1" modified="%2" created="%3" tags="%4"%6>%5</div>'.format([
6906 tiddler.title.htmlEncode(),
6907 tiddler.modifier.htmlEncode(),
6908 tiddler.modified.convertToYYYYMMDDHHMM(),
6909 tiddler.created.convertToYYYYMMDDHHMM(),
6910 tiddler.getTags().htmlEncode(),
6911 tiddler.escapeLineBreaks().htmlEncode(),
6912 extendedFieldAttributes
6915 throw exceptionText(e, config.messages.tiddlerSaveError.format([tiddler.title]));
6919 // ---------------------------------------------------------------------------------
6921 // ---------------------------------------------------------------------------------
6923 // @Deprecated: Use createElementAndWikify and this.termRegExp instead
6924 config.formatterHelpers.charFormatHelper = function(w)
6926 w.subWikify(createTiddlyElement(w.output,this.element),this.terminator);
6929 // @Deprecated: Use enclosedTextHelper and this.lookaheadRegExp instead
6930 config.formatterHelpers.monospacedByLineHelper = function(w)
6932 var lookaheadRegExp = new RegExp(this.lookahead,"mg");
6933 lookaheadRegExp.lastIndex = w.matchStart;
6934 var lookaheadMatch = lookaheadRegExp.exec(w.source);
6935 if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
6937 var text = lookaheadMatch[1];
6938 if(config.browser.isIE)
6939 text = text.replace(/\n/g,"\r");
6940 createTiddlyElement(w.output,"pre",null,null,text);
6941 w.nextMatch = lookaheadRegExp.lastIndex;
6945 // @Deprecated: Use <br> or <br /> instead of <<br>>
6946 config.macros.br.handler = function(place)
6948 createTiddlyElement(place,"br");
6951 // Find an entry in an array. Returns the array index or null
6952 // @Deprecated: Use indexOf instead
6953 Array.prototype.find = function(item)
6955 var i = this.indexOf(item);
6956 return i == -1 ? null : i;
6959 // Load a tiddler from an HTML DIV. The caller should make sure to later call Tiddler.changed()
6960 // @Deprecated: Use store.getLoader().internalizeTiddler instead
6961 Tiddler.prototype.loadFromDiv = function(divRef,title)
6963 return store.getLoader().internalizeTiddler(store,this,title,divRef);
6966 // Format the text for storage in an HTML DIV
6967 // @Deprecated Use store.getSaver().externalizeTiddler instead.
6968 Tiddler.prototype.saveToDiv = function()
6970 return store.getSaver().externalizeTiddler(store,this);
6973 // @Deprecated: Use store.allTiddlersAsHtml() instead
6974 function allTiddlersAsHtml()
6976 return store.allTiddlersAsHtml();
6979 // @Deprecated: Use refreshPageTemplate instead
6980 function applyPageTemplate(title)
6982 refreshPageTemplate(title);
6985 // @Deprecated: Use story.displayTiddlers instead
6986 function displayTiddlers(srcElement,titles,template,unused1,unused2,animate,slowly)
6988 story.displayTiddlers(srcElement,titles,template,animate,slowly);
6991 // @Deprecated: Use story.displayTiddler instead
6992 function displayTiddler(srcElement,title,template,unused1,unused2,animate,slowly)
6994 story.displayTiddler(srcElement,title,template,animate,slowly);
6997 // @Deprecated: Use functions on right hand side directly instead
6998 var createTiddlerPopup = Popup.create;
6999 var scrollToTiddlerPopup = Popup.show;
7000 var hideTiddlerPopup = Popup.remove;
7002 // @Deprecated: Use right hand side directly instead
7003 var regexpBackSlashEn = new RegExp("\\\\n","mg");
7004 var regexpBackSlash = new RegExp("\\\\","mg");
7005 var regexpBackSlashEss = new RegExp("\\\\s","mg");
7006 var regexpNewLine = new RegExp("\n","mg");
7007 var regexpCarriageReturn = new RegExp("\r","mg");
7008 // ---------------------------------------------------------------------------------
7010 merge(config.shadowTiddlers,{SiteTitle:'DevFire'});
7011 merge(config.shadowTiddlers,{MainMenu:"PageTemplate\nStyleSheet\nMainMenu\nDefaultTiddlers"});
7012 merge(config.shadowTiddlers,{SiteSubtitle:"a theme for ~TiddlyWiki"});
7013 merge(config.shadowTiddlers,{DefaultTiddlers:"LorumIpsum"});
7014 merge(config.shadowTiddlers,{LorumIpsum:"Aenean eros arcu, condimentum nec, dapibus ut, tincidunt sit amet, urna. Quisque viverra, eros sed imperdiet iaculis, est risus facilisis quam, id malesuada arcu nulla luctus urna. Nullam et est. Vestibulum velit sem, faucibus cursus, dapibus vestibulum, pellentesque et, urna. Donec luctus. Donec lectus. Aliquam eget eros facilisis tortor feugiat sollicitudin. Integer lobortis vulputate sapien. Sed iaculis erat ac nunc. Etiam eu enim. Mauris ipsum urna, rhoncus at, bibendum sit amet, euismod eget, dolor. Mauris fermentum quam vitae ligula. Vestibulum in libero feugiat justo dictum consectetuer. Vestibulum euismod purus eget elit. Nunc sed massa porta elit bibendum posuere. Nunc pulvinar justo sit amet odio. In sed est. Phasellus ornare elementum nulla. Nulla ipsum neque, cursus a, viverra a, imperdiet at, enim. Quisque facilisis, diam sed accumsan suscipit, odio arcu hendrerit dolor, quis aliquet massa nulla nec sem.\n!heading 1\n!!heading 2\n!!!heading3\n----\n<<tag button>>\nThis is a link to a [[StyleSheet]] tiddler.\n\n> This is a blockquote\n> This is a blockquote\n> This is a blockquote\n|>|>| !This is a header |h\n|column1|column2|column3|\n|row2| row2 |row2|\n|column1|column2|column3|\n|row2| row2 |row2|\n|column1|column2|column3|\n|row2| row2 |row2|"});
7015 // ---------------------------------------------------------------------------------
7018 <style type="text/css">
7042 margin: 4em 10em 3em;
7047 margin: 1em 0em 0em 0em;
7048 border-color: #f0f0f0 #606060 #404040 #d0d0d0;
7049 border-style: solid;
7054 #javascriptWarning {
7058 background-color: #dd1100;
7064 <!--POST-HEAD-START-->
7066 <!--POST-HEAD-END-->
7068 <body onload="main();" onunload="if(window.checkUnsavedChanges) checkUnsavedChanges();">
7069 <!--PRE-BODY-START-->
7072 <script type="text/javascript">
7075 document.write("<applet style='position:absolute;left:-1px' name='TiddlySaver' code='TiddlySaver.class' archive='TiddlySaver.jar' width='1' height='1'></applet>");
7078 <div id="copyright">
7079 Welcome to TiddlyWiki by Jeremy Ruston, Copyright © 2006 Osmosoft Limited
7082 <div id="javascriptWarning">This page requires JavaScript to function properly</div>
7084 <div id="saveTest"></div>
7085 <div id="contentWrapper"></div>
7086 <div id="contentStash"></div>
7087 <div id="storeArea">
7088 <div tiddler="(built-in shadow tiddler)" modifier="CameronRich" modified="200702240024" created="200702240024" tags="">changes, notes and errata</div>
7089 <div tiddler="Cam" modifier="YourName" modified="200804011313" created="200804011313" tags="">Type the text for 'YourName'</div>
7090 <div tiddler="Changelog" modifier="YourName" modified="200901301233" created="200702240022" tags="">@@bgcolor(#ff0000):color(#ffffff):Changes for 1.2.1@@\n\n!!__SSL Library__\n* Certificate verification now works for Firefox.\n* Extended the openssl API.\n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.2.0@@\n\n!!__SSL Library__\n* A self-signed certificate will be verified as ok provided that that it is on the certificate authority list.\n* Certificates are not verified when added as certificate authorities (since self-signed and expired certificates can be added to browsers etc)\n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.9@@\n\n!!__SSL Library__\n* Now support MS IIS resource kit certificates (thanks to Carsten Sørensen).\n* Fixed a memory leak when freeing more than one CA certificate.\n* The bigint library had a problem with squaring which affected classical reduction (thanks to Manuel Klimek).\n\n!!__axhttpd__\n* Brought back setuid()/setgid() as an option.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.8@@\n\n!!__SSL Library__\n* Now using a BSD style license.\n* Self-signed certificates can now be automatically generated (the keys still need to be provided).\n* A new API call //ssl_x509_create()// can be used to programatically create the certificate.\n* Certificate/keys can be loaded automatically given a file location.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.7@@\n\n!!__SSL Library__\n\n* Variable sized session id's is now better handled for session caching. It has meant a new API call //ssl_get_session_id_size()// and a change to //ssl_client_new()// to define the session id size.\n* Muliple records with a single header are now better supported (thanks to Hervé Sibert).\n* ~MD2 added for Verisign root cert verification (thanks to Byron Rakitzis).\n* The ~MD5/~SHA1 digests are calculated incrementally to reduce memory (thanks to Byron Rakitzis).\n* The bigint cache is now cleared regularly to reduce memory.\n\n!!__axhttpd__\n\n* Improved the POST handling (thanks to Christian Melki).\n* CSS files now work properly.\n* Lua's CGI launcher location is configurable.\n* //vfork()// is now used for CGI for performance reasons.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.6@@\n\n!!__SSL Library__\n\n* ~RC4 speed improvements\n* Lua samples/bindings now work properly\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.5@@\n\n!!__SSL Library__\n\n* Session id's can now be variable lengths in server hello messages.\n* 0 length client certificates are now supported.\n* ssl_version() now returns just the version and not the date.\n* ssl_write() was not sending complete packets under load.\n\n!!__axhttpd__\n\n* Completely updated the CGI code.\n* Lua now integrated - Lua scripts and Lua Pages now run.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.4@@\n\n!!__SSL Library__\n\n* Fixed a Win32 crypto library issue with non-Administrator users\n* Removed compiler warnings that showed up in ~FC6.\n* GNU TLS certificates are now accepted.\n* Separated the send/receive headers for HMAC calculations.\n* Fixed a compilation problem with swig/perl/~FC6.\n* Fixed an issue with loading PEM CA certificates.\n\n!!__axhttpd__\n\n* Made //setuid()/setgid()// call an mconf option.\n* Made //chroot()// an mconf option. Default to //chdir()// instead.\n* Removed optional permissions checking.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.1@@\n\n!!__SSL Library__\n\n* AES should now work on 16bit processors (there was an alignment problem).\n* Various freed objects are cleared before freeing.\n* Header files now installed in ///usr/local/include/axTLS//.\n* -DCYGWIN replaced with -~DCONFIG_PLATFORM_CYGWIN (and the same for Solaris).\n* removed "-noextern" option in Swig. Fixed some other warnings in Win32.\n* SSLCTX changed to ~SSL_CTX (to be consistent with openssl). SSLCTX still exists for backwards compatibility.\n* malloc() and friends call abort() on failure.\n* Fixed a memory leak in directory listings.\n* Added openssl() compatibility functions.\n* Fixed Cygwin 'make install' issue.\n\n!!__axhttpd__\n\n* main.c now becomes axhttpd.c.\n* Header file issue fixed (in mime_types.c).\n* //chroot()// now used for better security.\n* Basic authentication implemented (via .htpasswd).\n* SSL access/denial protection implemented (via .htaccess).\n* Directory access protection implemented (via .htaccess).\n* Can now have more than one CGI file extension in mconf.\n* "~If-Modified-Since" request now handled properly.\n* Performance tweaks to remove //ssl_find()//.</div>
7091 <div tiddler="DefaultTiddlers" modifier="CameronRich" modified="200702240019" created="200702240019" tags="">[[Read Me]]</div>
7092 <div tiddler="License" modifier="YourName" modified="200804011309" created="200702240022" tags="">axTLS uses a BSD style license:\n\nCopyright (c) 2008, Cameron Rich All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this\nlist of conditions and the following disclaimer. Redistributions in binary\nform must reproduce the above copyright notice, this list of conditions and\nthe following disclaimer in the documentation and/or other materials\nprovided with the distribution. Neither the name of the axTLS Project nor\nthe names of its contributors may be used to endorse or promote products\nderived from this software without specific prior written permission. \n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\nLIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\nOUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH\nDAMAGE.</div>
7093 <div tiddler="MainMenu" modifier="CameronRich" modified="200702250353" created="200702240021" tags="">[[Read Me]] \n[[Changelog]]\n[[axhttpd]]\n[[License]]</div>
7094 <div tiddler="PageTemplate" modifier="YourName" modified="200701122313" created="200701122350" tags="DevFireTheme"><div class='header' macro='gradient vert #390108 #900'>\n<div class='headerShadow'>\n<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;\n<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>\n</div>\n<div class='headerForeground'>\n<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;\n<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>\n</div>\n</div>\n<div id='mainMenu'>\n<div refresh='content' tiddler='MainMenu'></div>\n</div>\n<div id='sidebar'>\n<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>\n<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>\n</div>\n<div id='displayArea'>\n<div id='messageArea'></div>\n<div id='tiddlerDisplay'></div>\n</div></div>
7095 <div tiddler="Read Me" modifier="YourName" modified="200804011313" created="200702240020" tags="">!@@bgcolor(#ff0000):color(#ffffff):axTLS Quick Start Guide@@\n\nThis is a guide to get a small SSL web-server up and running quickly.\n\n!!__Introduction__\n\nThe axTLS project is an SSL client/server library using the ~TLSv1 protocol. It is designed to be small and fast, and is suited to embedded projects. A web server is included.\n\nThe basic web server + SSL library is around 60-70kB and is configurable for features or size.\n\n!!__Compilation__\n\nAll platforms require GNU make. This means on Win32 that Cygwin needs to be installed with "make" and various developer options selected.\n\nConfiguration now uses a tool called "mconf" which gives a nice way to configure options (similar to what is used in ~BusyBox and the Linux kernel).\n\nYou should be able to compile axTLS simply by extracting it, change into the extracted directory and typing:\n\n{{indent{{{{> make}}}\n\nSelect your platform type, save the configuration, exit, and then type "make" again.\n\nIf all goes well, you should end up with an executable called "axhttpd" (or axhttpd.exe) in the //_stage// directory.\n\nTo play with all the various axTLS options, type:\n\n{{indent{{{{> make menuconfig}}}\n\nSave the new configuration and rebuild.\n\n!!__Running it__\n\nTo run it, go to the //_stage// directory, and type (as superuser):\n\n{{indent{{{{> axhttpd}}}\n\nNote: you may have to set your ~LD_LIBRARY_PATH - e.g. go to //_stage// and type //export ~LD_LIBRARY_PATH=`pwd`//\n\nAnd then point your browser at https://127.0.0.1 And you should see a this html page with a padlock appearing on your browser. or type http://127.0.0.1 to see the same page unencrypted.\n\n!!__The axssl utilities__\n\nThe axssl suite of tools are the SSL test tools in the various language bindings. They are:\n\n* axssl - C sample\n* axssl.csharp - C# sample\n* axssl.vbnet - VB.NET sample\n* axtls.jar - Java sample\n* axssl.pl - Perl sample\n* axssl.lua - Lua sample\n\nAll the tools have identical command-line parameters. e.g. to run something interesting:\n\n{{indent{{{{> axssl s_server -verify -CAfile ../ssl/test/axTLS.ca_x509}}}\n\nand\n\n{{indent{{{{> axssl s_client -cert ../ssl/test/axTLS.x509_1024 -key ../ssl/test/axTLS.key_1024 -reconnect}}}\n\n!!!!C#\n\nIf building under Linux or other non-Win32 platforms, Mono must be installed and the executable is run as:\n\n{{indent{{{{> mono axssl.csharp.exe ...}}}\n\n!!!!Java\n\nThe java version is run as:\n\n{{indent{{{{> java -jar axtls.jar <options>}}}\n\n!!!!Perl\n\n{{indent{{{{> [perl] ./axssl.pl <options>}}}\n\nIf running under Win32, be sure to use the correct version of Perl (i.e. ~ActiveState's version works ok).\n\n!!!!Lua\n\n{{indent{{{{> [lua] ./axssl.lua <options>}}}\n\n!__Known Issues__\n\n* Firefox doesn't handle legacy ~SSLv2 at all well. Disabling ~SSLv2 still initiates a ~SSLv23 handshake (v1.5). And continuous pressing of the "Reload" page instigates a change to ~SSLv3 for some reason (even though the TLS 1.0 option is selected). This will cause a "Firefox and <server> cannot communicate securely because they have no common encryption algorithms" (v1.5), or "Firefox can't connect to <server> because the site uses a security protocol which isn't enabled" (v2.0). See bugzilla issues 343543 and 359484 (Comment #7). It's all broken (hopefully fixed soon).\n* Perl/Java bindings don't work on 64 bit Linux machines. I can't even compile the latest version of Perl on an ~AMD64 box (using ~FC3).\n* Java 1.4 or better is required for the Java interfaces.\n* Processes that fork can't use session resumption unless some form of IPC is used.\n* Ensure libperl.so and libaxtls.so are in the shared library path when running with the perl bindings. A way to do this is with:\n\n{{indent{{{{> export LD_LIBRARY_PATH=`perl -e 'use Config; print $Config{archlib};'`/CORE:.}}}\n* The lua sample requires the luabit library from http://luaforge.net/projects/bit.\n\n!!!!Win32 issues\n\n* Be careful about doing .NET executions on network drives - .NET complains with security exceptions on the binary. //TODO: Add a manifest file to prevent this.//\n* CGI has been removed from Win32 - it needs a lot more work to get it right.\n* The default Microsoft .NET SDK is v2.0.50727. Download from: http://msdn.microsoft.com/netframework/downloads/updates/default.aspx.\n\n!!!!Solaris issues\n\n* mconf doesn't work well - some manual tweaking is required for string values.\n* GNU make is required and needs to be in $PATH.\n* To get swig's library dependencies to work (and for the C library to be found), I needed to type:\n\n{{indent{{{{> export LD_LIBRARY_PATH=/usr/local/gcc-3.3.1/lib:.}}}\n\n!!!!Cygwin issues\n\n* The bindings all compile but don't run under Cygwin with the exception of Perl. This is due to win32 executables being incompatible with Cygwin libraries.\n\n</div>
7096 <div tiddler="SiteSubtitle" modifier="CameronRich" modified="200702240025" created="200702240025" tags="">changes, notes and errata</div>
7097 <div tiddler="SiteTitle" modifier="CameronRich" modified="200702240023" created="200702240023" tags="">axTLS Embedded SSL</div>
7098 <div tiddler="SiteUrl" modifier="CameronRich" modified="200702240025" created="200702240025" tags="">http://axtls.cerocclub.com.au</div>
7099 <div tiddler="StyleSheet" modifier="CameronRich" modified="200702250600" created="200701122350" tags="DevFireTheme">/***\nhttp://tiddlystyles.com/#theme:DevFire\nAuthor: Clint Checketts\n***/\n\n/*{{{*/\nbody {\nbackground: #000;\n}\n/*}}}*/\n/***\n!Link styles /% ============================================================= %/\n***/\n/*{{{*/\na,\na.button,\n#mainMenu a.button,\n#sidebarOptions .sliderPanel a{\n color: #ffbf00;\n border: 0;\n background: transparent;\n}\n\na:hover,\na.button:hover,\n#mainMenu a.button:hover,\n#sidebarOptions .sliderPanel a:hover\n#sidebarOptions .sliderPanel a:active{\n color: #ff7f00;\n border: 0;\n border-bottom: #ff7f00 1px dashed;\n background: transparent;\n text-decoration: none;\n}\n\n#displayArea .button.highlight{\n color: #ffbf00;\n background: #4c4c4c;\n}\n/*}}}*/\n/***\n!Header styles /% ============================================================= %/\n***/\n/*{{{*/\n.header{\n border-bottom: 2px solid #ffbf00;\n color: #fff;\n}\n\n.headerForeground a {\n color: #fff;\n}\n\n.header a:hover {\n border-bottom: 1px dashed #fff;\n}\n/*}}}*/\n/***\n!Main menu styles /% ============================================================= %/\n***/\n/*{{{*/\n#mainMenu {color: #fff;}\n#mainMenu h1{\n font-size: 1.1em;\n}\n#mainMenu li,#mainMenu ul{\n list-style: none;\n margin: 0;\n padding: 0;\n}\n/*}}}*/\n/***\n!Sidebar styles /% ============================================================= %/\n***/\n/*{{{*/\n#sidebar {\n right: 0;\n color: #fff;\n border: 2px solid #ffbf00;\n border-width: 0 0 2px 2px;\n}\n#sidebarOptions {\n background-color: #4c4c4c;\n padding: 0;\n}\n\n#sidebarOptions a{\n margin: 0;\n color: #ffbf00;\n border: 0;\n}\n#sidebarOptions a:hover {\n color: #4c4c4c;\n background-color: #ffbf00;\n\n}\n\n#sidebarOptions a:active {\n color: #ffbf00;\n background-color: transparent;\n}\n\n#sidebarOptions .sliderPanel {\n background-color: #333;\n margin: 0;\n}\n\n#sidebarTabs {background-color: #4c4c4c;}\n#sidebarTabs .tabSelected {\n padding: 3px 3px;\n cursor: default;\n color: #ffbf00;\n background-color: #666;\n}\n#sidebarTabs .tabUnselected {\n color: #ffbf00;\n background-color: #5f5f5f;\n padding: 0 4px;\n}\n\n#sidebarTabs .tabUnselected:hover,\n#sidebarTabs .tabContents {\n background-color: #666;\n}\n\n.listTitle{color: #FFF;}\n#sidebarTabs .tabContents a{\n color: #ffbf00;\n}\n\n#sidebarTabs .tabContents a:hover{\n color: #ff7f00;\n background: transparent;\n}\n\n#sidebarTabs .txtMoreTab .tabSelected,\n#sidebarTabs .txtMoreTab .tab:hover,\n#sidebarTabs .txtMoreTab .tabContents{\n color: #ffbf00;\n background: #4c4c4c;\n}\n\n#sidebarTabs .txtMoreTab .tabUnselected {\n color: #ffbf00;\n background: #5f5f5f;\n}\n\n.tab.tabSelected, .tab.tabSelected:hover{color: #ffbf00; border: 0; background-color: #4c4c4c;cursor:default;}\n.tab.tabUnselected {background-color: #666;}\n.tab.tabUnselected:hover{color:#ffbf00; border: 0;background-color: #4c4c4c;}\n.tabContents {\n background-color: #4c4c4c;\n border: 0;\n}\n.tabContents .tabContents{background: #666;}\n.tabContents .tabSelected{background: #666;}\n.tabContents .tabUnselected{background: #5f5f5f;}\n.tabContents .tab:hover{background: #666;}\n/*}}}*/\n/***\n!Message area styles /% ============================================================= %/\n***/\n/*{{{*/\n#messageArea {background-color: #666; color: #fff; border: 2px solid #ffbf00;}\n#messageArea a:link, #messageArea a:visited {color: #ffbf00; text-decoration:none;}\n#messageArea a:hover {color: #ff7f00;}\n#messageArea a:active {color: #ff7f00;}\n#messageArea .messageToolbar a{\n border: 1px solid #ffbf00;\n background: #4c4c4c;\n}\n/*}}}*/\n/***\n!Popup styles /% ============================================================= %/\n***/\n/*{{{*/\n.popup {color: #fff; background-color: #4c4c4c; border: 1px solid #ffbf00;}\n.popup li.disabled{color: #fff;}\n.popup a {color: #ffbf00; }\n.popup a:hover { background: transparent; color: #ff7f00; border: 0;}\n.popup hr {color: #ffbf00; background: #ffbf00;}\n/*}}}*/\n/***\n!Tiddler Display styles /% ============================================================= %/\n***/\n/*{{{*/\n.title{color: #fff;}\nh1, h2, h3, h4, h5 {\n color: #fff;\n background-color: transparent;\n border-bottom: 1px solid #333;\n}\n\n.subtitle{\n color: #666;\n}\n\n.viewer {color: #fff; }\n\n.viewer table{background: #666; color: #fff;}\n\n.viewer th {background-color: #996; color: #fff;}\n\n.viewer pre, .viewer code {color: #ddd; background-color: #4c4c4c; border: 1px solid #ffbf00;}\n\n.viewer hr {color: #666;}\n\n.tiddler .button {color: #4c4c4c;}\n.tiddler .button:hover { color: #ffbf00; background-color: #4c4c4c;}\n.tiddler .button:active {color: #ffbf00; background-color: #4c4c4c;}\n\n.toolbar {\n color: #4c4c4c;\n}\n\n.toolbar a.button,\n.toolbar a.button:hover,\n.toolbar a.button:active,\n.editorFooter a{\n border: 0;\n}\n\n.footer {\n color: #ddd;\n}\n\n.selected .footer {\n color: #888;\n}\n\n.highlight, .marked {\n color: #000;\n background-color: #ffe72f;\n}\n.editorFooter {\n color: #aaa;\n}\n\n.tab{\n-moz-border-radius-topleft: 3px;\n-moz-border-radius-topright: 3px;\n}\n\n.tagging,\n.tagged{\n background: #4c4c4c;\n border: 1px solid #4c4c4c; \n}\n\n.selected .tagging,\n.selected .tagged{\n background-color: #333;\n border: 1px solid #ffbf00;\n}\n\n.tagging .listTitle,\n.tagged .listTitle{\n color: #fff;\n}\n\n.tagging .button,\n.tagged .button{\n color: #ffbf00;\n border: 0;\n padding: 0;\n}\n\n.tagging .button:hover,\n.tagged .button:hover{\nbackground: transparent;\n}\n\n.selected .isTag .tagging.simple,\n.selected .tagged.simple,\n.isTag .tagging.simple,\n.tagged.simple {\n float: none;\n display: inline;\n border: 0;\n background: transparent;\n color: #fff;\n margin: 0;\n}\n\n.cascade {\n background: #4c4c4c;\n color: #ddd;\n border: 1px solid #ffbf00;\n}\n/*}}}*/</div>
7100 <div tiddler="axhttpd" modifier="YourName" modified="200804011308" created="200702242231" tags="">axhttpd is a small embedded web server using the axTLS library. It is based originally on the web server written by Doug Currie which is at http://www.hcsw.org/awhttpd.\n\n!@@bgcolor(#ff0000):color(#ffffff):axhttpd Features@@ \n\n!!__Basic Authentication__\n\nBasic Authentication uses a password file called ".htpasswd", in the directory to be protected. This file is formatted as the familiar colon-separated username/encrypted-password pair, records delimited by newlines. The protection does not carry over to subdirectories. The utility program htpasswd is included to help manually edit .htpasswd files.\n\nThe encryption of this password uses a proprietary algorithm due to the dependency of many crypt libraries on DES. An example is in [[/test_dir/no_http|https://127.0.0.1/test_dir/no_http]] (username 'abcd', password is '1234').\n\n//Note: This is an mconf enabled configuration option.//\n\n!!__SSL Protection__\n\nDirectories/files can be accessed using the 'http' or 'https' uri prefix. If normal http access for a directory needs to be disabled, then put "~SSLRequireSSL" into a '.htaccess' file in the directory to be protected. \n\nConversely, use "~SSLDenySSL" to deny access to directories via SSL.\n\nAn example is in [[/test_dir/no_http|http://127.0.0.1/test_dir/no_http]] and [[/test_dir/no_ssl|https://127.0.0.1/test_dir/no_ssl]].\n\nEntire directories can be denied access with a "Deny all" directive (regardless of SSL or authentication). An example is in [[/test_dir/bin|http://127.0.0.1/test_dir/bin]]\n\n!!__CGI__\n\nMost of the CGI 1.1 variables are now placed into the script environment and should work as normal.\n\n!!__Lua and Lua Pages__\n\nThis is a small scripting language gaining popularity in embedded applications due to its small footprint and fast speed.\n\nLua has been incorporated into the build, so simply select it and it will automatically install. Try pointing your browser at [[test_main.html]|http://127.0.0.1/lua/test_main.html]] to see an example of Lua Pages.\n\n//Note: This is an mconf enabled configuration option.//\n\n!!__Directory Listing__\n\nAn mconf option. Allow the files in directories to be displayed. An example is in [[/test_dir|http://127.0.0.1/test_dir]]\n\n!!__Other Features__\n\n* Timeout - HTTP 1.1 allows for persistent connections. This is the time allowed for this connection in seconds.\n* Daemon - Puts the process in daemon mode. \n* SSL session cache size - The size of the session cache (a heavily loaded server should maintain many sessions). A session will save on expensive SSL handshaking.\n\n</div>
7102 <!--POST-BODY-START-->
7104 <!--POST-BODY-END-->