Add axTLS sourcecode
[project/luci.git] / libs / nixio / axTLS / www / index.html
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">
3 <head>
4 <script type="text/javascript">
5 //<![CDATA[
6 var version = {major: 2, minor: 1, revision: 3, date: new Date("Nov 3, 2006"), extensions: {}};
7 //]]>
8 </script>
9 <!--
10 TiddlyWiki 2.1.3 by Jeremy Ruston, (jeremy [at] osmosoft [dot] com)
11
12 Copyright (c) Osmosoft Limited 2004-2006
13
14 Redistribution and use in source and binary forms, with or without modification,
15 are permitted provided that the following conditions are met:
16
17 Redistributions of source code must retain the above copyright notice, this
18 list of conditions and the following disclaimer.
19
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.
23
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.
27
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
37 DAMAGE.
38 -->
39 <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
40 <!--PRE-HEAD-START-->
41 <!--{{{-->
42 <link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>
43 <!--}}}-->
44 <!--PRE-HEAD-END-->
45 <title> axTLS Embedded SSL - changes, notes and errata </title>
46 <script type="text/javascript">
47 //<![CDATA[
48 // ---------------------------------------------------------------------------------
49 // Configuration repository
50 // ---------------------------------------------------------------------------------
51
52 // Miscellaneous options
53 var config = {
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
61 };
62
63 // Messages
64 config.messages = {
65 messageClose: {},
66 dates: {}
67 };
68
69 // Options that can be set in the options panel and/or cookies
70 config.options = {
71 chkRegExpSearch: false,
72 chkCaseSensitiveSearch: false,
73 chkAnimate: true,
74 chkSaveBackups: true,
75 chkAutoSave: false,
76 chkGenerateAnRssFeed: false,
77 chkSaveEmptyTemplate: false,
78 chkOpenInNewWindow: true,
79 chkToggleLinks: false,
80 chkHttpReadOnly: true,
81 chkForceMinorUpdate: false,
82 chkConfirmDelete: true,
83 chkInsertTabs: false,
84 txtBackupFolder: "",
85 txtMainTab: "tabTimeline",
86 txtMoreTab: "moreTabAll",
87 txtMaxEditRows: "30"
88 };
89
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}
101 ];
102
103 // Default tiddler templates
104 var DEFAULT_VIEW_TEMPLATE = 1;
105 var DEFAULT_EDIT_TEMPLATE = 2;
106 config.tiddlerTemplates = {
107 1: "ViewTemplate",
108 2: "EditTemplate"
109 };
110
111 // More messages (rather a legacy layout that shouldn't really be like this)
112 config.views = {
113 wikified: {
114 tag: {}
115 },
116 editor: {
117 tagChooser: {}
118 }
119 };
120
121 // Macros; each has a 'handler' member that is inserted later
122 config.macros = {
123 today: {},
124 version: {},
125 search: {sizeTextbox: 15},
126 tiddler: {},
127 tag: {},
128 tags: {},
129 tagging: {},
130 timeline: {},
131 allTags: {},
132 list: {
133 all: {},
134 missing: {},
135 orphans: {},
136 shadowed: {}
137 },
138 closeAll: {},
139 permaview: {},
140 saveChanges: {},
141 slider: {},
142 option: {},
143 newTiddler: {},
144 newJournal: {},
145 sparkline: {},
146 tabs: {},
147 gradient: {},
148 message: {},
149 view: {},
150 edit: {},
151 tagChooser: {},
152 toolbar: {},
153 br: {},
154 plugins: {},
155 refreshDisplay: {},
156 importTiddlers: {}
157 };
158
159 // Commands supported by the toolbar macro
160 config.commands = {
161 closeTiddler: {},
162 closeOthers: {},
163 editTiddler: {},
164 saveTiddler: {hideReadOnly: true},
165 cancelTiddler: {},
166 deleteTiddler: {hideReadOnly: true},
167 permalink: {},
168 references: {},
169 jump: {}
170 };
171
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();
175 config.browser = {
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
186 };
187
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]"
194 };
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]"
201 }
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 + "+))";
211
212 config.textPrimitives.cssLookahead = "(?:(" + config.textPrimitives.anyLetter + "+)\\(([^\\)\\|\\n]+)(?:\\):))|(?:(" + config.textPrimitives.anyLetter + "+):([^;\\|\\n]+);)";
213 config.textPrimitives.cssLookaheadRegExp = new RegExp(config.textPrimitives.cssLookahead,"mg");
214
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");
224
225 // ---------------------------------------------------------------------------------
226 // Shadow tiddlers
227 // ---------------------------------------------------------------------------------
228
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" +
244 "Error: #f88\n",
245 StyleSheet: "",
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>&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' 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<!--}}}-->",
253 MarkupPostHead: "",
254 MarkupPreBody: "",
255 MarkupPostBody: ""
256 };
257
258 // ---------------------------------------------------------------------------------
259 // Translateable strings
260 // ---------------------------------------------------------------------------------
261
262 // Strings in "double quotes" should be translated; strings in 'single quotes' should be left alone
263
264 merge(config.options,{
265 txtUserName: "YourName"});
266
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"});
305
306 merge(config.messages.messageClose,{
307 text: "close",
308 tooltip: "close this message area"});
309
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"];
314
315 merge(config.views.wikified.tag,{
316 labelNoTags: "no tags",
317 labelTags: "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'"});
323
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"});
329
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'"});
333
334 merge(config.views.editor.tagChooser,{
335 text: "tags",
336 tooltip: "Choose existing tags to add to this tiddler",
337 popupNone: "There are no tags defined",
338 tagTooltip: "Add the tag '%0'"});
339
340 merge(config.macros.search,{
341 label: "search",
342 prompt: "Search this TiddlyWiki",
343 accessKey: "F",
344 successMsg: "%0 tiddlers found matching %1",
345 failureMsg: "No tiddlers found matching %0"});
346
347 merge(config.macros.tagging,{
348 label: "tagging: ",
349 labelNotTag: "not tagging",
350 tooltip: "List of tiddlers tagged with '%0'"});
351
352 merge(config.macros.timeline,{
353 dateFormat: "DD MMM YYYY"});
354
355 merge(config.macros.allTags,{
356 tooltip: "Show tiddlers tagged with '%0'",
357 noTags: "There are no tagged tiddlers"});
358
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";
363
364 merge(config.macros.closeAll,{
365 label: "close all",
366 prompt: "Close all displayed tiddlers (except any that are being edited)"});
367
368 merge(config.macros.permaview,{
369 label: "permaview",
370 prompt: "Link to an URL that retrieves all the currently displayed tiddlers"});
371
372 merge(config.macros.saveChanges,{
373 label: "save changes",
374 prompt: "Save all tiddlers to create a new TiddlyWiki",
375 accessKey: "S"});
376
377 merge(config.macros.newTiddler,{
378 label: "new tiddler",
379 prompt: "Create a new tiddler",
380 title: "New Tiddler",
381 accessKey: "N"});
382
383 merge(config.macros.newJournal,{
384 label: "new journal",
385 prompt: "Create a new tiddler from the current date and time",
386 accessKey: "J"});
387
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",
392 listViewTemplate : {
393 columns: [
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'}
401 ],
402 rowClasses: [
403 {className: 'error', field: 'error'},
404 {className: 'warning', field: 'warning'}
405 ],
406 actions: [
407 {caption: "More actions...", name: ''},
408 {caption: "Remove systemConfig tag", name: 'remove'},
409 {caption: "Delete these tiddlers forever", name: 'delete'}
410 ]}
411 });
412
413 merge(config.macros.refreshDisplay,{
414 label: "refresh",
415 prompt: "Redraw the entire TiddlyWiki display"
416 });
417
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",
421 fetchLabel: "fetch",
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",
435 step5: "Done",
436 listViewTemplate: {
437 columns: [
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'}
442 ],
443 rowClasses: [
444 ],
445 actions: [
446 {caption: "More actions...", name: ''},
447 {caption: "Import these tiddlers", name: 'import'}
448 ]}
449 });
450
451 merge(config.commands.closeTiddler,{
452 text: "close",
453 tooltip: "Close this tiddler"});
454
455 merge(config.commands.closeOthers,{
456 text: "close others",
457 tooltip: "Close all other tiddlers"});
458
459 merge(config.commands.editTiddler,{
460 text: "edit",
461 tooltip: "Edit this tiddler",
462 readOnlyText: "view",
463 readOnlyTooltip: "View the source of this tiddler"});
464
465 merge(config.commands.saveTiddler,{
466 text: "done",
467 tooltip: "Save changes to this tiddler"});
468
469 merge(config.commands.cancelTiddler,{
470 text: "cancel",
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"});
475
476 merge(config.commands.deleteTiddler,{
477 text: "delete",
478 tooltip: "Delete this tiddler",
479 warning: "Are you sure you want to delete '%0'?"});
480
481 merge(config.commands.permalink,{
482 text: "permalink",
483 tooltip: "Permalink for this tiddler"});
484
485 merge(config.commands.references,{
486 text: "references",
487 tooltip: "Show tiddlers that link to this one",
488 popupNone: "No references"});
489
490 merge(config.commands.jump,{
491 text: "jump",
492 tooltip: "Jump to another open tiddler"});
493
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>>"});
514
515 // ---------------------------------------------------------------------------------
516 // Main
517 // ---------------------------------------------------------------------------------
518
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()
532
533 // Whether to use the JavaSaver applet
534 var useJavaSaver = config.browser.isSafari || config.browser.isOpera;
535
536 // Starting up
537 function main()
538 {
539 var now, then = new Date();
540 startingUp = true;
541 window.onbeforeunload = function(e) {if(window.confirmExit) return confirmExit();};
542 params = getParameters();
543 if(params)
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);
549 saveTest();
550 loadOptionsCookie();
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");
559 store.notifyAll();
560 restart();
561 if(pluginProblem)
562 {
563 story.displayTiddler(null,"PluginManager");
564 displayMessage(config.messages.customConfigError);
565 }
566 now = new Date();
567 if(config.displayStartupTime)
568 displayMessage("TiddlyWiki startup in " + (now-then)/1000 + " seconds");
569 startingUp = false;
570 }
571
572 // Restarting
573 function restart()
574 {
575 invokeParamifier(params,"onstart");
576 if(story.isEmpty())
577 {
578 var defaultParams = store.getTiddlerText("DefaultTiddlers").parseParams("open",null,false);
579 invokeParamifier(defaultParams,"onstart");
580 }
581 window.scrollTo(0,0);
582 }
583
584 function saveTest()
585 {
586 var saveTest = document.getElementById("saveTest");
587 if(saveTest.hasChildNodes())
588 alert(config.messages.savedSnapshotError);
589 saveTest.appendChild(document.createTextNode("savetest"));
590 }
591
592 function loadPlugins()
593 {
594 if(safeMode)
595 return false;
596 var configTiddlers = store.getTaggedTiddlers("systemConfig");
597 installedPlugins = [];
598 var hadProblem = false;
599 for(var t=0; t<configTiddlers.length; t++)
600 {
601 tiddler = configTiddlers[t];
602 pluginInfo = getPluginInfo(tiddler);
603 if(isPluginExecutable(pluginInfo))
604 {
605 pluginInfo.executed = true;
606 pluginInfo.error = false;
607 try
608 {
609 if(tiddler.text && tiddler.text != "")
610 window.eval(tiddler.text);
611 }
612 catch(e)
613 {
614 pluginInfo.log.push(config.messages.pluginError.format([exceptionText(e)]));
615 pluginInfo.error = true;
616 hadProblem = true;
617 }
618 }
619 else
620 pluginInfo.warning = true;
621 installedPlugins.push(pluginInfo);
622 }
623 return hadProblem;
624 }
625
626 function getPluginInfo(tiddler)
627 {
628 var p = store.getTiddlerSlices(tiddler.title,["Name","Description","Version","CoreVersion","Date","Source","Author","License","Browsers"]);
629 p.tiddler = tiddler;
630 p.title = tiddler.title;
631 p.log = [];
632 return p;
633 }
634
635 // Check that a particular plugin is valid for execution
636 function isPluginExecutable(plugin)
637 {
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"])
643 {
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;
650 if(w > 0)
651 return verifyTail(plugin,false,config.messages.pluginVersionError);
652 }
653 return true;
654 }
655
656 function verifyTail(plugin,result,message)
657 {
658 plugin.log.push(message);
659 return result;
660 }
661
662 function invokeMacro(place,macro,params,wikifier,tiddler)
663 {
664 try
665 {
666 var m = config.macros[macro];
667 if(m && m.handler)
668 m.handler(place,macro,params.readMacroParams(),wikifier,params,tiddler);
669 else
670 createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,config.messages.missingMacro]));
671 }
672 catch(ex)
673 {
674 createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,ex.toString()]));
675 }
676 }
677
678 // ---------------------------------------------------------------------------------
679 // Paramifiers
680 // ---------------------------------------------------------------------------------
681
682 function getParameters()
683 {
684 var p = null;
685 if(window.location.hash)
686 {
687 p = decodeURI(window.location.hash.substr(1));
688 if(config.browser.firefoxDate != null && config.browser.firefoxDate[1] < "20051111")
689 p = convertUTF8ToUnicode(p);
690 }
691 return p;
692 }
693
694 function invokeParamifier(params,handler)
695 {
696 if(!params || params.length == undefined || params.length <= 1)
697 return;
698 for(var t=1; t<params.length; t++)
699 {
700 var p = config.paramifiers[params[t].name];
701 if(p && p[handler] instanceof Function)
702 p[handler](params[t].value);
703 }
704 }
705
706 config.paramifiers = {};
707
708 config.paramifiers.start = {
709 oninit: function(v) {
710 safeMode = v.toLowerCase() == "safe";
711 }
712 };
713
714 config.paramifiers.open = {
715 onstart: function(v) {
716 story.displayTiddler("bottom",v,null,false,false);
717 }
718 };
719
720 config.paramifiers.story = {
721 onstart: function(v) {
722 var list = store.getTiddlerText(v,"").parseParams("open",null,false);
723 invokeParamifier(list,"onstart");
724 }
725 };
726
727 config.paramifiers.search = {
728 onstart: function(v) {
729 story.search(v,false,false);
730 }
731 };
732
733 config.paramifiers.searchRegExp = {
734 onstart: function(v) {
735 story.prototype.search(v,false,true);
736 }
737 };
738
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);
744 }
745 };
746
747 config.paramifiers.newTiddler = {
748 onstart: function(v) {
749 if(!readOnly)
750 {
751 story.displayTiddler(null,v,DEFAULT_EDIT_TEMPLATE);
752 story.focusTiddler(v,"text");
753 }
754 }
755 };
756
757 config.paramifiers.newJournal = {
758 onstart: function(v) {
759 if(!readOnly)
760 {
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");
765 }
766 }
767 };
768
769 // ---------------------------------------------------------------------------------
770 // Formatter helpers
771 // ---------------------------------------------------------------------------------
772
773 function Formatter(formatters)
774 {
775 this.formatters = [];
776 var pattern = [];
777 for(var n=0; n<formatters.length; n++)
778 {
779 pattern.push("(" + formatters[n].match + ")");
780 this.formatters.push(formatters[n]);
781 }
782 this.formatterRegExp = new RegExp(pattern.join("|"),"mg");
783 }
784
785 config.formatterHelpers = {
786
787 createElementAndWikify: function(w)
788 {
789 w.subWikifyTerm(createTiddlyElement(w.output,this.element),this.termRegExp);
790 },
791
792 inlineCssHelper: function(w)
793 {
794 var styles = [];
795 config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
796 var lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
797 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch)
798 {
799 var s,v;
800 if(lookaheadMatch[1])
801 {
802 s = lookaheadMatch[1].unDash();
803 v = lookaheadMatch[2];
804 }
805 else
806 {
807 s = lookaheadMatch[3].unDash();
808 v = lookaheadMatch[4];
809 }
810 if (s=="bgcolor")
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);
816 }
817 return styles;
818 },
819
820 applyCssHelper: function(e,styles)
821 {
822 for(var t=0; t< styles.length; t++)
823 {
824 try
825 {
826 e.style[styles[t].style] = styles[t].value;
827 }
828 catch (ex)
829 {
830 }
831 }
832 },
833
834 enclosedTextHelper: function(w)
835 {
836 this.lookaheadRegExp.lastIndex = w.matchStart;
837 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
838 if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
839 {
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;
845 }
846 },
847
848 isExternalLink: function(link)
849 {
850 if(store.tiddlerExists(link) || store.isShadowTiddler(link))
851 {
852 //# Definitely not an external link
853 return false;
854 }
855 var urlRegExp = new RegExp(config.textPrimitives.urlPattern,"mg");
856 if(urlRegExp.exec(link))
857 {
858 // Definitely an external link
859 return true;
860 }
861 if (link.indexOf(".")!=-1 || link.indexOf("\\")!=-1 || link.indexOf("/")!=-1)
862 {
863 //# Link contains . / or \ so is probably an external link
864 return true;
865 }
866 //# Otherwise assume it is not an external link
867 return false;
868 }
869
870 };
871
872 // ---------------------------------------------------------------------------------
873 // Standard formatters
874 // ---------------------------------------------------------------------------------
875
876 config.formatters = [
877 {
878 name: "table",
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"},
885
886 handler: function(w)
887 {
888 var table = createTiddlyElement(w.output,"table");
889 var prevColumns = [];
890 var currRowType = null;
891 var rowContainer;
892 var rowCount = 0;
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)
897 {
898 var nextRowType = lookaheadMatch[2];
899 if(nextRowType == "k")
900 {
901 table.className = lookaheadMatch[1];
902 w.nextMatch += lookaheadMatch[0].length+1;
903 }
904 else
905 {
906 if(nextRowType != currRowType)
907 {
908 rowContainer = createTiddlyElement(table,this.rowTypes[nextRowType]);
909 currRowType = nextRowType;
910 }
911 if(currRowType == "c")
912 {
913 // Caption
914 w.nextMatch++;
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);
919 }
920 else
921 {
922 this.rowHandler(w,createTiddlyElement(rowContainer,"tr",null,(rowCount&1)?"oddRow":"evenRow"),prevColumns);
923 rowCount++;
924 }
925 }
926 this.lookaheadRegExp.lastIndex = w.nextMatch;
927 lookaheadMatch = this.lookaheadRegExp.exec(w.source);
928 }
929 },
930 rowHandler: function(w,e,prevColumns)
931 {
932 var col = 0;
933 var colSpanCount = 1;
934 var prevCell = null;
935 this.cellRegExp.lastIndex = w.nextMatch;
936 var cellMatch = this.cellRegExp.exec(w.source);
937 while(cellMatch && cellMatch.index == w.nextMatch)
938 {
939 if(cellMatch[1] == "~")
940 {
941 // Rowspan
942 var last = prevColumns[col];
943 if(last)
944 {
945 last.rowSpanCount++;
946 last.element.setAttribute("rowspan",last.rowSpanCount);
947 last.element.setAttribute("rowSpan",last.rowSpanCount); // Needed for IE
948 last.element.valign = "center";
949 }
950 w.nextMatch = this.cellRegExp.lastIndex-1;
951 }
952 else if(cellMatch[1] == ">")
953 {
954 // Colspan
955 colSpanCount++;
956 w.nextMatch = this.cellRegExp.lastIndex-1;
957 }
958 else if(cellMatch[2])
959 {
960 // End of row
961 if(prevCell && colSpanCount > 1)
962 {
963 prevCell.setAttribute("colspan",colSpanCount);
964 prevCell.setAttribute("colSpan",colSpanCount); // Needed for IE
965 }
966 w.nextMatch = this.cellRegExp.lastIndex;
967 break;
968 }
969 else
970 {
971 // Cell
972 w.nextMatch++;
973 var styles = config.formatterHelpers.inlineCssHelper(w);
974 var spaceLeft = false;
975 var chr = w.source.substr(w.nextMatch,1);
976 while(chr == " ")
977 {
978 spaceLeft = true;
979 w.nextMatch++;
980 chr = w.source.substr(w.nextMatch,1);
981 }
982 var cell;
983 if(chr == "!")
984 {
985 cell = createTiddlyElement(e,"th");
986 w.nextMatch++;
987 }
988 else
989 cell = createTiddlyElement(e,"td");
990 prevCell = cell;
991 prevColumns[col] = {rowSpanCount:1, element:cell};
992 if(colSpanCount > 1)
993 {
994 cell.setAttribute("colspan",colSpanCount);
995 cell.setAttribute("colSpan",colSpanCount); // Needed for IE
996 colSpanCount = 1;
997 }
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";
1002 else if(spaceLeft)
1003 cell.align = "right";
1004 w.nextMatch--;
1005 }
1006 col++;
1007 this.cellRegExp.lastIndex = w.nextMatch;
1008 cellMatch = this.cellRegExp.exec(w.source);
1009 }
1010 }
1011 },
1012
1013 {
1014 name: "heading",
1015 match: "^!{1,5}",
1016 termRegExp: /(\n)/mg,
1017 handler: function(w)
1018 {
1019 w.subWikifyTerm(createTiddlyElement(w.output,"h" + w.matchLength),this.termRegExp);
1020 }
1021 },
1022
1023 {
1024 name: "list",
1025 match: "^(?:(?:(?:\\*)|(?:#)|(?:;)|(?::))+)",
1026 lookaheadRegExp: /^(?:(?:(\*)|(#)|(;)|(:))+)/mg,
1027 termRegExp: /(\n)/mg,
1028 handler: function(w)
1029 {
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)
1037 {
1038 if(lookaheadMatch[1])
1039 {
1040 listType = "ul";
1041 itemType = "li";
1042 }
1043 else if(lookaheadMatch[2])
1044 {
1045 listType = "ol";
1046 itemType = "li";
1047 }
1048 else if(lookaheadMatch[3])
1049 {
1050 listType = "dl";
1051 itemType = "dt";
1052 }
1053 else if(lookaheadMatch[4])
1054 {
1055 listType = "dl";
1056 itemType = "dd";
1057 }
1058 listLevel = lookaheadMatch[0].length;
1059 w.nextMatch += lookaheadMatch[0].length;
1060 if(listLevel > currLevel)
1061 {
1062 for(var t=currLevel; t<listLevel; t++)
1063 placeStack.push(createTiddlyElement(placeStack[placeStack.length-1],listType));
1064 }
1065 else if(listLevel < currLevel)
1066 {
1067 for(var t=currLevel; t>listLevel; t--)
1068 placeStack.pop();
1069 }
1070 else if(listLevel == currLevel && listType != currType)
1071 {
1072 placeStack.pop();
1073 placeStack.push(createTiddlyElement(placeStack[placeStack.length-1],listType));
1074 }
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);
1081 }
1082 }
1083 },
1084
1085 {
1086 name: "quoteByBlock",
1087 match: "^<<<\\n",
1088 termRegExp: /(^<<<(\n|$))/mg,
1089 element: "blockquote",
1090 handler: config.formatterHelpers.createElementAndWikify
1091 },
1092
1093 {
1094 name: "quoteByLine",
1095 match: "^>+",
1096 lookaheadRegExp: /^>+/mg,
1097 termRegExp: /(\n)/mg,
1098 element: "blockquote",
1099 handler: function(w)
1100 {
1101 var placeStack = [w.output];
1102 var currLevel = 0;
1103 var newLevel = w.matchLength;
1104 var t;
1105 do {
1106 if(newLevel > currLevel)
1107 {
1108 for(t=currLevel; t<newLevel; t++)
1109 placeStack.push(createTiddlyElement(placeStack[placeStack.length-1],this.element));
1110 }
1111 else if(newLevel < currLevel)
1112 {
1113 for(t=currLevel; t>newLevel; t--)
1114 placeStack.pop();
1115 }
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;
1122 if(matched)
1123 {
1124 newLevel = lookaheadMatch[0].length;
1125 w.nextMatch += lookaheadMatch[0].length;
1126 }
1127 } while(matched);
1128 }
1129 },
1130
1131 {
1132 name: "rule",
1133 match: "^----+$\\n?",
1134 handler: function(w)
1135 {
1136 createTiddlyElement(w.output,"hr");
1137 }
1138 },
1139
1140 {
1141 name: "monospacedByLine",
1142 match: "^\\{\\{\\{\\n",
1143 lookaheadRegExp: /^\{\{\{\n((?:^[^\n]*\n)+?)(^\}\}\}$\n?)/mg,
1144 element: "pre",
1145 handler: config.formatterHelpers.enclosedTextHelper
1146 },
1147
1148 {
1149 name: "monospacedByLineForCSS",
1150 match: "^/\\*[\\{]{3}\\*/\\n",
1151 lookaheadRegExp: /\/\*[\{]{3}\*\/\n*((?:^[^\n]*\n)+?)(\n*^\/\*[\}]{3}\*\/$\n?)/mg,
1152 element: "pre",
1153 handler: config.formatterHelpers.enclosedTextHelper
1154 },
1155
1156 {
1157 name: "monospacedByLineForPlugin",
1158 match: "^//\\{\\{\\{\\n",
1159 lookaheadRegExp: /^\/\/\{\{\{\n\n*((?:^[^\n]*\n)+?)(\n*^\/\/\}\}\}$\n?)/mg,
1160 element: "pre",
1161 handler: config.formatterHelpers.enclosedTextHelper
1162 },
1163
1164 {
1165 name: "monospacedByLineForTemplate",
1166 match: "^<!--[\\{]{3}-->\\n",
1167 lookaheadRegExp: /<!--[\{]{3}-->\n*((?:^[^\n]*\n)+?)(\n*^<!--[\}]{3}-->$\n?)/mg,
1168 element: "pre",
1169 handler: config.formatterHelpers.enclosedTextHelper
1170 },
1171
1172 {
1173 name: "wikifyCommentForPlugin",
1174 match: "^/\\*\\*\\*\\n",
1175 termRegExp: /(^\*\*\*\/\n)/mg,
1176 handler: function(w)
1177 {
1178 w.subWikifyTerm(w.output,this.termRegExp);
1179 }
1180 },
1181
1182 {
1183 name: "wikifyCommentForTemplate",
1184 match: "^<!---\\n",
1185 termRegExp: /(^--->\n)/mg,
1186 handler: function(w)
1187 {
1188 w.subWikifyTerm(w.output,this.termRegExp);
1189 }
1190 },
1191
1192 {
1193 name: "macro",
1194 match: "<<",
1195 lookaheadRegExp: /<<([^>\s]+)(?:\s*)((?:[^>]|(?:>(?!>)))*)>>/mg,
1196 handler: function(w)
1197 {
1198 this.lookaheadRegExp.lastIndex = w.matchStart;
1199 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
1200 if(lookaheadMatch && lookaheadMatch.index == w.matchStart && lookaheadMatch[1])
1201 {
1202 w.nextMatch = this.lookaheadRegExp.lastIndex;
1203 invokeMacro(w.output,lookaheadMatch[1],lookaheadMatch[2],w,w.tiddler);
1204 }
1205 }
1206 },
1207
1208 {
1209 name: "prettyLink",
1210 match: "\\[\\[",
1211 lookaheadRegExp: /\[\[(.*?)(?:\|(~)?(.*?))?\]\]/mg,
1212 handler: function(w)
1213 {
1214 this.lookaheadRegExp.lastIndex = w.matchStart;
1215 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
1216 if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
1217 {
1218 var e;
1219 var text = lookaheadMatch[1];
1220 if(lookaheadMatch[3])
1221 {
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);
1227 }
1228 else
1229 {
1230 // Simple bracketted link
1231 e = createTiddlyLink(w.output,text,false,null,w.isStatic);
1232 }
1233 createTiddlyText(e,text);
1234 w.nextMatch = this.lookaheadRegExp.lastIndex;
1235 }
1236 }
1237 },
1238
1239 {
1240 name: "unWikiLink",
1241 match: config.textPrimitives.unWikiLink+config.textPrimitives.wikiLink,
1242 handler: function(w)
1243 {
1244 w.outputText(w.output,w.matchStart+1,w.nextMatch);
1245 }
1246 },
1247
1248 {
1249 name: "wikiLink",
1250 match: config.textPrimitives.wikiLink,
1251 handler: function(w)
1252 {
1253 if(w.matchStart > 0)
1254 {
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)
1259 {
1260 w.outputText(w.output,w.matchStart,w.nextMatch);
1261 return;
1262 }
1263 }
1264 if(w.autoLinkWikiWords == true || store.isShadowTiddler(w.matchText))
1265 {
1266 var link = createTiddlyLink(w.output,w.matchText,false,null,w.isStatic);
1267 w.outputText(link,w.matchStart,w.nextMatch);
1268 }
1269 else
1270 {
1271 w.outputText(w.output,w.matchStart,w.nextMatch);
1272 }
1273 }
1274 },
1275
1276 {
1277 name: "urlLink",
1278 match: config.textPrimitives.urlPattern,
1279 handler: function(w)
1280 {
1281 w.outputText(createExternalLink(w.output,w.matchText),w.matchStart,w.nextMatch);
1282 }
1283 },
1284
1285 {
1286 name: "image",
1287 match: "\\[[<>]?[Ii][Mm][Gg]\\[",
1288 lookaheadRegExp: /\[(<?)(>?)[Ii][Mm][Gg]\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg,
1289 handler: function(w)
1290 {
1291 this.lookaheadRegExp.lastIndex = w.matchStart;
1292 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
1293 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) // Simple bracketted link
1294 {
1295 var e = w.output;
1296 if(lookaheadMatch[5])
1297 {
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");
1301 }
1302 var img = createTiddlyElement(e,"img");
1303 if(lookaheadMatch[1])
1304 img.align = "left";
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;
1311 }
1312 }
1313 },
1314
1315 {
1316 name: "html",
1317 match: "<[Hh][Tt][Mm][Ll]>",
1318 lookaheadRegExp: /<[Hh][Tt][Mm][Ll]>((?:.|\n)*?)<\/[Hh][Tt][Mm][Ll]>/mg,
1319 handler: function(w)
1320 {
1321 this.lookaheadRegExp.lastIndex = w.matchStart;
1322 var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
1323 if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
1324 {
1325 createTiddlyElement(w.output,"span").innerHTML = lookaheadMatch[1];
1326 w.nextMatch = this.lookaheadRegExp.lastIndex;
1327 }
1328 }
1329 },
1330
1331 {
1332 name: "commentByBlock",
1333 match: "/%",
1334 lookaheadRegExp: /\/%((?:.|\n)*?)%\//mg,
1335 handler: function(w)
1336 {
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;
1341 }
1342 },
1343
1344 {
1345 name: "boldByChar",
1346 match: "''",
1347 termRegExp: /('')/mg,
1348 element: "strong",
1349 handler: config.formatterHelpers.createElementAndWikify
1350 },
1351
1352 {
1353 name: "italicByChar",
1354 match: "//",
1355 termRegExp: /(\/\/)/mg,
1356 element: "em",
1357 handler: config.formatterHelpers.createElementAndWikify
1358 },
1359
1360 {
1361 name: "underlineByChar",
1362 match: "__",
1363 termRegExp: /(__)/mg,
1364 element: "u",
1365 handler: config.formatterHelpers.createElementAndWikify
1366 },
1367
1368 {
1369 name: "strikeByChar",
1370 match: "--(?!\\s|$)",
1371 termRegExp: /((?!\s)--|(?=\n\n))/mg,
1372 element: "strike",
1373 handler: config.formatterHelpers.createElementAndWikify
1374 },
1375
1376 {
1377 name: "superscriptByChar",
1378 match: "\\^\\^",
1379 termRegExp: /(\^\^)/mg,
1380 element: "sup",
1381 handler: config.formatterHelpers.createElementAndWikify
1382 },
1383
1384 {
1385 name: "subscriptByChar",
1386 match: "~~",
1387 termRegExp: /(~~)/mg,
1388 element: "sub",
1389 handler: config.formatterHelpers.createElementAndWikify
1390 },
1391
1392 {
1393 name: "monospacedByChar",
1394 match: "\\{\\{\\{",
1395 lookaheadRegExp: /\{\{\{((?:.|\n)*?)\}\}\}/mg,
1396 handler: function(w)
1397 {
1398 this.lookaheadRegExp.lastIndex = w.matchStart;
1399 var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
1400 if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
1401 {
1402 createTiddlyElement(w.output,"code",null,null,lookaheadMatch[1]);
1403 w.nextMatch = this.lookaheadRegExp.lastIndex;
1404 }
1405 }
1406 },
1407
1408 {
1409 name: "styleByChar",
1410 match: "@@",
1411 termRegExp: /(@@)/mg,
1412 handler: function(w)
1413 {
1414 var e = createTiddlyElement(w.output,"span");
1415 var styles = config.formatterHelpers.inlineCssHelper(w);
1416 if(styles.length == 0)
1417 e.className = "marked";
1418 else
1419 config.formatterHelpers.applyCssHelper(e,styles);
1420 w.subWikifyTerm(e,this.termRegExp);
1421 }
1422 },
1423
1424 {
1425 name: "lineBreak",
1426 match: "\\n|<br ?/?>",
1427 handler: function(w)
1428 {
1429 createTiddlyElement(w.output,"br");
1430 }
1431 },
1432
1433 {
1434 name: "rawText",
1435 match: "\\\"{3}|<nowiki>",
1436 lookaheadRegExp: /(?:\"{3}|<nowiki>)((?:.|\n)*?)(?:\"{3}|<\/nowiki>)/mg,
1437 handler: function(w)
1438 {
1439 this.lookaheadRegExp.lastIndex = w.matchStart;
1440 var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
1441 if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
1442 {
1443 createTiddlyElement(w.output,"span",null,null,lookaheadMatch[1]);
1444 w.nextMatch = this.lookaheadRegExp.lastIndex;
1445 }
1446 }
1447 },
1448
1449 {
1450 name: "mdash",
1451 match: "--",
1452 handler: function(w)
1453 {
1454 createTiddlyElement(w.output,"span").innerHTML = "&mdash;";
1455 }
1456 },
1457
1458 {
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)
1462 {
1463 createTiddlyElement(w.output,"span").innerHTML = w.matchText;
1464 }
1465 },
1466
1467 {
1468 name: "customClasses",
1469 match: "\\{\\{",
1470 termRegExp: /(\}\}\})/mg,
1471 lookaheadRegExp: /\{\{[\s]*([\w]+[\s\w]*)[\s]*\{(\n?)/mg,
1472 handler: function(w)
1473 {
1474 this.lookaheadRegExp.lastIndex = w.matchStart;
1475 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
1476 if(lookaheadMatch)
1477 {
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);
1481 }
1482 }
1483 }
1484
1485 ];
1486
1487 // ---------------------------------------------------------------------------------
1488 // Wikifier
1489 // ---------------------------------------------------------------------------------
1490
1491 function getParser(tiddler)
1492 {
1493 var f = formatter;
1494 if(tiddler!=null)
1495 {
1496 for(var i in config.parsers)
1497 {
1498 if(tiddler.isTagged(config.parsers[i].formatTag))
1499 {
1500 f = config.parsers[i];
1501 break;
1502 }
1503 }
1504 }
1505 return f;
1506 }
1507
1508 function wikify(source,output,highlightRegExp,tiddler)
1509 {
1510 if(source && source != "")
1511 {
1512 var wikifier = new Wikifier(source,getParser(tiddler),highlightRegExp,tiddler);
1513 wikifier.subWikifyUnterm(output);
1514 }
1515 }
1516
1517 function wikifyStatic(source,highlightRegExp,tiddler)
1518 {
1519 var e = createTiddlyElement(document.body,"div");
1520 e.style.display = "none";
1521 var html = "";
1522 if(source && source != "")
1523 {
1524 var wikifier = new Wikifier(source,getParser(tiddler),highlightRegExp,tiddler);
1525 wikifier.isStatic = true;
1526 wikifier.subWikifyUnterm(e);
1527 html = e.innerHTML;
1528 e.parentNode.removeChild(e);
1529 }
1530 return html;
1531 }
1532
1533 // Wikify a named tiddler to plain text
1534 function wikifyPlain(title)
1535 {
1536 if(store.tiddlerExists(title) || store.isShadowTiddler(title))
1537 {
1538 var wikifier = new Wikifier(store.getTiddlerText(title),formatter,null,store.getTiddler(title));
1539 return wikifier.wikifyPlain();
1540 }
1541 else
1542 return "";
1543 }
1544
1545 // Highlight plain text into an element
1546 function highlightify(source,output,highlightRegExp)
1547 {
1548 if(source && source != "")
1549 {
1550 var wikifier = new Wikifier(source,formatter,highlightRegExp);
1551 wikifier.outputText(output,0,source.length);
1552 }
1553 }
1554
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)
1561 {
1562 this.source = source;
1563 this.output = null;
1564 this.formatter = formatter;
1565 this.nextMatch = 0;
1566 this.autoLinkWikiWords = tiddler && tiddler.autoLinkWikiWords() == false ? false : true;
1567 this.highlightRegExp = highlightRegExp;
1568 this.highlightMatch = null;
1569 this.isStatic = false;
1570 if(highlightRegExp)
1571 {
1572 highlightRegExp.lastIndex = 0;
1573 this.highlightMatch = highlightRegExp.exec(source);
1574 }
1575 this.tiddler = tiddler;
1576 }
1577
1578 Wikifier.prototype.wikifyPlain = function()
1579 {
1580 var e = createTiddlyElement(document.body,"div");
1581 e.style.display = "none";
1582 this.subWikify(e);
1583 var text = getPlainText(e);
1584 e.parentNode.removeChild(e);
1585 return text;
1586 }
1587
1588 Wikifier.prototype.subWikify = function(output,terminator)
1589 {
1590 // Handle the terminated and unterminated cases separately
1591 if (terminator)
1592 this.subWikifyTerm(output,new RegExp("(" + terminator + ")","mg"));
1593 else
1594 this.subWikifyUnterm(output);
1595 }
1596
1597 Wikifier.prototype.subWikifyUnterm = function(output)
1598 {
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)
1606 {
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++)
1617 {
1618 if(formatterMatch[t])
1619 {
1620 this.formatter.formatters[t-1].handler(this);
1621 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
1622 break;
1623 }
1624 }
1625 // Get the next match
1626 formatterMatch = this.formatter.formatterRegExp.exec(this.source);
1627 }
1628 // Output any text after the last match
1629 if(this.nextMatch < this.source.length)
1630 {
1631 this.outputText(this.output,this.nextMatch,this.source.length);
1632 this.nextMatch = this.source.length;
1633 }
1634 // Restore the output pointer
1635 this.output = oldOutput;
1636 }
1637
1638 Wikifier.prototype.subWikifyTerm = function(output,terminatorRegExp)
1639 {
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)
1649 {
1650 // Check for a terminator match before the next formatter match
1651 if(terminatorMatch && (!formatterMatch || terminatorMatch.index <= formatterMatch.index))
1652 {
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;
1663 return;
1664 }
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++)
1675 {
1676 if(formatterMatch[t])
1677 {
1678 this.formatter.formatters[t-1].handler(this);
1679 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
1680 break;
1681 }
1682 }
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);
1687 }
1688 // Output any text after the last match
1689 if(this.nextMatch < this.source.length)
1690 {
1691 this.outputText(this.output,this.nextMatch,this.source.length);
1692 this.nextMatch = this.source.length;
1693 }
1694 // Restore the output pointer
1695 this.output = oldOutput;
1696 }
1697
1698 Wikifier.prototype.outputText = function(place,startPos,endPos)
1699 {
1700 // Check for highlights
1701 while(this.highlightMatch && (this.highlightRegExp.lastIndex > startPos) && (this.highlightMatch.index < endPos) && (startPos < endPos))
1702 {
1703 // Deal with any plain text before the highlight
1704 if(this.highlightMatch.index > startPos)
1705 {
1706 createTiddlyText(place,this.source.substring(startPos,this.highlightMatch.index));
1707 startPos = this.highlightMatch.index;
1708 }
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);
1716 }
1717 // Do the unhighlighted text left over
1718 if(startPos < endPos)
1719 {
1720 createTiddlyText(place,this.source.substring(startPos,endPos));
1721 }
1722 }
1723
1724 // ---------------------------------------------------------------------------------
1725 // Macro definitions
1726 // ---------------------------------------------------------------------------------
1727
1728 config.macros.today.handler = function(place,macroName,params)
1729 {
1730 var now = new Date();
1731 var text;
1732 if(params[0])
1733 text = now.formatString(params[0].trim());
1734 else
1735 text = now.toLocaleString();
1736 createTiddlyElement(place,"span",null,null,text);
1737 }
1738
1739 config.macros.version.handler = function(place)
1740 {
1741 createTiddlyElement(place,"span",null,null,version.major + "." + version.minor + "." + version.revision + (version.beta ? " (beta " + version.beta + ")" : ""));
1742 }
1743
1744 config.macros.list.handler = function(place,macroName,params)
1745 {
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);
1751 var results;
1752 if(this[type].handler)
1753 results = this[type].handler(params);
1754 for(var t = 0; t < results.length; t++)
1755 {
1756 var theListItem = document.createElement("li")
1757 theList.appendChild(theListItem);
1758 if(typeof results[t] == "string")
1759 createTiddlyLink(theListItem,results[t],true);
1760 else
1761 createTiddlyLink(theListItem,results[t].title,true);
1762 }
1763 }
1764
1765 config.macros.list.all.handler = function(params)
1766 {
1767 return store.reverseLookup("tags","excludeLists",false,"title");
1768 }
1769
1770 config.macros.list.missing.handler = function(params)
1771 {
1772 return store.getMissingLinks();
1773 }
1774
1775 config.macros.list.orphans.handler = function(params)
1776 {
1777 return store.getOrphans();
1778 }
1779
1780 config.macros.list.shadowed.handler = function(params)
1781 {
1782 return store.getShadowed();
1783 }
1784
1785 config.macros.allTags.handler = function(place,macroName,params)
1786 {
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++)
1792 {
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]);
1796 }
1797 }
1798
1799 config.macros.timeline.handler = function(place,macroName,params)
1800 {
1801 var field = params[0] ? params[0] : "modified";
1802 var tiddlers = store.reverseLookup("tags","excludeLists",false,field);
1803 var lastDay = "";
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--)
1806 {
1807 var tiddler = tiddlers[t];
1808 var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
1809 if(theDay != lastDay)
1810 {
1811 var theDateList = document.createElement("ul");
1812 place.appendChild(theDateList);
1813 createTiddlyElement(theDateList,"li",null,"listTitle",tiddler[field].formatString(this.dateFormat));
1814 lastDay = theDay;
1815 }
1816 var theDateListItem = createTiddlyElement(theDateList,"li",null,"listLink");
1817 theDateListItem.appendChild(createTiddlyLink(place,tiddler.title,true));
1818 }
1819 }
1820
1821 config.macros.search.handler = function(place,macroName,params)
1822 {
1823 var searchTimeout = null;
1824 var btn = createTiddlyButton(place,this.label,this.prompt,this.onClick);
1825 var txt = createTiddlyElement(place,"input",null,"txtOptionInput");
1826 if(params[0])
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)
1835 {
1836 txt.setAttribute("type","search");
1837 txt.setAttribute("results","5");
1838 }
1839 else
1840 txt.setAttribute("type","text");
1841 }
1842
1843 // Global because there's only ever one outstanding incremental search timer
1844 config.macros.search.timeout = null;
1845
1846 config.macros.search.doSearch = function(txt)
1847 {
1848 if(txt.value.length > 0)
1849 {
1850 story.search(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch);
1851 txt.setAttribute("lastSearchText",txt.value);
1852 }
1853 }
1854
1855 config.macros.search.onClick = function(e)
1856 {
1857 config.macros.search.doSearch(this.nextSibling);
1858 return false;
1859 }
1860
1861 config.macros.search.onKeyPress = function(e)
1862 {
1863 if(!e) var e = window.event;
1864 switch(e.keyCode)
1865 {
1866 case 13: // Ctrl-Enter
1867 case 10: // Ctrl-Enter on IE PC
1868 config.macros.search.doSearch(this);
1869 break;
1870 case 27: // Escape
1871 this.value = "";
1872 clearMessage();
1873 break;
1874 }
1875 if(this.value.length > 2)
1876 {
1877 if(this.value != this.getAttribute("lastSearchText"))
1878 {
1879 if(config.macros.search.timeout)
1880 clearTimeout(config.macros.search.timeout);
1881 var txt = this;
1882 config.macros.search.timeout = setTimeout(function() {config.macros.search.doSearch(txt);},500);
1883 }
1884 }
1885 else
1886 {
1887 if(config.macros.search.timeout)
1888 clearTimeout(config.macros.search.timeout);
1889 }
1890 }
1891
1892 config.macros.search.onFocus = function(e)
1893 {
1894 this.select();
1895 }
1896
1897 config.macros.tiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
1898 {
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);
1905 if(!args)
1906 {
1907 wrapper.setAttribute("refresh","content");
1908 wrapper.setAttribute("tiddler",tiddlerName);
1909 }
1910 var text = store.getTiddlerText(tiddlerName);
1911 if(text)
1912 {
1913 var stack = config.macros.tiddler.tiddlerStack;
1914 if(stack.indexOf(tiddlerName) !== -1)
1915 return;
1916 stack.push(tiddlerName);
1917 try
1918 {
1919 var n = args ? Math.min(args.length,9) : 0;
1920 for(var i=0; i<n; i++)
1921 {
1922 var placeholderRE = new RegExp("\\$" + (i + 1),"mg");
1923 text = text.replace(placeholderRE,args[i]);
1924 }
1925 config.macros.tiddler.renderText(wrapper,text,tiddlerName,params);
1926 }
1927 finally
1928 {
1929 stack.pop();
1930 }
1931 }
1932 }
1933
1934 config.macros.tiddler.renderText = function(place,text,tiddlerName,params)
1935 {
1936 wikify(text,place,null,store.getTiddler(tiddlerName));
1937 }
1938
1939 config.macros.tiddler.tiddlerStack = [];
1940
1941 config.macros.tag.handler = function(place,macroName,params)
1942 {
1943 createTagButton(place,params[0]);
1944 }
1945
1946 config.macros.tags.handler = function(place,macroName,params,wikifier,paramString,tiddler)
1947 {
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++)
1958 {
1959 createTagButton(createTiddlyElement(theList,"li"),tiddler.tags[t],tiddler.title);
1960 if(t<tiddler.tags.length-1)
1961 createTiddlyText(theList,sep);
1962 }
1963 }
1964
1965 config.macros.tagging.handler = function(place,macroName,params,wikifier,paramString,tiddler)
1966 {
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++)
1978 {
1979 createTiddlyLink(createTiddlyElement(theList,"li"),tagged[t].title,true);
1980 if(t<tagged.length-1)
1981 createTiddlyText(theList,sep);
1982 }
1983 }
1984
1985 config.macros.closeAll.handler = function(place)
1986 {
1987 createTiddlyButton(place,this.label,this.prompt,this.onClick);
1988 }
1989
1990 config.macros.closeAll.onClick = function(e)
1991 {
1992 story.closeAllTiddlers();
1993 return false;
1994 }
1995
1996 config.macros.permaview.handler = function(place)
1997 {
1998 createTiddlyButton(place,this.label,this.prompt,this.onClick);
1999 }
2000
2001 config.macros.permaview.onClick = function(e)
2002 {
2003 story.permaView();
2004 return false;
2005 }
2006
2007 config.macros.saveChanges.handler = function(place)
2008 {
2009 if(!readOnly)
2010 createTiddlyButton(place,this.label,this.prompt,this.onClick,null,null,this.accessKey);
2011 }
2012
2013 config.macros.saveChanges.onClick = function(e)
2014 {
2015 saveChanges();
2016 return false;
2017 }
2018
2019 config.macros.slider.onClickSlider = function(e)
2020 {
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"));
2027 else
2028 n.style.display = isOpen ? "none" : "block";
2029 config.options[cookie] = !isOpen;
2030 saveOptionCookie(cookie);
2031 return false;
2032 }
2033
2034 config.macros.slider.createSlider = function(place,cookie,title,tooltip)
2035 {
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);
2042 return panel;
2043 }
2044
2045 config.macros.slider.handler = function(place,macroName,params)
2046 {
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]);
2051 if(text)
2052 wikify(text,panel,null,store.getTiddler(params[1]));
2053 }
2054
2055 config.macros.option.onChangeOption = function(e)
2056 {
2057 var opt = this.getAttribute("option");
2058 var elementType,valueField;
2059 if(opt)
2060 {
2061 switch(opt.substr(0,3))
2062 {
2063 case "txt":
2064 elementType = "input";
2065 valueField = "value";
2066 break;
2067 case "chk":
2068 elementType = "input";
2069 valueField = "checked";
2070 break;
2071 }
2072 config.options[opt] = this[valueField];
2073 saveOptionCookie(opt);
2074 var nodes = document.getElementsByTagName(elementType);
2075 for(var t=0; t<nodes.length; t++)
2076 {
2077 var optNode = nodes[t].getAttribute("option");
2078 if(opt == optNode)
2079 nodes[t][valueField] = this[valueField];
2080 }
2081 }
2082 return(true);
2083 }
2084
2085 config.macros.option.handler = function(place,macroName,params)
2086 {
2087 var opt = params[0];
2088 if(config.options[opt] == undefined)
2089 return;
2090 var c;
2091 switch(opt.substr(0,3))
2092 {
2093 case "txt":
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];
2100 break;
2101 case "chk":
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];
2109 break;
2110 }
2111 }
2112
2113
2114
2115 config.macros.newTiddler.createNewTiddlerButton = function(place,title,params,label,prompt,accessKey,newFocus,isJournal)
2116 {
2117 var tags = [];
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);
2134 return btn;
2135 }
2136
2137 config.macros.newTiddler.onClickNewTiddler = function()
2138 {
2139 var title = this.getAttribute("newTitle");
2140 if(this.getAttribute("isJournal"))
2141 {
2142 var now = new Date();
2143 title = now.formatString(title.trim());
2144 }
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);
2155 return false;
2156 }
2157
2158 config.macros.newTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
2159 {
2160 if(!readOnly)
2161 {
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);
2166 }
2167 }
2168
2169 config.macros.newJournal.handler = function(place,macroName,params,wikifier,paramString,tiddler)
2170 {
2171 if(!readOnly)
2172 {
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);
2177 }
2178 }
2179
2180 config.macros.sparkline.handler = function(place,macroName,params)
2181 {
2182 var data = [];
2183 var min = 0;
2184 var max = 0;
2185 for(var t=0; t<params.length; t++)
2186 {
2187 var v = parseInt(params[t]);
2188 if(v < min)
2189 min = v;
2190 if(v > max)
2191 max = v;
2192 data.push(v);
2193 }
2194 if(data.length < 1)
2195 return;
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++)
2203 {
2204 var tick = document.createElement("img");
2205 tick.border = 0;
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);
2215 }
2216 }
2217
2218 config.macros.tabs.handler = function(place,macroName,params)
2219 {
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++)
2227 {
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);
2234 tab.title = prompt;
2235 if(config.options[cookie] == label)
2236 validTab = true;
2237 }
2238 if(!validTab)
2239 config.options[cookie] = params[1];
2240 place.appendChild(wrapper);
2241 this.switchTab(tabset,config.options[cookie]);
2242 }
2243
2244 config.macros.tabs.onClickTab = function(e)
2245 {
2246 config.macros.tabs.switchTab(this.parentNode,this.getAttribute("tab"));
2247 return false;
2248 }
2249
2250 config.macros.tabs.switchTab = function(tabset,tab)
2251 {
2252 var cookie = tabset.getAttribute("cookie");
2253 var theTab = null
2254 var nodes = tabset.childNodes;
2255 for(var t=0; t<nodes.length; t++)
2256 if(nodes[t].getAttribute && nodes[t].getAttribute("tab") == tab)
2257 {
2258 theTab = nodes[t];
2259 theTab.className = "tab tabSelected";
2260 }
2261 else
2262 nodes[t].className = "tab tabUnselected"
2263 if(theTab)
2264 {
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));
2271 if(cookie)
2272 {
2273 config.options[cookie] = tab;
2274 saveOptionCookie(cookie);
2275 }
2276 }
2277 }
2278
2279 // <<gradient [[tiddler name]] vert|horiz rgb rgb rgb rgb... >>
2280 config.macros.gradient.handler = function(place,macroName,params,wikifier)
2281 {
2282 var terminator = ">>";
2283 var panel;
2284 if(wikifier)
2285 panel = createTiddlyElement(place,"div",null,"gradient");
2286 else
2287 panel = place;
2288 panel.style.position = "relative";
2289 panel.style.overflow = "hidden";
2290 panel.style.zIndex = "0";
2291 var t;
2292 if(wikifier)
2293 {
2294 var styles = config.formatterHelpers.inlineCssHelper(wikifier);
2295 config.formatterHelpers.applyCssHelper(panel,styles);
2296 }
2297 var colours = [];
2298 for(t=1; t<params.length; t++)
2299 {
2300 var c = new RGB(params[t]);
2301 if(c)
2302 colours.push(c);
2303 }
2304 drawGradient(panel,params[0] != "vert",colours);
2305 if(wikifier)
2306 wikifier.subWikify(panel,terminator);
2307 if(document.all)
2308 {
2309 panel.style.height = "100%";
2310 panel.style.width = "100%";
2311 }
2312 }
2313
2314 config.macros.message.handler = function(place,macroName,params)
2315 {
2316 if(params[0])
2317 {
2318 var m = config;
2319 var p = params[0].split(".");
2320 for(var t=0; t<p.length; t++)
2321 {
2322 if(p[t] in m)
2323 m = m[p[t]];
2324 else
2325 break;
2326 }
2327 createTiddlyText(place,m.toString().format(params.splice(1)));
2328 }
2329 }
2330
2331 config.macros.view.handler = function(place,macroName,params,wikifier,paramString,tiddler)
2332 {
2333 if((tiddler instanceof Tiddler) && params[0])
2334 {
2335 var value = store.getValue(tiddler,params[0]);
2336 if(value != undefined)
2337 switch(params[1])
2338 {
2339 case undefined:
2340 highlightify(value,place,highlightHack);
2341 break;
2342 case "link":
2343 createTiddlyLink(place,value,true);
2344 break;
2345 case "wikified":
2346 wikify(value,place,highlightHack,tiddler);
2347 break;
2348 case "date":
2349 value = Date.convertFromYYYYMMDDHHMM(value);
2350 if(params[2])
2351 createTiddlyText(place,value.formatString(params[2]));
2352 else
2353 createTiddlyText(place,value);
2354 break;
2355 }
2356 }
2357 }
2358
2359 config.macros.edit.handler = function(place,macroName,params,wikifier,paramString,tiddler)
2360 {
2361 var field = params[0];
2362 if((tiddler instanceof Tiddler) && field)
2363 {
2364 story.setDirty(tiddler.title,true);
2365 if(field != "text")
2366 {
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);
2373 if(!v)
2374 v = "";
2375 e.value = v;
2376 e.setAttribute("size","40");
2377 e.setAttribute("autocomplete","off");
2378 place.appendChild(e);
2379 }
2380 else
2381 {
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);
2388 if(!v)
2389 v = "";
2390 e.value = v;
2391 var rows = 10;
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);
2400 }
2401 }
2402 }
2403
2404 config.macros.tagChooser.onClick = function(e)
2405 {
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++)
2413 {
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"));
2417 }
2418 Popup.show(popup,false);
2419 e.cancelBubble = true;
2420 if(e.stopPropagation) e.stopPropagation();
2421 return(false);
2422 }
2423
2424 config.macros.tagChooser.onTagClick = function(e)
2425 {
2426 if(!e) var e = window.event;
2427 var tag = this.getAttribute("tag");
2428 var title = this.getAttribute("tiddler");
2429 if(!readOnly)
2430 story.setTiddlerTag(title,tag,0);
2431 return(false);
2432 }
2433
2434 config.macros.tagChooser.handler = function(place,macroName,params,wikifier,paramString,tiddler)
2435 {
2436 if(tiddler instanceof Tiddler)
2437 {
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);
2442 }
2443 }
2444
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)
2451 {
2452 if(typeof commandName != "string")
2453 {
2454 var c = null;
2455 for(var t in config.commands)
2456 if(config.commands[t] == commandName)
2457 c = t;
2458 commandName = c;
2459 }
2460 if((tiddler instanceof Tiddler) && (typeof commandName == "string"))
2461 {
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))
2469
2470 {
2471 var btn = createTiddlyButton(null,text,tooltip,this.onClickCommand);
2472 btn.setAttribute("commandName", commandName);
2473 btn.setAttribute("tiddler", title);
2474 if(theClass)
2475 addClass(btn,theClass);
2476 place.appendChild(btn);
2477 }
2478 }
2479 }
2480
2481 config.macros.toolbar.onClickCommand = function(e)
2482 {
2483 if(!e) var e = window.event;
2484 var command = config.commands[this.getAttribute("commandName")];
2485 return command.handler(e,this,this.getAttribute("tiddler"));
2486 }
2487
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)
2490 {
2491 var children = place.getElementsByTagName("a")
2492 for(var t=0; t<children.length; t++)
2493 {
2494 var c = children[t];
2495 if(hasClass(c,theClass) && c.getAttribute && c.getAttribute("commandName"))
2496 {
2497 if(c.onclick instanceof Function)
2498 c.onclick.call(c,event);
2499 break;
2500 }
2501 }
2502 }
2503
2504 config.macros.toolbar.handler = function(place,macroName,params,wikifier,paramString,tiddler)
2505 {
2506 for(var t=0; t<params.length; t++)
2507 {
2508 var c = params[t];
2509 var theClass = "";
2510 switch(c.substr(0,1))
2511 {
2512 case "+":
2513 theClass = "defaultCommand";
2514 c = c.substr(1);
2515 break;
2516 case "-":
2517 theClass = "cancelCommand";
2518 c = c.substr(1);
2519 break;
2520 }
2521 if(c in config.commands)
2522 this.createCommand(place,c,tiddler,theClass);
2523 }
2524 }
2525
2526 config.macros.plugins.handler = function(place,macroName,params,wikifier,paramString,tiddler)
2527 {
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);
2533 }
2534
2535 config.macros.plugins.refresh = function(place,params)
2536 {
2537 var selectedRows = [];
2538 ListView.forEachSelector(place,function(e,rowName) {
2539 if(e.checked)
2540 selectedRows.push(e.getAttribute("rowName"));
2541 });
2542 removeChildren(place);
2543 params = params.parseParams("anon");
2544 var plugins = installedPlugins.slice(0);
2545 var t,tiddler,p;
2546 var configTiddlers = store.getTaggedTiddlers("systemConfig");
2547 for(t=0; t<configTiddlers.length; t++)
2548 {
2549 tiddler = configTiddlers[t];
2550 if(plugins.findByField("title",tiddler.title) == null)
2551 {
2552 p = getPluginInfo(tiddler);
2553 p.executed = false;
2554 p.log.splice(0,0,this.skippedText);
2555 plugins.push(p);
2556 }
2557 }
2558 for(t=0; t<plugins.length; t++)
2559 {
2560 var p = plugins[t];
2561 p.forced = p.tiddler.isTagged("systemConfigForce");
2562 p.disabled = p.tiddler.isTagged("systemConfigDisable");
2563 p.Selected = selectedRows.indexOf(plugins[t].title) != -1;
2564 }
2565 if(plugins.length == 0)
2566 createTiddlyElement(place,"em",null,null,this.noPluginText);
2567 else
2568 ListView.create(place,plugins,this.listViewTemplate,this.onSelectCommand);
2569 }
2570
2571 config.macros.plugins.onSelectCommand = function(command,rowNames)
2572 {
2573 var t;
2574 switch(command)
2575 {
2576 case "remove":
2577 for(t=0; t<rowNames.length; t++)
2578 store.setTiddlerTag(rowNames[t],false,"systemConfig");
2579 break;
2580 case "delete":
2581 if(rowNames.length > 0 && confirm(config.macros.plugins.confirmDeleteText.format([rowNames.join(", ")])))
2582 {
2583 for(t=0; t<rowNames.length; t++)
2584 {
2585 store.removeTiddler(rowNames[t]);
2586 story.closeTiddler(rowNames[t],true,false);
2587 }
2588 }
2589 break;
2590 }
2591 if(config.options.chkAutoSave)
2592 saveChanges(true);
2593 }
2594
2595 config.macros.refreshDisplay.handler = function(place)
2596 {
2597 createTiddlyButton(place,this.label,this.prompt,this.onClick);
2598 }
2599
2600 config.macros.refreshDisplay.onClick = function(e)
2601 {
2602 refreshAll();
2603 return false;
2604 }
2605
2606 config.macros.importTiddlers.handler = function(place,macroName,params,wikifier,paramString,tiddler)
2607 {
2608 if(readOnly)
2609 {
2610 createTiddlyElement(place,"div",null,"marked",this.readOnlyWarning);
2611 return;
2612 }
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";
2620 input.size = 50;
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);
2638 }
2639
2640 config.macros.importTiddlers.getFeeds = function(feeds)
2641 {
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")});
2645 return feeds;
2646 }
2647
2648 config.macros.importTiddlers.onFeedChange = function(e)
2649 {
2650 var importer = findRelated(this,"importTiddler","className","parentNode");
2651 importer.inputBox.value = this.value;
2652 this.selectedIndex = 0;
2653 }
2654
2655 config.macros.importTiddlers.onBrowseChange = function(e)
2656 {
2657 var importer = findRelated(this,"importTiddler","className","parentNode");
2658 importer.inputBox.value = "file://" + this.value;
2659 }
2660
2661 config.macros.importTiddlers.onFetch = function(e)
2662 {
2663 var importer = findRelated(this,"importTiddler","className","parentNode");
2664 var url = importer.inputBox.value;
2665 var cutoff = findRelated(importer.firstChild,"step2","className","nextSibling");
2666 while(cutoff)
2667 {
2668 var temp = cutoff.nextSibling;
2669 cutoff.parentNode.removeChild(cutoff);
2670 cutoff = temp;
2671 }
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);
2675 }
2676
2677 config.macros.importTiddlers.onLoad = function(status,params,responseText,url,xhr)
2678 {
2679 if(!status)
2680 {
2681 displayMessage(this.fetchError);
2682 return;
2683 }
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))
2690 // return;
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))
2696 {
2697 alert(config.messages.invalidFileError.format([url]));
2698 return;
2699 }
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
2711 doc.open();
2712 doc.writeln(content);
2713 doc.close();
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
2721 var tiddlers = [];
2722 importStore.forEachTiddler(function(title,tiddler)
2723 {
2724 var t = {};
2725 t.title = title;
2726 t.modified = tiddler.modified;
2727 t.modifier = tiddler.modifier;
2728 t.text = tiddler.text.substr(0,50);
2729 t.tags = tiddler.tags;
2730 tiddlers.push(t);
2731 });
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;
2738 }
2739
2740 config.macros.importTiddlers.onSelectCommand = function(listView,command,rowNames)
2741 {
2742 var importer = findRelated(listView,"importTiddler","className","parentNode");
2743 switch(command)
2744 {
2745 case "import":
2746 config.macros.importTiddlers.doImport(importer,rowNames);
2747 break;
2748 }
2749 if(config.options.chkAutoSave)
2750 saveChanges(true);
2751 }
2752
2753 config.macros.importTiddlers.doImport = function(importer,rowNames)
2754 {
2755 var theStore = importer.store;
2756 var overwrite = new Array();
2757 var t;
2758 for(t=0; t<rowNames.length; t++)
2759 {
2760 if(store.tiddlerExists(rowNames[t]))
2761 overwrite.push(rowNames[t]);
2762 }
2763 if(overwrite.length > 0)
2764 if(!confirm(this.confirmOverwriteText.format([overwrite.join(", ")])))
2765 return;
2766 for(t=0; t<rowNames.length; t++)
2767 {
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);
2772 }
2773 store.notifyAll();
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++)
2778 {
2779 createTiddlyLink(step,rowNames[t],true);
2780 createTiddlyElement(step,"br");
2781 }
2782 createTiddlyElement(importer,"h2",null,"step5",this.step5);
2783 }
2784 // ---------------------------------------------------------------------------------
2785 // Menu and toolbar commands
2786 // ---------------------------------------------------------------------------------
2787
2788 config.commands.closeTiddler.handler = function(event,src,title)
2789 {
2790 story.closeTiddler(title,true,event.shiftKey || event.altKey);
2791 return false;
2792 }
2793
2794 config.commands.closeOthers.handler = function(event,src,title)
2795 {
2796 story.closeAllTiddlers(title);
2797 return false;
2798 }
2799
2800 config.commands.editTiddler.handler = function(event,src,title)
2801 {
2802 clearMessage();
2803 story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
2804 story.focusTiddler(title,"text");
2805 return false;
2806 }
2807
2808 config.commands.saveTiddler.handler = function(event,src,title)
2809 {
2810 var newTitle = story.saveTiddler(title,event.shiftKey);
2811 if(newTitle)
2812 story.displayTiddler(null,newTitle);
2813 return false;
2814 }
2815
2816 config.commands.cancelTiddler.handler = function(event,src,title)
2817 {
2818 if(story.hasChanges(title) && !readOnly)
2819 if(!confirm(this.warning.format([title])))
2820 return false;
2821 story.setDirty(title,false);
2822 story.displayTiddler(null,title);
2823 return false;
2824 }
2825
2826 config.commands.deleteTiddler.handler = function(event,src,title)
2827 {
2828 var deleteIt = true;
2829 if (config.options.chkConfirmDelete)
2830 deleteIt = confirm(this.warning.format([title]));
2831 if (deleteIt)
2832 {
2833 store.removeTiddler(title);
2834 story.closeTiddler(title,true,event.shiftKey || event.altKey);
2835 if(config.options.chkAutoSave)
2836 saveChanges();
2837 }
2838 return false;
2839 }
2840
2841 config.commands.permalink.handler = function(event,src,title)
2842 {
2843 var t = encodeURIComponent(String.encodeTiddlyLink(title));
2844 if(window.location.hash != t)
2845 window.location.hash = t;
2846 return false;
2847 }
2848
2849 config.commands.references.handler = function(event,src,title)
2850 {
2851 var popup = Popup.create(src);
2852 if(popup)
2853 {
2854 var references = store.getReferringTiddlers(title);
2855 var c = false;
2856 for(var r=0; r<references.length; r++)
2857 if(references[r].title != title && !references[r].isTagged("excludeLists"))
2858 {
2859 createTiddlyLink(createTiddlyElement(popup,"li"),references[r].title,true);
2860 c = true;
2861 }
2862 if(!c)
2863 createTiddlyText(createTiddlyElement(popup,"li",null,"disabled"),this.popupNone);
2864 }
2865 Popup.show(popup,false);
2866 event.cancelBubble = true;
2867 if (event.stopPropagation) event.stopPropagation();
2868 return false;
2869 }
2870
2871 config.commands.jump.handler = function(event,src,title)
2872 {
2873 var popup = Popup.create(src);
2874 if(popup)
2875 {
2876 story.forEachTiddler(function(title,element) {
2877 createTiddlyLink(createTiddlyElement(popup,"li"),title,true);
2878 });
2879 }
2880 Popup.show(popup,false);
2881 event.cancelBubble = true;
2882 if (event.stopPropagation) event.stopPropagation();
2883 return false;
2884 }
2885
2886 // ---------------------------------------------------------------------------------
2887 // Tiddler() object
2888 // ---------------------------------------------------------------------------------
2889
2890 function Tiddler()
2891 {
2892 this.title = null;
2893 this.text = null;
2894 this.modifier = null;
2895 this.modified = new Date();
2896 this.created = new Date();
2897 this.links = [];
2898 this.linksUpdated = false;
2899 this.tags = [];
2900 return this;
2901 }
2902
2903 Tiddler.prototype.getLinks = function()
2904 {
2905 if(this.linksUpdated==false)
2906 this.changed();
2907 return this.links;
2908 }
2909
2910 // Format the text for storage in an RSS item
2911 Tiddler.prototype.saveToRss = function(url)
2912 {
2913 var s = [];
2914 s.push("<item>");
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>");
2921 s.push("</item>");
2922 return(s.join("\n"));
2923 }
2924
2925 // Change the text and other attributes of a tiddler
2926 Tiddler.prototype.set = function(title,text,modifier,modified,tags,created,fields)
2927 {
2928 this.assign(title,text,modifier,modified,tags,created,fields);
2929 this.changed();
2930 return this;
2931 }
2932
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)
2935 {
2936 if(title != undefined)
2937 this.title = title;
2938 if(text != undefined)
2939 this.text = text;
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)
2951 this.tags = [];
2952 return this;
2953 }
2954
2955 // Get the tags for a tiddler as a string (space delimited, using [[brackets]] for tags containing spaces)
2956 Tiddler.prototype.getTags = function()
2957 {
2958 return String.encodeTiddlyLinkList(this.tags);
2959 }
2960
2961 // Test if a tiddler carries a tag
2962 Tiddler.prototype.isTagged = function(tag)
2963 {
2964 return this.tags.indexOf(tag) != -1;
2965 }
2966
2967 // Static method to convert "\n" to newlines, "\s" to "\"
2968 Tiddler.unescapeLineBreaks = function(text)
2969 {
2970 return text ? text.unescapeLineBreaks() : "";
2971 }
2972
2973 // Convert newlines to "\n", "\" to "\s"
2974 Tiddler.prototype.escapeLineBreaks = function()
2975 {
2976 return this.text.escapeLineBreaks();
2977 }
2978
2979 // Updates the secondary information (like links[] array) after a change to a tiddler
2980 Tiddler.prototype.changed = function()
2981 {
2982 this.links = [];
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);
2987 while(formatMatch)
2988 {
2989 if(t==0 && formatMatch[1] && formatMatch[1] != this.title) // wikiWordLink
2990 {
2991 if(formatMatch.index > 0)
2992 {
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]);
2998 }
2999 else
3000 this.links.pushUnique(formatMatch[1]);
3001 }
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);
3008 }
3009 this.linksUpdated = true;
3010 return;
3011 }
3012
3013 Tiddler.prototype.getSubtitle = function()
3014 {
3015 var theModifier = this.modifier;
3016 if(!theModifier)
3017 theModifier = config.messages.subtitleUnknown;
3018 var theModified = this.modified;
3019 if(theModified)
3020 theModified = theModified.toLocaleString();
3021 else
3022 theModified = config.messages.subtitleUnknown;
3023 return(config.messages.tiddlerLinkTooltip.format([this.title,theModifier,theModified]));
3024 }
3025
3026 Tiddler.prototype.isReadOnly = function()
3027 {
3028 return readOnly;
3029 }
3030
3031 Tiddler.prototype.autoLinkWikiWords = function()
3032 {
3033 return !(this.isTagged("systemConfig") || this.isTagged("excludeMissing"));
3034 }
3035
3036 Tiddler.prototype.generateFingerprint = function()
3037 {
3038 return "0x" + Crypto.hexSha1Str(this.text);
3039 }
3040
3041 // ---------------------------------------------------------------------------------
3042 // TiddlyWiki() object contains Tiddler()s
3043 // ---------------------------------------------------------------------------------
3044
3045 function TiddlyWiki()
3046 {
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() {
3053 tiddlers = {};
3054 this.setDirty(false);
3055 };
3056 this.fetchTiddler = function(title) {
3057 return tiddlers[title];
3058 };
3059 this.deleteTiddler = function(title) {
3060 delete this.slices[title];
3061 delete tiddlers[title];
3062 };
3063 this.addTiddler = function(tiddler) {
3064 delete this.slices[tiddler.title];
3065 tiddlers[tiddler.title] = tiddler;
3066 };
3067 this.forEachTiddler = function(callback) {
3068 for(var t in tiddlers)
3069 {
3070 var tiddler = tiddlers[t];
3071 if(tiddler instanceof Tiddler)
3072 callback.call(this,t,tiddler);
3073 }
3074 };
3075 }
3076
3077 // Set the dirty flag
3078 TiddlyWiki.prototype.setDirty = function(dirty)
3079 {
3080 this.dirty = dirty;
3081 }
3082
3083 TiddlyWiki.prototype.isDirty = function()
3084 {
3085 return this.dirty;
3086 }
3087
3088 TiddlyWiki.prototype.suspendNotifications = function()
3089 {
3090 this.notificationLevel--;
3091 }
3092
3093 TiddlyWiki.prototype.resumeNotifications = function()
3094 {
3095 this.notificationLevel++;
3096 }
3097
3098 // Invoke the notification handlers for a particular tiddler
3099 TiddlyWiki.prototype.notify = function(title,doBlanket)
3100 {
3101 if(!this.notificationLevel)
3102 for(var t=0; t<this.namedNotifications.length; t++)
3103 {
3104 var n = this.namedNotifications[t];
3105 if((n.name == null && doBlanket) || (n.name == title))
3106 n.notify(title);
3107 }
3108 }
3109
3110 // Invoke the notification handlers for all tiddlers
3111 TiddlyWiki.prototype.notifyAll = function()
3112 {
3113 if(!this.notificationLevel)
3114 for(var t=0; t<this.namedNotifications.length; t++)
3115 {
3116 var n = this.namedNotifications[t];
3117 if(n.name)
3118 n.notify(n.name);
3119 }
3120 }
3121
3122 // Add a notification handler to a tiddler
3123 TiddlyWiki.prototype.addNotification = function(title,fn)
3124 {
3125 for (var i=0; i<this.namedNotifications.length; i++)
3126 if((this.namedNotifications[i].name == title) && (this.namedNotifications[i].notify == fn))
3127 return this;
3128 this.namedNotifications.push({name: title, notify: fn});
3129 return this;
3130 }
3131
3132 TiddlyWiki.prototype.removeTiddler = function(title)
3133 {
3134 var tiddler = this.fetchTiddler(title);
3135 if(tiddler)
3136 {
3137 this.deleteTiddler(title);
3138 this.notify(title,true);
3139 this.setDirty(true);
3140 }
3141 }
3142
3143 TiddlyWiki.prototype.tiddlerExists = function(title)
3144 {
3145 var t = this.fetchTiddler(title);
3146 return (t != undefined);
3147 }
3148
3149 TiddlyWiki.prototype.isShadowTiddler = function(title)
3150 {
3151 return typeof config.shadowTiddlers[title] == "string";
3152 }
3153
3154 TiddlyWiki.prototype.getTiddler = function(title)
3155 {
3156 var t = this.fetchTiddler(title);
3157 if(t != undefined)
3158 return t;
3159 else
3160 return null;
3161 }
3162
3163 TiddlyWiki.prototype.getTiddlerText = function(title,defaultText)
3164 {
3165 var tiddler = this.fetchTiddler(title);
3166 if(tiddler)
3167 return tiddler.text;
3168 if(!title)
3169 return defaultText;
3170 var pos = title.indexOf(config.textPrimitives.sliceSeparator);
3171 if(pos != -1)
3172 {
3173 var slice = this.getTiddlerSlice(title.substr(0,pos),title.substr(pos + config.textPrimitives.sliceSeparator.length));
3174 if(slice)
3175 return slice;
3176 }
3177 if(this.isShadowTiddler(title))
3178 return config.shadowTiddlers[title];
3179 if(defaultText != undefined)
3180 return defaultText;
3181 return null;
3182 }
3183
3184 TiddlyWiki.prototype.slicesRE = /(?:[\'\/]*~?(\w+)[\'\/]*\:[\'\/]*\s*(.*?)\s*$)|(?:\|[\'\/]*~?(\w+)\:?[\'\/]*\|\s*(.*?)\s*\|)/gm
3185
3186 // @internal
3187 TiddlyWiki.prototype.calcAllSlices = function(title)
3188 {
3189 var slices = {};
3190 var text = this.getTiddlerText(title,"");
3191 this.slicesRE.lastIndex = 0;
3192 do
3193 {
3194 var m = this.slicesRE.exec(text);
3195 if (m)
3196 {
3197 if (m[1])
3198 slices[m[1]] = m[2];
3199 else
3200 slices[m[3]] = m[4];
3201 }
3202 }
3203 while(m);
3204 return slices;
3205 }
3206
3207 // Returns the slice of text of the given name
3208 //#
3209 //# A text slice is a substring in the tiddler's text that is defined
3210 //# either like this
3211 //# aName: textSlice
3212 //# or
3213 //# |aName:| textSlice |
3214 //# or
3215 //# |aName| textSlice |
3216 //#
3217 //# In the text the name (or name:) may be decorated with '' or //. I.e.
3218 //# this would also a possible text slice:
3219 //#
3220 //# |''aName:''| textSlice |
3221 //#
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)
3225 {
3226 var slices = this.slices[title];
3227 if (!slices) {
3228 slices = this.calcAllSlices(title);
3229 this.slices[title] = slices;
3230 }
3231 return slices[sliceName];
3232 }
3233
3234 // Build an hashmap of the specified named slices of a tiddler
3235 TiddlyWiki.prototype.getTiddlerSlices = function(title,sliceNames)
3236 {
3237 var r = {};
3238 for(var t=0; t<sliceNames.length; t++)
3239 {
3240 var slice = this.getTiddlerSlice(title,sliceNames[t]);
3241 if(slice)
3242 r[sliceNames[t]] = slice;
3243 }
3244 return r;
3245 }
3246
3247 TiddlyWiki.prototype.getRecursiveTiddlerText = function(title,defaultText,depth)
3248 {
3249 var bracketRegExp = new RegExp("(?:\\[\\[([^\\]]+)\\]\\])","mg");
3250 var text = this.getTiddlerText(title,null);
3251 if(text == null)
3252 return defaultText;
3253 var textOut = [];
3254 var lastPos = 0;
3255 do {
3256 var match = bracketRegExp.exec(text);
3257 if(match)
3258 {
3259 textOut.push(text.substr(lastPos,match.index-lastPos));
3260 if(match[1])
3261 {
3262 if(depth <= 0)
3263 textOut.push(match[1]);
3264 else
3265 textOut.push(this.getRecursiveTiddlerText(match[1],"[[" + match[1] + "]]",depth-1));
3266 }
3267 lastPos = match.index + match[0].length;
3268 }
3269 else
3270 textOut.push(text.substr(lastPos));
3271 } while(match);
3272 return(textOut.join(""));
3273 }
3274
3275 TiddlyWiki.prototype.setTiddlerTag = function(title,status,tag)
3276 {
3277 var tiddler = this.fetchTiddler(title);
3278 if(tiddler)
3279 {
3280 var t = tiddler.tags.indexOf(tag);
3281 if(t != -1)
3282 tiddler.tags.splice(t,1);
3283 if(status)
3284 tiddler.tags.push(tag);
3285 tiddler.changed();
3286 this.notify(title,true);
3287 this.setDirty(true);
3288 }
3289 }
3290
3291 TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags,fields)
3292 {
3293 var tiddler = this.fetchTiddler(title);
3294 var created;
3295 if(tiddler)
3296 {
3297 created = tiddler.created; // Preserve created date
3298 this.deleteTiddler(title);
3299 }
3300 else
3301 {
3302 tiddler = new Tiddler();
3303 created = modified;
3304 }
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);
3311 return tiddler;
3312 }
3313
3314 TiddlyWiki.prototype.createTiddler = function(title)
3315 {
3316 var tiddler = this.fetchTiddler(title);
3317 if(!tiddler)
3318 {
3319 tiddler = new Tiddler();
3320 tiddler.title = title;
3321 this.addTiddler(tiddler);
3322 this.setDirty(true);
3323 }
3324 return tiddler;
3325 }
3326
3327 // Load contents of a tiddlywiki from an HTML DIV
3328 TiddlyWiki.prototype.loadFromDiv = function(src,idPrefix,noUpdate)
3329 {
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);
3334 if(!noUpdate)
3335 {
3336 for(var i = 0;i<tiddlers.length; i++)
3337 tiddlers[i].changed();
3338 }
3339 }
3340
3341 TiddlyWiki.prototype.updateTiddlers = function()
3342 {
3343 this.tiddlersUpdated = true;
3344 this.forEachTiddler(function(title,tiddler) {
3345 tiddler.changed();
3346 });
3347 }
3348
3349 // Return all tiddlers formatted as an HTML string
3350 TiddlyWiki.prototype.allTiddlersAsHtml = function()
3351 {
3352 return store.getSaver().externalize(store);
3353 }
3354
3355 // Return an array of tiddlers matching a search regular expression
3356 TiddlyWiki.prototype.search = function(searchRegExp,sortField,excludeTag)
3357 {
3358 var candidates = this.reverseLookup("tags",excludeTag,false);
3359 var results = [];
3360 for(var t=0; t<candidates.length; t++)
3361 {
3362 if((candidates[t].title.search(searchRegExp) != -1) || (candidates[t].text.search(searchRegExp) != -1))
3363 results.push(candidates[t]);
3364 }
3365 if(!sortField)
3366 sortField = "title";
3367 results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
3368 return results;
3369 }
3370
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()
3373 {
3374 var results = [];
3375 this.forEachTiddler(function(title,tiddler) {
3376 for(var g=0; g<tiddler.tags.length; g++)
3377 {
3378 var tag = tiddler.tags[g];
3379 var f = false;
3380 for(var c=0; c<results.length; c++)
3381 if(results[c][0] == tag)
3382 {
3383 f = true;
3384 results[c][1]++;
3385 }
3386 if(!f)
3387 results.push([tag,1]);
3388 }
3389 });
3390 results.sort(function(a,b) {return a[0].toLowerCase() < b[0].toLowerCase() ? -1 : (a[0].toLowerCase() == b[0].toLowerCase() ? 0 : +1);});
3391 return results;
3392 }
3393
3394 // Return an array of the tiddlers that are tagged with a given tag
3395 TiddlyWiki.prototype.getTaggedTiddlers = function(tag,sortField)
3396 {
3397 return this.reverseLookup("tags",tag,true,sortField);
3398 }
3399
3400 // Return an array of the tiddlers that link to a given tiddler
3401 TiddlyWiki.prototype.getReferringTiddlers = function(title,unusedParameter,sortField)
3402 {
3403 if(!this.tiddlersUpdated)
3404 this.updateTiddlers();
3405 return this.reverseLookup("links",title,true,sortField);
3406 }
3407
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)
3411 {
3412 var results = [];
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)
3417 f = lookupMatch;
3418 if(f)
3419 results.push(tiddler);
3420 });
3421 if(!sortField)
3422 sortField = "title";
3423 results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
3424 return results;
3425 }
3426
3427 // Return the tiddlers as a sorted array
3428 TiddlyWiki.prototype.getTiddlers = function(field,excludeTag)
3429 {
3430 var results = [];
3431 this.forEachTiddler(function(title,tiddler) {
3432 if(excludeTag == undefined || !tiddler.isTagged(excludeTag))
3433 results.push(tiddler);
3434 });
3435 if(field)
3436 results.sort(function(a,b) {return a[field] < b[field] ? -1 : (a[field] == b[field] ? 0 : +1);});
3437 return results;
3438 }
3439
3440 // Return array of names of tiddlers that are referred to but not defined
3441 TiddlyWiki.prototype.getMissingLinks = function(sortField)
3442 {
3443 if(!this.tiddlersUpdated)
3444 this.updateTiddlers();
3445 var results = [];
3446 this.forEachTiddler(function (title,tiddler) {
3447 for(var n=0; n<tiddler.links.length;n++)
3448 {
3449 var link = tiddler.links[n];
3450 if(this.fetchTiddler(link) == null && !this.isShadowTiddler(link))
3451 results.pushUnique(link);
3452 }
3453 });
3454 results.sort();
3455 return results;
3456 }
3457
3458 // Return an array of names of tiddlers that are defined but not referred to
3459 TiddlyWiki.prototype.getOrphans = function()
3460 {
3461 var results = [];
3462 this.forEachTiddler(function (title,tiddler) {
3463 if(this.getReferringTiddlers(title).length == 0 && !tiddler.isTagged("excludeLists"))
3464 results.push(title);
3465 });
3466 results.sort();
3467 return results;
3468 }
3469
3470 // Return an array of names of all the shadow tiddlers
3471 TiddlyWiki.prototype.getShadowed = function()
3472 {
3473 var results = [];
3474 for(var t in config.shadowTiddlers)
3475 if(typeof config.shadowTiddlers[t] == "string")
3476 results.push(t);
3477 results.sort();
3478 return results;
3479 }
3480
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)
3483 {
3484 var t = (typeof tiddler == 'string') ? this.getTiddler(tiddler) : tiddler;
3485 return t instanceof Tiddler ? t : null;
3486 }
3487
3488 TiddlyWiki.prototype.getLoader = function()
3489 {
3490 if (!this.loader)
3491 this.loader = new TW21Loader();
3492 return this.loader;
3493 }
3494
3495 TiddlyWiki.prototype.getSaver = function()
3496 {
3497 if (!this.saver)
3498 this.saver = new TW21Saver();
3499 return this.saver;
3500 }
3501
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);
3507 }
3508
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]);
3513 }
3514
3515 function StringFieldAccess(n, readOnly) {
3516 this.set = 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];};
3520 }
3521
3522 function DateFieldAccess(n) {
3523 this.set = function(t,v) {
3524 var d = v instanceof Date ? v : Date.convertFromYYYYMMDDHHMM(v);
3525 if (d != t[n]) {
3526 t[n] = d; return true;
3527 }
3528 };
3529 this.get = function(t) {return t[n].convertToYYYYMMDDHHMM();}
3530 }
3531
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;
3537 }
3538 };
3539 this.get = function(t) {return String.encodeTiddlyLinkList(t[n]);}
3540 }
3541
3542 TiddlyWiki.standardFieldAccess = {
3543 // The set functions return true when setting the data has changed the value.
3544
3545 "title": new StringFieldAccess("title", true),
3546 // Handle the "tiddler" field name as the title
3547 "tiddler": new StringFieldAccess("title", true),
3548
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")
3554 };
3555
3556 TiddlyWiki.isStandardField = function(name) {
3557 return TiddlyWiki.standardFieldAccess[name] != undefined;
3558 }
3559
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);
3568 if (!t)
3569 return;
3570
3571 fieldName = fieldName.toLowerCase();
3572
3573 var isRemove = (value === undefined) || (value === null);
3574
3575 if (!t.fields)
3576 t.fields = {};
3577
3578 var accessor = TiddlyWiki.standardFieldAccess[fieldName];
3579 if (accessor) {
3580 if (isRemove)
3581 // don't remove StandardFields
3582 return;
3583 var h = TiddlyWiki.standardFieldAccess[fieldName];
3584 if (!h.set(t, value))
3585 return;
3586
3587 } else {
3588 var oldValue = t.fields[fieldName];
3589
3590 if (isRemove) {
3591 if (oldValue !== undefined) {
3592 // deletes a single field
3593 delete t.fields[fieldName];
3594 } else {
3595 // no concrete value is defined for the fieldName
3596 // so we guess this is a namespace path.
3597
3598 // delete all fields in a namespace
3599 var re = new RegExp('^'+fieldName+'\\.');
3600 var dirty = false;
3601 for (var n in t.fields) {
3602 if (n.match(re)) {
3603 delete t.fields[n];
3604 dirty = true;
3605 }
3606 }
3607 if (!dirty)
3608 return
3609 }
3610
3611 } else {
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()
3616 : String(value);
3617 if (oldValue == value)
3618 return;
3619 t.fields[fieldName] = value;
3620 }
3621 }
3622
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);
3627 }
3628
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);
3634 if (!t)
3635 return undefined;
3636
3637 fieldName = fieldName.toLowerCase();
3638
3639 var accessor = TiddlyWiki.standardFieldAccess[fieldName];
3640 if (accessor) {
3641 return accessor.get(t);
3642 }
3643
3644 return t.fields ? t.fields[fieldName] : undefined;
3645 }
3646
3647 // Calls the callback function for every field in the tiddler.
3648 //
3649 // When callback function returns a non-false value the iteration stops
3650 // and that value is returned.
3651 //
3652 // The order of the fields is not defined.
3653 //
3654 // @param callback a function(tiddler, fieldName, value).
3655 //
3656 TiddlyWiki.prototype.forEachField = function(tiddler, callback, onlyExtendedFields) {
3657 var t = this.resolveTiddler(tiddler);
3658 if (!t)
3659 return undefined;
3660
3661 if (t.fields) {
3662 for (var n in t.fields) {
3663 var result = callback(t, n, t.fields[n]);
3664 if (result)
3665 return result;
3666 }
3667 }
3668
3669 if (onlyExtendedFields)
3670 return undefined;
3671
3672 for (var n in TiddlyWiki.standardFieldAccess) {
3673 if (n == "tiddler")
3674 // even though the "title" field can also be referenced through the name "tiddler"
3675 // we only visit this field once.
3676 continue;
3677
3678 var result = callback(t, n, TiddlyWiki.standardFieldAccess[n].get(t));
3679 if (result)
3680 return result;
3681 }
3682
3683 return undefined;
3684 };
3685 // ---------------------------------------------------------------------------------
3686 // Story functions
3687 // ---------------------------------------------------------------------------------
3688
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)
3693 {
3694 this.container = container;
3695 this.idPrefix = idPrefix;
3696 this.highlightRegExp = null;
3697 }
3698
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)
3704 {
3705 var place = document.getElementById(this.container);
3706 if(!place)
3707 return;
3708 var e = place.firstChild;
3709 while(e)
3710 {
3711 var n = e.nextSibling;
3712 var title = e.getAttribute("tiddler");
3713 fn.call(this,title,e);
3714 e = n;
3715 }
3716 }
3717
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)
3721 {
3722 for(var t = titles.length-1;t>=0;t--)
3723 this.displayTiddler(srcElement,titles[t],template,animate,slowly);
3724 }
3725
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)
3737 {
3738 var place = document.getElementById(this.container);
3739 var tiddlerElem = document.getElementById(this.idPrefix + title);
3740 if(tiddlerElem)
3741 this.refreshTiddler(title,template);
3742 else
3743 {
3744 var before = this.positionTiddler(srcElement);
3745 tiddlerElem = this.createTiddler(place,before,title,template);
3746 }
3747 if(srcElement && typeof srcElement !== "string")
3748 {
3749 if(anim && config.options.chkAnimate && (animate == undefined || animate == true))
3750 anim.startAnimating(new Cascade(title,srcElement,tiddlerElem,slowly),new Scroller(tiddlerElem,slowly));
3751 else
3752 window.scrollTo(0,ensureVisible(tiddlerElem));
3753 }
3754 }
3755
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)
3761 {
3762 var place = document.getElementById(this.container);
3763 var before;
3764 if(typeof srcElement == "string")
3765 {
3766 switch(srcElement)
3767 {
3768 case "top":
3769 before = place.firstChild;
3770 break;
3771 case "bottom":
3772 before = null;
3773 break;
3774 }
3775 }
3776 else
3777 {
3778 var after = this.findContainingTiddler(srcElement);
3779 if(after == null)
3780 before = place.firstChild;
3781 else if(after.nextSibling)
3782 before = after.nextSibling;
3783 else
3784 before = null;
3785 }
3786 return before;
3787 }
3788
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)
3795 {
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);
3800 return tiddlerElem;
3801 }
3802
3803 // Overridable for choosing the name of the template to apply for a tiddler
3804 Story.prototype.chooseTemplateForTiddler = function(title,template)
3805 {
3806 if(!template)
3807 template = DEFAULT_VIEW_TEMPLATE;
3808 if(template == DEFAULT_VIEW_TEMPLATE || template == DEFAULT_EDIT_TEMPLATE)
3809 template = config.tiddlerTemplates[template];
3810 return template;
3811 }
3812
3813 // Overridable for extracting the text of a template from a tiddler
3814 Story.prototype.getTemplateForTiddler = function(title,template,tiddler)
3815 {
3816 return store.getRecursiveTiddlerText(template,null,10);
3817 }
3818
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)
3824 {
3825 var tiddlerElem = document.getElementById(this.idPrefix + title);
3826 if(tiddlerElem)
3827 {
3828 if(tiddlerElem.getAttribute("dirty") == "true" && !force)
3829 return tiddlerElem;
3830 template = this.chooseTemplateForTiddler(title,template);
3831 var currTemplate = tiddlerElem.getAttribute("template");
3832 if((template != currTemplate) || force)
3833 {
3834 var tiddler = store.getTiddler(title);
3835 if(!tiddler)
3836 {
3837 tiddler = new Tiddler();
3838 if(store.isShadowTiddler(title))
3839 tiddler.set(title,store.getTiddlerText(title),config.views.wikified.shadowModifier,version.date,[],version.date);
3840 else
3841 {
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);
3846 }
3847 }
3848 tiddlerElem.setAttribute("tags",tiddler.tags.join(" "));
3849 tiddlerElem.setAttribute("tiddler",title);
3850 tiddlerElem.setAttribute("template",template);
3851 var me = this;
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");
3861 else
3862 removeClass(tiddlerElem,"isTag");
3863 if(!store.tiddlerExists(title))
3864 {
3865 if(store.isShadowTiddler(title))
3866 addClass(tiddlerElem,"shadow");
3867 else
3868 addClass(tiddlerElem,"missing");
3869 }
3870 else
3871 {
3872 removeClass(tiddlerElem,"shadow");
3873 removeClass(tiddlerElem,"missing");
3874 }
3875 }
3876 }
3877 return tiddlerElem;
3878 }
3879
3880 // Refresh all tiddlers in the Story
3881 Story.prototype.refreshAllTiddlers = function()
3882 {
3883 var place = document.getElementById(this.container);
3884 var e = place.firstChild;
3885 if(!e)
3886 return;
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);
3890 }
3891
3892 // Default tiddler onmouseover/out event handlers
3893 Story.prototype.onTiddlerMouseOver = function(e)
3894 {
3895 if(window.addClass instanceof Function)
3896 addClass(this,"selected");
3897 }
3898
3899 Story.prototype.onTiddlerMouseOut = function(e)
3900 {
3901 if(window.removeClass instanceof Function)
3902 removeClass(this,"selected");
3903 }
3904
3905 // Default tiddler ondblclick event handler
3906 Story.prototype.onTiddlerDblClick = function(e)
3907 {
3908 if(!e) var e = window.event;
3909 var theTarget = resolveTarget(e);
3910 if(theTarget && theTarget.nodeName.toLowerCase() != "input" && theTarget.nodeName.toLowerCase() != "textarea")
3911 {
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();
3917 return true;
3918 }
3919 else
3920 return false;
3921 }
3922
3923 Story.prototype.onTiddlerKeyPress = function(e)
3924 {
3925 if(!e) var e = window.event;
3926 clearMessage();
3927 var consume = false;
3928 var title = this.getAttribute("tiddler");
3929 var target = resolveTarget(e);
3930 switch(e.keyCode)
3931 {
3932 case 9: // Tab
3933 if(config.options.chkInsertTabs && target.tagName.toLowerCase() == "textarea")
3934 {
3935 replaceSelection(target,String.fromCharCode(9));
3936 consume = true;
3937 }
3938 if(config.isOpera)
3939 {
3940 target.onblur = function()
3941 {
3942 this.focus();
3943 this.onblur = null;
3944 }
3945 }
3946 break;
3947 case 13: // Ctrl-Enter
3948 case 10: // Ctrl-Enter on IE PC
3949 case 77: // Ctrl-Enter is "M" on some platforms
3950 if(e.ctrlKey)
3951 {
3952 blurElement(this);
3953 config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
3954 consume = true;
3955 }
3956 break;
3957 case 27: // Escape
3958 blurElement(this);
3959 config.macros.toolbar.invokeCommand(this,"cancelCommand",e);
3960 consume = true;
3961 break;
3962 }
3963 e.cancelBubble = consume;
3964 if(consume)
3965 {
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
3969 }
3970 return(!consume);
3971 };
3972
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)
3976 {
3977 var tiddlerElem = document.getElementById(this.idPrefix + title);
3978 var e = null;
3979 if(tiddlerElem != null)
3980 {
3981 var children = tiddlerElem.getElementsByTagName("*");
3982 for (var t=0; t<children.length; t++)
3983 {
3984 var c = children[t];
3985 if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
3986 {
3987 if(!e)
3988 e = c;
3989 if(c.getAttribute("edit") == field)
3990 e = c;
3991 }
3992 }
3993 }
3994 return e;
3995 }
3996
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)
3999 {
4000 var e = this.getTiddlerField(title,field);
4001 if(e)
4002 {
4003 e.focus();
4004 e.select();
4005 }
4006 }
4007
4008 // Ensures that a specified tiddler does not have the focus
4009 Story.prototype.blurTiddler = function(title)
4010 {
4011 var tiddlerElem = document.getElementById(this.idPrefix + title);
4012 if(tiddlerElem != null && tiddlerElem.focus && tiddlerElem.blur)
4013 {
4014 tiddlerElem.focus();
4015 tiddlerElem.blur();
4016 }
4017 }
4018
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)
4026 {
4027 var c = story.getTiddlerField(title,field);
4028
4029 var tags = c.value.readBracketedList();
4030 tags.setItem(tag,mode);
4031 c.value = String.encodeTiddlyLinkList(tags);
4032 }
4033
4034 // The same as setTiddlerField but preset to the "tags" field
4035 Story.prototype.setTiddlerTag = function(title,tag,mode)
4036 {
4037 Story.prototype.setTiddlerField(title,tag,mode,"tags");
4038 }
4039
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)
4045 {
4046 var tiddlerElem = document.getElementById(this.idPrefix + title);
4047 if(tiddlerElem != null)
4048 {
4049 clearMessage();
4050 this.scrubTiddler(tiddlerElem);
4051 if(anim && config.options.chkAnimate && animate)
4052 anim.startAnimating(new Slider(tiddlerElem,false,slowly,"all"));
4053 else
4054 tiddlerElem.parentNode.removeChild(tiddlerElem);
4055 }
4056 }
4057
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)
4062 {
4063 tiddlerElem.id = null;
4064 }
4065
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)
4070 {
4071 var tiddlerElem = document.getElementById(this.idPrefix + title);
4072 if(tiddlerElem != null)
4073 tiddlerElem.setAttribute("dirty",dirty ? "true" : "false");
4074 }
4075
4076 // Is a particular tiddler dirty (with unsaved changes)?
4077 Story.prototype.isDirty = function(title)
4078 {
4079 var tiddlerElem = document.getElementById(this.idPrefix + title);
4080 if(tiddlerElem != null)
4081 return tiddlerElem.getAttribute("dirty") == "true";
4082 return null;
4083 }
4084
4085 // Determine whether any open tiddler are dirty
4086 Story.prototype.areAnyDirty = function()
4087 {
4088 var r = false;
4089 this.forEachTiddler(function(title,element) {
4090 if(this.isDirty(title))
4091 r = true;
4092 });
4093 return r;
4094 }
4095
4096 // Close all tiddlers in the story
4097 Story.prototype.closeAllTiddlers = function(exclude)
4098 {
4099 clearMessage();
4100 this.forEachTiddler(function(title,element) {
4101 if((title != exclude) && element.getAttribute("dirty") != "true")
4102 this.closeTiddler(title);
4103 });
4104 window.scrollTo(0,0);
4105 }
4106
4107 // Check if there are any tiddlers in the story
4108 Story.prototype.isEmpty = function()
4109 {
4110 var place = document.getElementById(this.container);
4111 return(place && place.firstChild == null);
4112 }
4113
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)
4119 {
4120 this.closeAllTiddlers();
4121 highlightHack = new RegExp(useRegExp ? text : text.escapeRegExp(),useCaseSensitive ? "mg" : "img");
4122 var matches = store.search(highlightHack,"title","excludeSearch");
4123 var titles = [];
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]));
4131 else
4132 displayMessage(config.macros.search.failureMsg.format([q + text + q]));
4133 }
4134
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)
4139 {
4140 while(e && !hasClass(e,"tiddler"))
4141 e = e.parentNode;
4142 return(e);
4143 }
4144
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)
4149 {
4150 if(e && e.getAttribute)
4151 {
4152 var f = e.getAttribute("edit");
4153 if(f)
4154 fields[f] = e.value.replace(/\r/mg,"");;
4155 if(e.hasChildNodes())
4156 {
4157 var c = e.childNodes;
4158 for(var t=0; t<c.length; t++)
4159 this.gatherSaveFields(c[t],fields)
4160 }
4161 }
4162 }
4163
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)
4167 {
4168 var e = document.getElementById(this.idPrefix + title);
4169 if(e != null)
4170 {
4171 var fields = {};
4172 this.gatherSaveFields(e,fields);
4173 var tiddler = store.fetchTiddler(title);
4174 if (!tiddler)
4175 return false;
4176 for(var n in fields)
4177 if (store.getValue(title,n) != fields[n])
4178 return true;
4179 }
4180 return false;
4181 }
4182
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)
4188 {
4189 var tiddlerElem = document.getElementById(this.idPrefix + title);
4190 if(tiddlerElem != null)
4191 {
4192 var fields = {};
4193 this.gatherSaveFields(tiddlerElem,fields);
4194 var newTitle = fields.title ? fields.title : title;
4195 if(store.tiddlerExists(newTitle) && newTitle != title)
4196 {
4197 if(confirm(config.messages.overwriteWarning.format([newTitle.toString()])))
4198 this.closeTiddler(newTitle,false,false);
4199 else
4200 return null;
4201 }
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)
4214 saveChanges();
4215 return newTitle;
4216 }
4217 return null;
4218 }
4219
4220 Story.prototype.permaView = function()
4221 {
4222 var links = [];
4223 this.forEachTiddler(function(title,element) {
4224 links.push(String.encodeTiddlyLink(title));
4225 });
4226 var t = encodeURIComponent(links.join(" "));
4227 if(t == "")
4228 t = "#";
4229 if(window.location.hash != t)
4230 window.location.hash = t;
4231 }
4232
4233 // ---------------------------------------------------------------------------------
4234 // Message area
4235 // ---------------------------------------------------------------------------------
4236
4237 function getMessageDiv()
4238 {
4239 var msgArea = document.getElementById("messageArea");
4240 if(!msgArea)
4241 return null;
4242 if(!msgArea.hasChildNodes())
4243 createTiddlyButton(createTiddlyElement(msgArea,"div",null,"messageToolbar"),
4244 config.messages.messageClose.text,
4245 config.messages.messageClose.tooltip,
4246 clearMessage);
4247 msgArea.style.display = "block";
4248 return createTiddlyElement(msgArea,"div");
4249 }
4250
4251 function displayMessage(text,linkText)
4252 {
4253 var e = getMessageDiv();
4254 if(!e)
4255 {
4256 alert(text);
4257 return;
4258 }
4259 if(linkText)
4260 {
4261 var link = createTiddlyElement(e,"a",null,null,text);
4262 link.href = linkText;
4263 link.target = "_blank";
4264 }
4265 else
4266 e.appendChild(document.createTextNode(text));
4267 }
4268
4269 function clearMessage()
4270 {
4271 var msgArea = document.getElementById("messageArea");
4272 if(msgArea)
4273 {
4274 removeChildren(msgArea);
4275 msgArea.style.display = "none";
4276 }
4277 return false;
4278 }
4279
4280 // ---------------------------------------------------------------------------------
4281 // Refresh mechanism
4282 // ---------------------------------------------------------------------------------
4283
4284 config.refreshers = {
4285 link: function(e,changeList)
4286 {
4287 var title = e.getAttribute("tiddlyLink");
4288 refreshTiddlyLink(e,title);
4289 return true;
4290 },
4291
4292 tiddler: function(e,changeList)
4293 {
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);
4298 else
4299 refreshElements(e,changeList);
4300 return true;
4301 },
4302
4303 content: function(e,changeList)
4304 {
4305 var title = e.getAttribute("tiddler");
4306 var force = e.getAttribute("force");
4307 if(force != null || changeList == null || changeList.indexOf(title) != -1)
4308 {
4309 removeChildren(e);
4310 wikify(store.getTiddlerText(title,title),e);
4311 return true;
4312 }
4313 else
4314 return false;
4315 },
4316
4317 macro: function(e,changeList)
4318 {
4319 var macro = e.getAttribute("macroName");
4320 var params = e.getAttribute("params");
4321 if(macro)
4322 macro = config.macros[macro];
4323 if(macro && macro.refresh)
4324 macro.refresh(e,params);
4325 return true;
4326 }
4327 };
4328
4329 function refreshElements(root,changeList)
4330 {
4331 var nodes = root.childNodes;
4332 for(var c=0; c<nodes.length; c++)
4333 {
4334 var e = nodes[c],type;
4335 if(e.getAttribute)
4336 type = e.getAttribute("refresh");
4337 else
4338 type = null;
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);
4345 }
4346 }
4347
4348 function applyHtmlMacros(root,tiddler)
4349 {
4350 var e = root.firstChild;
4351 while(e)
4352 {
4353 var nextChild = e.nextSibling;
4354 if(e.getAttribute)
4355 {
4356 var macro = e.getAttribute("macro");
4357 if(macro)
4358 {
4359 var params = "";
4360 var p = macro.indexOf(" ");
4361 if(p != -1)
4362 {
4363 params = macro.substr(p+1);
4364 macro = macro.substr(0,p);
4365 }
4366 invokeMacro(e,macro,params,null,tiddler);
4367 }
4368 }
4369 if(e.hasChildNodes())
4370 applyHtmlMacros(e,tiddler);
4371 e = nextChild;
4372 }
4373 }
4374
4375 function refreshPageTemplate(title)
4376 {
4377 var stash = createTiddlyElement(document.body,"div");
4378 stash.style.display = "none";
4379 var display = document.getElementById("tiddlerDisplay");
4380 var nodes,t;
4381 if(display)
4382 {
4383 nodes = display.childNodes;
4384 for(t=nodes.length-1; t>=0; t--)
4385 stash.appendChild(nodes[t]);
4386 }
4387 var wrapper = document.getElementById("contentWrapper");
4388 if(!title)
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);
4396 if(!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);
4402 }
4403
4404 function refreshDisplay(hint)
4405 {
4406 var e = document.getElementById("contentWrapper");
4407 if(typeof hint == "string")
4408 hint = [hint];
4409 refreshElements(e,hint);
4410 }
4411
4412 function refreshPageTitle()
4413 {
4414 document.title = wikifyPlain("SiteTitle") + " - " + wikifyPlain("SiteSubtitle");
4415 }
4416
4417 function refreshStyles(title)
4418 {
4419 setStylesheet(title == null ? "" : store.getRecursiveTiddlerText(title,"",10),title);
4420 }
4421
4422 function refreshColorPalette(title)
4423 {
4424 if(!startingUp)
4425 refreshAll();
4426 }
4427
4428 function refreshAll()
4429 {
4430 refreshPageTemplate();
4431 refreshDisplay();
4432 refreshStyles("StyleSheetLayout");
4433 refreshStyles("StyleSheetColors");
4434 refreshStyles("StyleSheet");
4435 refreshStyles("StyleSheetPrint");
4436 }
4437
4438 // ---------------------------------------------------------------------------------
4439 // Options cookie stuff
4440 // ---------------------------------------------------------------------------------
4441
4442 function loadOptionsCookie()
4443 {
4444 if(safeMode)
4445 return;
4446 var cookies = document.cookie.split(";");
4447 for(var c=0; c<cookies.length; c++)
4448 {
4449 var p = cookies[c].indexOf("=");
4450 if(p != -1)
4451 {
4452 var name = cookies[c].substr(0,p).trim();
4453 var value = cookies[c].substr(p+1).trim();
4454 switch(name.substr(0,3))
4455 {
4456 case "txt":
4457 config.options[name] = unescape(value);
4458 break;
4459 case "chk":
4460 config.options[name] = value == "true";
4461 break;
4462 }
4463 }
4464 }
4465 }
4466
4467 function saveOptionCookie(name)
4468 {
4469 if(safeMode)
4470 return;
4471 var c = name + "=";
4472 switch(name.substr(0,3))
4473 {
4474 case "txt":
4475 c += escape(config.options[name].toString());
4476 break;
4477 case "chk":
4478 c += config.options[name] ? "true" : "false";
4479 break;
4480 }
4481 c += "; expires=Fri, 1 Jan 2038 12:00:00 UTC; path=/";
4482 document.cookie = c;
4483 }
4484
4485 // ---------------------------------------------------------------------------------
4486 // Saving
4487 // ---------------------------------------------------------------------------------
4488
4489 var saveUsingSafari = false;
4490
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>';
4493
4494 // If there are unsaved changes, force the user to confirm before exitting
4495 function confirmExit()
4496 {
4497 hadConfirmExit = true;
4498 if((store && store.isDirty && store.isDirty()) || (story && story.areAnyDirty && story.areAnyDirty()))
4499 return config.messages.confirmExit;
4500 }
4501
4502 // Give the user a chance to save changes before exitting
4503 function checkUnsavedChanges()
4504 {
4505 if(store && store.isDirty && store.isDirty() && window.hadConfirmExit === false)
4506 {
4507 if(confirm(config.messages.unsavedChangesWarning))
4508 saveChanges();
4509 }
4510 }
4511
4512 function updateMarkupBlock(s,blockName,tiddlerName)
4513 {
4514 return s.replaceChunk(
4515 "<!--%0-START-->".format([blockName]),
4516 "<!--%0-END-->".format([blockName]),
4517 "\n" + store.getRecursiveTiddlerText(tiddlerName,"") + "\n");
4518 }
4519
4520 // Save this tiddlywiki with the pending changes
4521 function saveChanges(onlyIfDirty)
4522 {
4523 if(onlyIfDirty && !store.isDirty())
4524 return;
4525 clearMessage();
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:")
4530 {
4531 alert(config.messages.notFileUrlError);
4532 if(store.tiddlerExists(config.messages.saveInstructions))
4533 story.displayTiddler(null,config.messages.saveInstructions);
4534 return;
4535 }
4536 var localPath = getLocalPath(originalPath);
4537 // Load the original file
4538 var original = loadFile(localPath);
4539 if(original == null)
4540 {
4541 alert(config.messages.cantSaveError);
4542 if(store.tiddlerExists(config.messages.saveInstructions))
4543 story.displayTiddler(null,config.messages.saveInstructions);
4544 return;
4545 }
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))
4551 {
4552 alert(config.messages.invalidFileError.format([localPath]));
4553 return;
4554 }
4555 // Save the backup
4556 if(config.options.chkSaveBackups)
4557 {
4558 var backupPath = getBackupPath(localPath);
4559 var backup = saveFile(backupPath,original);
4560 if(backup)
4561 displayMessage(config.messages.backupSaved,"file://" + backupPath);
4562 else
4563 alert(config.messages.backupFailed);
4564 }
4565 // Save Rss
4566 if(config.options.chkGenerateAnRssFeed)
4567 {
4568 var rssPath = localPath.substr(0,localPath.lastIndexOf(".")) + ".xml";
4569 var rssSave = saveFile(rssPath,convertUnicodeToUTF8(generateRss()));
4570 if(rssSave)
4571 displayMessage(config.messages.rssSaved,"file://" + rssPath);
4572 else
4573 alert(config.messages.rssFailed);
4574 }
4575 // Save empty template
4576 if(config.options.chkSaveEmptyTemplate)
4577 {
4578 var emptyPath,p;
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";
4583 else
4584 emptyPath = localPath + ".empty.html";
4585 var empty = original.substr(0,posOpeningDiv + startSaveArea.length) + original.substr(posClosingDiv);
4586 var emptySave = saveFile(emptyPath,empty);
4587 if(emptySave)
4588 displayMessage(config.messages.emptySaved,"file://" + emptyPath);
4589 else
4590 alert(config.messages.emptyFailed);
4591 }
4592 var save;
4593 try
4594 {
4595 // Save new file
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);
4606 }
4607 catch (e)
4608 {
4609 showException(e);
4610 }
4611 if(save)
4612 {
4613 displayMessage(config.messages.mainSaved,"file://" + localPath);
4614 store.setDirty(false);
4615 }
4616 else
4617 alert(config.messages.mainFailed);
4618 }
4619
4620 function getLocalPath(originalPath)
4621 {
4622 // Remove any location or query part of the URL
4623 var argPos = originalPath.indexOf("?");
4624 if(argPos != -1)
4625 originalPath = originalPath.substr(0,argPos);
4626 var hashPos = originalPath.indexOf("#");
4627 if(hashPos != -1)
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..."
4637 var localPath;
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"),"\\");
4648 return localPath;
4649 }
4650
4651 function getBackupPath(localPath)
4652 {
4653 var backSlash = true;
4654 var dirPathPos = localPath.lastIndexOf("\\");
4655 if(dirPathPos == -1)
4656 {
4657 dirPathPos = localPath.lastIndexOf("/");
4658 backSlash = false;
4659 }
4660 var backupFolder = config.options.txtBackupFolder;
4661 if(!backupFolder || backupFolder == "")
4662 backupFolder = ".";
4663 var backupPath = localPath.substr(0,dirPathPos) + (backSlash ? "\\" : "/") + backupFolder + localPath.substr(dirPathPos);
4664 backupPath = backupPath.substr(0,backupPath.lastIndexOf(".")) + "." + (new Date()).convertToYYYYMMDDHHMMSSMMM() + ".html";
4665 return backupPath;
4666 }
4667
4668 function generateRss()
4669 {
4670 var s = [];
4671 var d = new Date();
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" + ">");
4678 if(u)
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>");
4687 // The body
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));
4692 // And footer
4693 s.push("</channel>");
4694 s.push("</rss>");
4695 // Save it all
4696 return s.join("\n");
4697 }
4698
4699
4700 // UTF-8 encoding rules:
4701 // 0x0000 - 0x007F: 0xxxxxxx
4702 // 0x0080 - 0x07FF: 110xxxxx 10xxxxxx
4703 // 0x0800 - 0xFFFF: 1110xxxx 10xxxxxx 10xxxxxx
4704
4705 function convertUTF8ToUnicode(u)
4706 {
4707 if(window.netscape == undefined)
4708 return manualConvertUTF8ToUnicode(u);
4709 else
4710 return mozConvertUTF8ToUnicode(u);
4711 }
4712
4713 function manualConvertUTF8ToUnicode(utf)
4714 {
4715 var uni = utf;
4716 var src = 0;
4717 var dst = 0;
4718 var b1, b2, b3;
4719 var c;
4720 while(src < utf.length)
4721 {
4722 b1 = utf.charCodeAt(src++);
4723 if(b1 < 0x80)
4724 dst++;
4725 else if(b1 < 0xE0)
4726 {
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));
4730 }
4731 else
4732 {
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));
4737 }
4738 }
4739 return(uni);
4740 }
4741
4742 function mozConvertUTF8ToUnicode(u)
4743 {
4744 try
4745 {
4746 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
4747 var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
4748 converter.charset = "UTF-8";
4749 }
4750 catch(e)
4751 {
4752 return manualConvertUTF8ToUnicode(u);
4753 } // fallback
4754 var s = converter.ConvertToUnicode(u);
4755 var fin = converter.Finish();
4756 return (fin.length > 0) ? s+fin : s;
4757 }
4758
4759 function convertUnicodeToUTF8(s)
4760 {
4761 if(window.netscape == undefined)
4762 return manualConvertUnicodeToUTF8(s);
4763 else
4764 return mozConvertUnicodeToUTF8(s);
4765 }
4766
4767 function manualConvertUnicodeToUTF8(s)
4768 {
4769 var re = /[^\u0000-\u007F]/g ;
4770 return s.replace(re, function($0) {return("&#" + $0.charCodeAt(0).toString() + ";");})
4771 }
4772
4773 function mozConvertUnicodeToUTF8(s)
4774 {
4775 try
4776 {
4777 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
4778 var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
4779 converter.charset = "UTF-8";
4780 }
4781 catch(e)
4782 {
4783 return manualConvertUnicodeToUTF8(s);
4784 } // fallback
4785 var u = converter.ConvertFromUnicode(s);
4786 var fin = converter.Finish();
4787 if(fin.length > 0)
4788 return u + fin;
4789 else
4790 return u;
4791 }
4792
4793 function saveFile(fileUrl, content)
4794 {
4795 var r = null;
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);
4802 return(r);
4803 }
4804
4805 function loadFile(fileUrl)
4806 {
4807 var r = null;
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);
4814 return(r);
4815 }
4816
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)
4819 {
4820 try
4821 {
4822 var fso = new ActiveXObject("Scripting.FileSystemObject");
4823 }
4824 catch(e)
4825 {
4826 //alert("Exception while attempting to save\n\n" + e.toString());
4827 return(null);
4828 }
4829 var file = fso.OpenTextFile(filePath,2,-1,0);
4830 file.Write(content);
4831 file.Close();
4832 return(true);
4833 }
4834
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)
4837 {
4838 try
4839 {
4840 var fso = new ActiveXObject("Scripting.FileSystemObject");
4841 var file = fso.OpenTextFile(filePath,1);
4842 var content = file.ReadAll();
4843 file.Close();
4844 }
4845 catch(e)
4846 {
4847 //alert("Exception while attempting to load\n\n" + e.toString());
4848 return(null);
4849 }
4850 return(content);
4851 }
4852
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)
4855 {
4856 if(window.Components)
4857 try
4858 {
4859 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
4860 var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
4861 file.initWithPath(filePath);
4862 if (!file.exists())
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);
4867 out.flush();
4868 out.close();
4869 return(true);
4870 }
4871 catch(e)
4872 {
4873 //alert("Exception while attempting to save\n\n" + e);
4874 return(false);
4875 }
4876 return(null);
4877 }
4878
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)
4881 {
4882 if(window.Components)
4883 try
4884 {
4885 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
4886 var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
4887 file.initWithPath(filePath);
4888 if (!file.exists())
4889 return(null);
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()));
4895 }
4896 catch(e)
4897 {
4898 //alert("Exception while attempting to load\n\n" + e);
4899 return(false);
4900 }
4901 return(null);
4902 }
4903
4904 function javaUrlToFilename(url)
4905 {
4906 var f = "//localhost";
4907 if(url.indexOf(f) == 0)
4908 return url.substring(f.length);
4909 var i = url.indexOf(":");
4910 if(i > 0)
4911 return url.substring(i-1);
4912 return url;
4913 }
4914
4915 function javaSaveFile(filePath, content)
4916 {
4917 try
4918 {
4919 if(document.applets["TiddlySaver"])
4920 return document.applets["TiddlySaver"].saveFile(javaUrlToFilename(filePath),"UTF-8",content);
4921 }
4922 catch(e)
4923 {
4924 }
4925 try
4926 {
4927 var s = new java.io.PrintStream(new java.io.FileOutputStream(javaUrlToFilename(filePath)));
4928 s.print(content);
4929 s.close();
4930 }
4931 catch(e)
4932 {
4933 return null;
4934 }
4935 return true;
4936 }
4937
4938 function javaLoadFile(filePath)
4939 {
4940 try
4941 {
4942 if(document.applets["TiddlySaver"])
4943 return String(document.applets["TiddlySaver"].loadFile(javaUrlToFilename(filePath),"UTF-8"));
4944 }
4945 catch(e)
4946 {
4947 }
4948 var content = [];
4949 try
4950 {
4951 var r = new java.io.BufferedReader(new java.io.FileReader(javaUrlToFilename(filePath)));
4952 var line;
4953 while ((line = r.readLine()) != null)
4954 content.push(new String(line));
4955 r.close();
4956 }
4957 catch(e)
4958 {
4959 return null;
4960 }
4961 return content.join("\n");
4962 }
4963
4964
4965 // ---------------------------------------------------------------------------------
4966 // Remote HTTP requests
4967 // ---------------------------------------------------------------------------------
4968
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)
4981 {
4982 // Get an xhr object
4983 var x;
4984 try
4985 {
4986 x = new XMLHttpRequest(); // Modern
4987 }
4988 catch(e)
4989 {
4990 try
4991 {
4992 x = new ActiveXObject("Msxml2.XMLHTTP"); // IE 6
4993 }
4994 catch (e)
4995 {
4996 return null;
4997 }
4998 }
4999 // Install callback
5000 x.onreadystatechange = function()
5001 {
5002 if (x.readyState == 4)
5003 {
5004 if ((x.status == 0 || x.status == 200) && callback)
5005 {
5006 callback(true,params,x.responseText,url,x);
5007 }
5008 else
5009 callback(false,params,null,url,x);
5010 }
5011 }
5012 // Send request
5013 if(window.netscape && window.netscape.security && document.location.protocol.indexOf("http") == -1)
5014 window.netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
5015 try
5016 {
5017 url = url + (url.indexOf("?") < 0 ? "?" : "&") + "nocache=" + Math.random();
5018 x.open("GET",url,true);
5019 if (x.overrideMimeType)
5020 x.overrideMimeType("text/html");
5021 x.send(null);
5022 }
5023 catch (e)
5024 {
5025 alert("Error in send " + e);
5026 return null;
5027 }
5028 return x;
5029 }
5030 // ---------------------------------------------------------------------------------
5031 // TiddlyWiki-specific utility functions
5032 // ---------------------------------------------------------------------------------
5033
5034 function createTiddlyButton(theParent,theText,theTooltip,theAction,theClass,theId,theAccessKey)
5035 {
5036 var theButton = document.createElement("a");
5037 if(theAction)
5038 {
5039 theButton.onclick = theAction;
5040 theButton.setAttribute("href","javascript:;");
5041 }
5042 if(theTooltip)
5043 theButton.setAttribute("title",theTooltip);
5044 if(theText)
5045 theButton.appendChild(document.createTextNode(theText));
5046 if(theClass)
5047 theButton.className = theClass;
5048 else
5049 theButton.className = "button";
5050 if(theId)
5051 theButton.id = theId;
5052 if(theParent)
5053 theParent.appendChild(theButton);
5054 if(theAccessKey)
5055 theButton.setAttribute("accessKey",theAccessKey);
5056 return(theButton);
5057 }
5058
5059 function createTiddlyLink(place,title,includeText,theClass,isStatic)
5060 {
5061 var text = includeText ? title : null;
5062 var i = getTiddlyLinkInfo(title,theClass)
5063 var btn;
5064 if(isStatic)
5065 btn = createExternalLink(place,"#" + title);
5066 else
5067 btn = createTiddlyButton(place,text,i.subTitle,onClickTiddlerLink,i.classes);
5068 btn.setAttribute("refresh","link");
5069 btn.setAttribute("tiddlyLink",title);
5070 return(btn);
5071 }
5072
5073 function refreshTiddlyLink(e,title)
5074 {
5075 var i = getTiddlyLinkInfo(title,e.className);
5076 e.className = i.classes;
5077 e.title = i.subTitle;
5078 }
5079
5080 function getTiddlyLinkInfo(title,currClasses)
5081 {
5082 var classes = currClasses ? currClasses.split(" ") : [];
5083 classes.pushUnique("tiddlyLink");
5084 var tiddler = store.fetchTiddler(title);
5085 var subTitle;
5086 if(tiddler)
5087 {
5088 subTitle = tiddler.getSubtitle();
5089 classes.pushUnique("tiddlyLinkExisting");
5090 classes.remove("tiddlyLinkNonExisting");
5091 classes.remove("shadow");
5092 }
5093 else
5094 {
5095 classes.remove("tiddlyLinkExisting");
5096 classes.pushUnique("tiddlyLinkNonExisting");
5097 if(store.isShadowTiddler(title))
5098 {
5099 subTitle = config.messages.shadowedTiddlerToolTip.format([title]);
5100 classes.pushUnique("shadow");
5101 }
5102 else
5103 {
5104 subTitle = config.messages.undefinedTiddlerToolTip.format([title]);
5105 classes.remove("shadow");
5106 }
5107 }
5108 return {classes: classes.join(" "), subTitle: subTitle};
5109 }
5110
5111 function createExternalLink(place,url)
5112 {
5113 var theLink = document.createElement("a");
5114 theLink.className = "externalLink";
5115 theLink.href = url;
5116 theLink.title = config.messages.externalLinkTooltip.format([url]);
5117 if(config.options.chkOpenInNewWindow)
5118 theLink.target = "_blank";
5119 place.appendChild(theLink);
5120 return(theLink);
5121 }
5122
5123 // Event handler for clicking on a tiddly link
5124 function onClickTiddlerLink(e)
5125 {
5126 if (!e) var e = window.event;
5127 var theTarget = resolveTarget(e);
5128 var theLink = theTarget;
5129 var title = null;
5130 do {
5131 title = theLink.getAttribute("tiddlyLink");
5132 theLink = theLink.parentNode;
5133 } while(title == null && theLink != null);
5134 if(title)
5135 {
5136 var toggling = e.metaKey || e.ctrlKey;
5137 if(config.options.chkToggleLinks)
5138 toggling = !toggling;
5139 var opening;
5140 if(toggling && document.getElementById("tiddler" + title))
5141 story.closeTiddler(title,true,e.shiftKey || e.altKey);
5142 else
5143 story.displayTiddler(theTarget,title,null,true,e.shiftKey || e.altKey);
5144 }
5145 clearMessage();
5146 return(false);
5147 }
5148
5149 // Create a button for a tag with a popup listing all the tiddlers that it tags
5150 function createTagButton(place,tag,excludeTiddler)
5151 {
5152 var theTag = createTiddlyButton(place,tag,config.views.wikified.tag.tooltip.format([tag]),onClickTag);
5153 theTag.setAttribute("tag",tag);
5154 if(excludeTiddler)
5155 theTag.setAttribute("tiddler",excludeTiddler);
5156 return(theTag);
5157 }
5158
5159 // Event handler for clicking on a tiddler tag
5160 function onClickTag(e)
5161 {
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");
5167 if(popup && tag)
5168 {
5169 var tagged = store.getTaggedTiddlers(tag);
5170 var titles = [];
5171 var li,r;
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)
5177 {
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++)
5182 {
5183 createTiddlyLink(createTiddlyElement(popup,"li"),titles[r],true);
5184 }
5185 }
5186 else
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]));
5191 }
5192 Popup.show(popup,false);
5193 e.cancelBubble = true;
5194 if (e.stopPropagation) e.stopPropagation();
5195 return(false);
5196 }
5197
5198 // Event handler for 'open all' on a tiddler popup
5199 function onClickTagOpenAll(e)
5200 {
5201 if (!e) var e = window.event;
5202 var tag = this.getAttribute("tag");
5203 var tagged = store.getTaggedTiddlers(tag);
5204 var titles = [];
5205 for(var t=0; t<tagged.length; t++)
5206 titles.push(tagged[t].title);
5207 story.displayTiddlers(this,titles);
5208 return(false);
5209 }
5210
5211 function onClickError(e)
5212 {
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();
5221 return false;
5222 }
5223
5224 function createTiddlyDropDown(place,onchange,options)
5225 {
5226 var sel = createTiddlyElement(place,"select");
5227 sel.onchange = onchange;
5228 for(var t=0; t<options.length; t++)
5229 {
5230 var e = createTiddlyElement(sel,"option",null,null,options[t].caption);
5231 e.value = options[t].name;
5232 }
5233 }
5234
5235 function createTiddlyError(place,title,text)
5236 {
5237 var btn = createTiddlyButton(place,title,null,onClickError,"errorButton");
5238 if (text) btn.setAttribute("errorText",text);
5239 }
5240
5241 function merge(dst,src,preserveExisting)
5242 {
5243 for (p in src)
5244 if (!preserveExisting || dst[p] === undefined)
5245 dst[p] = src[p];
5246 return dst;
5247 }
5248
5249 // Returns a string containing the description of an exception, optionally prepended by a message
5250 function exceptionText(e, message)
5251 {
5252 var s = e.description ? e.description : e.toString();
5253 return message ? "%0:\n%1".format([message, s]) : s;
5254 }
5255
5256 // Displays an alert of an exception description with optional message
5257 function showException(e, message)
5258 {
5259 alert(exceptionText(e, message));
5260 }
5261
5262 // ---------------------------------------------------------------------------------
5263 // Animation engine
5264 // ---------------------------------------------------------------------------------
5265
5266 function Animator()
5267 {
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
5271 return this;
5272 }
5273
5274 // Start animation engine
5275 Animator.prototype.startAnimating = function() // Variable number of arguments
5276 {
5277 for(var t=0; t<arguments.length; t++)
5278 this.animations.push(arguments[t]);
5279 if(this.running == 0)
5280 {
5281 var me = this;
5282 this.timerID = window.setInterval(function() {me.doAnimate(me);},5);
5283 }
5284 this.running += arguments.length;
5285 }
5286
5287 // Perform an animation engine tick, calling each of the known animation modules
5288 Animator.prototype.doAnimate = function(me)
5289 {
5290 var a = 0;
5291 while(a < me.animations.length)
5292 {
5293 var animation = me.animations[a];
5294 if(animation.tick())
5295 a++;
5296 else
5297 {
5298 me.animations.splice(a,1);
5299 if(--me.running == 0)
5300 window.clearInterval(me.timerID);
5301 }
5302 }
5303 }
5304
5305 // Map a 0..1 value to 0..1, but slow down at the start and end
5306 Animator.slowInSlowOut = function(progress)
5307 {
5308 return(1-((Math.cos(progress * Math.PI)+1)/2));
5309 }
5310
5311 // ---------------------------------------------------------------------------------
5312 // Cascade animation
5313 // ---------------------------------------------------------------------------------
5314
5315 function Cascade(text,startElement,targetElement,slowly)
5316 {
5317 var winWidth = findWindowWidth();
5318 var winHeight = findWindowHeight();
5319 this.elements = [];
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);
5332 this.progress = -1;
5333 this.steps = slowly ? config.cascadeSlow : config.cascadeFast;
5334 this.text = text;
5335 this.tick();
5336 return this;
5337 }
5338
5339 Cascade.prototype.tick = function()
5340 {
5341 this.progress++;
5342 if(this.progress >= this.steps)
5343 {
5344 while(this.elements.length > 0)
5345 this.removeTail();
5346 this.targetElement.style.position = "static";
5347 this.targetElement.style.zIndex = "";
5348 return false;
5349 }
5350 else
5351 {
5352 if(this.elements.length > 0 && this.progress > config.cascadeDepth)
5353 this.removeTail();
5354 if(this.progress < (this.steps - config.cascadeDepth))
5355 {
5356 var f = Animator.slowInSlowOut(this.progress/(this.steps - config.cascadeDepth - 1));
5357 var e = createTiddlyElement(document.body,"div",null,"cascade",this.text);
5358 e.style.zIndex = 1;
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);
5365 }
5366 return true;
5367 }
5368 }
5369
5370 Cascade.prototype.removeTail = function()
5371 {
5372 var e = this.elements[0];
5373 e.parentNode.removeChild(e);
5374 this.elements.shift();
5375 }
5376
5377 // ---------------------------------------------------------------------------------
5378 // Scroller animation
5379 // ---------------------------------------------------------------------------------
5380
5381 function Scroller(targetElement,slowly)
5382 {
5383 this.targetElement = targetElement;
5384 this.startScroll = findScrollY();
5385 this.targetScroll = ensureVisible(targetElement);
5386 this.progress = 0;
5387 this.step = slowly ? config.animSlow : config.animFast;
5388 return this;
5389 }
5390
5391 Scroller.prototype.tick = function()
5392 {
5393 this.progress += this.step;
5394 if(this.progress > 1)
5395 {
5396 window.scrollTo(0,this.targetScroll);
5397 return false;
5398 }
5399 else
5400 {
5401 var f = Animator.slowInSlowOut(this.progress);
5402 window.scrollTo(0,this.startScroll + (this.targetScroll-this.startScroll) * f);
5403 return true;
5404 }
5405 }
5406
5407 // ---------------------------------------------------------------------------------
5408 // Slider animation
5409 // ---------------------------------------------------------------------------------
5410
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)
5413 {
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;
5421 if(opening)
5422 {
5423 this.progress = 0;
5424 element.style.height = "0px";
5425 element.style.display = "block";
5426 }
5427 else
5428 {
5429 this.progress = 1;
5430 this.step = -this.step;
5431 }
5432 element.style.overflow = "hidden";
5433 return this;
5434 }
5435
5436 Slider.prototype.stop = function()
5437 {
5438 if(this.opening)
5439 {
5440 this.element.style.height = "auto";
5441 this.element.style.opacity = 1;
5442 this.element.style.filter = "alpha(opacity:100)";
5443 }
5444 else
5445 {
5446 switch(this.deleteMode)
5447 {
5448 case "none":
5449 this.element.style.display = "none";
5450 break;
5451 case "all":
5452 this.element.parentNode.removeChild(this.element);
5453 break;
5454 case "children":
5455 removeChildren(this.element);
5456 break;
5457 }
5458 }
5459 }
5460
5461 Slider.prototype.tick = function()
5462 {
5463 this.progress += this.step;
5464 if(this.progress < 0 || this.progress > 1)
5465 {
5466 this.stop();
5467 return false;
5468 }
5469 else
5470 {
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 +")";
5476 return true;
5477 }
5478 }
5479
5480 // ---------------------------------------------------------------------------------
5481 // Popup menu
5482 // ---------------------------------------------------------------------------------
5483
5484 var Popup = {
5485 stack: [] // Array of objects with members root: and popup:
5486 };
5487
5488 Popup.create = function(root)
5489 {
5490 Popup.remove();
5491 var popup = createTiddlyElement(document.body,"ol","popup","popup");
5492 Popup.stack.push({root: root, popup: popup});
5493 return popup;
5494 }
5495
5496 Popup.onDocumentClick = function(e)
5497 {
5498 if (!e) var e = window.event;
5499 var target = resolveTarget(e);
5500 if(e.eventPhase == undefined)
5501 Popup.remove();
5502 else if(e.eventPhase == Event.BUBBLING_PHASE || e.eventPhase == Event.AT_TARGET)
5503 Popup.remove();
5504 return true;
5505 }
5506
5507 Popup.show = function(unused,slowly)
5508 {
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));
5525 else
5526 window.scrollTo(0,ensureVisible(curr.popup));
5527 }
5528
5529 Popup.remove = function()
5530 {
5531 if(Popup.stack.length > 0)
5532 {
5533 Popup.removeFrom(0);
5534 }
5535 }
5536
5537 Popup.removeFrom = function(from)
5538 {
5539 for(var t=Popup.stack.length-1; t>=from; t--)
5540 {
5541 var p = Popup.stack[t];
5542 removeClass(p.root,"highlight");
5543 p.popup.parentNode.removeChild(p.popup);
5544 }
5545 Popup.stack = Popup.stack.slice(0,from);
5546 }
5547
5548 // ---------------------------------------------------------------------------------
5549 // ListView gadget
5550 // ---------------------------------------------------------------------------------
5551
5552 var ListView = {};
5553
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)
5561 {
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++)
5566 {
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);
5572 }
5573 var tbody = createTiddlyElement(table,"tbody");
5574 for(var rc=0; rc<listObject.length; rc++)
5575 {
5576 rowObject = listObject[rc];
5577 r = createTiddlyElement(tbody,"tr");
5578 for(var c=0; c<listTemplate.rowClasses.length; c++)
5579 {
5580 if(rowObject[listTemplate.rowClasses[c].field])
5581 addClass(r,listTemplate.rowClasses[c].className);
5582 }
5583 rowObject.rowElement = rowObject;
5584 rowObject.colElements = {};
5585 for(var cc=0; cc<listTemplate.columns.length; cc++)
5586 {
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;
5594 }
5595 }
5596 if(callback && listTemplate.actions)
5597 createTiddlyDropDown(place,ListView.getCommandHandler(callback),listTemplate.actions);
5598 if(callback && listTemplate.buttons)
5599 {
5600 for(t=0; t<listTemplate.buttons.length; t++)
5601 {
5602 var a = listTemplate.buttons[t];
5603 if(a && a.name != "")
5604 createTiddlyButton(place,a.caption,null,ListView.getCommandHandler(callback,a.name,a.allowEmptySelection));
5605 }
5606 }
5607 return table;
5608 }
5609
5610 ListView.getCommandHandler = function(callback,name,allowEmptySelection)
5611 {
5612 return function(e)
5613 {
5614 var view = findRelated(this,"TABLE",null,"previousSibling");
5615 var tiddlers = [];
5616 ListView.forEachSelector(view,function(e,rowName) {
5617 if(e.checked)
5618 tiddlers.push(rowName);
5619 });
5620 if(tiddlers.length == 0 && !allowEmptySelection)
5621 alert(config.messages.nothingSelected);
5622 else
5623 {
5624 if(this.nodeName.toLowerCase() == "select")
5625 {
5626 callback(view,this.value,tiddlers);
5627 this.selectedIndex = 0;
5628 }
5629 else
5630 callback(view,name,tiddlers);
5631 }
5632 };
5633 }
5634
5635 // Invoke a callback for each selector checkbox in the listview
5636 // view - <table> element of listView
5637 // callback(checkboxElement,rowName)
5638 // where
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)
5643 {
5644 var checkboxes = view.getElementsByTagName("input");
5645 var hadOne = false;
5646 for(var t=0; t<checkboxes.length; t++)
5647 {
5648 var cb = checkboxes[t];
5649 if(cb.getAttribute("type") == "checkbox")
5650 {
5651 var rn = cb.getAttribute("rowName");
5652 if(rn)
5653 {
5654 callback(cb,rn);
5655 hadOne = true;
5656 }
5657 }
5658 }
5659 return hadOne;
5660 }
5661
5662 ListView.columnTypes = {};
5663
5664 ListView.columnTypes.String = {
5665 createHeader: function(place,columnTemplate,col)
5666 {
5667 createTiddlyText(place,columnTemplate.title);
5668 },
5669 createItem: function(place,listObject,field,columnTemplate,col,row)
5670 {
5671 var v = listObject[field];
5672 if(v != undefined)
5673 createTiddlyText(place,v);
5674 }
5675 };
5676
5677 ListView.columnTypes.Date = {
5678 createHeader: ListView.columnTypes.String.createHeader,
5679 createItem: function(place,listObject,field,columnTemplate,col,row)
5680 {
5681 var v = listObject[field];
5682 if(v != undefined)
5683 createTiddlyText(place,v.formatString(columnTemplate.dateFormat));
5684 }
5685 };
5686
5687 ListView.columnTypes.StringList = {
5688 createHeader: ListView.columnTypes.String.createHeader,
5689 createItem: function(place,listObject,field,columnTemplate,col,row)
5690 {
5691 var v = listObject[field];
5692 if(v != undefined)
5693 {
5694 for(var t=0; t<v.length; t++)
5695 {
5696 createTiddlyText(place,v[t]);
5697 createTiddlyElement(place,"br");
5698 }
5699 }
5700 }
5701 };
5702
5703 ListView.columnTypes.Selector = {
5704 createHeader: function(place,columnTemplate,col)
5705 {
5706 createTiddlyCheckbox(place,null,false,this.onHeaderChange);
5707 },
5708 createItem: function(place,listObject,field,columnTemplate,col,row)
5709 {
5710 var e = createTiddlyCheckbox(place,null,listObject[field],null);
5711 e.setAttribute("rowName",listObject[columnTemplate.rowName]);
5712 },
5713 onHeaderChange: function(e)
5714 {
5715 var state = this.checked;
5716 var view = findRelated(this,"TABLE");
5717 if(!view)
5718 return;
5719 ListView.forEachSelector(view,function(e,rowName) {
5720 e.checked = state;
5721 });
5722 }
5723 };
5724
5725 ListView.columnTypes.Tags = {
5726 createHeader: ListView.columnTypes.String.createHeader,
5727 createItem: function(place,listObject,field,columnTemplate,col,row)
5728 {
5729 var tags = listObject[field];
5730 createTiddlyText(place,String.encodeTiddlyLinkList(tags));
5731 }
5732 };
5733
5734 ListView.columnTypes.Boolean = {
5735 createHeader: ListView.columnTypes.String.createHeader,
5736 createItem: function(place,listObject,field,columnTemplate,col,row)
5737 {
5738 if(listObject[field] == true)
5739 createTiddlyText(place,columnTemplate.trueText);
5740 if(listObject[field] == false)
5741 createTiddlyText(place,columnTemplate.falseText);
5742 }
5743 };
5744
5745 ListView.columnTypes.TagCheckbox = {
5746 createHeader: ListView.columnTypes.String.createHeader,
5747 createItem: function(place,listObject,field,columnTemplate,col,row)
5748 {
5749 var e = createTiddlyCheckbox(place,null,listObject[field],this.onChange);
5750 e.setAttribute("tiddler",listObject.title);
5751 e.setAttribute("tag",columnTemplate.tag);
5752 },
5753 onChange : function(e)
5754 {
5755 var tag = this.getAttribute("tag");
5756 var tiddler = this.getAttribute("tiddler");
5757 store.setTiddlerTag(tiddler,this.checked,tag);
5758 }
5759 };
5760
5761 ListView.columnTypes.TiddlerLink = {
5762 createHeader: ListView.columnTypes.String.createHeader,
5763 createItem: function(place,listObject,field,columnTemplate,col,row)
5764 {
5765 var v = listObject[field];
5766 if(v != undefined)
5767 {
5768 var link = createTiddlyLink(place,listObject[columnTemplate.tiddlerLink],false,null);
5769 createTiddlyText(link,listObject[field]);
5770 }
5771 }
5772 };
5773 // ---------------------------------------------------------------------------------
5774 // Augmented methods for the JavaScript Number(), Array(), String() and Date() objects
5775 // ---------------------------------------------------------------------------------
5776
5777 // Clamp a number to a range
5778 Number.prototype.clamp = function(min,max)
5779 {
5780 var c = this;
5781 if(c < min)
5782 c = min;
5783 if(c > max)
5784 c = max;
5785 return c;
5786 }
5787
5788 // Add indexOf function if browser does not support it
5789 if(!Array.indexOf) {
5790 Array.prototype.indexOf = function(item,from)
5791 {
5792 if(!from)
5793 from = 0;
5794 for(var i=from; i<this.length; i++)
5795 if(this[i] === item)
5796 return i;
5797 return -1;
5798 }}
5799
5800 // Find an entry in a given field of the members of an array
5801 Array.prototype.findByField = function(field,value)
5802 {
5803 for(var t=0; t<this.length; t++)
5804 if(this[t][field] == value)
5805 return t;
5806 return null;
5807 }
5808
5809 // Return whether an entry exists in an array
5810 Array.prototype.contains = function(item)
5811 {
5812 return this.indexOf(item) != -1;
5813 };
5814
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)
5819 {
5820 var p = this.indexOf(value);
5821 if(mode == 0)
5822 mode = (p == -1) ? +1 : -1;
5823 if(mode == +1)
5824 {
5825 if(p == -1)
5826 this.push(value);
5827 }
5828 else if(mode == -1)
5829 {
5830 if(p != -1)
5831 this.splice(p,1);
5832 }
5833 }
5834
5835 // Return whether one of a list of values exists in an array
5836 Array.prototype.containsAny = function(items)
5837 {
5838 for(var i=0; i<items.length; i++)
5839 if (this.indexOf(items[i]) != -1)
5840 return true;
5841 return false;
5842 };
5843
5844 // Return whether all of a list of values exists in an array
5845 Array.prototype.containsAll = function(items)
5846 {
5847 for (var i = 0; i<items.length; i++)
5848 if (this.indexOf(items[i]) == -1)
5849 return false;
5850 return true;
5851 };
5852
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)
5855 {
5856 if(unique != undefined && unique == false)
5857 this.push(item);
5858 else
5859 {
5860 if(this.indexOf(item) == -1)
5861 this.push(item);
5862 }
5863 }
5864
5865 Array.prototype.remove = function(item)
5866 {
5867 var p = this.indexOf(item);
5868 if(p != -1)
5869 this.splice(p,1);
5870 }
5871
5872 // Get characters from the right end of a string
5873 String.prototype.right = function(n)
5874 {
5875 if(n < this.length)
5876 return this.slice(this.length-n);
5877 else
5878 return this;
5879 }
5880
5881 // Trim whitespace from both ends of a string
5882 String.prototype.trim = function()
5883 {
5884 return this.replace(/^\s*|\s*$/g,"");
5885 }
5886
5887 // Convert a string from a CSS style property name to a JavaScript style name ("background-color" -> "backgroundColor")
5888 String.prototype.unDash = function()
5889 {
5890 var s = this.split("-");
5891 if(s.length > 1)
5892 for(var t=1; t<s.length; t++)
5893 s[t] = s[t].substr(0,1).toUpperCase() + s[t].substr(1);
5894 return s.join("");
5895 }
5896
5897 // Substitute substrings from an array into a format string that includes '%1'-type specifiers
5898 String.prototype.format = function(substrings)
5899 {
5900 var subRegExp = /(?:%(\d+))/mg;
5901 var currPos = 0;
5902 var r = [];
5903 do {
5904 var match = subRegExp.exec(this);
5905 if(match && match[1])
5906 {
5907 if(match.index > currPos)
5908 r.push(this.substring(currPos,match.index));
5909 r.push(substrings[parseInt(match[1])]);
5910 currPos = subRegExp.lastIndex;
5911 }
5912 } while(match);
5913 if(currPos < this.length)
5914 r.push(this.substring(currPos,this.length));
5915 return r.join("");
5916 }
5917
5918 // Escape any special RegExp characters with that character preceded by a backslash
5919 String.prototype.escapeRegExp = function()
5920 {
5921 var s = "\\^$*+?()=!|,{}[].";
5922 var c = this;
5923 for(var t=0; t<s.length; t++)
5924 c = c.replace(new RegExp("\\" + s.substr(t,1),"g"),"\\" + s.substr(t,1));
5925 return c;
5926 }
5927
5928 // Convert "\" to "\s", newlines to "\n" (and remove carriage returns)
5929 String.prototype.escapeLineBreaks = function()
5930 {
5931 return this.replace(/\\/mg,"\\s").replace(/\n/mg,"\\n").replace(/\r/mg,"");
5932 }
5933
5934 // Convert "\n" to newlines, "\b" to " ", "\s" to "\" (and remove carriage returns)
5935 String.prototype.unescapeLineBreaks = function()
5936 {
5937 return this.replace(/\\n/mg,"\n").replace(/\\b/mg," ").replace(/\\s/mg,"\\").replace(/\r/mg,"");
5938 }
5939
5940 // Convert & to "&amp;", < to "&lt;", > to "&gt;" and " to "&quot;"
5941 String.prototype.htmlEncode = function()
5942 {
5943 return(this.replace(/&/mg,"&amp;").replace(/</mg,"&lt;").replace(/>/mg,"&gt;").replace(/\"/mg,"&quot;"));
5944 }
5945
5946 // Convert "&amp;" to &, "&lt;" to <, "&gt;" to > and "&quot;" to "
5947 String.prototype.htmlDecode = function()
5948 {
5949 return(this.replace(/&amp;/mg,"&").replace(/&lt;/mg,"<").replace(/&gt;/mg,">").replace(/&quot;/mg,"\""));
5950 }
5951
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)
5965 {
5966 var parseToken = function(match,p)
5967 {
5968 var n;
5969 if(match[p]) // Double quoted
5970 n = match[p];
5971 else if(match[p+1]) // Single quoted
5972 n = match[p+1];
5973 else if(match[p+2]) // Double-square-bracket quoted
5974 n = match[p+2];
5975 else if(match[p+3]) // Double-brace quoted
5976 try
5977 {
5978 n = match[p+3];
5979 if(allowEval)
5980 n = window.eval(n);
5981 }
5982 catch(e)
5983 {
5984 throw "Unable to evaluate {{" + match[p+3] + "}}: " + exceptionText(e);
5985 }
5986 else if(match[p+4]) // Unquoted
5987 n = match[p+4];
5988 else if(match[p+5]) // empty quote
5989 n = "";
5990 return n;
5991 };
5992 var r = [{}];
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 + ")";
6001 var re = noNames
6002 ? new RegExp(token,"mg")
6003 : new RegExp(skipSpace + token + skipSpace + "(?:(\\:)" + skipSpace + token + ")?","mg");
6004 var params = [];
6005 do {
6006 var match = re.exec(this);
6007 if(match)
6008 {
6009 var n = parseToken(match,1);
6010 if(noNames)
6011 r.push({name: "", value: n});
6012 else
6013 {
6014 var v = parseToken(match,8);
6015 if(v == null && defaultName)
6016 {
6017 v = n;
6018 n = defaultName;
6019 }
6020 else if(v == null && defaultValue)
6021 v = defaultValue;
6022 r.push({name: n, value: v});
6023 if(cascadeDefaults)
6024 {
6025 defaultName = n;
6026 defaultValue = v;
6027 }
6028 }
6029 }
6030 } while(match);
6031 // Summarise parameters into first element
6032 for(var t=1; t<r.length; t++)
6033 {
6034 if(r[0][r[t].name])
6035 r[0][r[t].name].push(r[t].value);
6036 else
6037 r[0][r[t].name] = [r[t].value];
6038 }
6039 return r;
6040 }
6041
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()
6046 {
6047 var p = this.parseParams("list",null,true,true);
6048 var n = [];
6049 for(var t=1; t<p.length; t++)
6050 n.push(p[t].value);
6051 return n;
6052 }
6053
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)
6056 {
6057 var p = this.parseParams("list",null,false,true);
6058 var n = [];
6059 for(var t=1; t<p.length; t++)
6060 n.pushUnique(p[t].value,unique);
6061 return n;
6062 }
6063
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)
6066 {
6067 var s = this.indexOf(start);
6068 if(s != -1)
6069 {
6070 s += start.length;
6071 var e = this.indexOf(end,s);
6072 if(e != -1)
6073 return [s, e];
6074 }
6075 }
6076
6077 // Replace a chunk of a string given start and end markers
6078 String.prototype.replaceChunk = function(start,end,sub)
6079 {
6080 var r = this.getChunkRange(start,end);
6081 return r
6082 ? this.substring(0,r[0]) + sub + this.substring(r[1])
6083 : this;
6084 }
6085
6086 // Returns a chunk of a string between start and end markers, or undefined
6087 String.prototype.getChunk = function(start,end)
6088 {
6089 var r = this.getChunkRange(start,end);
6090 if (r)
6091 return this.substring(r[0],r[1]);
6092 }
6093
6094
6095 // Static method to bracket a string with double square brackets if it contains a space
6096 String.encodeTiddlyLink = function(title)
6097 {
6098 if(title.indexOf(" ") == -1)
6099 return(title);
6100 else
6101 return("[[" + title + "]]");
6102 }
6103
6104 // Static method to encodeTiddlyLink for every item in an array and join them with spaces
6105 String.encodeTiddlyLinkList = function(list)
6106 {
6107 if(list)
6108 {
6109 var results = [];
6110 for(var t=0; t<list.length; t++)
6111 results.push(String.encodeTiddlyLink(list[t]));
6112 return results.join(" ");
6113 }
6114 else
6115 return "";
6116 }
6117
6118 // Static method to left-pad a string with 0s to a certain width
6119 String.zeroPad = function(n,d)
6120 {
6121 var s = n.toString();
6122 if(s.length < d)
6123 s = "000000000000000000000000000".substr(0,d-s.length) + s;
6124 return(s);
6125 }
6126
6127 String.prototype.startsWith = function(prefix)
6128 {
6129 return !prefix || this.substring(0,prefix.length) == prefix;
6130 }
6131
6132 // Returns the first value of the given named parameter.
6133 //#
6134 //# @param params
6135 //# as returned by parseParams or null/undefined
6136 //# @return [may be null/undefined]
6137 //#
6138 function getParam(params, name, defaultValue) {
6139 if (!params)
6140 return defaultValue;
6141 var p = params[0][name];
6142 return p ? p[0] : defaultValue;
6143 }
6144
6145 // Returns the first value of the given boolean named parameter.
6146 //#
6147 //# @param params
6148 //# as returned by parseParams or null/undefined
6149 //#
6150 function getFlag(params, name, defaultValue) {
6151 return !!getParam(params, name, defaultValue);
6152 }
6153
6154 // Substitute date components into a string
6155 Date.prototype.formatString = function(template)
6156 {
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());
6182 return t;
6183 }
6184
6185 Date.prototype.getWeek = function()
6186 {
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;
6193 }
6194
6195 Date.prototype.getYearForWeekNo = function()
6196 {
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();
6202 }
6203
6204 Date.prototype.getHours12 = function()
6205 {
6206 var h = this.getHours();
6207 return h > 12 ? h-12 : ( h > 0 ? h : 12 );
6208 }
6209
6210 Date.prototype.getAmPm = function()
6211 {
6212 return this.getHours() >= 12 ? "pm" : "am";
6213 }
6214
6215 Date.prototype.daySuffix = function()
6216 {
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";
6222 return "th";
6223 }
6224
6225 // Convert a date to local YYYYMMDDHHMM string format
6226 Date.prototype.convertToLocalYYYYMMDDHHMM = function()
6227 {
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));
6229 }
6230
6231 // Convert a date to UTC YYYYMMDDHHMM string format
6232 Date.prototype.convertToYYYYMMDDHHMM = function()
6233 {
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));
6235 }
6236
6237 // Convert a date to UTC YYYYMMDD.HHMMSSMMM string format
6238 Date.prototype.convertToYYYYMMDDHHMMSSMMM = function()
6239 {
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));
6241 }
6242
6243 // Static method to create a date from a UTC YYYYMMDDHHMM format string
6244 Date.convertFromYYYYMMDDHHMM = function(d)
6245 {
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));
6251 return(theDate);
6252 }
6253
6254 // ---------------------------------------------------------------------------------
6255 // Crypto functions and associated conversion routines
6256 // ---------------------------------------------------------------------------------
6257
6258 // Crypto "namespace"
6259 function Crypto() {}
6260
6261 // Convert a string to an array of big-endian 32-bit words
6262 Crypto.strToBe32s = function(str)
6263 {
6264 var be = Array();
6265 var len = Math.floor(str.length/4);
6266 var i, j;
6267 for(i=0, j=0; i<len; i++, j+=4)
6268 {
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);
6270 }
6271 while (j<str.length)
6272 {
6273 be[j>>2] |= (str.charCodeAt(j)&0xff)<<(24-(j*8)%32);
6274 j++;
6275 }
6276 return be;
6277 }
6278
6279 // Convert an array of big-endian 32-bit words to a string
6280 Crypto.be32sToStr = function(be)
6281 {
6282 var str = "";
6283 for(var i=0;i<be.length*32;i+=8)
6284 str += String.fromCharCode((be[i>>5]>>>(24-i%32)) & 0xff);
6285 return str;
6286 }
6287
6288 // Convert an array of big-endian 32-bit words to a hex string
6289 Crypto.be32sToHex = function(be)
6290 {
6291 var hex = "0123456789ABCDEF";
6292 var str = "";
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);
6295 return str;
6296 }
6297
6298 // Return, in hex, the SHA-1 hash of a string
6299 Crypto.hexSha1Str = function(str)
6300 {
6301 return Crypto.be32sToHex(Crypto.sha1Str(str));
6302 }
6303
6304 // Return the SHA-1 hash of a string
6305 Crypto.sha1Str = function(str)
6306 {
6307 return Crypto.sha1(Crypto.strToBe32s(str),str.length);
6308 }
6309
6310 // Calculate the SHA-1 hash of an array of blen bytes of big-endian 32-bit words
6311 Crypto.sha1 = function(x,blen)
6312 {
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)
6316 {
6317 var lsw = (a&0xFFFF)+(b&0xFFFF);
6318 var msw = (a>>16)+(b>>16)+(lsw>>16);
6319 return (msw<<16)|(lsw&0xFFFF);
6320 };
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)
6324 {
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);
6328 };
6329 // Bitwise rotate left a 32-bit integer by 1 bit
6330 rol32 = function(n)
6331 {
6332 return (n>>>31)|(n<<1);
6333 };
6334
6335 var len = blen*8;
6336 // Append padding so length in bits is 448 mod 512
6337 x[len>>5] |= 0x80 << (24-len%32);
6338 // Append length
6339 x[((len+64>>9)<<4)+15] = len;
6340 var w = Array(80);
6341
6342 var k1 = 0x5A827999;
6343 var k2 = 0x6ED9EBA1;
6344 var k3 = 0x8F1BBCDC;
6345 var k4 = 0xCA62C1D6;
6346
6347 var h0 = 0x67452301;
6348 var h1 = 0xEFCDAB89;
6349 var h2 = 0x98BADCFE;
6350 var h3 = 0x10325476;
6351 var h4 = 0xC3D2E1F0;
6352
6353 for(var i=0;i<x.length;i+=16)
6354 {
6355 var j,t;
6356 var a = h0;
6357 var b = h1;
6358 var c = h2;
6359 var d = h3;
6360 var e = h4;
6361 for(j = 0;j<16;j++)
6362 {
6363 w[j] = x[i+j];
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;
6366 }
6367 for(j=16;j<20;j++)
6368 {
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;
6372 }
6373 for(j=20;j<40;j++)
6374 {
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;
6378 }
6379 for(j=40;j<60;j++)
6380 {
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;
6384 }
6385 for(j=60;j<80;j++)
6386 {
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;
6390 }
6391
6392 h0 = add32(h0,a);
6393 h1 = add32(h1,b);
6394 h2 = add32(h2,c);
6395 h3 = add32(h3,d);
6396 h4 = add32(h4,e);
6397 }
6398 return Array(h0,h1,h2,h3,h4);
6399 }
6400
6401 // ---------------------------------------------------------------------------------
6402 // RGB colour object
6403 // ---------------------------------------------------------------------------------
6404
6405 // Construct an RGB colour object from a '#rrggbb', '#rgb' or 'rgb(n,n,n)' string or from separate r,g,b values
6406 function RGB(r,g,b)
6407 {
6408 this.r = 0;
6409 this.g = 0;
6410 this.b = 0;
6411 if(typeof r == "string")
6412 {
6413 if(r.substr(0,1) == "#")
6414 {
6415 if(r.length == 7)
6416 {
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;
6420 }
6421 else
6422 {
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;
6426 }
6427 }
6428 else
6429 {
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);
6432 if (c)
6433 {
6434 this.r = parseInt(c[1],10)/255;
6435 this.g = parseInt(c[2],10)/255;
6436 this.b = parseInt(c[3],10)/255;
6437 }
6438 }
6439 }
6440 else
6441 {
6442 this.r = r;
6443 this.g = g;
6444 this.b = b;
6445 }
6446 return this;
6447 }
6448
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)
6454 {
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);
6456 }
6457
6458 // Return an rgb colour as a #rrggbb format hex string
6459 RGB.prototype.toString = function()
6460 {
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));
6467 }
6468
6469 // ---------------------------------------------------------------------------------
6470 // DOM utilities - many derived from www.quirksmode.org
6471 // ---------------------------------------------------------------------------------
6472
6473 function drawGradient(place,horiz,colours)
6474 {
6475 for(var t=0; t<= 100; t+=2)
6476 {
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;
6485 var f = t/100;
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();
6488 }
6489 }
6490
6491 function createTiddlyText(theParent,theText)
6492 {
6493 return theParent.appendChild(document.createTextNode(theText));
6494 }
6495
6496 function createTiddlyCheckbox(theParent,caption,checked,onChange)
6497 {
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";
6504 if(caption)
6505 wikify(caption,theParent);
6506 return cb;
6507 }
6508
6509 function createTiddlyElement(theParent,theElement,theID,theClass,theText)
6510 {
6511 var e = document.createElement(theElement);
6512 if(theClass != null)
6513 e.className = theClass;
6514 if(theID != null)
6515 e.setAttribute("id",theID);
6516 if(theText != null)
6517 e.appendChild(document.createTextNode(theText));
6518 if(theParent != null)
6519 theParent.appendChild(e);
6520 return(e);
6521 }
6522
6523 // Add an event handler
6524 // Thanks to John Resig, via QuirksMode
6525 function addEvent(obj,type,fn)
6526 {
6527 if(obj.attachEvent)
6528 {
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]);
6532 }
6533 else
6534 obj.addEventListener(type,fn,false);
6535 }
6536
6537 // Remove an event handler
6538 // Thanks to John Resig, via QuirksMode
6539 function removeEvent(obj,type,fn)
6540 {
6541 if(obj.detachEvent)
6542 {
6543 obj.detachEvent('on'+type,obj[type+fn]);
6544 obj[type+fn] = null;
6545 }
6546 else
6547 obj.removeEventListener(type,fn,false);
6548 }
6549
6550 function addClass(e,theClass)
6551 {
6552 var currClass = e.className.split(" ");
6553 if(currClass.indexOf(theClass) == -1)
6554 e.className += " " + theClass;
6555 }
6556
6557 function removeClass(e,theClass)
6558 {
6559 var currClass = e.className.split(" ");
6560 var i = currClass.indexOf(theClass);
6561 while(i != -1)
6562 {
6563 currClass.splice(i,1);
6564 i = currClass.indexOf(theClass);
6565 }
6566 e.className = currClass.join(" ");
6567 }
6568
6569 function hasClass(e,theClass)
6570 {
6571 if(e.className)
6572 {
6573 if(e.className.split(" ").indexOf(theClass) != -1)
6574 return true;
6575 }
6576 return false;
6577 }
6578
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)
6581 {
6582 name = name ? name : "tagName";
6583 relative = relative ? relative : "parentNode";
6584 if(name == "className")
6585 {
6586 while(e && !hasClass(e,value))
6587 {
6588 e = e[relative];
6589 }
6590 }
6591 else
6592 {
6593 while(e && e[name] != value)
6594 {
6595 e = e[relative];
6596 }
6597 }
6598 return e;
6599 }
6600
6601 // Resolve the target object of an event
6602 function resolveTarget(e)
6603 {
6604 var obj;
6605 if (e.target)
6606 obj = e.target;
6607 else if (e.srcElement)
6608 obj = e.srcElement;
6609 if (obj.nodeType == 3) // defeat Safari bug
6610 obj = obj.parentNode;
6611 return(obj);
6612 }
6613
6614 // Return the content of an element as plain text with no formatting
6615 function getPlainText(e)
6616 {
6617 var text = "";
6618 if(e.innerText)
6619 text = e.innerText;
6620 else if(e.textContent)
6621 text = e.textContent;
6622 return text;
6623 }
6624
6625 // Get the scroll position for window.scrollTo necessary to scroll a given element into view
6626 function ensureVisible(e)
6627 {
6628 var posTop = findPosY(e);
6629 var posBot = posTop + e.offsetHeight;
6630 var winTop = findScrollY();
6631 var winHeight = findWindowHeight();
6632 var winBot = winTop + winHeight;
6633 if(posTop < winTop)
6634 return(posTop);
6635 else if(posBot > winBot)
6636 {
6637 if(e.offsetHeight < winHeight)
6638 return(posTop - (winHeight - e.offsetHeight));
6639 else
6640 return(posTop);
6641 }
6642 else
6643 return(winTop);
6644 }
6645
6646 // Get the current width of the display window
6647 function findWindowWidth()
6648 {
6649 return(window.innerWidth ? window.innerWidth : document.documentElement.clientWidth);
6650 }
6651
6652 // Get the current height of the display window
6653 function findWindowHeight()
6654 {
6655 return(window.innerHeight ? window.innerHeight : document.documentElement.clientHeight);
6656 }
6657
6658 // Get the current horizontal page scroll position
6659 function findScrollX()
6660 {
6661 return(window.scrollX ? window.scrollX : document.documentElement.scrollLeft);
6662 }
6663
6664 // Get the current vertical page scroll position
6665 function findScrollY()
6666 {
6667 return(window.scrollY ? window.scrollY : document.documentElement.scrollTop);
6668 }
6669
6670 function findPosX(obj)
6671 {
6672 var curleft = 0;
6673 while (obj.offsetParent)
6674 {
6675 curleft += obj.offsetLeft;
6676 obj = obj.offsetParent;
6677 }
6678 return curleft;
6679 }
6680
6681 function findPosY(obj)
6682 {
6683 var curtop = 0;
6684 while (obj.offsetParent)
6685 {
6686 curtop += obj.offsetTop;
6687 obj = obj.offsetParent;
6688 }
6689 return curtop;
6690 }
6691
6692 // Blur a particular element
6693 function blurElement(e)
6694 {
6695 if(e != null && e.focus && e.blur)
6696 {
6697 e.focus();
6698 e.blur();
6699 }
6700 }
6701
6702 // Create a non-breaking space
6703 function insertSpacer(place)
6704 {
6705 var e = document.createTextNode(String.fromCharCode(160));
6706 if(place)
6707 place.appendChild(e);
6708 return e;
6709 }
6710
6711 // Remove all children of a node
6712 function removeChildren(e)
6713 {
6714 while(e.hasChildNodes())
6715 e.removeChild(e.firstChild);
6716 }
6717
6718 // Add a stylesheet, replacing any previous custom stylesheet
6719 function setStylesheet(s,id)
6720 {
6721 if(!id)
6722 id = "customStyleSheet";
6723 var n = document.getElementById(id);
6724 if(document.createStyleSheet) // Test for IE's non-standard createStyleSheet method
6725 {
6726 if(n)
6727 n.parentNode.removeChild(n);
6728 // This failed without the &nbsp;
6729 document.getElementsByTagName("head")[0].insertAdjacentHTML("beforeEnd","&nbsp;<style id='" + id + "'>" + s + "</style>");
6730 }
6731 else
6732 {
6733 if(n)
6734 n.replaceChild(document.createTextNode(s),n.firstChild);
6735 else
6736 {
6737 var n = document.createElement("style");
6738 n.type = "text/css";
6739 n.id = id;
6740 n.appendChild(document.createTextNode(s));
6741 document.getElementsByTagName("head")[0].appendChild(n);
6742 }
6743 }
6744 }
6745
6746 // Replace the current selection of a textarea or text input and scroll it into view
6747
6748 function replaceSelection(e,text)
6749 {
6750 if (e.setSelectionRange)
6751 {
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);
6758 }
6759 else if (document.selection)
6760 {
6761 var range = document.selection.createRange();
6762 if (range.parentElement() == e)
6763 {
6764 var isCollapsed = range.text == "";
6765 range.text = text;
6766 if (!isCollapsed)
6767 {
6768 range.moveStart('character', -text.length);
6769 range.select();
6770 }
6771 }
6772 }
6773 }
6774
6775 // Returns the text of the given (text) node, possibly merging subsequent text nodes
6776 function getNodeText(e)
6777 {
6778 var t = "";
6779 while (e && e.nodeName == "#text")
6780 {
6781 t += e.nodeValue;
6782 e = e.nextSibling;
6783 }
6784 return t;
6785 }
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)
6791 //#
6792 //# store must implement:
6793 //# function createTiddler(title).
6794 //#
6795
6796 function LoaderBase()
6797 {
6798 }
6799
6800 LoaderBase.prototype.loadTiddler = function(store,e,tiddlers)
6801 {
6802 var title = this.getTitle(store, e);
6803 if (title)
6804 {
6805 var tiddler = store.createTiddler(title);
6806 this.internalizeTiddler(store, tiddler, title, e);
6807 tiddlers.push(tiddler);
6808 }
6809 }
6810
6811 LoaderBase.prototype.loadTiddlers = function(store,nodes)
6812 {
6813 var tiddlers = [];
6814 for (var t = 0; t < nodes.length; t++)
6815 {
6816 try
6817 {
6818 this.loadTiddler(store, nodes[t], tiddlers);
6819 }
6820 catch(e)
6821 {
6822 showException(e, config.messages.tiddlerLoadError.format([this.getTitle(store, nodes[t])]));
6823 }
6824 }
6825 return tiddlers;
6826 }
6827
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)
6833 //#
6834 //# store must implement:
6835 //# function getTiddlers(sortByFieldName)
6836 //#
6837
6838 function SaverBase()
6839 {
6840 }
6841
6842 SaverBase.prototype.externalize = function(store)
6843 {
6844 var results = [];
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");
6849 }
6850 //--------------------------------
6851 // TW21Loader (inherits from LoaderBase)
6852
6853 function TW21Loader() {};
6854
6855 TW21Loader.prototype = new LoaderBase();
6856
6857 TW21Loader.prototype.getTitle = function(store, e) {
6858 var title = null;
6859 if(e.getAttribute)
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);
6865 }
6866 return title;
6867 }
6868
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");
6876 var fields = {};
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();
6882 }
6883 }
6884 tiddler.assign(title,text,modifier,modified,tags,created, fields);
6885 return tiddler;
6886 };
6887
6888 //--------------------------------
6889 // TW21Saver (inherits from SaverBase)
6890
6891 function TW21Saver() {};
6892
6893 TW21Saver.prototype = new SaverBase();
6894
6895 TW21Saver.prototype.externalizeTiddler = function(store, tiddler)
6896 {
6897 try {
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()]);
6904 }, true);
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
6913 ]);
6914 } catch (e) {
6915 throw exceptionText(e, config.messages.tiddlerSaveError.format([tiddler.title]));
6916 }
6917 }
6918
6919 // ---------------------------------------------------------------------------------
6920 // Deprecated code
6921 // ---------------------------------------------------------------------------------
6922
6923 // @Deprecated: Use createElementAndWikify and this.termRegExp instead
6924 config.formatterHelpers.charFormatHelper = function(w)
6925 {
6926 w.subWikify(createTiddlyElement(w.output,this.element),this.terminator);
6927 }
6928
6929 // @Deprecated: Use enclosedTextHelper and this.lookaheadRegExp instead
6930 config.formatterHelpers.monospacedByLineHelper = function(w)
6931 {
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)
6936 {
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;
6942 }
6943 }
6944
6945 // @Deprecated: Use <br> or <br /> instead of <<br>>
6946 config.macros.br.handler = function(place)
6947 {
6948 createTiddlyElement(place,"br");
6949 }
6950
6951 // Find an entry in an array. Returns the array index or null
6952 // @Deprecated: Use indexOf instead
6953 Array.prototype.find = function(item)
6954 {
6955 var i = this.indexOf(item);
6956 return i == -1 ? null : i;
6957 }
6958
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)
6962 {
6963 return store.getLoader().internalizeTiddler(store,this,title,divRef);
6964 }
6965
6966 // Format the text for storage in an HTML DIV
6967 // @Deprecated Use store.getSaver().externalizeTiddler instead.
6968 Tiddler.prototype.saveToDiv = function()
6969 {
6970 return store.getSaver().externalizeTiddler(store,this);
6971 }
6972
6973 // @Deprecated: Use store.allTiddlersAsHtml() instead
6974 function allTiddlersAsHtml()
6975 {
6976 return store.allTiddlersAsHtml();
6977 }
6978
6979 // @Deprecated: Use refreshPageTemplate instead
6980 function applyPageTemplate(title)
6981 {
6982 refreshPageTemplate(title);
6983 }
6984
6985 // @Deprecated: Use story.displayTiddlers instead
6986 function displayTiddlers(srcElement,titles,template,unused1,unused2,animate,slowly)
6987 {
6988 story.displayTiddlers(srcElement,titles,template,animate,slowly);
6989 }
6990
6991 // @Deprecated: Use story.displayTiddler instead
6992 function displayTiddler(srcElement,title,template,unused1,unused2,animate,slowly)
6993 {
6994 story.displayTiddler(srcElement,title,template,animate,slowly);
6995 }
6996
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;
7001
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 // ---------------------------------------------------------------------------------
7009 // End of scripts
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 // ---------------------------------------------------------------------------------
7016 //]]>
7017 </script>
7018 <style type="text/css">
7019
7020 #saveTest {
7021 display: none;
7022 }
7023
7024 .zoomer {
7025 display: none;
7026 }
7027
7028 #messageArea {
7029 display: none;
7030 }
7031
7032 #copyright {
7033 display: none;
7034 }
7035
7036 .popup {
7037 position: absolute;
7038 }
7039
7040 #storeArea {
7041 display: none;
7042 margin: 4em 10em 3em;
7043 }
7044
7045 #storeArea div {
7046 padding: 0.5em;
7047 margin: 1em 0em 0em 0em;
7048 border-color: #f0f0f0 #606060 #404040 #d0d0d0;
7049 border-style: solid;
7050 border-width: 2px;
7051 overflow: auto;
7052 }
7053
7054 #javascriptWarning {
7055 width: 100%;
7056 text-align: center;
7057 font-weight: bold;
7058 background-color: #dd1100;
7059 color: #fff;
7060 padding:1em 0em;
7061 }
7062
7063 </style>
7064 <!--POST-HEAD-START-->
7065
7066 <!--POST-HEAD-END-->
7067 </head>
7068 <body onload="main();" onunload="if(window.checkUnsavedChanges) checkUnsavedChanges();">
7069 <!--PRE-BODY-START-->
7070
7071 <!--PRE-BODY-END-->
7072 <script type="text/javascript">
7073 //<![CDATA[
7074 if (useJavaSaver)
7075 document.write("<applet style='position:absolute;left:-1px' name='TiddlySaver' code='TiddlySaver.class' archive='TiddlySaver.jar' width='1' height='1'></applet>");
7076 //]]>
7077 </script>
7078 <div id="copyright">
7079 Welcome to TiddlyWiki by Jeremy Ruston, Copyright &copy; 2006 Osmosoft Limited
7080 </div>
7081 <noscript>
7082 <div id="javascriptWarning">This page requires JavaScript to function properly</div>
7083 </noscript>
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 &quot;-noextern&quot; 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* &quot;~If-Modified-Since&quot; 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 &quot;AS IS&quot;\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">&lt;div class='header' macro='gradient vert #390108 #900'&gt;\n&lt;div class='headerShadow'&gt;\n&lt;span class='siteTitle' refresh='content' tiddler='SiteTitle'&gt;&lt;/span&gt;&amp;nbsp;\n&lt;span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'&gt;&lt;/span&gt;\n&lt;/div&gt;\n&lt;div class='headerForeground'&gt;\n&lt;span class='siteTitle' refresh='content' tiddler='SiteTitle'&gt;&lt;/span&gt;&amp;nbsp;\n&lt;span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'&gt;&lt;/span&gt;\n&lt;/div&gt;\n&lt;/div&gt;\n&lt;div id='mainMenu'&gt;\n&lt;div refresh='content' tiddler='MainMenu'&gt;&lt;/div&gt;\n&lt;/div&gt;\n&lt;div id='sidebar'&gt;\n&lt;div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'&gt;&lt;/div&gt;\n&lt;div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'&gt;&lt;/div&gt;\n&lt;/div&gt;\n&lt;div id='displayArea'&gt;\n&lt;div id='messageArea'&gt;&lt;/div&gt;\n&lt;div id='tiddlerDisplay'&gt;&lt;/div&gt;\n&lt;/div&gt;</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 &quot;make&quot; and various developer options selected.\n\nConfiguration now uses a tool called &quot;mconf&quot; 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{{{{&gt; make}}}\n\nSelect your platform type, save the configuration, exit, and then type &quot;make&quot; again.\n\nIf all goes well, you should end up with an executable called &quot;axhttpd&quot; (or axhttpd.exe) in the //_stage// directory.\n\nTo play with all the various axTLS options, type:\n\n{{indent{{{{&gt; 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{{{{&gt; 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{{{{&gt; axssl s_server -verify -CAfile ../ssl/test/axTLS.ca_x509}}}\n\nand\n\n{{indent{{{{&gt; 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{{{{&gt; mono axssl.csharp.exe ...}}}\n\n!!!!Java\n\nThe java version is run as:\n\n{{indent{{{{&gt; java -jar axtls.jar &lt;options&gt;}}}\n\n!!!!Perl\n\n{{indent{{{{&gt; [perl] ./axssl.pl &lt;options&gt;}}}\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{{{{&gt; [lua] ./axssl.lua &lt;options&gt;}}}\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 &quot;Reload&quot; page instigates a change to ~SSLv3 for some reason (even though the TLS 1.0 option is selected). This will cause a &quot;Firefox and &lt;server&gt; cannot communicate securely because they have no common encryption algorithms&quot; (v1.5), or &quot;Firefox can't connect to &lt;server&gt; because the site uses a security protocol which isn't enabled&quot; (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{{{{&gt; 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{{{{&gt; 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 &quot;.htpasswd&quot;, 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 &quot;~SSLRequireSSL&quot; into a '.htaccess' file in the directory to be protected. \n\nConversely, use &quot;~SSLDenySSL&quot; 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 &quot;Deny all&quot; 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>
7101 </div>
7102 <!--POST-BODY-START-->
7103
7104 <!--POST-BODY-END-->
7105 </body>
7106 </html>