1 <!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2 <html xmlns=
"http://www.w3.org/1999/xhtml" xml:
lang=
"en" lang=
"en">
4 <script id=
"versionArea" type=
"text/javascript">
6 var version = {title:
"TiddlyWiki", major:
2, minor:
4, revision:
1, date: new Date(
"Aug 4, 2008"), extensions: {}};
9 <meta http-equiv=
"Content-Type" content=
"text/html;charset=utf-8" />
10 <meta name=
"copyright" content=
"
11 TiddlyWiki created by Jeremy Ruston, (jeremy [at] osmosoft [dot] com)
13 Copyright (c) UnaMesa Association 2004-2008
15 Redistribution and use in source and binary forms, with or without modification,
16 are permitted provided that the following conditions are met:
18 Redistributions of source code must retain the above copyright notice, this
19 list of conditions and the following disclaimer.
21 Redistributions in binary form must reproduce the above copyright notice, this
22 list of conditions and the following disclaimer in the documentation and/or other
23 materials provided with the distribution.
25 Neither the name of the UnaMesa Association nor the names of its contributors may be
26 used to endorse or promote products derived from this software without specific
27 prior written permission.
29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY
30 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
31 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
32 SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
33 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
34 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
35 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
37 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
42 <link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'
/>
45 <title> The Killer App of the Future! Reloaded
</title>
46 <style id=
"styleArea" type=
"text/css">
47 #saveTest {display:none;}
48 #messageArea {display:none;}
49 #copyright {display:none;}
50 #storeArea {display:none;}
51 #storeArea div {padding:
0.5em; margin:
1em
0em
0em
0em; border-color:#fff #
666 #
444 #ddd; border-style:solid; border-width:
2px; overflow:auto;}
52 #shadowArea {display:none;}
53 #javascriptWarning {width:
100%; text-align:center; font-weight:bold; background-color:#dd1100; color:#fff; padding:
1em
0em;}
55 <!--POST-HEAD-START-->
59 <body onload=
"main();" onunload=
"if(window.checkUnsavedChanges) checkUnsavedChanges(); if(window.scrubNodes) scrubNodes(document.body);">
64 Welcome to TiddlyWiki created by Jeremy Ruston, Copyright
© 2007 UnaMesa Association
67 <div id=
"javascriptWarning">This page requires JavaScript to function properly.
<br /><br />If you are using Microsoft Internet Explorer you may need to click on the yellow bar above and select 'Allow Blocked Content'. You must then click 'Yes' on the following security warning.
</div>
69 <div id=
"saveTest"></div>
70 <div id=
"backstageCloak"></div>
71 <div id=
"backstageButton"></div>
72 <div id=
"backstageArea"><div id=
"backstageToolbar"></div></div>
74 <div id=
"backstagePanel"></div>
76 <div id=
"contentWrapper"></div>
77 <div id=
"contentStash"></div>
79 <div title=
"MarkupPreHead">
81 <link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' /
>
82 <!--}}}--
></pre>
84 <div title=
"ColorPalette">
101 <div title=
"StyleSheetColors">
103 body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
105 a {color:[[ColorPalette::PrimaryMid]];}
106 a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
109 h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
110 h1 {border-bottom:
2px solid [[ColorPalette::TertiaryLight]];}
111 h2,h3 {border-bottom:
1px solid [[ColorPalette::TertiaryLight]];}
113 .button {color:[[ColorPalette::PrimaryDark]]; border:
1px solid [[ColorPalette::Background]];}
114 .button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
115 .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:
1px solid [[ColorPalette::SecondaryDark]];}
117 .header {background:[[ColorPalette::PrimaryMid]];}
118 .headerShadow {color:[[ColorPalette::Foreground]];}
119 .headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
120 .headerForeground {color:[[ColorPalette::Background]];}
121 .headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}
123 .tabSelected{color:[[ColorPalette::PrimaryDark]];
124 background:[[ColorPalette::TertiaryPale]];
125 border-left:
1px solid [[ColorPalette::TertiaryLight]];
126 border-top:
1px solid [[ColorPalette::TertiaryLight]];
127 border-right:
1px solid [[ColorPalette::TertiaryLight]];
129 .tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
130 .tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:
1px solid [[ColorPalette::TertiaryLight]];}
131 .tabContents .button {border:
0;}
134 #sidebarOptions input {border:
1px solid [[ColorPalette::PrimaryMid]];}
135 #sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
136 #sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
137 #sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
138 #sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}
140 .wizard {background:[[ColorPalette::PrimaryPale]]; border:
1px solid [[ColorPalette::PrimaryMid]];}
141 .wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
142 .wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
143 .wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
144 border:
1px solid [[ColorPalette::PrimaryMid]];}
145 .wizardStep.wizardStepDone {background:[[ColorPalette::TertiaryLight]];}
146 .wizardFooter {background:[[ColorPalette::PrimaryPale]];}
147 .wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
148 .wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border:
1px solid;
149 border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
150 .wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
151 .wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border:
1px solid;
152 border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}
154 .wizard .notChanged {background:transparent;}
155 .wizard .changedLocally {background:#
80ff80;}
156 .wizard .changedServer {background:#
8080ff;}
157 .wizard .changedBoth {background:#ff8080;}
158 .wizard .notFound {background:#ffff80;}
159 .wizard .putToServer {background:#ff80ff;}
160 .wizard .gotFromServer {background:#
80ffff;}
162 #messageArea {border:
1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
163 #messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}
165 .popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:
2px solid [[ColorPalette::TertiaryMid]];}
167 .popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:
1px solid [[ColorPalette::TertiaryMid]]; border-top:
1px solid [[ColorPalette::TertiaryMid]]; border-right:
2px solid [[ColorPalette::TertiaryDark]]; border-bottom:
2px solid [[ColorPalette::TertiaryDark]];}
168 .popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:
1px;}
169 .popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
170 .popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
171 .popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
172 .popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
173 .popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
174 .listBreak div {border-bottom:
1px solid [[ColorPalette::TertiaryDark]];}
176 .tiddler .defaultCommand {font-weight:bold;}
178 .shadow .title {color:[[ColorPalette::TertiaryDark]];}
180 .title {color:[[ColorPalette::SecondaryDark]];}
181 .subtitle {color:[[ColorPalette::TertiaryDark]];}
183 .toolbar {color:[[ColorPalette::PrimaryMid]];}
184 .toolbar a {color:[[ColorPalette::TertiaryLight]];}
185 .selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
186 .selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}
188 .tagging, .tagged {border:
1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
189 .selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:
1px solid [[ColorPalette::TertiaryMid]];}
190 .tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
191 .tagging .button, .tagged .button {border:none;}
193 .footer {color:[[ColorPalette::TertiaryLight]];}
194 .selected .footer {color:[[ColorPalette::TertiaryMid]];}
196 .sparkline {background:[[ColorPalette::PrimaryPale]]; border:
0;}
197 .sparktick {background:[[ColorPalette::PrimaryDark]];}
199 .error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
200 .warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
201 .lowlight {background:[[ColorPalette::TertiaryLight]];}
203 .zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:
3px solid [[ColorPalette::TertiaryMid]];}
205 .imageLink, #displayArea .imageLink {background:transparent;}
207 .annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:
2px solid [[ColorPalette::SecondaryMid]];}
209 .viewer .listTitle {list-style-type:none; margin-left:-
2em;}
210 .viewer .button {border:
1px solid [[ColorPalette::SecondaryMid]];}
211 .viewer blockquote {border-left:
3px solid [[ColorPalette::TertiaryDark]];}
213 .viewer table, table.twtable {border:
2px solid [[ColorPalette::TertiaryDark]];}
214 .viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:
1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
215 .viewer td, .viewer tr, .twtable td, .twtable tr {border:
1px solid [[ColorPalette::TertiaryDark]];}
217 .viewer pre {border:
1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
218 .viewer code {color:[[ColorPalette::SecondaryDark]];}
219 .viewer hr {border:
0; border-top:dashed
1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}
221 .highlight, .marked {background:[[ColorPalette::SecondaryLight]];}
223 .editor input {border:
1px solid [[ColorPalette::PrimaryMid]];}
224 .editor textarea {border:
1px solid [[ColorPalette::PrimaryMid]]; width:
100%;}
225 .editorFooter {color:[[ColorPalette::TertiaryMid]];}
227 #backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
228 #backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
229 #backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
230 #backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
231 #backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
232 #backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
233 #backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
234 .backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
235 .backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
236 #backstageCloak {background:[[ColorPalette::Foreground]]; opacity:
0.6; filter:'alpha(opacity:
60)';}
239 <div title=
"StyleSheetLayout">
241 * html .tiddler {height:
1%;}
243 body {font-size:
.75em; font-family:arial,helvetica; margin:
0; padding:
0;}
245 h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
246 h1,h2,h3 {padding-bottom:
1px; margin-top:
1.2em;margin-bottom:
0.3em;}
247 h4,h5,h6 {margin-top:
1em;}
248 h1 {font-size:
1.35em;}
249 h2 {font-size:
1.25em;}
250 h3 {font-size:
1.1em;}
256 a {text-decoration:none;}
258 dt {font-weight:bold;}
260 ol {list-style-type:decimal;}
261 ol ol {list-style-type:lower-alpha;}
262 ol ol ol {list-style-type:lower-roman;}
263 ol ol ol ol {list-style-type:decimal;}
264 ol ol ol ol ol {list-style-type:lower-alpha;}
265 ol ol ol ol ol ol {list-style-type:lower-roman;}
266 ol ol ol ol ol ol ol {list-style-type:decimal;}
268 .txtOptionInput {width:
11em;}
270 #contentWrapper .chkOptionInput {border:
0;}
272 .externalLink {text-decoration:underline;}
274 .indent {margin-left:
3em;}
275 .outdent {margin-left:
3em; text-indent:-
3em;}
276 code.escaped {white-space:nowrap;}
278 .tiddlyLinkExisting {font-weight:bold;}
279 .tiddlyLinkNonExisting {font-style:italic;}
281 /* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
282 a.tiddlyLinkNonExisting.shadow {font-weight:bold;}
284 #mainMenu .tiddlyLinkExisting,
285 #mainMenu .tiddlyLinkNonExisting,
286 #sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
287 #sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}
289 .header {position:relative;}
290 .header a:hover {background:transparent;}
291 .headerShadow {position:relative; padding:
4.5em
0em
1em
1em; left:-
1px; top:-
1px;}
292 .headerForeground {position:absolute; padding:
4.5em
0em
1em
1em; left:
0px; top:
0px;}
294 .siteTitle {font-size:
3em;}
295 .siteSubtitle {font-size:
1.2em;}
297 #mainMenu {position:absolute; left:
0; width:
10em; text-align:right; line-height:
1.6em; padding:
1.5em
0.5em
0.5em
0.5em; font-size:
1.1em;}
299 #sidebar {position:absolute; right:
3px; width:
16em; font-size:
.9em;}
300 #sidebarOptions {padding-top:
0.3em;}
301 #sidebarOptions a {margin:
0em
0.2em; padding:
0.2em
0.3em; display:block;}
302 #sidebarOptions input {margin:
0.4em
0.5em;}
303 #sidebarOptions .sliderPanel {margin-left:
1em; padding:
0.5em; font-size:
.85em;}
304 #sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:
0;}
305 #sidebarOptions .sliderPanel input {margin:
0 0 .3em
0;}
306 #sidebarTabs .tabContents {width:
15em; overflow:hidden;}
308 .wizard {padding:
0.1em
1em
0em
2em;}
309 .wizard h1 {font-size:
2em; font-weight:bold; background:none; padding:
0em
0em
0em
0em; margin:
0.4em
0em
0.2em
0em;}
310 .wizard h2 {font-size:
1.2em; font-weight:bold; background:none; padding:
0em
0em
0em
0em; margin:
0.4em
0em
0.2em
0em;}
311 .wizardStep {padding:
1em
1em
1em
1em;}
312 .wizard .button {margin:
0.5em
0em
0em
0em; font-size:
1.2em;}
313 .wizardFooter {padding:
0.8em
0.4em
0.8em
0em;}
314 .wizardFooter .status {padding:
0em
0.4em
0em
0.4em; margin-left:
1em;}
315 .wizard .button {padding:
0.1em
0.2em
0.1em
0.2em;}
317 #messageArea {position:fixed; top:
2em; right:
0em; margin:
0.5em; padding:
0.5em; z-index:
2000; _position:absolute;}
318 .messageToolbar {display:block; text-align:right; padding:
0.2em
0.2em
0.2em
0.2em;}
319 #messageArea a {text-decoration:underline;}
321 .tiddlerPopupButton {padding:
0.2em
0.2em
0.2em
0.2em;}
322 .popupTiddler {position: absolute; z-index:
300; padding:
1em
1em
1em
1em; margin:
0;}
324 .popup {position:absolute; z-index:
300; font-size:
.9em; padding:
0; list-style:none; margin:
0;}
325 .popup .popupMessage {padding:
0.4em;}
326 .popup hr {display:block; height:
1px; width:auto; padding:
0; margin:
0.2em
0em;}
327 .popup li.disabled {padding:
0.4em;}
328 .popup li a {display:block; padding:
0.4em; font-weight:normal; cursor:pointer;}
329 .listBreak {font-size:
1px; line-height:
1px;}
330 .listBreak div {margin:
2px
0;}
332 .tabset {padding:
1em
0em
0em
0.5em;}
333 .tab {margin:
0em
0em
0em
0.25em; padding:
2px;}
334 .tabContents {padding:
0.5em;}
335 .tabContents ul, .tabContents ol {margin:
0; padding:
0;}
336 .txtMainTab .tabContents li {list-style:none;}
337 .tabContents li.listLink { margin-left:
.75em;}
339 #contentWrapper {display:block;}
340 #splashScreen {display:none;}
342 #displayArea {margin:
1em
17em
0em
14em;}
344 .toolbar {text-align:right; font-size:
.9em;}
346 .tiddler {padding:
1em
1em
0em
1em;}
348 .missing .viewer,.missing .title {font-style:italic;}
350 .title {font-size:
1.6em; font-weight:bold;}
352 .missing .subtitle {display:none;}
353 .subtitle {font-size:
1.1em;}
355 .tiddler .button {padding:
0.2em
0.4em;}
357 .tagging {margin:
0.5em
0.5em
0.5em
0; float:left; display:none;}
358 .isTag .tagging {display:block;}
359 .tagged {margin:
0.5em; float:right;}
360 .tagging, .tagged {font-size:
0.9em; padding:
0.25em;}
361 .tagging ul, .tagged ul {list-style:none; margin:
0.25em; padding:
0;}
362 .tagClear {clear:both;}
364 .footer {font-size:
.9em;}
365 .footer li {display:inline;}
367 .annotation {padding:
0.5em; margin:
0.5em;}
369 * html .viewer pre {width:
99%; padding:
0 0 1em
0;}
370 .viewer {line-height:
1.4em; padding-top:
0.5em;}
371 .viewer .button {margin:
0em
0.25em; padding:
0em
0.25em;}
372 .viewer blockquote {line-height:
1.5em; padding-left:
0.8em;margin-left:
2.5em;}
373 .viewer ul, .viewer ol {margin-left:
0.5em; padding-left:
1.5em;}
375 .viewer table, table.twtable {border-collapse:collapse; margin:
0.8em
1.0em;}
376 .viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:
3px;}
377 table.listView {font-size:
0.85em; margin:
0.8em
1.0em;}
378 table.listView th, table.listView td, table.listView tr {padding:
0px
3px
0px
3px;}
380 .viewer pre {padding:
0.5em; margin-left:
0.5em; font-size:
1.2em; line-height:
1.4em; overflow:auto;}
381 .viewer code {font-size:
1.2em; line-height:
1.4em;}
383 .editor {font-size:
1.1em;}
384 .editor input, .editor textarea {display:block; width:
100%; font:inherit;}
385 .editorFooter {padding:
0.25em
0em; font-size:
.9em;}
386 .editorFooter .button {padding-top:
0px; padding-bottom:
0px;}
388 .fieldsetFix {border:
0; padding:
0; margin:
1px
0px
1px
0px;}
390 .sparkline {line-height:
1em;}
391 .sparktick {outline:
0;}
393 .zoomer {font-size:
1.1em; position:absolute; overflow:hidden;}
394 .zoomer div {padding:
1em;}
396 * html #backstage {width:
99%;}
397 * html #backstageArea {width:
99%;}
398 #backstageArea {display:none; position:relative; overflow: hidden; z-index:
150; padding:
0.3em
0.5em
0.3em
0.5em;}
399 #backstageToolbar {position:relative;}
400 #backstageArea a {font-weight:bold; margin-left:
0.5em; padding:
0.3em
0.5em
0.3em
0.5em;}
401 #backstageButton {display:none; position:absolute; z-index:
175; top:
0em; right:
0em;}
402 #backstageButton a {padding:
0.1em
0.4em
0.1em
0.4em; margin:
0.1em
0.1em
0.1em
0.1em;}
403 #backstage {position:relative; width:
100%; z-index:
50;}
404 #backstagePanel {display:none; z-index:
100; position:absolute; width:
90%; margin:
0em
3em
0em
3em; padding:
1em
1em
1em
1em;}
405 .backstagePanelFooter {padding-top:
0.2em; float:right;}
406 .backstagePanelFooter a {padding:
0.2em
0.4em
0.2em
0.4em;}
407 #backstageCloak {display:none; z-index:
20; position:absolute; width:
100%; height:
100px;}
409 .whenBackstage {display:none;}
410 .backstageVisible .whenBackstage {display:block;}
413 <div title=
"StyleSheetLocale">
415 StyleSheet for use when a translation requires any css style changes.
416 This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which need larger font sizes.
419 body {font-size:
0.8em;}
420 #sidebarOptions {font-size:
1.05em;}
421 #sidebarOptions a {font-style:normal;}
422 #sidebarOptions .sliderPanel {font-size:
0.95em;}
423 .subtitle {font-size:
0.8em;}
424 .viewer table.listView {font-size:
0.95em;}
427 <div title=
"StyleSheetPrint">
430 #mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none ! important;}
431 #displayArea {margin:
1em
1em
0em
1em;}
432 /* Fixes a feature in Firefox
1.5.0.2 where print preview displays the noscript content */
433 noscript {display:none;}
437 <div title=
"PageTemplate">
438 <pre><!--{{{--
>
439 <div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'
>
440 <div class='headerShadow'
>
441 <span class='siteTitle' refresh='content' tiddler='SiteTitle'
></span
>&nbsp;
442 <span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'
></span
>
444 <div class='headerForeground'
>
445 <span class='siteTitle' refresh='content' tiddler='SiteTitle'
></span
>&nbsp;
446 <span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'
></span
>
449 <div id='mainMenu' refresh='content' tiddler='MainMenu'
></div
>
450 <div id='sidebar'
>
451 <div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'
></div
>
452 <div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'
></div
>
454 <div id='displayArea'
>
455 <div id='messageArea'
></div
>
456 <div id='tiddlerDisplay'
></div
>
458 <!--}}}--
></pre>
460 <div title=
"ViewTemplate">
461 <pre><!--{{{--
>
462 <div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'
></div
>
463 <div class='title' macro='view title'
></div
>
464 <div class='subtitle'
><span macro='view modifier link'
></span
>,
<span macro='view modified date'
></span
> (
<span macro='message views.wikified.createdPrompt'
></span
> <span macro='view created date'
></span
>)
</div
>
465 <div class='tagging' macro='tagging'
></div
>
466 <div class='tagged' macro='tags'
></div
>
467 <div class='viewer' macro='view text wikified'
></div
>
468 <div class='tagClear'
></div
>
469 <!--}}}--
></pre>
471 <div title=
"EditTemplate">
472 <pre><!--{{{--
>
473 <div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'
></div
>
474 <div class='title' macro='view title'
></div
>
475 <div class='editor' macro='edit title'
></div
>
476 <div macro='annotations'
></div
>
477 <div class='editor' macro='edit text'
></div
>
478 <div class='editor' macro='edit tags'
></div
><div class='editorFooter'
><span macro='message views.editor.tagPrompt'
></span
><span macro='tagChooser'
></span
></div
>
479 <!--}}}--
></pre>
481 <div title=
"GettingStarted">
482 <pre>To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
483 * SiteTitle
& SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
484 * MainMenu: The menu (usually on the left)
485 * DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
486 You'll also need to enter your username for signing your edits:
<<option txtUserName
>></pre>
488 <div title=
"OptionsPanel">
489 <pre>These InterfaceOptions for customising TiddlyWiki are saved in your browser
491 Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)
493 <<option txtUserName
>>
494 <<option chkSaveBackups
>> SaveBackups
495 <<option chkAutoSave
>> AutoSave
496 <<option chkRegExpSearch
>> RegExpSearch
497 <<option chkCaseSensitiveSearch
>> CaseSensitiveSearch
498 <<option chkAnimate
>> EnableAnimations
501 Also see AdvancedOptions
</pre>
503 <div title=
"ImportTiddlers">
504 <pre><<importTiddlers
>></pre>
507 <!--POST-SHADOWAREA-->
509 <div title=
"Commands" modifier=
"Zot" created=
"200806191121" changecount=
"1">
512 <div title=
"CompilingInLinux" modifier=
"YourName" created=
"200809032031" modified=
"200809032034" changecount=
"6">
513 <pre>Get [[Eclipse
3.4|http://www.eclipse.org]]
</pre>
515 <div title=
"Completed" modifier=
"BillBurdick" created=
"200808151459" changecount=
"1">
516 <pre>*[''DONE''] The Wedge
518 **Sauerbraten scripting hooks into event flows using the Chain of Command pattern
520 *[''DONE'']Two guys in a box
521 **Peer sends login request
522 **Master sends reference to map and initial location (coordinates and orientation)
526 **N player enhancements
527 **use Scribe instead of point-to-point messaging
528 *[''DONE'']~Shub-Niggurath
529 **map sharing with PAST
535 <div title=
"DefaultTiddlers" modifier=
"BillBurdick" created=
"200806171607" modified=
"200809130549" changecount=
"16">
542 <div title=
"Done" modifier=
"WRB" created=
"200807192043" modified=
"200807192045" changecount=
"2">
543 <pre>7/
18/
08 Roy: Upgrade to latest code base (
2008 6/
19)
546 <div title=
"EmergencyWorspaceReconstruction" modifier=
"BillBurdick" created=
"200809272113" changecount=
"1">
548 # find your eclipse workspace directory
549 # rename .metadata to .metadata.bak
552 # General-
>Existing Projects into Workspace
553 # select your workspace directory as the root directory
554 # import the projects
</pre>
556 <div title=
"FeatureList" modifier=
"BillBurdick" created=
"200809161520" modified=
"200809241829" changecount=
"3">
557 <pre>Dev Features (copied from Roy's post on the cubedevblog)
559 *P2P Networking – All information is stored in a cloud. Models, maps, everything. No central server means that there’s no downtime, and no need to worry about which server your friends are on. Uses Free Pastry for communication. Although this is all independent of our Sauer mods, it's a big deal, so I had to mention it!
561 *Socket level control of Sauerbraten - We've spliced in a socket to communicate with Sauer so you can control the engine from any outside application that uses this protocol. We're writing our stuff in Groovy but you could make your own game using Python or what have you. This allows developers to use whatever language they wish.
563 *World of Warcraft style mouse handling - Sauers mouse handling is frightening for anything other than an FPS, so we've reworked it to be exactly like WoW's. You can even orbit the camera around your toon for screenshots. There are still some bugs/quirks with this in edit mode, but in general it makes editing Sauer maps so much easier.
565 *Randomly generated maze maps - Nothing too fancy, but we don't have many artists helping on the project yet so anything that up the appearance of content is good. Our sauer socket protocol has additional functions to allow external creation of maps.
566 *Importing of Dwarf Fortress maps - There's a popular indie game called Dwarf Fortress which uses old school ASCII style maps. We have an importer that can load these maps into Sauer using the same interface as the random maze generation.
568 *Custom models/costumes – I wrote a model importer that imports md2 and md3 models and creates the md3.cfg so you can load them into Sauer. We modded the engine to allow changing player models on the fly.
570 *Glass Pane Image Layer. Add in images and text to the hud on the fly.
572 *Sandboxed, mobile code (attached to maps, libraries in the cloud)
</pre>
574 <div title=
"Flow" modifier=
"Zot" created=
"200806191220" modified=
"200806191220" changecount=
"2">
575 <pre>~PCs can only perform limited actions without approval
578 * walking within range of the current beacon
580 Actions requiring approval
581 * moving to another beacon
584 * opening things (doors, chests, etc.)
586 When a PC is awaiting approval, it FREEZES
587 * it doesn't queue up multiple requests and overload the moderator
589 The hudgun will change based on context (i.e. where the cursor is)
591 * interacting with an object
592 * interacting with a monster
595 * if there is one choice, indicate it with the hudgun
596 * if there is more than one choice
597 ** indicate multiple with the hudgun
598 ** pop up a menu for the player to choose an appropriate action
601 <div title=
"Goals" modifier=
"WRB" created=
"200807151359" changecount=
"1">
602 <pre>*Basic Interaction
604 **Sauerbraten uses a high performance, client/server network layer.
605 **Since we are using Java to control Sauerbraten, we have the option of using one of several available p2p platforms instead of Sauerbraten's networking
606 **This may unacceptably impact performance, but we'll start off using [[Pastry|http://freepastry.rice.edu]] and see where that leads us
607 **Since MUDS are more social, they may not need such rapid interaction (the ones we wrote in the late
80s were certainly a lot slower than what we can do today).
609 *MUD (automatic moderation)
612 <div title=
"HowToReallyCleanAnEclipseProject" modifier=
"BillBurdick" created=
"200809272000" modified=
"200809272020" changecount=
"2">
613 <pre># select the project in the Package Explorer view
614 # uncheck the menu item Project-
>Build Automatically
615 # choose the menu item Project-
>Clean...
616 # make sure Clean projects selected below is selected
617 # make sure your project is selected in the list
619 # recheck Project-
>Build Automatically
620 # if the project is a groovy project, right-click on it and choose Refresh
</pre>
622 <div title=
"License" modifier=
"BillBurdick" created=
"200808291619" changecount=
"1">
623 <pre>Plexus uses the very friendly, ZLIB open source license (http://www.opensource.org/licenses/zlib-license.php):
625 Copyright (c)
2008 TEAM CTHULHU, Bill Burdick, Roy Riggs
627 This software is provided 'as-is', without any express or implied
628 warranty. In no event will the authors be held liable for any damages
629 arising from the use of this software.
631 Permission is granted to anyone to use this software for any purpose,
632 including commercial applications, and to alter it and redistribute it
633 freely, subject to the following restrictions:
635 1. The origin of this software must not be misrepresented; you must not
636 claim that you wrote the original software. If you use this software
637 in a product, an acknowledgment in the product documentation would be
638 appreciated but is not required.
640 2. Altered source versions must be plainly marked as such, and must not be
641 misrepresented as being the original software.
643 3. This notice may not be removed or altered from any source
646 <div title=
"MainMenu" modifier=
"BillBurdick" created=
"200806171608" modified=
"200809280522" changecount=
"19">
647 <pre>[img[TEAM CTHULHU|docs/images/cthulhu.jpg][http://teamcthulhu.com]]
655 [[Commands|docs/COMMANDS.txt]]
665 [[TiddlyWiki|http://www.tiddlywiki.com]]
<<version
>></pre>
667 <div title=
"MapStorage" modifier=
"BillBurdick" created=
"200808080153" modified=
"200808122251" changecount=
"8">
673 *[DONE] properties file: path -
> id (+ metadata)
674 [DONE] Root documents
675 *[DONE] directories stored by well known keys, and mutable
679 *way to save the cloud to disk that handles old versions
680 **save replace operation in cloud queue
685 *save the map list to the cloud
686 *way to save map to the cloud
</pre>
688 <div title=
"MobileCode" modifier=
"BillBurdick" created=
"200809280523" modified=
"200809280742" changecount=
"6">
691 What can you use a peer-to-peer network for? Messaging and file sharing. Duh.
693 How about a hive-mind?
695 We have a new feature now: Groovy code for maps. In Sauerbraten, your map is stored in a map.ogz file. Plexus stores each map in its own directory in your profile's cache. You can use textures and sounds just like you would normally, store in the packages directory and also from the current map's directory, using a variable that provides the cache directory which currently contains the map. As usualy, there is a map.cfg file, but there is also an important map.groovy file which you can provide.
697 Map.groovy can contain Groovy code which executes in a sandbox in the peer. This means you can load other peoples' maps with their associated Groovy code and you don't have to worry about their code accessing your files. We use the Java sandbox to restrict read and write access to only the map's directory (among other draconian restrictions). Please, if you are into this sort of thing, check out our Sandbox.groovy code to verify for yourself! So you have groovy code that can live in each map. We also provide a mapProps map (i.e. hash table) for you which you can use to store persistent map data. It's reinitialized when maps load, but map.groovy can save and load data with it. And we provide a globalProps map that maps can share with each other. We'll also provide some cloud storage access for stronger persistence.
699 OK, so you have code for each map. Now you can make brains for shop keepers, traps, etc. and of course you can use this to implement game mechanics. In the next weeks and months, we're going to implement
"cloud libraries
" which you can use in your map.groovy code. This will allow people to write game mechanics and other frameworks that you can use across different maps. We'll be upgrading the plexus cache to be a real Git cache (it already shares some ideas with Git anyway; it implements content-addressable files and directories) and you'll be able to configure your peer to back up selected parts of the cloud in your own private Git cache, for good measure. This will help protect the integrity of the cloud, since there is no central server.
701 That's all kind of a logical extension of what we have, but there's something more that I'm pretty interested in seeing actually run. We plan to allow distributed control of brains. The current idea is to make brains //relatively// stateless, so that each
"tick,
" a brain
"wakes up
" and starts over (kind of like the movie Memento). This model allows brains to transfer around between peers. The peer in a world with an ID closest to the brain's ID will control it. This is using a DHT to arbitrate control over the set of available brains. When peers join or leave from a world, brains will shuffle around to accommodate the new state of the ring (Pastry nodes are arranged in a ring, according to their ~IDs). To help manage this, brains will have two
"storage areas:
" a local
"peer area
" for incidental bookkeeping and a global
"cloud area
" for major information that has to be restored when a brain jarringly transfers over to another peer.
703 All of the peers have the code for all of the brains because they come with the map but a given brain will only have one 'pilot peer' at a time. A world's DHT consists of only the peers in that world, not the full Plexus Pastry ring. Pastry doesn't have
"subrings
" right now that would make this easy to implement, but each world does use a SCRIBE topic for communication, so we can leverage that. My current approach is to use a cloud properties object (like the one that Plexus uses for players, maps, and costumes) to assign brains to peers. Maybe the topic root can be in charge of shuffling brains after a join or leave. Right now, cloud properties are not stored in PAST -- they are read in by the root peer and then managed entirely with SCRIBE and direct messaging (for booting), but that can change if it's not good enough.
706 <div title=
"MudLingua" modifier=
"Zot" created=
"200806191123" changecount=
"1">
707 <pre>A primordial language of creation
</pre>
709 <div title=
"News" modifier=
"Zot" created=
"200806190726" changecount=
"1">
710 <pre>!
2008 6/
19 Capture the Flag
711 Well, a new version of Sauerbraten is out: Capture the Flag (http://www.cubeengine.com/forum.php4?action=display_thread
&thread_id=
1782). I made a
"dist
" branch and put it in there and tagged the assassin version and that one (with date tags). I'll have to merge that into the main branch now. Let's see how well git's famous merging capabilities work...
</pre>
713 <div title=
"OldDocs" modifier=
"BillBurdick" created=
"200806191152" modified=
"200806191156" changecount=
"2">
714 <pre>[[Philosophy|docs/p2pmud-old/philosophy.html]]
715 [[Tutorial|docs/p2pmud-old/tutorial.html]]
716 [[Parser|docs/p2pmud-old/parser_tutorial.html]]
719 <div title=
"PeerToPeer" modifier=
"Zot" created=
"200806191132" changecount=
"1">
720 <pre>Here is a slippery topic. At the most basic level, peer to peer (p2p) used to mean that applications communicate directly instead of having to use a server (or several servers). Peer to peer has come to mean the use of an
"overlay
" network atop a conventional network. There is no central control in a pure p2p network.
</pre>
722 <div title=
"Philosophy" modifier=
"BillBurdick" created=
"200809280504" modified=
"200810040542" changecount=
"14">
723 <pre>!NOTE! THIS IS FROM OUR PREVIOUS PROJECT, ~P2PMUD, BUT THE PHILOSOPHY CARRIES ON IN PLEXUS
725 Plexus uses Groovy, instead of Javascript
726 Plexus uses Sauerbraten and SwingX, instead of a web browser (XUL and HTML)
727 Plexus uses Pastry for true peer-to-peer networking, instead of AJAX
732 <p
><h2
>Basic design goals and philosophy behind the p2pmud project
</h2
>
733 <p
>Read this if you're interested in developing with p2pmud
735 <p
>p2pmud is written in a combination of XUL and Javascript. XUL is an XML based language used for describing UI
736 (user interface) and if you are a coding/scripting type of person you've surely heard of Javascript. Odds are
737 however, you probably don't really know today's Javascript.
739 <p
>Javascript has silently evolved over the last few years. While it started out as a clunkly little scripting
740 language, it has blossomed into a very dynamic, full-featured language. Java may have gotten the spotlight
741 for a long time, but its going to have to move over and make way for Javascript sometime soon.
743 <h2
>p2pmud is a layered system
</h2
>
744 Each layer builds or relies on the previous layers beneath them. You can choose
745 to pitch in and help out on the project at any layer(s) that suit you. There is a core layer, Level I, which
746 provides a foundation layer. The next layer implementing actual game mechanics, is called Level II. Sometimes
747 people are referred to by their Level, where Level here describes what they like to do. Someone who likes to
748 create adventures and populate them with monsters and treasure is referred to Level III. They may or may not
749 write anything in Javascript, they may just use what was provided by Level II. Level IV is considered a player
750 who doesn't code or build at all, they just want to play. Level IV's are not to be looked down upon, after all,
751 they are ultimately the ones we went to all this trouble for!
753 <h2 class=
"cmd
">Level I
</h2
>
754 P2PMud at its heart implements mechanism, NOT policy. P2PMud is an enabling technology to provide the framework
755 to make distributed MUDs work. It provides the ability for each local mud to discover other MUDs and create dynamic
756 portals between them. We're not talking just instant messages here either. When your character steps through a portal,
757 they and all their inventory are streamed across the virtual network so they actually appear in the other MUD. It also
758 affords transaction base world persistence, meaning it continuously saves the state of your local MUD so it is *always*
759 backed up and can be restored from any point. No matter whatever happens out there, you are safe in knowing you can always
760 put things back the way they were on your own MUD. p2pmud provides access to all of this from a Javascript interface.
763 <h2 class=
"cmd
">Level II
</h2
>
764 Since just a framework by itself is not very interesting, p2pmud provides a default game mechanics system (GMS)
765 written in Javascript. This reference layer can be used as is to create your own world, or you can modify it as you see
766 fit to build your own GMS! This is where all of the game mechanics are described and game policy is implemented. Love d20
767 rules? Start a project to implement d20!
769 <h2 class=
"cmd
">Level III
</h2
>
770 Even a GMS by itself is not too useful to players. If your love is building worlds and adventures, just pick a
771 GMS that suits you and start building away. Each GMS will have a set of tools or commands you can use to create rooms,
772 spaceships, weapons, monsters, or whatever. They likely will come will entire libraries of objects you can use to build with.
773 If you get bit by the scripting bug, you too can use Javascript to implement that +
20 flaming broadsword with whatever special
774 powers you can dream up! Whichever GMS you choose will provide an interface for game interactions your scripts can build on.
776 <h2 class=
"cmd
">Level IV
</h2
>
777 If you're screaming that you just want to play already, the intent is that each GMS comes with a default world
778 that consists of a single house your character can live in. Install p2pmud and your GMS of choice. Once you've launched it,
779 you can use the peer discovery to create portals to worlds created by other people. Venture out through a portal into the virtual
780 world, kill the baddies and get the girl! Maybe some day you'll learn to understand why others love to build so much and choose
781 to expand your own little corner of the world!
783 <p
>Objects created in one GMS are not likely to work in another, so we expect to see a number of different virtual worlds spring
784 up around each GMS. Since the GMS layers implement policy, these will likely start to develop around particular themes. For
785 example, one GMS might be more suited for space adventures, one for ancient worlds, etc.
789 <div title=
"Plexus" modifier=
"BillBurdick" created=
"200806171636" modified=
"200808291619" changecount=
"12">
790 <pre>P2PMud is a peer-to-peer multiuser dungeon with a [[long history|Reloaded]]
792 And it's The Killer App of the Future!
794 The //''P2PMud [[Reloaded]]''// project adds support to the fantastic ''[[Sauerbraten|http://sauerbraten.org]]'' engine to allow remote scripting and hooking in at deep levels, with the goal of minimizing changes to the Sauerbraten source code.
796 Part of this is putting in hooks for moderated play, for instance, chain of command-style hooks for weapon hits. You can register a hook that Sauerbraten calls instead of the normal hit code. The hook may invoke a
"defaulthit
" command to proceed with normal damage.
798 [[Here|http://repo.or.cz/w/SauerbratenRemote.git?a=blob_plain;f=index.html;hb=HEAD]] is the latest version of this document and [[here|http://repo.or.cz/w/SauerbratenRemote.git]] is the source code repository.
</pre>
800 <div title=
"PotentialIssues" modifier=
"Zot" created=
"200806191223" modified=
"200806191224" changecount=
"2">
801 <pre>relaying (if using Sauerbraten's server)
802 *to prevent, set curmsg = p.length(), otherwise it multicasts the current message
</pre>
804 <div title=
"Reloaded" modifier=
"BillBurdick" created=
"200806171638" modified=
"200810101524" changecount=
"20">
805 <pre>~P2PMud traces its roots back to the murky depths of history. In
1988, Roy Riggs first wrote a tiny multi-user dungeon in C, one of the earliest ~MMORPGs that ran over the Internet (while, maybe an MORPG, anyway.) It took only a matter of days, or perhaps even hours, for Bill Burdick to join Roy in this glorious endevour and together they formed the sinister TEAM CTHULHU!
807 Since that time, TEAM CTHULHU's MUD has metamophosed many times, each time shedding its carapace to reveal something bizarrely different from its predecessor.
809 You can even see a reference to TEAM CTHULHU in the CREDITS file of [[LPMud|http://www.genesismud.org/]]. We came up with an idea called
"forwarders
" for our original MUD which became
"shadows
" in ~LPMud. There's a fairly early version of ~LPMud [[here|http://ibiblio.net/pub/historic-linux/ftp-archives/sunsite.unc.edu/Nov-
06-
1994/games/muds/]].
811 !TEAM CTHULHU Timeline
812 #Fall
1988, Roy Riggs write a miniscule MUD in C
813 #Fall
1988, Bill Burdick jumps on board and we write a Franz LISP-based version, quickly switching to KCL
814 #Fall
1988, we switch to our own language, MOB, a multiple-inheritance, OOPL on top of LISP, with Smalltalk-like syntax in square brackets (ala Objective-C)
815 **over the next two years, we get
4 semesters of credit for enhancements to this version
816 #Summer
1990, we switch to Sean Barrett's excellent Sludge VM, which supports prototype-based inheritance.
818 #Spring
2002, we start p2pmud in using JXTA and Kawa, a Scheme implementation on top of Java
819 #Early
2004, we switch to Mozilla and Javascript. The MUD lives entirely in one HTML file on your disk (like tiddly wiki). Your browser hooks up to other browsers via a PHP
"switchboard
"
820 **Several releases on the p2pmud.com website
821 #Fall
2005, we commission Steven Missal to create Lovecraftian art
822 #Spring
2008, we switch to Sauerbraten, Groovy, and Pastry (on top of Java)
</pre>
824 <div title=
"RoadMap" modifier=
"BillBurdick" created=
"200806191122" modified=
"200810180649" changecount=
"256">
826 *Do not allow connection unless Plubble can connect back
827 *Handle error loading a map from a peer that drops out
828 *camera rotation problem on Linux
830 *if this is a new plexus version, offer to toast the cache
831 *Pastry reconnect business (multiple root peers?)
832 *set CPPFLAGS -DTC in src/Makefile's call to src/enet/configure
833 *Make md3load handle missing files (deactivate md3skin, md3pitch, md3anim, md3link, mdlspec, mdlscale, mdltrans, etc.) -- maybe make it load a default model (like Zippy the Pinhead)
834 *make updateMappingDiag() also display results for dumpents
835 *Diag panes for sauer and pastry cmds
836 *plexus profile editor in sauer (prepopulate wizard)
837 *combine swing windows
838 *only show diag tools until the first successful connect. Then make it an option.
840 *Monsters (phantom players)
842 *responsibility allocation
843 **maybe have a doc for each world that allocates responsibilities among the peers of a world
844 **when responsibilities or peers join or leave, responsibilities shuffle around
845 **we can store responsibility allocation in PAST
846 **might need a world-ping
847 *Create, destroy, and move objects (GUIDs)
850 **Save hook defined in map.groovy
851 **Save menu choice (which calls map's save hook)
853 **clean up obsolete maps
856 **use crosshairs for aiming when they are visible
857 **register sauer cmds for hit effects based on shooter and/or target
858 **show bio when you shoot someone with an examination gun
862 **Directory entry ownership by digital signatures (peers and groups) -- only an owner can change it
865 **specify box with attachment point, vector, and height
866 **specify driving position with attachment point
867 **driver can switch between player view and vehicle view
868 **physics interacts with box
869 **players can move around inside, fire grenades, etc.
870 *groovy libraries in the cloud
872 *DONE Detect double nat with upnp
873 *DONE Update costume list as thumbs arrive (don't wait for all of them before updating)
874 *DONE constumes getting reset to null (mr fixit) -- seems connected to costume uploading
875 *DONE when costume becomes null, sometimes it doesn't change it
876 *DONE fix case for md2 import
877 *DONE check response values to verify mappings succeed, remove upnp mappings during shutdown, remove cleanup call (make button?)
878 *DONE limbo name not showing up
880 *DONE Map name sometimes shows up as
"none
" when you are on a map
881 *DONE Blank costume no longer switches to Mr Fixit
882 *DONE players can taunt each other
883 *DONE Filter out Thumbs.db and
0 length files
884 *DONE drag selection with left button
885 *DONE cleanup connecting to existing sauer
886 *DONE remove date from directory properties so that dirs are not mistakenly unique
887 *DONE cloud
"power light
"
888 *DONE aiming/edit selection problem
889 *DONE Hidden worlds (not advertised, but part of the cloud)
890 *DONE Finish tutorial menus
891 *DONE [replaced by map.groovy] fix bug in saveGroovyData()
892 *DONE [replaced by map.groovy
& mapProps] persist JSON object as groovy file in map dir for metadata map development
893 *DONE allow turning off recoil for shots
894 *DONE allow user to restart plexus from sauer
895 *DONE cache player updates for your map so follow can teleport immediately
896 **DONE restore world when you restart sauer
897 *DONE pick random port in range
898 *DONE disable Launch Sauer until connected
899 *DONE test costume uploading
900 *DONE broadcast disconnect with shutting down
901 *DONE check java version on startup
902 *DONE remote guild names not showing
903 *DONE incoming cloud change notification to user
904 *DONE show feedback for unsuccessful port back-connection during ip discovery
905 *DONE Missing player problem -- when loading map, players clear out of sauer but not out of plexus id/name tables
906 *DONE Groovy version of build script
907 *DONE JSON format for cloud properties
908 *DONE (fluke?) on find map, couldn't find map with tume id instead of map id
909 *DONE upload/download progress notification
910 *DONE notification of droppage
912 *DONE Clean button in prep gui
913 *DONE options for cleanup on start/completely fresh start
915 *DONE left msg uses nodename instead of player name
916 *DONE switches remote player costume before d/l completed
917 *DONE download into temp dirs and only rename when completed
918 *DONE map member count is always
0
919 *DONE don't subscribe to map until successfully downloaded
921 *DONE Rework existing editing menu, much doesn't apply
922 *DONE decouple camera speed from players speed
923 *DONE Make players start off in Limbo
925 *DONE make LMB detach camera to orbit around the toon
926 *DONE make sauer load models from plexus folder
927 *DONE Global whispers
928 *DONE Map name on load screen
929 *DONE Make push handle regular maps (make manifest that places it in a subdir)
931 *DONE Replace map should broadcast
"switchmap
" pastry cmd
932 *DONE Message dialog: showmessage title text
933 *DONE make cursors load from plexus/dist
934 *DONE separate backup directory
935 *DONE Use UPnP to discover external IP address
936 *DONE delete player on peer disconnect
937 *DONE clean up players on world connection
938 *DONE Show connection count in world menu
939 *DONE Investigate occasional sauer/groovy lag on linux
940 *DONE Headless peer on Plubble
941 *DONE include UPnP to open external port in firewall
942 *DONE add tc_respawn command to put players back at spawn points
943 *DONE don't show original menu on startup
944 *DONE Hide Gun in First Person mode
945 *DONE Disable weapon swapping, disable shooting
946 *DONE Make Limbo map come up by default
947 *DONE Rename wowmode to tcmode
948 *DONE make cursor float above HUD
949 *DONE load limbo in sauer on startup
950 *DONE Show only filename on map thumbnails when loading
951 *DONE HUD add peer count
952 *DONE launch sauer in sep thread while connecting to pastry
953 *DONE add button to reload sauer
954 *DONE move commands from groovy init into a .cfg file
956 **DONE Check out out of PAST disk space errors
957 **DONE Global, shared directory for cloud
960 **DONE Presence topic for total online count
961 **DONE available world listing/selection from gui
964 **DONE Map connection count
973 **materials for sensors (use var to set material id) (callback set on material id)
974 **inventory mgmt (+ gui)
978 **dead monsters drop loot
979 **wizard guns (create monster, move, talk, paralyze, destroy)
980 **saved map chunks
</pre>
982 <div title=
"SiteSubtitle" modifier=
"Zot" created=
"200806171609" modified=
"200806191121" changecount=
"11">
983 <pre>The Killer App of the Future! //[[Reloaded]]//
</pre>
985 <div title=
"SiteTitle" modifier=
"Zot" created=
"200806171608" modified=
"200806191146" changecount=
"9">
986 <pre>[img[P2PMud|docs/images/p2pmud.gif]]
</pre>
988 <div title=
"StartingOut" modifier=
"BillBurdick" created=
"200807142041" modified=
"200809272113" changecount=
"12">
989 <pre>*P2PMud is currently based on the Assassin version of Sauerbraten, which you can download [[here|http://sourceforge.net/project/showfiles.php?group_id=
102911&package_id=
110363&release_id=
563483]]
990 *This repository is an Eclipse project, so you'll probably want Eclipse and the CDT feature for C/C++
991 Once you have the stuff, you need to:
993 *Copy/link sauer_client and scripts/autoexec.cfg into the top level of the sauerbraten directory (in the same directory as server.bat).
994 *Start the groovy command server in Eclipse (using the Cmd Server launcher)
995 *Start sauer (I find it's much better to start up with the -t option when testing, so it doesn't take over my screen)
996 !
<html
><span style=
"background: red
">HAVING PROBLEMS?
</span
></html
>
997 HowToReallyCleanAnEclipseProject
998 EmergencyWorspaceReconstruction
</pre>
1000 <div title=
"StyleSheet" modifier=
"BillBurdick" created=
"200809280501" modified=
"200809280525" changecount=
"6">
1002 .cthulhu { text-decoration: none;
1004 font-family:
"arial
";
1006 font-weight: medium;
1007 background-color: #
282619;
1009 padding-bottom:
3px;
1011 .cmd.cthulhu { text-decoration: none;
1013 font-family:
"arial
";
1015 font-weight: medium;
1017 a.cthulhu:link { text-decoration: none;
1019 font-family:
"arial
";
1023 a.cthulhu:visited { text-decoration: none;
1025 font-family:
"arial
";
1031 <div title=
"ToDo" modifier=
"WRB" created=
"200806171610" modified=
"200807192045" changecount=
"5">
1038 Update docs to refer to new platform (Groovy/Sauerbraten) instead of old (Javascript/Mozilla)
1042 Drag and drop for browser images?
1045 master player -- invisible/intangible and does not transmit coords to the server
1047 puppet -- move according to instructions (default is frozen)
1048 normal -- moves with limitations (movement blockers)
1050 beacons: players can move as long as they remain within the radius of at least one authorized beacon
1051 This allows cell-style hand off
1052 walls: players can pass through authorized walls
1056 moveto id x y z -- sets id's movement target to location
1058 contexthook hook -- when active, execute hook on context change for player1
1059 usecontext on/off -- set whether player1's contexthook is active
1061 authorize id blocker
1062 unauthorize id blocker
1063 puppet id -- makes id move toward its movement target and if it's a player unyoke the controls
1064 unpuppet id -- makes id ignore its movement target and if it's a player, yoke the controls
1069 move -- shoot destination, then target(s)
1070 authorize -- shoot blocker, then target(s)
1071 unauthorize -- shoot blocker, then target(s)
1080 mastercommand cmd
</pre>
1082 <div title=
"Toys" modifier=
"BillBurdick" created=
"200808151458" changecount=
"1">
1083 <pre>launch gun -- sets velocity on opponents
1086 *jet-skate ramp target contest: skates raise your max speed, turn on rumble pack, and apply small random adjustments to your position and orientation at random intervals
1088 might be doable with only triggers and watcher code
1090 roman candles/wand of fireballs
1091 physics for wall splat
1093 monsters control assigned by DHT
1094 map sharing includes map.groovy file in addition to map.ogz and map.cfg
1096 *Each world gets a branch off the initial starting directory (a dir with a readme)
1097 *Git tree objects are stored in PAST, cached in a local Git repo, and extracted into a local directory
</pre>
1099 <div title=
"UseCases" modifier=
"BillBurdick" created=
"200807300630" modified=
"200807300650" changecount=
"3">
1101 ##player rolls over an item
1102 ##item adds to inventory
1103 ##player presses
"i
" key
1104 ##inventory menu pops up
1105 ##player chooses a different item from menu
1107 ##player chooses drop
1108 ##item leaves inventory and appears on map
1110 ##player approaches door
1111 ##door says
"locked
"
1112 ##player finds key and reapproaches door
1115 ##player enters shop
1116 ##shop keeper says
"welcome to the shop
"
1117 ##player switches to shopping gun
1118 ##items are visible on the counter
1119 ##player shoots item
1120 ##menu pops up and offers buy choice
1121 ##player selects
"buy
" and item gets added to inventory and gold is deducted
1122 ##player shoots shop keeper
1123 ##shop keeper menu pops up and offers to buy items
</pre>
1125 <div title=
"Zot" modifier=
"Zot" created=
"200806171649" changecount=
"1">
1126 <pre>Zot is one of the founding members of [[TEAM CTHULHU|http://teamcthulhu.com]]
</pre>
1129 <!--POST-STOREAREA-->
1130 <!--POST-BODY-START-->
1131 <!--POST-BODY-END-->
1132 <script id=
"jsArea" type=
"text/javascript">
1137 // * This code is designed to be readable but for compactness it only includes brief comments. You can see fuller comments
1138 // in the project Subversion repository at http://svn.tiddlywiki.org/Trunk/core/
1140 // * You should never need to modify this source code directly. TiddlyWiki is carefully designed to allow deep customisation
1141 // without changing the core code. Please consult the development group at http://groups.google.com/group/TiddlyWikiDev
1145 //-- Configuration repository
1148 // Miscellaneous options
1150 numRssItems:
20, // Number of items in the RSS feed
1151 animDuration:
400, // Duration of UI animations in milliseconds
1152 cascadeFast:
20, // Speed for cascade animations (higher == slower)
1153 cascadeSlow:
60, // Speed for EasterEgg cascade animations
1154 cascadeDepth:
5, // Depth of cascade animation
1155 locale:
"en" // W3C language tag
1158 // Hashmap of alternative parsers for the wikifier
1159 config.parsers = {};
1162 config.adaptors = {};
1163 config.defaultAdaptor = null;
1169 config.annotations = {};
1171 // Custom fields to be automatically added to new tiddlers
1172 config.defaultCustomFields = {};
1181 // Options that can be set in the options panel and/or cookies
1183 chkRegExpSearch: false,
1184 chkCaseSensitiveSearch: false,
1185 chkIncrementalSearch: true,
1187 chkSaveBackups: true,
1189 chkGenerateAnRssFeed: false,
1190 chkSaveEmptyTemplate: false,
1191 chkOpenInNewWindow: true,
1192 chkToggleLinks: false,
1193 chkHttpReadOnly: true,
1194 chkForceMinorUpdate: false,
1195 chkConfirmDelete: true,
1196 chkInsertTabs: false,
1197 chkUsePreForStorage: true, // Whether to use
<pre> format for storage
1198 chkDisplayInstrumentation: false,
1199 txtBackupFolder:
"",
1200 txtEditorFocus:
"text",
1201 txtMainTab:
"tabTimeline",
1202 txtMoreTab:
"moreTabAll",
1203 txtMaxEditRows:
"30",
1204 txtFileSystemCharSet:
"UTF-8",
1207 config.optionsDesc = {};
1209 // Default tiddler templates
1210 var DEFAULT_VIEW_TEMPLATE =
1;
1211 var DEFAULT_EDIT_TEMPLATE =
2;
1212 config.tiddlerTemplates = {
1217 // More messages (rather a legacy layout that should not really be like this)
1228 config.backstageTasks = [
"save",
"sync",
"importTask",
"tweak",
"upgrade",
"plugins"];
1230 // Macros; each has a 'handler' member that is inserted later
1234 search: {sizeTextbox:
15},
1260 view: {defaultView:
"text"},
1268 source:
"http://www.tiddlywiki.com/upgrade/",
1269 backupExtension:
"pre.core.upgrade"
1275 // Commands supported by the toolbar macro
1280 saveTiddler: {hideReadOnly: true},
1282 deleteTiddler: {hideReadOnly: true},
1284 references: {type:
"popup"},
1285 jump: {type:
"popup"},
1286 syncing: {type:
"popup"},
1287 fields: {type:
"popup"}
1290 // Browser detection... In a very few places, there's nothing else for it but to know what browser we're using.
1291 config.userAgent = navigator.userAgent.toLowerCase();
1293 isIE: config.userAgent.indexOf(
"msie") != -
1 && config.userAgent.indexOf(
"opera") == -
1,
1294 isGecko: config.userAgent.indexOf(
"gecko") != -
1,
1295 ieVersion: /MSIE (\d.\d)/i.exec(config.userAgent), // config.browser.ieVersion[
1], if it exists, will be the IE version string, eg
"6.0"
1296 isSafari: config.userAgent.indexOf(
"applewebkit") != -
1,
1297 isBadSafari: !((new RegExp(
"[\u0150\u0170]",
"g")).test(
"\u0150")),
1298 firefoxDate: /gecko\/(\d{
8})/i.exec(config.userAgent), // config.browser.firefoxDate[
1], if it exists, will be Firefox release date as
"YYYYMMDD"
1299 isOpera: config.userAgent.indexOf(
"opera") != -
1,
1300 isLinux: config.userAgent.indexOf(
"linux") != -
1,
1301 isUnix: config.userAgent.indexOf(
"x11") != -
1,
1302 isMac: config.userAgent.indexOf(
"mac") != -
1,
1303 isWindows: config.userAgent.indexOf(
"win") != -
1
1306 // Basic regular expressions
1307 config.textPrimitives = {
1308 upperLetter:
"[A-Z\u00c0-\u00de\u0150\u0170]",
1309 lowerLetter:
"[a-z0-9_\\-\u00df-\u00ff\u0151\u0171]",
1310 anyLetter:
"[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]",
1311 anyLetterStrict:
"[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]"
1313 if(config.browser.isBadSafari) {
1314 config.textPrimitives = {
1315 upperLetter:
"[A-Z\u00c0-\u00de]",
1316 lowerLetter:
"[a-z0-9_\\-\u00df-\u00ff]",
1317 anyLetter:
"[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff]",
1318 anyLetterStrict:
"[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff]"
1321 config.textPrimitives.sliceSeparator =
"::";
1322 config.textPrimitives.sectionSeparator =
"##";
1323 config.textPrimitives.urlPattern =
"(?:file|http|https|mailto|ftp|irc|news|data):[^\\s'\"]+(?:/|\\b)
";
1324 config.textPrimitives.unWikiLink = "~
";
1325 config.textPrimitives.wikiLink = "(?:(?:
" + config.textPrimitives.upperLetter + "+
" +
1326 config.textPrimitives.lowerLetter + "+
" +
1327 config.textPrimitives.upperLetter +
1328 config.textPrimitives.anyLetter + "*)|(?:
" +
1329 config.textPrimitives.upperLetter + "{
2,}
" +
1330 config.textPrimitives.lowerLetter + "+))
";
1332 config.textPrimitives.cssLookahead = "(?:(
" + config.textPrimitives.anyLetter + "+)\\(([^\\)\\|\\n]+)(?:\\):))|(?:(
" + config.textPrimitives.anyLetter + "+):([^;\\|\\n]+);)
";
1333 config.textPrimitives.cssLookaheadRegExp = new RegExp(config.textPrimitives.cssLookahead,"mg
");
1335 config.textPrimitives.brackettedLink = "\\[\\[([^\\]]+)\\]\\]
";
1336 config.textPrimitives.titledBrackettedLink = "\\[\\[([^\\[\\]\\|]+)\\|([^\\[\\]\\|]+)\\]\\]
";
1337 config.textPrimitives.tiddlerForcedLinkRegExp = new RegExp("(?:
" + config.textPrimitives.titledBrackettedLink + ")|(?:
" +
1338 config.textPrimitives.brackettedLink + ")|(?:
" +
1339 config.textPrimitives.urlPattern + ")
","mg
");
1340 config.textPrimitives.tiddlerAnyLinkRegExp = new RegExp("(
"+ config.textPrimitives.wikiLink + ")|(?:
" +
1341 config.textPrimitives.titledBrackettedLink + ")|(?:
" +
1342 config.textPrimitives.brackettedLink + ")|(?:
" +
1343 config.textPrimitives.urlPattern + ")
","mg
");
1347 function() {return config.browser.isIE;},
1348 function() {return true;}
1352 downTriangle: ["\u25BC
","\u25BE
"],
1353 downArrow: ["\u2193
","\u2193
"],
1354 bentArrowLeft: ["\u2190
","\u21A9
"],
1355 bentArrowRight: ["\u2192
","\u21AA
"]
1360 //-- Shadow tiddlers
1363 config.shadowTiddlers = {
1369 TabTimeline: '<<timeline>>',
1370 TabAll: '<<list all>>',
1371 TabTags: '<<allTags excludeLists>>',
1372 TabMoreMissing: '<<list missing>>',
1373 TabMoreOrphans: '<<list orphans>>',
1374 TabMoreShadowed: '<<list shadowed>>',
1375 AdvancedOptions: '<<options>>',
1376 PluginManager: '<<plugins>>',
1377 ToolbarCommands: '|~ViewToolbar|closeTiddler closeOthers +editTiddler > fields syncing permalink references jump|\n|~EditToolbar|+saveTiddler -cancelTiddler deleteTiddler|'
1381 //-- Translateable strings
1384 // Strings in "double quotes
" should be translated; strings in 'single quotes' should be left alone
1386 merge(config.options,{
1387 txtUserName: "YourName
"});
1389 merge(config.tasks,{
1390 save: {text: "save
", tooltip: "Save your changes to this TiddlyWiki
", action: saveChanges},
1391 sync: {text: "sync
", tooltip: "Synchronise changes with other TiddlyWiki files and servers
", content: '<<sync>>'},
1392 importTask: {text: "import
", tooltip: "Import tiddlers and plugins from other TiddlyWiki files and servers
", content: '<<importTiddlers>>'},
1393 tweak: {text: "tweak
", tooltip: "Tweak the appearance and behaviour of TiddlyWiki
", content: '<<options>>'},
1394 upgrade: {text: "upgrade
", tooltip: "Upgrade TiddlyWiki core code
", content: '<<upgrade>>'},
1395 plugins: {text: "plugins
", tooltip: "Manage installed plugins
", content: '<<plugins>>'}
1398 // Options that can be set in the options panel and/or cookies
1399 merge(config.optionsDesc,{
1400 txtUserName: "Username for signing your edits
",
1401 chkRegExpSearch: "Enable regular expressions for searches
",
1402 chkCaseSensitiveSearch: "Case-sensitive searching
",
1403 chkIncrementalSearch: "Incremental key-by-key searching
",
1404 chkAnimate: "Enable animations
",
1405 chkSaveBackups: "Keep backup file when saving changes
",
1406 chkAutoSave: "Automatically save changes
",
1407 chkGenerateAnRssFeed: "Generate an RSS feed when saving changes
",
1408 chkSaveEmptyTemplate: "Generate an empty template when saving changes
",
1409 chkOpenInNewWindow: "Open external links in a new window
",
1410 chkToggleLinks: "Clicking on links to open tiddlers causes them to close
",
1411 chkHttpReadOnly: "Hide editing features when viewed over HTTP
",
1412 chkForceMinorUpdate: "Don't update modifier username and date when editing tiddlers
",
1413 chkConfirmDelete: "Require confirmation before deleting tiddlers
",
1414 chkInsertTabs: "Use the tab key to insert tab characters instead of moving between fields
",
1415 txtBackupFolder: "Name of folder to use for backups
",
1416 txtMaxEditRows: "Maximum number of rows in edit boxes
",
1417 txtFileSystemCharSet: "Default character set for saving changes (Firefox/Mozilla only)
"});
1419 merge(config.messages,{
1420 customConfigError: "Problems were encountered loading plugins. See PluginManager for details
",
1421 pluginError: "Error: %
0",
1422 pluginDisabled: "Not executed because disabled via 'systemConfigDisable' tag
",
1423 pluginForced: "Executed because forced via 'systemConfigForce' tag
",
1424 pluginVersionError: "Not executed because this plugin needs a newer version of TiddlyWiki
",
1425 nothingSelected: "Nothing is selected. You must select one or more items first
",
1426 savedSnapshotError: "It appears that this TiddlyWiki has been incorrectly saved. Please see http://www.tiddlywiki.com/#DownloadSoftware for details
",
1427 subtitleUnknown: "(unknown)
",
1428 undefinedTiddlerToolTip: "The tiddler '%
0' doesn't yet exist
",
1429 shadowedTiddlerToolTip: "The tiddler '%
0' doesn't yet exist, but has a pre-defined shadow value
",
1430 tiddlerLinkTooltip: "%
0 - %
1, %
2",
1431 externalLinkTooltip: "External link to %
0",
1432 noTags: "There are no tagged tiddlers
",
1433 notFileUrlError: "You need to save this TiddlyWiki to a file before you can save changes
",
1434 cantSaveError: "It's not possible to save changes. Possible reasons include:\n- your browser doesn't support saving (Firefox, Internet Explorer, Safari and Opera all work if properly configured)\n- the pathname to your TiddlyWiki file contains illegal characters\n- the TiddlyWiki HTML file has been moved or renamed
",
1435 invalidFileError: "The original file '%
0' does not appear to be a valid TiddlyWiki
",
1436 backupSaved: "Backup saved
",
1437 backupFailed: "Failed to save backup file
",
1438 rssSaved: "RSS feed saved
",
1439 rssFailed: "Failed to save RSS feed file
",
1440 emptySaved: "Empty template saved
",
1441 emptyFailed: "Failed to save empty template file
",
1442 mainSaved: "Main TiddlyWiki file saved
",
1443 mainFailed: "Failed to save main TiddlyWiki file. Your changes have not been saved
",
1444 macroError: "Error in macro <<\%
0>>",
1445 macroErrorDetails: "Error while executing macro <<\%
0>>:\n%
1",
1446 missingMacro: "No such macro
",
1447 overwriteWarning: "A tiddler named '%
0' already exists. Choose OK to overwrite it
",
1448 unsavedChangesWarning: "WARNING! There are unsaved changes in TiddlyWiki\n\nChoose OK to save\nChoose CANCEL to discard
",
1449 confirmExit: "--------------------------------\n\nThere are unsaved changes in TiddlyWiki. If you continue you will lose those changes\n\n--------------------------------
",
1450 saveInstructions: "SaveChanges
",
1451 unsupportedTWFormat: "Unsupported TiddlyWiki format '%
0'
",
1452 tiddlerSaveError: "Error when saving tiddler '%
0'
",
1453 tiddlerLoadError: "Error when loading tiddler '%
0'
",
1454 wrongSaveFormat: "Cannot save with storage format '%
0'. Using standard format for save.
",
1455 invalidFieldName: "Invalid field name %
0",
1456 fieldCannotBeChanged: "Field '%
0' cannot be changed
",
1457 loadingMissingTiddler: "Attempting to retrieve the tiddler '%
0' from the '%
1' server at:\n\n'%
2' in the workspace '%
3'
",
1458 upgradeDone: "The upgrade to version %
0 is now complete\n\nClick 'OK' to reload the newly upgraded TiddlyWiki
"});
1460 merge(config.messages.messageClose,{
1462 tooltip: "close this message area
"});
1464 config.messages.backstage = {
1465 open: {text: "backstage
", tooltip: "Open the backstage area to perform authoring and editing tasks
"},
1466 close: {text: "close
", tooltip: "Close the backstage area
"},
1467 prompt: "backstage:
",
1469 edit: {text: "edit
", tooltip: "Edit the tiddler '%
0'
"}
1473 config.messages.listView = {
1474 tiddlerTooltip: "Click for the full text of this tiddler
",
1475 previewUnavailable: "(preview not available)
"
1478 config.messages.dates.months = ["January
", "February
", "March
", "April
", "May
", "June
", "July
", "August
", "September
", "October
", "November
","December
"];
1479 config.messages.dates.days = ["Sunday
", "Monday
", "Tuesday
", "Wednesday
", "Thursday
", "Friday
", "Saturday
"];
1480 config.messages.dates.shortMonths = ["Jan
", "Feb
", "Mar
", "Apr
", "May
", "Jun
", "Jul
", "Aug
", "Sep
", "Oct
", "Nov
", "Dec
"];
1481 config.messages.dates.shortDays = ["Sun
", "Mon
", "Tue
", "Wed
", "Thu
", "Fri
", "Sat
"];
1482 // suffixes for dates, eg "1st
","2nd
","3rd
"..."30th
","31st
"
1483 config.messages.dates.daySuffixes = ["st
","nd
","rd
","th
","th
","th
","th
","th
","th
","th
",
1484 "th
","th
","th
","th
","th
","th
","th
","th
","th
","th
",
1485 "st
","nd
","rd
","th
","th
","th
","th
","th
","th
","th
",
1487 config.messages.dates.am = "am
";
1488 config.messages.dates.pm = "pm
";
1490 merge(config.messages.tiddlerPopup,{
1493 merge(config.views.wikified.tag,{
1494 labelNoTags: "no tags
",
1495 labelTags: "tags:
",
1496 openTag: "Open tag '%
0'
",
1497 tooltip: "Show tiddlers tagged with '%
0'
",
1498 openAllText: "Open all
",
1499 openAllTooltip: "Open all of these tiddlers
",
1500 popupNone: "No other tiddlers tagged with '%
0'
"});
1502 merge(config.views.wikified,{
1503 defaultText: "The tiddler '%
0' doesn't yet exist. Double-click to create it
",
1504 defaultModifier: "(missing)
",
1505 shadowModifier: "(built-in shadow tiddler)
",
1506 dateFormat: "DD MMM YYYY
",
1507 createdPrompt: "created
"});
1509 merge(config.views.editor,{
1510 tagPrompt: "Type tags separated with spaces, [[use double square brackets]] if necessary, or add existing
",
1511 defaultText: "Type the text for '%
0'
"});
1513 merge(config.views.editor.tagChooser,{
1515 tooltip: "Choose existing tags to add to this tiddler
",
1516 popupNone: "There are no tags defined
",
1517 tagTooltip: "Add the tag '%
0'
"});
1519 merge(config.messages,{
1522 {unit: 1024*1024*1024, template: "%
0\u00a0GB
"},
1523 {unit: 1024*1024, template: "%
0\u00a0MB
"},
1524 {unit: 1024, template: "%
0\u00a0KB
"},
1525 {unit: 1, template: "%
0\u00a0B
"}
1528 merge(config.macros.search,{
1530 prompt: "Search this TiddlyWiki
",
1532 successMsg: "%
0 tiddlers found matching %
1",
1533 failureMsg: "No tiddlers found matching %
0"});
1535 merge(config.macros.tagging,{
1537 labelNotTag: "not tagging
",
1538 tooltip: "List of tiddlers tagged with '%
0'
"});
1540 merge(config.macros.timeline,{
1541 dateFormat: "DD MMM YYYY
"});
1543 merge(config.macros.allTags,{
1544 tooltip: "Show tiddlers tagged with '%
0'
",
1545 noTags: "There are no tagged tiddlers
"});
1547 config.macros.list.all.prompt = "All tiddlers in alphabetical order
";
1548 config.macros.list.missing.prompt = "Tiddlers that have links to them but are not defined
";
1549 config.macros.list.orphans.prompt = "Tiddlers that are not linked to from any other tiddlers
";
1550 config.macros.list.shadowed.prompt = "Tiddlers shadowed with default contents
";
1551 config.macros.list.touched.prompt = "Tiddlers that have been modified locally
";
1553 merge(config.macros.closeAll,{
1555 prompt: "Close all displayed tiddlers (except any that are being edited)
"});
1557 merge(config.macros.permaview,{
1559 prompt: "Link to an URL that retrieves all the currently displayed tiddlers
"});
1561 merge(config.macros.saveChanges,{
1562 label: "save changes
",
1563 prompt: "Save all tiddlers to create a new TiddlyWiki
",
1566 merge(config.macros.newTiddler,{
1567 label: "new tiddler
",
1568 prompt: "Create a new tiddler
",
1569 title: "New Tiddler
",
1572 merge(config.macros.newJournal,{
1573 label: "new journal
",
1574 prompt: "Create a new tiddler from the current date and time
",
1577 merge(config.macros.options,{
1578 wizardTitle: "Tweak advanced options
",
1579 step1Title: "These options are saved in cookies in your browser
",
1580 step1Html: "<input type='hidden' name='markList'
></input><br><input type='checkbox' checked='false' name='chkUnknown'
>Show unknown options
</input>",
1581 unknownDescription: "//(unknown)//
",
1584 {name: 'Option', field: 'option', title: "Option
", type: 'String'},
1585 {name: 'Description', field: 'description', title: "Description
", type: 'WikiText'},
1586 {name: 'Name', field: 'name', title: "Name
", type: 'String'}
1589 {className: 'lowlight', field: 'lowlight'}
1593 merge(config.macros.plugins,{
1594 wizardTitle: "Manage plugins
",
1595 step1Title: "Currently loaded plugins
",
1596 step1Html: "<input type='hidden' name='markList'
></input>", // DO NOT TRANSLATE
1597 skippedText: "(This plugin has not been executed because it was added since startup)
",
1598 noPluginText: "There are no plugins installed
",
1599 confirmDeleteText: "Are you sure you want to delete these plugins:\n\n%
0",
1600 removeLabel: "remove systemConfig tag
",
1601 removePrompt: "Remove systemConfig tag
",
1602 deleteLabel: "delete
",
1603 deletePrompt: "Delete these tiddlers forever
",
1606 {name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
1607 {name: 'Tiddler', field: 'tiddler', title: "Tiddler
", type: 'Tiddler'},
1608 {name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size
", type: 'Size'},
1609 {name: 'Forced', field: 'forced', title: "Forced
", tag: 'systemConfigForce', type: 'TagCheckbox'},
1610 {name: 'Disabled', field: 'disabled', title: "Disabled
", tag: 'systemConfigDisable', type: 'TagCheckbox'},
1611 {name: 'Executed', field: 'executed', title: "Loaded
", type: 'Boolean', trueText: "Yes
", falseText: "No
"},
1612 {name: 'Startup Time', field: 'startupTime', title: "Startup Time
", type: 'String'},
1613 {name: 'Error', field: 'error', title: "Status
", type: 'Boolean', trueText: "Error
", falseText: "OK
"},
1614 {name: 'Log', field: 'log', title: "Log
", type: 'StringList'}
1617 {className: 'error', field: 'error'},
1618 {className: 'warning', field: 'warning'}
1622 merge(config.macros.toolbar,{
1624 morePrompt: "Reveal further commands
"
1627 merge(config.macros.refreshDisplay,{
1629 prompt: "Redraw the entire TiddlyWiki display
"
1632 merge(config.macros.importTiddlers,{
1633 readOnlyWarning: "You cannot import into a read-only TiddlyWiki file. Try opening it from a file:// URL
",
1634 wizardTitle: "Import tiddlers from another file or server
",
1635 step1Title: "Step
1: Locate the server or TiddlyWiki file
",
1636 step1Html: "Specify the type of the server:
<select name='selTypes'
><option value=''
>Choose...
</option></select><br>Enter the URL or pathname here:
<input type='text' size=
50 name='txtPath'
><br>...or browse for a file:
<input type='file' size=
50 name='txtBrowse'
><br><hr>...or select a pre-defined feed:
<select name='selFeeds'
><option value=''
>Choose...
</option></select>",
1638 openPrompt: "Open the connection to this file or server
",
1639 openError: "There were problems fetching the tiddlywiki file
",
1640 statusOpenHost: "Opening the host
",
1641 statusGetWorkspaceList: "Getting the list of available workspaces
",
1642 step2Title: "Step
2: Choose the workspace
",
1643 step2Html: "Enter a workspace name:
<input type='text' size=
50 name='txtWorkspace'
><br>...or select a workspace:
<select name='selWorkspace'
><option value=''
>Choose...
</option></select>",
1644 cancelLabel: "cancel
",
1645 cancelPrompt: "Cancel this import
",
1646 statusOpenWorkspace: "Opening the workspace
",
1647 statusGetTiddlerList: "Getting the list of available tiddlers
",
1648 errorGettingTiddlerList: "Error getting list of tiddlers, click Cancel to try again
",
1649 step3Title: "Step
3: Choose the tiddlers to import
",
1650 step3Html: "<input type='hidden' name='markList'
></input><br><input type='checkbox' checked='true' name='chkSync'
>Keep these tiddlers linked to this server so that you can synchronise subsequent changes
</input><br><input type='checkbox' name='chkSave'
>Save the details of this server in a 'systemServer' tiddler called:
</input> <input type='text' size=
25 name='txtSaveTiddler'
>",
1651 importLabel: "import
",
1652 importPrompt: "Import these tiddlers
",
1653 confirmOverwriteText: "Are you sure you want to overwrite these tiddlers:\n\n%
0",
1654 step4Title: "Step
4: Importing %
0 tiddler(s)
",
1655 step4Html: "<input type='hidden' name='markReport'
></input>", // DO NOT TRANSLATE
1657 donePrompt: "Close this wizard
",
1658 statusDoingImport: "Importing tiddlers
",
1659 statusDoneImport: "All tiddlers imported
",
1660 systemServerNamePattern: "%
2 on %
1",
1661 systemServerNamePatternNoWorkspace: "%
1",
1662 confirmOverwriteSaveTiddler: "The tiddler '%
0' already exists. Click 'OK' to overwrite it with the details of this server, or 'Cancel' to leave it unchanged
",
1663 serverSaveTemplate: "|''Type:''|%
0|\n|''URL:''|%
1|\n|''Workspace:''|%
2|\n\nThis tiddler was automatically created to record the details of this server
",
1664 serverSaveModifier: "(System)
",
1667 {name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
1668 {name: 'Tiddler', field: 'tiddler', title: "Tiddler
", type: 'Tiddler'},
1669 {name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size
", type: 'Size'},
1670 {name: 'Tags', field: 'tags', title: "Tags
", type: 'Tags'}
1676 merge(config.macros.upgrade,{
1677 wizardTitle: "Upgrade TiddlyWiki core code
",
1678 step1Title: "Update or repair this TiddlyWiki to the latest release
",
1679 step1Html: "You are about to upgrade to the latest release of the TiddlyWiki core code (from
<a href='%
0' class='externalLink' target='_blank'
>%
1</a>). Your content will be preserved across the upgrade.
<br><br>Note that core upgrades have been known to interfere with older plugins. If you run into problems with the upgraded file, see
<a href='http://www.tiddlywiki.org/wiki/CoreUpgrades' class='externalLink' target='_blank'
>http://www.tiddlywiki.org/wiki/CoreUpgrades
</a>",
1680 errorCantUpgrade: "Unable to upgrade this TiddlyWiki. You can only perform upgrades on TiddlyWiki files stored locally
",
1681 errorNotSaved: "You must save changes before you can perform an upgrade
",
1682 step2Title: "Confirm the upgrade details
",
1683 step2Html_downgrade: "You are about to downgrade to TiddlyWiki version %
0 from %
1.
<br><br>Downgrading to an earlier version of the core code is not recommended
",
1684 step2Html_restore: "This TiddlyWiki appears to be already using the latest version of the core code (%
0).
<br><br>You can continue to upgrade anyway to ensure that the core code hasn't been corrupted or damaged
",
1685 step2Html_upgrade: "You are about to upgrade to TiddlyWiki version %
0 from %
1",
1686 upgradeLabel: "upgrade
",
1687 upgradePrompt: "Prepare for the upgrade process
",
1688 statusPreparingBackup: "Preparing backup
",
1689 statusSavingBackup: "Saving backup file
",
1690 errorSavingBackup: "There was a problem saving the backup file
",
1691 statusLoadingCore: "Loading core code
",
1692 errorLoadingCore: "Error loading the core code
",
1693 errorCoreFormat: "Error with the new core code
",
1694 statusSavingCore: "Saving the new core code
",
1695 statusReloadingCore: "Reloading the new core code
",
1696 startLabel: "start
",
1697 startPrompt: "Start the upgrade process
",
1698 cancelLabel: "cancel
",
1699 cancelPrompt: "Cancel the upgrade process
",
1700 step3Title: "Upgrade cancelled
",
1701 step3Html: "You have cancelled the upgrade process
"
1704 merge(config.macros.sync,{
1707 {name: 'Selected', field: 'selected', rowName: 'title', type: 'Selector'},
1708 {name: 'Tiddler', field: 'tiddler', title: "Tiddler
", type: 'Tiddler'},
1709 {name: 'Server Type', field: 'serverType', title: "Server type
", type: 'String'},
1710 {name: 'Server Host', field: 'serverHost', title: "Server host
", type: 'String'},
1711 {name: 'Server Workspace', field: 'serverWorkspace', title: "Server workspace
", type: 'String'},
1712 {name: 'Status', field: 'status', title: "Synchronisation status
", type: 'String'},
1713 {name: 'Server URL', field: 'serverUrl', title: "Server URL
", text: "View
", type: 'Link'}
1718 {caption: "Sync these tiddlers
", name: 'sync'}
1720 wizardTitle: "Synchronize with external servers and files
",
1721 step1Title: "Choose the tiddlers you want to synchronize
",
1722 step1Html: "<input type='hidden' name='markList'
></input>", // DO NOT TRANSLATE
1724 syncPrompt: "Sync these tiddlers
",
1725 hasChanged: "Changed while unplugged
",
1726 hasNotChanged: "Unchanged while unplugged
",
1728 none: {text: "...
", display:null, className:'notChanged'},
1729 changedServer: {text: "Changed on server
", display:null, className:'changedServer'},
1730 changedLocally: {text: "Changed while unplugged
", display:null, className:'changedLocally'},
1731 changedBoth: {text: "Changed while unplugged and on server
", display:null, className:'changedBoth'},
1732 notFound: {text: "Not found on server
", display:null, className:'notFound'},
1733 putToServer: {text: "Saved update on server
", display:null, className:'putToServer'},
1734 gotFromServer: {text: "Retrieved update from server
", display:null, className:'gotFromServer'}
1738 merge(config.macros.annotations,{
1741 merge(config.commands.closeTiddler,{
1743 tooltip: "Close this tiddler
"});
1745 merge(config.commands.closeOthers,{
1746 text: "close others
",
1747 tooltip: "Close all other tiddlers
"});
1749 merge(config.commands.editTiddler,{
1751 tooltip: "Edit this tiddler
",
1752 readOnlyText: "view
",
1753 readOnlyTooltip: "View the source of this tiddler
"});
1755 merge(config.commands.saveTiddler,{
1757 tooltip: "Save changes to this tiddler
"});
1759 merge(config.commands.cancelTiddler,{
1761 tooltip: "Undo changes to this tiddler
",
1762 warning: "Are you sure you want to abandon your changes to '%
0'?
",
1763 readOnlyText: "done
",
1764 readOnlyTooltip: "View this tiddler normally
"});
1766 merge(config.commands.deleteTiddler,{
1768 tooltip: "Delete this tiddler
",
1769 warning: "Are you sure you want to delete '%
0'?
"});
1771 merge(config.commands.permalink,{
1773 tooltip: "Permalink for this tiddler
"});
1775 merge(config.commands.references,{
1777 tooltip: "Show tiddlers that link to this one
",
1778 popupNone: "No references
"});
1780 merge(config.commands.jump,{
1782 tooltip: "Jump to another open tiddler
"});
1784 merge(config.commands.syncing,{
1786 tooltip: "Control synchronisation of this tiddler with a server or external file
",
1787 currentlySyncing: "<div>Currently syncing via
<span class='popupHighlight'
>'%
0'
</span> to:</
"+"div
><div>host:
<span class='popupHighlight'
>%
1</span></
"+"div
><div>workspace:
<span class='popupHighlight'
>%
2</span></
"+"div
>", // Note escaping of closing <div> tag
1788 notCurrentlySyncing: "Not currently syncing
",
1789 captionUnSync: "Stop synchronising this tiddler
",
1790 chooseServer: "Synchronise this tiddler with another server:
",
1791 currServerMarker: "\u25cf
",
1792 notCurrServerMarker: " "});
1794 merge(config.commands.fields,{
1796 tooltip: "Show the extended fields of this tiddler
",
1797 emptyText: "There are no extended fields for this tiddler
",
1800 {name: 'Field', field: 'field', title: "Field
", type: 'String'},
1801 {name: 'Value', field: 'value', title: "Value
", type: 'String'}
1808 merge(config.shadowTiddlers,{
1809 DefaultTiddlers: "[[GettingStarted]]
",
1810 MainMenu: "[[GettingStarted]]
",
1811 SiteTitle: "My TiddlyWiki
",
1812 SiteSubtitle: "a reusable non-linear personal web notebook
",
1813 SiteUrl: "http://www.tiddlywiki.com/
",
1814 SideBarOptions: '<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal "DD MMM YYYY
" "journal
">><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel "options \u00bb
" "Change TiddlyWiki advanced options
">>',
1815 SideBarTabs: '<<tabs txtMainTab "Timeline
" "Timeline
" TabTimeline "All
" "All tiddlers
" TabAll "Tags
" "All tags
" TabTags "More
" "More lists
" TabMore>>',
1816 TabMore: '<<tabs txtMoreTab "Missing
" "Missing tiddlers
" TabMoreMissing "Orphans
" "Orphaned tiddlers
" TabMoreOrphans "Shadowed
" "Shadowed tiddlers
" TabMoreShadowed>>'
1819 merge(config.annotations,{
1820 AdvancedOptions: "This shadow tiddler provides access to several advanced options
",
1821 ColorPalette: "These values in this shadow tiddler determine the colour scheme of the ~TiddlyWiki user interface
",
1822 DefaultTiddlers: "The tiddlers listed in this shadow tiddler will be automatically displayed when ~TiddlyWiki starts up
",
1823 EditTemplate: "The HTML template in this shadow tiddler determines how tiddlers look while they are being edited
",
1824 GettingStarted: "This shadow tiddler provides basic usage instructions
",
1825 ImportTiddlers: "This shadow tiddler provides access to importing tiddlers
",
1826 MainMenu: "This shadow tiddler is used as the contents of the main menu in the left-hand column of the screen
",
1827 MarkupPreHead: "This tiddler is inserted at the top of the
<head> section of the TiddlyWiki HTML file
",
1828 MarkupPostHead: "This tiddler is inserted at the bottom of the
<head> section of the TiddlyWiki HTML file
",
1829 MarkupPreBody: "This tiddler is inserted at the top of the
<body> section of the TiddlyWiki HTML file
",
1830 MarkupPostBody: "This tiddler is inserted at the end of the
<body> section of the TiddlyWiki HTML file immediately after the script block
",
1831 OptionsPanel: "This shadow tiddler is used as the contents of the options panel slider in the right-hand sidebar
",
1832 PageTemplate: "The HTML template in this shadow tiddler determines the overall ~TiddlyWiki layout
",
1833 PluginManager: "This shadow tiddler provides access to the plugin manager
",
1834 SideBarOptions: "This shadow tiddler is used as the contents of the option panel in the right-hand sidebar
",
1835 SideBarTabs: "This shadow tiddler is used as the contents of the tabs panel in the right-hand sidebar
",
1836 SiteSubtitle: "This shadow tiddler is used as the second part of the page title
",
1837 SiteTitle: "This shadow tiddler is used as the first part of the page title
",
1838 SiteUrl: "This shadow tiddler should be set to the full target URL for publication
",
1839 StyleSheetColors: "This shadow tiddler contains CSS definitions related to the color of page elements. ''DO NOT EDIT THIS TIDDLER'', instead make your changes in the StyleSheet shadow tiddler
",
1840 StyleSheet: "This tiddler can contain custom CSS definitions
",
1841 StyleSheetLayout: "This shadow tiddler contains CSS definitions related to the layout of page elements. ''DO NOT EDIT THIS TIDDLER'', instead make your changes in the StyleSheet shadow tiddler
",
1842 StyleSheetLocale: "This shadow tiddler contains CSS definitions related to the translation locale
",
1843 StyleSheetPrint: "This shadow tiddler contains CSS definitions for printing
",
1844 TabAll: "This shadow tiddler contains the contents of the 'All' tab in the right-hand sidebar
",
1845 TabMore: "This shadow tiddler contains the contents of the 'More' tab in the right-hand sidebar
",
1846 TabMoreMissing: "This shadow tiddler contains the contents of the 'Missing' tab in the right-hand sidebar
",
1847 TabMoreOrphans: "This shadow tiddler contains the contents of the 'Orphans' tab in the right-hand sidebar
",
1848 TabMoreShadowed: "This shadow tiddler contains the contents of the 'Shadowed' tab in the right-hand sidebar
",
1849 TabTags: "This shadow tiddler contains the contents of the 'Tags' tab in the right-hand sidebar
",
1850 TabTimeline: "This shadow tiddler contains the contents of the 'Timeline' tab in the right-hand sidebar
",
1851 ToolbarCommands: "This shadow tiddler determines which commands are shown in tiddler toolbars
",
1852 ViewTemplate: "The HTML template in this shadow tiddler determines how tiddlers look
"
1859 var params = null; // Command line parameters
1860 var store = null; // TiddlyWiki storage
1861 var story = null; // Main story
1862 var formatter = null; // Default formatters for the wikifier
1863 var anim = typeof Animator == "function
" ? new Animator() : null; // Animation engine
1864 var readOnly = false; // Whether we're in readonly mode
1865 var highlightHack = null; // Embarrassing hack department...
1866 var hadConfirmExit = false; // Don't warn more than once
1867 var safeMode = false; // Disable all plugins and cookies
1868 var showBackstage; // Whether to include the backstage area
1869 var installedPlugins = []; // Information filled in when plugins are executed
1870 var startingUp = false; // Whether we're in the process of starting up
1871 var pluginInfo,tiddler; // Used to pass information to plugins in loadPlugins()
1873 // Whether to use the JavaSaver applet
1874 var useJavaSaver = (config.browser.isSafari || config.browser.isOpera) && (document.location.toString().substr(0,4) != "http
");
1879 var t10,t9,t8,t7,t6,t5,t4,t3,t2,t1,t0 = new Date();
1881 window.onbeforeunload = function(e) {if(window.confirmExit) return confirmExit();};
1882 params = getParameters();
1884 params = params.parseParams("open
",null,false);
1885 store = new TiddlyWiki();
1886 invokeParamifier(params,"oninit
");
1887 story = new Story("tiddlerDisplay
","tiddler
");
1888 addEvent(document,"click
",Popup.onDocumentClick);
1890 loadOptionsCookie();
1891 for(var s=0; s<config.notifyTiddlers.length; s++)
1892 store.addNotification(config.notifyTiddlers[s].name,config.notifyTiddlers[s].notify);
1894 loadShadowTiddlers();
1896 store.loadFromDiv("storeArea
","store
",true);
1898 invokeParamifier(params,"onload
");
1900 readOnly = (window.location.protocol == "file:
") ? false : config.options.chkHttpReadOnly;
1901 var pluginProblem = loadPlugins();
1903 formatter = new Formatter(config.formatters);
1904 invokeParamifier(params,"onconfig
");
1905 story.switchTheme(config.options.txtTheme);
1906 showBackstage = !readOnly;
1914 story.displayTiddler(null,"PluginManager
");
1915 displayMessage(config.messages.customConfigError);
1917 for(var m in config.macros) {
1918 if(config.macros[m].init)
1919 config.macros[m].init();
1925 if(config.options.chkDisplayInstrumentation) {
1926 displayMessage("LoadShadows
" + (t2-t1) + " ms
");
1927 displayMessage("LoadFromDiv
" + (t3-t2) + " ms
");
1928 displayMessage("LoadPlugins
" + (t5-t4) + " ms
");
1929 displayMessage("Notify
" + (t7-t6) + " ms
");
1930 displayMessage("Restart
" + (t8-t7) + " ms
");
1931 displayMessage("Macro init
" + (t9-t8) + " ms
");
1932 displayMessage("Total:
" + (t10-t0) + " ms
");
1940 invokeParamifier(params,"onstart
");
1941 if(story.isEmpty()) {
1942 story.displayDefaultTiddlers();
1944 window.scrollTo(0,0);
1949 var s = document.getElementById("saveTest
");
1950 if(s.hasChildNodes())
1951 alert(config.messages.savedSnapshotError);
1952 s.appendChild(document.createTextNode("savetest
"));
1955 function loadShadowTiddlers()
1957 var shadows = new TiddlyWiki();
1958 shadows.loadFromDiv("shadowArea
","shadows
",true);
1959 shadows.forEachTiddler(function(title,tiddler){config.shadowTiddlers[title] = tiddler.text;});
1963 function loadPlugins()
1967 var tiddlers = store.getTaggedTiddlers("systemConfig
");
1971 var nPlugins = tiddlers.length;
1972 installedPlugins = [];
1973 for(var i=0; i<nPlugins; i++) {
1974 var p = getPluginInfo(tiddlers[i]);
1975 installedPlugins[i] = p;
1983 var visit = function(p) {
1987 var reqs = p.Requires;
1989 reqs = reqs.readBracketedList();
1990 for(var i=0; i<reqs.length; i++)
1991 visit(map[reqs[i]]);
1995 for(i=0; i<nPlugins; i++)
1996 visit(installedPlugins[i]);
1997 for(i=0; i<toLoad.length; i++) {
2000 tiddler = p.tiddler;
2001 if(isPluginExecutable(p)) {
2002 if(isPluginEnabled(p)) {
2004 var startTime = new Date();
2007 window.eval(tiddler.text);
2010 p.log.push(config.messages.pluginError.format([exceptionText(ex)]));
2013 pluginInfo.startupTime = String((new Date()) - startTime) + "ms
";
2021 return nLoaded != nPlugins;
2024 function getPluginInfo(tiddler)
2026 var p = store.getTiddlerSlices(tiddler.title,["Name
","Description
","Version
","Requires
","CoreVersion
","Date
","Source
","Author
","License
","Browsers
"]);
2027 p.tiddler = tiddler;
2028 p.title = tiddler.title;
2033 // Check that a particular plugin is valid for execution
2034 function isPluginExecutable(plugin)
2036 if(plugin.tiddler.isTagged("systemConfigForce
")) {
2037 plugin.log.push(config.messages.pluginForced);
2040 if(plugin["CoreVersion
"]) {
2041 var coreVersion = plugin["CoreVersion
"].split(".
");
2042 var w = parseInt(coreVersion[0],10) - version.major;
2043 if(w == 0 && coreVersion[1])
2044 w = parseInt(coreVersion[1],10) - version.minor;
2045 if(w == 0 && coreVersion[2])
2046 w = parseInt(coreVersion[2],10) - version.revision;
2048 plugin.log.push(config.messages.pluginVersionError);
2055 function isPluginEnabled(plugin)
2057 if(plugin.tiddler.isTagged("systemConfigDisable
")) {
2058 plugin.log.push(config.messages.pluginDisabled);
2064 function invokeMacro(place,macro,params,wikifier,tiddler)
2067 var m = config.macros[macro];
2069 m.handler(place,macro,params.readMacroParams(),wikifier,params,tiddler);
2071 createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,config.messages.missingMacro]));
2073 createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,ex.toString()]));
2081 function getParameters()
2084 if(window.location.hash) {
2085 p = decodeURIComponent(window.location.hash.substr(1));
2086 if(config.browser.firefoxDate != null && config.browser.firefoxDate[1] < "20051111")
2087 p = convertUTF8ToUnicode(p);
2092 function invokeParamifier(params,handler)
2094 if(!params || params.length == undefined || params.length <= 1)
2096 for(var t=1; t<params.length; t++) {
2097 var p = config.paramifiers[params[t].name];
2098 if(p && p[handler] instanceof Function)
2099 p[handler](params[t].value);
2103 config.paramifiers = {};
2105 config.paramifiers.start = {
2106 oninit: function(v) {
2107 safeMode = v.toLowerCase() == "safe
";
2111 config.paramifiers.open = {
2112 onstart: function(v) {
2113 if(!readOnly || store.tiddlerExists(v) || store.isShadowTiddler(v))
2114 story.displayTiddler("bottom
",v,null,false,null);
2118 config.paramifiers.story = {
2119 onstart: function(v) {
2120 var list = store.getTiddlerText(v,"").parseParams("open
",null,false);
2121 invokeParamifier(list,"onstart
");
2125 config.paramifiers.search = {
2126 onstart: function(v) {
2127 story.search(v,false,false);
2131 config.paramifiers.searchRegExp = {
2132 onstart: function(v) {
2133 story.prototype.search(v,false,true);
2137 config.paramifiers.tag = {
2138 onstart: function(v) {
2139 story.displayTiddlers(null,store.filterTiddlers("[tag[
"+v+"]]
"),null,false,null);
2143 config.paramifiers.newTiddler = {
2144 onstart: function(v) {
2146 story.displayTiddler(null,v,DEFAULT_EDIT_TEMPLATE);
2147 story.focusTiddler(v,"text
");
2152 config.paramifiers.newJournal = {
2153 onstart: function(v) {
2155 var now = new Date();
2156 var title = now.formatString(v.trim());
2157 story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
2158 story.focusTiddler(title,"text
");
2163 config.paramifiers.readOnly = {
2164 onconfig: function(v) {
2165 var p = v.toLowerCase();
2166 readOnly = p == "yes
" ? true : (p == "no
" ? false : readOnly);
2170 config.paramifiers.theme = {
2171 onconfig: function(v) {
2172 story.switchTheme(v);
2176 config.paramifiers.upgrade = {
2177 onstart: function(v) {
2182 config.paramifiers.recent= {
2183 onstart: function(v) {
2185 var tiddlers=store.getTiddlers("modified
","excludeLists
").reverse();
2186 for(var i=0; i<v && i<tiddlers.length; i++)
2187 titles.push(tiddlers[i].title);
2188 story.displayTiddlers(null,titles);
2192 config.paramifiers.filter = {
2193 onstart: function(v) {
2194 story.displayTiddlers(null,store.filterTiddlers(v),null,false);
2199 //-- Formatter helpers
2202 function Formatter(formatters)
2204 this.formatters = [];
2206 for(var n=0; n<formatters.length; n++) {
2207 pattern.push("(
" + formatters[n].match + ")
");
2208 this.formatters.push(formatters[n]);
2210 this.formatterRegExp = new RegExp(pattern.join("|
"),"mg
");
2213 config.formatterHelpers = {
2215 createElementAndWikify: function(w)
2217 w.subWikifyTerm(createTiddlyElement(w.output,this.element),this.termRegExp);
2220 inlineCssHelper: function(w)
2223 config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
2224 var lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
2225 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
2227 if(lookaheadMatch[1]) {
2228 s = lookaheadMatch[1].unDash();
2229 v = lookaheadMatch[2];
2231 s = lookaheadMatch[3].unDash();
2232 v = lookaheadMatch[4];
2235 s = "backgroundColor
";
2236 styles.push({style: s, value: v});
2237 w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
2238 config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
2239 lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
2244 applyCssHelper: function(e,styles)
2246 for(var t=0; t< styles.length; t++) {
2248 e.style[styles[t].style] = styles[t].value;
2254 enclosedTextHelper: function(w)
2256 this.lookaheadRegExp.lastIndex = w.matchStart;
2257 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2258 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
2259 var text = lookaheadMatch[1];
2260 if(config.browser.isIE)
2261 text = text.replace(/\n/g,"\r
");
2262 createTiddlyElement(w.output,this.element,null,null,text);
2263 w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
2267 isExternalLink: function(link)
2269 if(store.tiddlerExists(link) || store.isShadowTiddler(link)) {
2272 var urlRegExp = new RegExp(config.textPrimitives.urlPattern,"mg
");
2273 if(urlRegExp.exec(link)) {
2276 if(link.indexOf(".
")!=-1 || link.indexOf("\\
")!=-1 || link.indexOf("/
")!=-1 || link.indexOf("#
")!=-1) {
2285 //-- Standard formatters
2288 config.formatters = [
2291 match: "^\\|(?:[^\\n]*)\\|(?:[fhck]?)$
",
2292 lookaheadRegExp: /^\|([^\n]*)\|([fhck]?)$/mg,
2293 rowTermRegExp: /(\|(?:[fhck]?)$\n?)/mg,
2294 cellRegExp: /(?:\|([^\n\|]*)\|)|(\|[fhck]?$\n?)/mg,
2295 cellTermRegExp: /((?:\x20*)\|)/mg,
2296 rowTypes: {"c
":"caption
", "h
":"thead
", "":"tbody
", "f
":"tfoot
"},
2297 handler: function(w)
2299 var table = createTiddlyElement(w.output,"table
",null,"twtable
");
2300 var prevColumns = [];
2301 var currRowType = null;
2304 w.nextMatch = w.matchStart;
2305 this.lookaheadRegExp.lastIndex = w.nextMatch;
2306 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2307 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
2308 var nextRowType = lookaheadMatch[2];
2309 if(nextRowType == "k
") {
2310 table.className = lookaheadMatch[1];
2311 w.nextMatch += lookaheadMatch[0].length+1;
2313 if(nextRowType != currRowType) {
2314 rowContainer = createTiddlyElement(table,this.rowTypes[nextRowType]);
2315 currRowType = nextRowType;
2317 if(currRowType == "c
") {
2320 if(rowContainer != table.firstChild)
2321 table.insertBefore(rowContainer,table.firstChild);
2322 rowContainer.setAttribute("align
",rowCount == 0?"top
":"bottom
");
2323 w.subWikifyTerm(rowContainer,this.rowTermRegExp);
2325 var theRow = createTiddlyElement(rowContainer,"tr
",null,(rowCount&1)?"oddRow
":"evenRow
");
2326 theRow.onmouseover = function() {addClass(this,"hoverRow
");};
2327 theRow.onmouseout = function() {removeClass(this,"hoverRow
");};
2328 this.rowHandler(w,theRow,prevColumns);
2332 this.lookaheadRegExp.lastIndex = w.nextMatch;
2333 lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2336 rowHandler: function(w,e,prevColumns)
2339 var colSpanCount = 1;
2340 var prevCell = null;
2341 this.cellRegExp.lastIndex = w.nextMatch;
2342 var cellMatch = this.cellRegExp.exec(w.source);
2343 while(cellMatch && cellMatch.index == w.nextMatch) {
2344 if(cellMatch[1] == "~
") {
2346 var last = prevColumns[col];
2348 last.rowSpanCount++;
2349 last.element.setAttribute("rowspan
",last.rowSpanCount);
2350 last.element.setAttribute("rowSpan
",last.rowSpanCount); // Needed for IE
2351 last.element.valign = "center
";
2353 w.nextMatch = this.cellRegExp.lastIndex-1;
2354 } else if(cellMatch[1] == ">") {
2357 w.nextMatch = this.cellRegExp.lastIndex-1;
2358 } else if(cellMatch[2]) {
2360 if(prevCell && colSpanCount > 1) {
2361 prevCell.setAttribute("colspan
",colSpanCount);
2362 prevCell.setAttribute("colSpan
",colSpanCount); // Needed for IE
2364 w.nextMatch = this.cellRegExp.lastIndex;
2369 var styles = config.formatterHelpers.inlineCssHelper(w);
2370 var spaceLeft = false;
2371 var chr = w.source.substr(w.nextMatch,1);
2375 chr = w.source.substr(w.nextMatch,1);
2379 cell = createTiddlyElement(e,"th
");
2382 cell = createTiddlyElement(e,"td
");
2385 prevColumns[col] = {rowSpanCount:1,element:cell};
2386 if(colSpanCount > 1) {
2387 cell.setAttribute("colspan
",colSpanCount);
2388 cell.setAttribute("colSpan
",colSpanCount); // Needed for IE
2391 config.formatterHelpers.applyCssHelper(cell,styles);
2392 w.subWikifyTerm(cell,this.cellTermRegExp);
2393 if(w.matchText.substr(w.matchText.length-2,1) == " ") // spaceRight
2394 cell.align = spaceLeft ? "center
" : "left
";
2396 cell.align = "right
";
2400 this.cellRegExp.lastIndex = w.nextMatch;
2401 cellMatch = this.cellRegExp.exec(w.source);
2409 termRegExp: /(\n)/mg,
2410 handler: function(w)
2412 w.subWikifyTerm(createTiddlyElement(w.output,"h
" + w.matchLength),this.termRegExp);
2418 match: "^(?:[\\*#;:]+)
",
2419 lookaheadRegExp: /^(?:(?:(\*)|(#)|(;)|(:))+)/mg,
2420 termRegExp: /(\n)/mg,
2421 handler: function(w)
2423 var stack = [w.output];
2424 var currLevel = 0, currType = null;
2425 var listLevel, listType, itemType, baseType;
2426 w.nextMatch = w.matchStart;
2427 this.lookaheadRegExp.lastIndex = w.nextMatch;
2428 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2429 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
2430 if(lookaheadMatch[1]) {
2433 } else if(lookaheadMatch[2]) {
2436 } else if(lookaheadMatch[3]) {
2439 } else if(lookaheadMatch[4]) {
2444 baseType = listType;
2445 listLevel = lookaheadMatch[0].length;
2446 w.nextMatch += lookaheadMatch[0].length;
2448 if(listLevel > currLevel) {
2449 for(t=currLevel; t<listLevel; t++) {
2450 var target = (currLevel == 0) ? stack[stack.length-1] : stack[stack.length-1].lastChild;
2451 stack.push(createTiddlyElement(target,listType));
2453 } else if(listType!=baseType && listLevel==1) {
2454 w.nextMatch -= lookaheadMatch[0].length;
2456 } else if(listLevel < currLevel) {
2457 for(t=currLevel; t>listLevel; t--)
2459 } else if(listLevel == currLevel && listType != currType) {
2461 stack.push(createTiddlyElement(stack[stack.length-1].lastChild,listType));
2463 currLevel = listLevel;
2464 currType = listType;
2465 var e = createTiddlyElement(stack[stack.length-1],itemType);
2466 w.subWikifyTerm(e,this.termRegExp);
2467 this.lookaheadRegExp.lastIndex = w.nextMatch;
2468 lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2474 name: "quoteByBlock
",
2476 termRegExp: /(^<<<(\n|$))/mg,
2477 element: "blockquote
",
2478 handler: config.formatterHelpers.createElementAndWikify
2482 name: "quoteByLine
",
2484 lookaheadRegExp: /^>+/mg,
2485 termRegExp: /(\n)/mg,
2486 element: "blockquote
",
2487 handler: function(w)
2489 var stack = [w.output];
2491 var newLevel = w.matchLength;
2494 if(newLevel > currLevel) {
2495 for(t=currLevel; t<newLevel; t++)
2496 stack.push(createTiddlyElement(stack[stack.length-1],this.element));
2497 } else if(newLevel < currLevel) {
2498 for(t=currLevel; t>newLevel; t--)
2501 currLevel = newLevel;
2502 w.subWikifyTerm(stack[stack.length-1],this.termRegExp);
2503 createTiddlyElement(stack[stack.length-1],"br
");
2504 this.lookaheadRegExp.lastIndex = w.nextMatch;
2505 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2506 var matched = lookaheadMatch && lookaheadMatch.index == w.nextMatch;
2508 newLevel = lookaheadMatch[0].length;
2509 w.nextMatch += lookaheadMatch[0].length;
2517 match: "^----+$\\n?
",
2518 handler: function(w)
2520 createTiddlyElement(w.output,"hr
");
2525 name: "monospacedByLine
",
2526 match: "^(?:/\\*\\{\\{\\{\\*/|\\{\\{\\{|//\\{\\{\\{|
<!--\\{\\{\\{-->)\\n
",
2528 handler: function(w)
2530 switch(w.matchText) {
2531 case "/*{{{*/\n
": // CSS
2532 this.lookaheadRegExp = /\/\*\{\{\{\*\/\n*((?:^[^\n]*\n)+?)(\n*^\/\*\}\}\}\*\/$\n?)/mg;
2534 case "{{{\n
": // monospaced block
2535 this.lookaheadRegExp = /^\{\{\{\n((?:^[^\n]*\n)+?)(^\}\}\}$\n?)/mg;
2537 case "//{{{\n
": // plugin
2538 this.lookaheadRegExp = /^\/\/\{\{\{\n\n*((?:^[^\n]*\n)+?)(\n*^\/\/\}\}\}$\n?)/mg;
2540 case "<!--{{{-->\n
": //template
2541 this.lookaheadRegExp = /<!--\{\{\{-->\n*((?:^[^\n]*\n)+?)(\n*^<!--\}\}\}-->$\n?)/mg;
2546 config.formatterHelpers.enclosedTextHelper.call(this,w);
2551 name: "wikifyComment
",
2552 match: "^(?:/\\*\\*\\*|
<!---)\\n",
2553 handler: function(w)
2555 var termRegExp = (w.matchText == "/***\n") ? (/(^\*\*\*\/\n)/mg) : (/(^--->\n)/mg);
2556 w.subWikifyTerm(w.output,termRegExp);
2563 lookaheadRegExp: /<<([^
>\s]+)(?:\s*)((?:[^
>]|(?:
>(?!
>)))*)
>>/mg,
2564 handler: function(w)
2566 this.lookaheadRegExp.lastIndex = w.matchStart;
2567 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2568 if(lookaheadMatch && lookaheadMatch.index == w.matchStart && lookaheadMatch[
1]) {
2569 w.nextMatch = this.lookaheadRegExp.lastIndex;
2570 invokeMacro(w.output,lookaheadMatch[
1],lookaheadMatch[
2],w,w.tiddler);
2578 lookaheadRegExp: /\[\[(.*?)(?:\|(~)?(.*?))?\]\]/mg,
2579 handler: function(w)
2581 this.lookaheadRegExp.lastIndex = w.matchStart;
2582 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2583 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
2585 var text = lookaheadMatch[
1];
2586 if(lookaheadMatch[
3]) {
2587 // Pretty bracketted link
2588 var link = lookaheadMatch[
3];
2589 e = (!lookaheadMatch[
2] && config.formatterHelpers.isExternalLink(link)) ?
2590 createExternalLink(w.output,link) : createTiddlyLink(w.output,decodeURIComponent(link),false,null,w.isStatic,w.tiddler);
2592 // Simple bracketted link
2593 e = createTiddlyLink(w.output,decodeURIComponent(text),false,null,w.isStatic,w.tiddler);
2595 createTiddlyText(e,text);
2596 w.nextMatch = this.lookaheadRegExp.lastIndex;
2603 match: config.textPrimitives.unWikiLink+
"?"+config.textPrimitives.wikiLink,
2604 handler: function(w)
2606 if(w.matchText.substr(
0,
1) == config.textPrimitives.unWikiLink) {
2607 w.outputText(w.output,w.matchStart+
1,w.nextMatch);
2610 if(w.matchStart
> 0) {
2611 var preRegExp = new RegExp(config.textPrimitives.anyLetterStrict,
"mg");
2612 preRegExp.lastIndex = w.matchStart-
1;
2613 var preMatch = preRegExp.exec(w.source);
2614 if(preMatch.index == w.matchStart-
1) {
2615 w.outputText(w.output,w.matchStart,w.nextMatch);
2619 if(w.autoLinkWikiWords || store.isShadowTiddler(w.matchText)) {
2620 var link = createTiddlyLink(w.output,w.matchText,false,null,w.isStatic,w.tiddler);
2621 w.outputText(link,w.matchStart,w.nextMatch);
2623 w.outputText(w.output,w.matchStart,w.nextMatch);
2630 match: config.textPrimitives.urlPattern,
2631 handler: function(w)
2633 w.outputText(createExternalLink(w.output,w.matchText),w.matchStart,w.nextMatch);
2639 match:
"\\[[<>]?[Ii][Mm][Gg]\\[",
2640 lookaheadRegExp: /\[([<]?)(
>?)[Ii][Mm][Gg]\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg,
2641 handler: function(w)
2643 this.lookaheadRegExp.lastIndex = w.matchStart;
2644 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2645 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
2647 if(lookaheadMatch[
5]) {
2648 var link = lookaheadMatch[
5];
2649 e = config.formatterHelpers.isExternalLink(link) ? createExternalLink(w.output,link) : createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
2650 addClass(e,
"imageLink");
2652 var img = createTiddlyElement(e,
"img");
2653 if(lookaheadMatch[
1])
2655 else if(lookaheadMatch[
2])
2656 img.align =
"right";
2657 if(lookaheadMatch[
3]) {
2658 img.title = lookaheadMatch[
3];
2659 img.setAttribute(
"alt",lookaheadMatch[
3]);
2661 img.src = lookaheadMatch[
4];
2662 w.nextMatch = this.lookaheadRegExp.lastIndex;
2669 match:
"<[Hh][Tt][Mm][Ll]>",
2670 lookaheadRegExp: /<[Hh][Tt][Mm][Ll]
>((?:.|\n)*?)<\/[Hh][Tt][Mm][Ll]
>/mg,
2671 handler: function(w)
2673 this.lookaheadRegExp.lastIndex = w.matchStart;
2674 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2675 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
2676 createTiddlyElement(w.output,
"span").innerHTML = lookaheadMatch[
1];
2677 w.nextMatch = this.lookaheadRegExp.lastIndex;
2683 name:
"commentByBlock",
2685 lookaheadRegExp: /\/%((?:.|\n)*?)%\//mg,
2686 handler: function(w)
2688 this.lookaheadRegExp.lastIndex = w.matchStart;
2689 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2690 if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
2691 w.nextMatch = this.lookaheadRegExp.lastIndex;
2696 name:
"characterFormat",
2697 match:
"''|//|__|\\^\\^|~~|--(?!\\s|$)|\\{\\{\\{",
2698 handler: function(w)
2700 switch(w.matchText) {
2702 w.subWikifyTerm(w.output.appendChild(document.createElement(
"strong")),/('')/mg);
2705 w.subWikifyTerm(createTiddlyElement(w.output,
"em"),/(\/\/)/mg);
2708 w.subWikifyTerm(createTiddlyElement(w.output,
"u"),/(__)/mg);
2711 w.subWikifyTerm(createTiddlyElement(w.output,
"sup"),/(\^\^)/mg);
2714 w.subWikifyTerm(createTiddlyElement(w.output,
"sub"),/(~~)/mg);
2717 w.subWikifyTerm(createTiddlyElement(w.output,
"strike"),/(--)/mg);
2720 var lookaheadRegExp = /\{\{\{((?:.|\n)*?)\}\}\}/mg;
2721 lookaheadRegExp.lastIndex = w.matchStart;
2722 var lookaheadMatch = lookaheadRegExp.exec(w.source);
2723 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
2724 createTiddlyElement(w.output,
"code",null,null,lookaheadMatch[
1]);
2725 w.nextMatch = lookaheadRegExp.lastIndex;
2733 name:
"customFormat",
2735 handler: function(w)
2737 switch(w.matchText) {
2739 var e = createTiddlyElement(w.output,
"span");
2740 var styles = config.formatterHelpers.inlineCssHelper(w);
2741 if(styles.length ==
0)
2742 e.className =
"marked";
2744 config.formatterHelpers.applyCssHelper(e,styles);
2745 w.subWikifyTerm(e,/(@@)/mg);
2748 var lookaheadRegExp = /\{\{[\s]*([\w]+[\s\w]*)[\s]*\{(\n?)/mg;
2749 lookaheadRegExp.lastIndex = w.matchStart;
2750 var lookaheadMatch = lookaheadRegExp.exec(w.source);
2751 if(lookaheadMatch) {
2752 w.nextMatch = lookaheadRegExp.lastIndex;
2753 e = createTiddlyElement(w.output,lookaheadMatch[
2] ==
"\n" ?
"div" :
"span",null,lookaheadMatch[
1]);
2754 w.subWikifyTerm(e,/(\}\}\})/mg);
2764 handler: function(w)
2766 createTiddlyElement(w.output,
"span").innerHTML =
"—";
2772 match:
"\\n|<br ?/?>",
2773 handler: function(w)
2775 createTiddlyElement(w.output,
"br");
2781 match:
"\\\"{
3}|
<nowiki>",
2782 lookaheadRegExp: /(?:\"{
3}|
<nowiki>)((?:.|\n)*?)(?:\
"{3}|<\/nowiki>)/mg,
2783 handler: function(w)
2785 this.lookaheadRegExp.lastIndex = w.matchStart;
2786 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2787 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
2788 createTiddlyElement(w.output,"span
",null,null,lookaheadMatch[1]);
2789 w.nextMatch = this.lookaheadRegExp.lastIndex;
2795 name: "htmlEntitiesEncoding
",
2796 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};)
",
2797 handler: function(w)
2799 createTiddlyElement(w.output,"span
").innerHTML = w.matchText;
2809 function getParser(tiddler,format)
2813 format = tiddler.fields["wikiformat
"];
2816 for(i in config.parsers) {
2817 if(format == config.parsers[i].format)
2818 return config.parsers[i];
2821 for(i in config.parsers) {
2822 if(tiddler.isTagged(config.parsers[i].formatTag))
2823 return config.parsers[i];
2830 function wikify(source,output,highlightRegExp,tiddler)
2833 var wikifier = new Wikifier(source,getParser(tiddler),highlightRegExp,tiddler);
2834 var t0 = new Date();
2835 wikifier.subWikify(output);
2836 if(tiddler && config.options.chkDisplayInstrumentation)
2837 displayMessage("wikify:
" +tiddler.title+ " in
" + (new Date()-t0) + " ms
");
2841 function wikifyStatic(source,highlightRegExp,tiddler,format)
2843 var e = createTiddlyElement(document.body,"pre
");
2844 e.style.display = "none
";
2846 if(source && source != "") {
2848 tiddler = new Tiddler("temp
");
2849 var wikifier = new Wikifier(source,getParser(tiddler,format),highlightRegExp,tiddler);
2850 wikifier.isStatic = true;
2851 wikifier.subWikify(e);
2858 function wikifyPlain(title,theStore,limit)
2862 if(theStore.tiddlerExists(title) || theStore.isShadowTiddler(title)) {
2863 return wikifyPlainText(theStore.getTiddlerText(title),limit,tiddler);
2869 function wikifyPlainText(text,limit,tiddler)
2872 text = text.substr(0,limit);
2873 var wikifier = new Wikifier(text,formatter,null,tiddler);
2874 return wikifier.wikifyPlain();
2877 function highlightify(source,output,highlightRegExp,tiddler)
2880 var wikifier = new Wikifier(source,formatter,highlightRegExp,tiddler);
2881 wikifier.outputText(output,0,source.length);
2885 function Wikifier(source,formatter,highlightRegExp,tiddler)
2887 this.source = source;
2889 this.formatter = formatter;
2891 this.autoLinkWikiWords = tiddler && tiddler.autoLinkWikiWords() == false ? false : true;
2892 this.highlightRegExp = highlightRegExp;
2893 this.highlightMatch = null;
2894 this.isStatic = false;
2895 if(highlightRegExp) {
2896 highlightRegExp.lastIndex = 0;
2897 this.highlightMatch = highlightRegExp.exec(source);
2899 this.tiddler = tiddler;
2902 Wikifier.prototype.wikifyPlain = function()
2904 var e = createTiddlyElement(document.body,"div
");
2905 e.style.display = "none
";
2907 var text = getPlainText(e);
2912 Wikifier.prototype.subWikify = function(output,terminator)
2916 this.subWikifyTerm(output,new RegExp("(
" + terminator + ")
","mg
"));
2918 this.subWikifyUnterm(output);
2924 Wikifier.prototype.subWikifyUnterm = function(output)
2926 var oldOutput = this.output;
2927 this.output = output;
2928 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
2929 var formatterMatch = this.formatter.formatterRegExp.exec(this.source);
2930 while(formatterMatch) {
2931 // Output any text before the match
2932 if(formatterMatch.index > this.nextMatch)
2933 this.outputText(this.output,this.nextMatch,formatterMatch.index);
2934 // Set the match parameters for the handler
2935 this.matchStart = formatterMatch.index;
2936 this.matchLength = formatterMatch[0].length;
2937 this.matchText = formatterMatch[0];
2938 this.nextMatch = this.formatter.formatterRegExp.lastIndex;
2939 for(var t=1; t<formatterMatch.length; t++) {
2940 if(formatterMatch[t]) {
2941 this.formatter.formatters[t-1].handler(this);
2942 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
2946 formatterMatch = this.formatter.formatterRegExp.exec(this.source);
2948 if(this.nextMatch < this.source.length) {
2949 this.outputText(this.output,this.nextMatch,this.source.length);
2950 this.nextMatch = this.source.length;
2952 this.output = oldOutput;
2955 Wikifier.prototype.subWikifyTerm = function(output,terminatorRegExp)
2957 var oldOutput = this.output;
2958 this.output = output;
2959 terminatorRegExp.lastIndex = this.nextMatch;
2960 var terminatorMatch = terminatorRegExp.exec(this.source);
2961 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
2962 var formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
2963 while(terminatorMatch || formatterMatch) {
2964 if(terminatorMatch && (!formatterMatch || terminatorMatch.index <= formatterMatch.index)) {
2965 if(terminatorMatch.index > this.nextMatch)
2966 this.outputText(this.output,this.nextMatch,terminatorMatch.index);
2967 this.matchText = terminatorMatch[1];
2968 this.matchLength = terminatorMatch[1].length;
2969 this.matchStart = terminatorMatch.index;
2970 this.nextMatch = this.matchStart + this.matchLength;
2971 this.output = oldOutput;
2974 if(formatterMatch.index > this.nextMatch)
2975 this.outputText(this.output,this.nextMatch,formatterMatch.index);
2976 this.matchStart = formatterMatch.index;
2977 this.matchLength = formatterMatch[0].length;
2978 this.matchText = formatterMatch[0];
2979 this.nextMatch = this.formatter.formatterRegExp.lastIndex;
2980 for(var t=1; t<formatterMatch.length; t++) {
2981 if(formatterMatch[t]) {
2982 this.formatter.formatters[t-1].handler(this);
2983 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
2987 terminatorRegExp.lastIndex = this.nextMatch;
2988 terminatorMatch = terminatorRegExp.exec(this.source);
2989 formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
2991 if(this.nextMatch < this.source.length) {
2992 this.outputText(this.output,this.nextMatch,this.source.length);
2993 this.nextMatch = this.source.length;
2995 this.output = oldOutput;
2998 Wikifier.prototype.outputText = function(place,startPos,endPos)
3000 while(this.highlightMatch && (this.highlightRegExp.lastIndex > startPos) && (this.highlightMatch.index < endPos) && (startPos < endPos)) {
3001 if(this.highlightMatch.index > startPos) {
3002 createTiddlyText(place,this.source.substring(startPos,this.highlightMatch.index));
3003 startPos = this.highlightMatch.index;
3005 var highlightEnd = Math.min(this.highlightRegExp.lastIndex,endPos);
3006 var theHighlight = createTiddlyElement(place,"span
",null,"highlight
",this.source.substring(startPos,highlightEnd));
3007 startPos = highlightEnd;
3008 if(startPos >= this.highlightRegExp.lastIndex)
3009 this.highlightMatch = this.highlightRegExp.exec(this.source);
3011 if(startPos < endPos) {
3012 createTiddlyText(place,this.source.substring(startPos,endPos));
3017 //-- Macro definitions
3020 config.macros.today.handler = function(place,macroName,params)
3022 var now = new Date();
3023 var text = params[0] ? now.formatString(params[0].trim()) : now.toLocaleString();
3024 createTiddlyElement(place,"span
",null,null,text);
3027 config.macros.version.handler = function(place)
3029 createTiddlyElement(place,"span
",null,null,formatVersion());
3032 config.macros.list.handler = function(place,macroName,params)
3034 var type = params[0] || "all
";
3035 var list = document.createElement("ul
");
3036 place.appendChild(list);
3037 if(this[type].prompt)
3038 createTiddlyElement(list,"li
",null,"listTitle
",this[type].prompt);
3040 if(this[type].handler)
3041 results = this[type].handler(params);
3042 for(var t = 0; t < results.length; t++) {
3043 var li = document.createElement("li
");
3044 list.appendChild(li);
3045 createTiddlyLink(li,typeof results[t] == "string
" ? results[t] : results[t].title,true);
3049 config.macros.list.all.handler = function(params)
3051 return store.reverseLookup("tags
","excludeLists
",false,"title
");
3054 config.macros.list.missing.handler = function(params)
3056 return store.getMissingLinks();
3059 config.macros.list.orphans.handler = function(params)
3061 return store.getOrphans();
3064 config.macros.list.shadowed.handler = function(params)
3066 return store.getShadowed();
3069 config.macros.list.touched.handler = function(params)
3071 return store.getTouched();
3074 config.macros.list.filter.handler = function(params)
3076 var filter = params[1];
3079 var tiddlers = store.filterTiddlers(filter);
3080 for(var t=0; t<tiddlers.length; t++)
3081 results.push(tiddlers[t].title);
3086 config.macros.allTags.handler = function(place,macroName,params)
3088 var tags = store.getTags(params[0]);
3089 var ul = createTiddlyElement(place,"ul
");
3090 if(tags.length == 0)
3091 createTiddlyElement(ul,"li
",null,"listTitle
",this.noTags);
3092 for(var t=0; t<tags.length; t++) {
3093 var title = tags[t][0];
3094 var info = getTiddlyLinkInfo(title);
3095 var li = createTiddlyElement(ul,"li
");
3096 var btn = createTiddlyButton(li,title + " (
" + tags[t][1] + ")
",this.tooltip.format([title]),onClickTag,info.classes);
3097 btn.setAttribute("tag
",title);
3098 btn.setAttribute("refresh
","link
");
3099 btn.setAttribute("tiddlyLink
",title);
3103 config.macros.timeline.handler = function(place,macroName,params)
3105 var field = params[0] || "modified
";
3106 var tiddlers = store.reverseLookup("tags
","excludeLists
",false,field);
3108 var last = params[1] ? tiddlers.length-Math.min(tiddlers.length,parseInt(params[1])) : 0;
3109 var dateFormat = params[2] || this.dateFormat;
3110 for(var t=tiddlers.length-1; t>=last; t--) {
3111 var tiddler = tiddlers[t];
3112 var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
3113 if(theDay != lastDay) {
3114 var ul = document.createElement("ul
");
3115 place.appendChild(ul);
3116 createTiddlyElement(ul,"li
",null,"listTitle
",tiddler[field].formatString(dateFormat));
3119 createTiddlyElement(ul,"li
",null,"listLink
").appendChild(createTiddlyLink(place,tiddler.title,true));
3123 config.macros.tiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3125 params = paramString.parseParams("name
",null,true,false,true);
3126 var names = params[0]["name
"];
3127 var tiddlerName = names[0];
3128 var className = names[1] || null;
3129 var args = params[0]["with
"];
3130 var wrapper = createTiddlyElement(place,"span
",null,className);
3132 wrapper.setAttribute("refresh
","content
");
3133 wrapper.setAttribute("tiddler
",tiddlerName);
3135 var text = store.getTiddlerText(tiddlerName);
3137 var stack = config.macros.tiddler.tiddlerStack;
3138 if(stack.indexOf(tiddlerName) !== -1)
3140 stack.push(tiddlerName);
3142 var n = args ? Math.min(args.length,9) : 0;
3143 for(var i=0; i<n; i++) {
3144 var placeholderRE = new RegExp("\\$
" + (i + 1),"mg
");
3145 text = text.replace(placeholderRE,args[i]);
3147 config.macros.tiddler.renderText(wrapper,text,tiddlerName,params);
3154 config.macros.tiddler.renderText = function(place,text,tiddlerName,params)
3156 wikify(text,place,null,store.getTiddler(tiddlerName));
3159 config.macros.tiddler.tiddlerStack = [];
3161 config.macros.tag.handler = function(place,macroName,params)
3163 createTagButton(place,params[0],null,params[1],params[2]);
3166 config.macros.tags.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3168 params = paramString.parseParams("anon
",null,true,false,false);
3169 var ul = createTiddlyElement(place,"ul
");
3170 var title = getParam(params,"anon
","");
3171 if(title && store.tiddlerExists(title))
3172 tiddler = store.getTiddler(title);
3173 var sep = getParam(params,"sep
"," ");
3174 var lingo = config.views.wikified.tag;
3175 var prompt = tiddler.tags.length == 0 ? lingo.labelNoTags : lingo.labelTags;
3176 createTiddlyElement(ul,"li
",null,"listTitle
",prompt.format([tiddler.title]));
3177 for(var t=0; t<tiddler.tags.length; t++) {
3178 createTagButton(createTiddlyElement(ul,"li
"),tiddler.tags[t],tiddler.title);
3179 if(t<tiddler.tags.length-1)
3180 createTiddlyText(ul,sep);
3184 config.macros.tagging.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3186 params = paramString.parseParams("anon
",null,true,false,false);
3187 var ul = createTiddlyElement(place,"ul
");
3188 var title = getParam(params,"anon
","");
3189 if(title == "" && tiddler instanceof Tiddler)
3190 title = tiddler.title;
3191 var sep = getParam(params,"sep
"," ");
3192 ul.setAttribute("title
",this.tooltip.format([title]));
3193 var tagged = store.getTaggedTiddlers(title);
3194 var prompt = tagged.length == 0 ? this.labelNotTag : this.label;
3195 createTiddlyElement(ul,"li
",null,"listTitle
",prompt.format([title,tagged.length]));
3196 for(var t=0; t<tagged.length; t++) {
3197 createTiddlyLink(createTiddlyElement(ul,"li
"),tagged[t].title,true);
3198 if(t<tagged.length-1)
3199 createTiddlyText(ul,sep);
3203 config.macros.closeAll.handler = function(place)
3205 createTiddlyButton(place,this.label,this.prompt,this.onClick);
3208 config.macros.closeAll.onClick = function(e)
3210 story.closeAllTiddlers();
3214 config.macros.permaview.handler = function(place)
3216 createTiddlyButton(place,this.label,this.prompt,this.onClick);
3219 config.macros.permaview.onClick = function(e)
3225 config.macros.saveChanges.handler = function(place,macroName,params)
3228 createTiddlyButton(place,params[0] || this.label,params[1] || this.prompt,this.onClick,null,null,this.accessKey);
3231 config.macros.saveChanges.onClick = function(e)
3237 config.macros.slider.onClickSlider = function(ev)
3239 var e = ev || window.event;
3240 var n = this.nextSibling;
3241 var cookie = n.getAttribute("cookie
");
3242 var isOpen = n.style.display != "none
";
3243 if(config.options.chkAnimate && anim && typeof Slider == "function
")
3244 anim.startAnimating(new Slider(n,!isOpen,null,"none
"));
3246 n.style.display = isOpen ? "none
" : "block
";
3247 config.options[cookie] = !isOpen;
3248 saveOptionCookie(cookie);
3252 config.macros.slider.createSlider = function(place,cookie,title,tooltip)
3254 var c = cookie || "";
3255 var btn = createTiddlyButton(place,title,tooltip,this.onClickSlider);
3256 var panel = createTiddlyElement(null,"div
",null,"sliderPanel
");
3257 panel.setAttribute("cookie
",c);
3258 panel.style.display = config.options[c] ? "block
" : "none
";
3259 place.appendChild(panel);
3263 config.macros.slider.handler = function(place,macroName,params)
3265 var panel = this.createSlider(place,params[0],params[2],params[3]);
3266 var text = store.getTiddlerText(params[1]);
3267 panel.setAttribute("refresh
","content
");
3268 panel.setAttribute("tiddler
",params[1]);
3270 wikify(text,panel,null,store.getTiddler(params[1]));
3273 // <<gradient [[tiddler name]] vert|horiz rgb rgb rgb rgb... >>
3274 config.macros.gradient.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3276 var panel = wikifier ? createTiddlyElement(place,"div
",null,"gradient
") : place;
3277 panel.style.position = "relative
";
3278 panel.style.overflow = "hidden
";
3279 panel.style.zIndex = "0";
3281 var styles = config.formatterHelpers.inlineCssHelper(wikifier);
3282 config.formatterHelpers.applyCssHelper(panel,styles);
3284 params = paramString.parseParams("color
");
3285 var locolors = [], hicolors = [];
3286 for(var t=2; t<params.length; t++) {
3287 var c = new RGB(params[t].value);
3288 if(params[t].name == "snap
") {
3289 hicolors[hicolors.length-1] = c;
3295 drawGradient(panel,params[1].value != "vert
",locolors,hicolors);
3297 wikifier.subWikify(panel,">>");
3299 panel.style.height = "100%
";
3300 panel.style.width = "100%
";
3304 config.macros.message.handler = function(place,macroName,params)
3307 var names = params[0].split(".
");
3308 var lookupMessage = function(root,nameIndex) {
3309 if(names[nameIndex] in root) {
3310 if(nameIndex < names.length-1)
3311 return (lookupMessage(root[names[nameIndex]],nameIndex+1));
3313 return root[names[nameIndex]];
3317 var m = lookupMessage(config,0);
3319 m = lookupMessage(window,0);
3320 createTiddlyText(place,m.toString().format(params.splice(1)));
3325 config.macros.view.views = {
3326 text: function(value,place,params,wikifier,paramString,tiddler) {
3327 highlightify(value,place,highlightHack,tiddler);
3329 link: function(value,place,params,wikifier,paramString,tiddler) {
3330 createTiddlyLink(place,value,true);
3332 wikified: function(value,place,params,wikifier,paramString,tiddler) {
3334 value=params[2].unescapeLineBreaks().format([value]);
3335 wikify(value,place,highlightHack,tiddler);
3337 date: function(value,place,params,wikifier,paramString,tiddler) {
3338 value = Date.convertFromYYYYMMDDHHMM(value);
3339 createTiddlyText(place,value.formatString(params[2] ? params[2] : config.views.wikified.dateFormat));
3343 config.macros.view.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3345 if((tiddler instanceof Tiddler) && params[0]) {
3346 var value = store.getValue(tiddler,params[0]);
3348 var type = params[1] || config.macros.view.defaultView;
3349 var handler = config.macros.view.views[type];
3351 handler(value,place,params,wikifier,paramString,tiddler);
3356 config.macros.edit.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3358 var field = params[0];
3359 var rows = params[1] || 0;
3360 var defVal = params[2] || '';
3361 if((tiddler instanceof Tiddler) && field) {
3362 story.setDirty(tiddler.title,true);
3364 if(field != "text
" && !rows) {
3365 e = createTiddlyElement(null,"input
");
3366 if(tiddler.isReadOnly())
3367 e.setAttribute("readOnly
","readOnly
");
3368 e.setAttribute("edit
",field);
3369 e.setAttribute("type
","text
");
3370 e.value = store.getValue(tiddler,field) || defVal;
3371 e.setAttribute("size
","40");
3372 e.setAttribute("autocomplete
","off
");
3373 place.appendChild(e);
3375 var wrapper1 = createTiddlyElement(null,"fieldset
",null,"fieldsetFix
");
3376 var wrapper2 = createTiddlyElement(wrapper1,"div
");
3377 e = createTiddlyElement(wrapper2,"textarea
");
3378 if(tiddler.isReadOnly())
3379 e.setAttribute("readOnly
","readOnly
");
3380 e.value = v = store.getValue(tiddler,field) || defVal;
3382 var lines = v.match(/\n/mg);
3383 var maxLines = Math.max(parseInt(config.options.txtMaxEditRows),5);
3384 if(lines != null && lines.length > rows)
3385 rows = lines.length + 5;
3386 rows = Math.min(rows,maxLines);
3387 e.setAttribute("rows
",rows);
3388 e.setAttribute("edit
",field);
3389 place.appendChild(wrapper1);
3395 config.macros.tagChooser.onClick = function(ev)
3397 var e = ev || window.event;
3398 var lingo = config.views.editor.tagChooser;
3399 var popup = Popup.create(this);
3400 var tags = store.getTags("excludeLists
");
3401 if(tags.length == 0)
3402 createTiddlyText(createTiddlyElement(popup,"li
"),lingo.popupNone);
3403 for(var t=0; t<tags.length; t++) {
3404 var tag = createTiddlyButton(createTiddlyElement(popup,"li
"),tags[t][0],lingo.tagTooltip.format([tags[t][0]]),config.macros.tagChooser.onTagClick);
3405 tag.setAttribute("tag
",tags[t][0]);
3406 tag.setAttribute("tiddler
",this.getAttribute("tiddler
"));
3409 e.cancelBubble = true;
3410 if(e.stopPropagation) e.stopPropagation();
3414 config.macros.tagChooser.onTagClick = function(ev)
3416 var e = ev || window.event;
3417 if(e.metaKey || e.ctrlKey) stopEvent(e); //# keep popup open on CTRL-click
3418 var tag = this.getAttribute("tag
");
3419 var title = this.getAttribute("tiddler
");
3421 story.setTiddlerTag(title,tag,0);
3425 config.macros.tagChooser.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3427 if(tiddler instanceof Tiddler) {
3428 var lingo = config.views.editor.tagChooser;
3429 var btn = createTiddlyButton(place,lingo.text,lingo.tooltip,this.onClick);
3430 btn.setAttribute("tiddler
",tiddler.title);
3434 config.macros.refreshDisplay.handler = function(place)
3436 createTiddlyButton(place,this.label,this.prompt,this.onClick);
3439 config.macros.refreshDisplay.onClick = function(e)
3445 config.macros.annotations.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3447 var title = tiddler ? tiddler.title : null;
3448 var a = title ? config.annotations[title] : null;
3449 if(!tiddler || !title || !a)
3451 var text = a.format([title]);
3452 wikify(text,createTiddlyElement(place,"div
",null,"annotation
"),null,tiddler);
3456 //-- NewTiddler and NewJournal macros
3459 config.macros.newTiddler.createNewTiddlerButton = function(place,title,params,label,prompt,accessKey,newFocus,isJournal)
3462 for(var t=1; t<params.length; t++) {
3463 if((params[t].name == "anon
" && t != 1) || (params[t].name == "tag
"))
3464 tags.push(params[t].value);
3466 label = getParam(params,"label
",label);
3467 prompt = getParam(params,"prompt
",prompt);
3468 accessKey = getParam(params,"accessKey
",accessKey);
3469 newFocus = getParam(params,"focus
",newFocus);
3470 var customFields = getParam(params,"fields
","");
3471 if(!customFields && !store.isShadowTiddler(title))
3472 customFields = String.encodeHashMap(config.defaultCustomFields);
3473 var btn = createTiddlyButton(place,label,prompt,this.onClickNewTiddler,null,null,accessKey);
3474 btn.setAttribute("newTitle
",title);
3475 btn.setAttribute("isJournal
",isJournal ? "true
" : "false
");
3477 btn.setAttribute("params
",tags.join("|
"));
3478 btn.setAttribute("newFocus
",newFocus);
3479 btn.setAttribute("newTemplate
",getParam(params,"template
",DEFAULT_EDIT_TEMPLATE));
3480 if(customFields !== "")
3481 btn.setAttribute("customFields
",customFields);
3482 var text = getParam(params,"text
");
3483 if(text !== undefined)
3484 btn.setAttribute("newText
",text);
3488 config.macros.newTiddler.onClickNewTiddler = function()
3490 var title = this.getAttribute("newTitle
");
3491 if(this.getAttribute("isJournal
") == "true
") {
3492 title = new Date().formatString(title.trim());
3494 var params = this.getAttribute("params
");
3495 var tags = params ? params.split("|
") : [];
3496 var focus = this.getAttribute("newFocus
");
3497 var template = this.getAttribute("newTemplate
");
3498 var customFields = this.getAttribute("customFields
");
3499 if(!customFields && !store.isShadowTiddler(title))
3500 customFields = String.encodeHashMap(config.defaultCustomFields);
3501 story.displayTiddler(null,title,template,false,null,null);
3502 var tiddlerElem = story.getTiddler(title);
3504 story.addCustomFields(tiddlerElem,customFields);
3505 var text = this.getAttribute("newText
");
3506 if(typeof text == "string
")
3507 story.getTiddlerField(title,"text
").value = text.format([title]);
3508 for(var t=0;t<tags.length;t++)
3509 story.setTiddlerTag(title,tags[t],+1);
3510 story.focusTiddler(title,focus);
3514 config.macros.newTiddler.handler = function(place,macroName,params,wikifier,paramString)
3517 params = paramString.parseParams("anon
",null,true,false,false);
3518 var title = params[1] && params[1].name == "anon
" ? params[1].value : this.title;
3519 title = getParam(params,"title
",title);
3520 this.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"title
",false);
3524 config.macros.newJournal.handler = function(place,macroName,params,wikifier,paramString)
3527 params = paramString.parseParams("anon
",null,true,false,false);
3528 var title = params[1] && params[1].name == "anon
" ? params[1].value : config.macros.timeline.dateFormat;
3529 title = getParam(params,"title
",title);
3530 config.macros.newTiddler.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"text
",true);
3538 config.macros.search.handler = function(place,macroName,params)
3540 var searchTimeout = null;
3541 var btn = createTiddlyButton(place,this.label,this.prompt,this.onClick,"searchButton
");
3542 var txt = createTiddlyElement(place,"input
",null,"txtOptionInput searchField
");
3544 txt.value = params[0];
3545 txt.onkeyup = this.onKeyPress;
3546 txt.onfocus = this.onFocus;
3547 txt.setAttribute("size
",this.sizeTextbox);
3548 txt.setAttribute("accessKey
",this.accessKey);
3549 txt.setAttribute("autocomplete
","off
");
3550 txt.setAttribute("lastSearchText
","");
3551 if(config.browser.isSafari) {
3552 txt.setAttribute("type
","search
");
3553 txt.setAttribute("results
","5");
3555 txt.setAttribute("type
","text
");
3559 // Global because there's only ever one outstanding incremental search timer
3560 config.macros.search.timeout = null;
3562 config.macros.search.doSearch = function(txt)
3564 if(txt.value.length > 0) {
3565 story.search(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch);
3566 txt.setAttribute("lastSearchText
",txt.value);
3570 config.macros.search.onClick = function(e)
3572 config.macros.search.doSearch(this.nextSibling);
3576 config.macros.search.onKeyPress = function(ev)
3578 var e = ev || window.event;
3580 case 13: // Ctrl-Enter
3581 case 10: // Ctrl-Enter on IE PC
3582 config.macros.search.doSearch(this);
3589 if(config.options.chkIncrementalSearch) {
3590 if(this.value.length > 2) {
3591 if(this.value != this.getAttribute("lastSearchText
")) {
3592 if(config.macros.search.timeout)
3593 clearTimeout(config.macros.search.timeout);
3595 config.macros.search.timeout = setTimeout(function() {config.macros.search.doSearch(txt);},500);
3598 if(config.macros.search.timeout)
3599 clearTimeout(config.macros.search.timeout);
3604 config.macros.search.onFocus = function(e)
3613 config.macros.tabs.handler = function(place,macroName,params)
3615 var cookie = params[0];
3616 var numTabs = (params.length-1)/3;
3617 var wrapper = createTiddlyElement(null,"div
",null,"tabsetWrapper
" + cookie);
3618 var tabset = createTiddlyElement(wrapper,"div
",null,"tabset
");
3619 tabset.setAttribute("cookie
",cookie);
3620 var validTab = false;
3621 for(var t=0; t<numTabs; t++) {
3622 var label = params[t*3+1];
3623 var prompt = params[t*3+2];
3624 var content = params[t*3+3];
3625 var tab = createTiddlyButton(tabset,label,prompt,this.onClickTab,"tab tabUnselected
");
3626 tab.setAttribute("tab
",label);
3627 tab.setAttribute("content
",content);
3629 if(config.options[cookie] == label)
3633 config.options[cookie] = params[1];
3634 place.appendChild(wrapper);
3635 this.switchTab(tabset,config.options[cookie]);
3638 config.macros.tabs.onClickTab = function(e)
3640 config.macros.tabs.switchTab(this.parentNode,this.getAttribute("tab
"));
3644 config.macros.tabs.switchTab = function(tabset,tab)
3646 var cookie = tabset.getAttribute("cookie
");
3648 var nodes = tabset.childNodes;
3649 for(var t=0; t<nodes.length; t++) {
3650 if(nodes[t].getAttribute && nodes[t].getAttribute("tab
") == tab) {
3652 theTab.className = "tab tabSelected
";
3654 nodes[t].className = "tab tabUnselected
";
3658 if(tabset.nextSibling && tabset.nextSibling.className == "tabContents
")
3659 removeNode(tabset.nextSibling);
3660 var tabContent = createTiddlyElement(null,"div
",null,"tabContents
");
3661 tabset.parentNode.insertBefore(tabContent,tabset.nextSibling);
3662 var contentTitle = theTab.getAttribute("content
");
3663 wikify(store.getTiddlerText(contentTitle),tabContent,null,store.getTiddler(contentTitle));
3665 config.options[cookie] = tab;
3666 saveOptionCookie(cookie);
3672 //-- Tiddler toolbar
3675 // Create a toolbar command button
3676 config.macros.toolbar.createCommand = function(place,commandName,tiddler,className)
3678 if(typeof commandName != "string
") {
3680 for(var t in config.commands) {
3681 if(config.commands[t] == commandName)
3686 if((tiddler instanceof Tiddler) && (typeof commandName == "string
")) {
3687 var command = config.commands[commandName];
3688 if(command.isEnabled ? command.isEnabled(tiddler) : this.isCommandEnabled(command,tiddler)) {
3689 var text = command.getText ? command.getText(tiddler) : this.getCommandText(command,tiddler);
3690 var tooltip = command.getTooltip ? command.getTooltip(tiddler) : this.getCommandTooltip(command,tiddler);
3692 switch(command.type) {
3694 cmd = this.onClickPopup;
3698 cmd = this.onClickCommand;
3701 var btn = createTiddlyButton(null,text,tooltip,cmd);
3702 btn.setAttribute("commandName
",commandName);
3703 btn.setAttribute("tiddler
",tiddler.title);
3705 addClass(btn,className);
3706 place.appendChild(btn);
3711 config.macros.toolbar.isCommandEnabled = function(command,tiddler)
3713 var title = tiddler.title;
3714 var ro = tiddler.isReadOnly();
3715 var shadow = store.isShadowTiddler(title) && !store.tiddlerExists(title);
3716 return (!ro || (ro && !command.hideReadOnly)) && !(shadow && command.hideShadow);
3719 config.macros.toolbar.getCommandText = function(command,tiddler)
3721 return tiddler.isReadOnly() && command.readOnlyText || command.text;
3724 config.macros.toolbar.getCommandTooltip = function(command,tiddler)
3726 return tiddler.isReadOnly() && command.readOnlyTooltip || command.tooltip;
3729 config.macros.toolbar.onClickCommand = function(ev)
3731 var e = ev || window.event;
3732 e.cancelBubble = true;
3733 if(e.stopPropagation) e.stopPropagation();
3734 var command = config.commands[this.getAttribute("commandName
")];
3735 return command.handler(e,this,this.getAttribute("tiddler
"));
3738 config.macros.toolbar.onClickPopup = function(ev)
3740 var e = ev || window.event;
3741 e.cancelBubble = true;
3742 if(e.stopPropagation) e.stopPropagation();
3743 var popup = Popup.create(this);
3744 var command = config.commands[this.getAttribute("commandName
")];
3745 var title = this.getAttribute("tiddler
");
3746 var tiddler = store.fetchTiddler(title);
3747 popup.setAttribute("tiddler
",title);
3748 command.handlePopup(popup,title);
3753 // Invoke the first command encountered from a given place that is tagged with a specified class
3754 config.macros.toolbar.invokeCommand = function(place,className,event)
3756 var children = place.getElementsByTagName("a
");
3757 for(var t=0; t<children.length; t++) {
3758 var c = children[t];
3759 if(hasClass(c,className) && c.getAttribute && c.getAttribute("commandName
")) {
3760 if(c.onclick instanceof Function)
3761 c.onclick.call(c,event);
3767 config.macros.toolbar.onClickMore = function(ev)
3769 var e = this.nextSibling;
3770 e.style.display = "inline
";
3775 config.macros.toolbar.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3777 for(var t=0; t<params.length; t++) {
3781 var btn = createTiddlyButton(place,this.moreLabel,this.morePrompt,config.macros.toolbar.onClickMore);
3782 addClass(btn,"moreCommand
");
3783 var e = createTiddlyElement(place,"span
",null,"moreCommand
");
3784 e.style.display = "none
";
3789 switch(c.substr(0,1)) {
3791 className = "defaultCommand
";
3795 className = "cancelCommand
";
3799 if(c in config.commands)
3800 this.createCommand(place,c,tiddler,className);
3807 //-- Menu and toolbar commands
3810 config.commands.closeTiddler.handler = function(event,src,title)
3812 if(story.isDirty(title) && !readOnly) {
3813 if(!confirm(config.commands.cancelTiddler.warning.format([title])))
3816 story.setDirty(title,false);
3817 story.closeTiddler(title,true);
3821 config.commands.closeOthers.handler = function(event,src,title)
3823 story.closeAllTiddlers(title);
3827 config.commands.editTiddler.handler = function(event,src,title)
3830 var tiddlerElem = story.getTiddler(title);
3831 var fields = tiddlerElem.getAttribute("tiddlyFields
");
3832 story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE,false,null,fields);
3833 story.focusTiddler(title,config.options.txtEditorFocus||"text
");
3837 config.commands.saveTiddler.handler = function(event,src,title)
3839 var newTitle = story.saveTiddler(title,event.shiftKey);
3841 story.displayTiddler(null,newTitle);
3845 config.commands.cancelTiddler.handler = function(event,src,title)
3847 if(story.hasChanges(title) && !readOnly) {
3848 if(!confirm(this.warning.format([title])))
3851 story.setDirty(title,false);
3852 story.displayTiddler(null,title);
3856 config.commands.deleteTiddler.handler = function(event,src,title)
3858 var deleteIt = true;
3859 if(config.options.chkConfirmDelete)
3860 deleteIt = confirm(this.warning.format([title]));
3862 store.removeTiddler(title);
3863 story.closeTiddler(title,true);
3869 config.commands.permalink.handler = function(event,src,title)
3871 var t = encodeURIComponent(String.encodeTiddlyLink(title));
3872 if(window.location.hash != t)
3873 window.location.hash = t;
3877 config.commands.references.handlePopup = function(popup,title)
3879 var references = store.getReferringTiddlers(title);
3881 for(var r=0; r<references.length; r++) {
3882 if(references[r].title != title && !references[r].isTagged("excludeLists
")) {
3883 createTiddlyLink(createTiddlyElement(popup,"li
"),references[r].title,true);
3888 createTiddlyText(createTiddlyElement(popup,"li
",null,"disabled
"),this.popupNone);
3891 config.commands.jump.handlePopup = function(popup,title)
3893 story.forEachTiddler(function(title,element) {
3894 createTiddlyLink(createTiddlyElement(popup,"li
"),title,true,null,false,null,true);
3898 config.commands.syncing.handlePopup = function(popup,title)
3900 var tiddler = store.fetchTiddler(title);
3903 var serverType = tiddler.getServerType();
3904 var serverHost = tiddler.fields['server.host'];
3905 var serverWorkspace = tiddler.fields['server.workspace'];
3906 if(!serverWorkspace)
3907 serverWorkspace = "";
3909 var e = createTiddlyElement(popup,"li
",null,"popupMessage
");
3910 e.innerHTML = config.commands.syncing.currentlySyncing.format([serverType,serverHost,serverWorkspace]);
3912 createTiddlyElement(popup,"li
",null,"popupMessage
",config.commands.syncing.notCurrentlySyncing);
3915 createTiddlyElement(createTiddlyElement(popup,"li
",null,"listBreak
"),"div
");
3916 var btn = createTiddlyButton(createTiddlyElement(popup,"li
"),this.captionUnSync,null,config.commands.syncing.onChooseServer);
3917 btn.setAttribute("tiddler
",title);
3918 btn.setAttribute("server.type
","");
3920 createTiddlyElement(createTiddlyElement(popup,"li
",null,"listBreak
"),"div
");
3921 createTiddlyElement(popup,"li
",null,"popupMessage
",config.commands.syncing.chooseServer);
3922 var feeds = store.getTaggedTiddlers("systemServer
","title
");
3923 for(var t=0; t<feeds.length; t++) {
3925 var feedServerType = store.getTiddlerSlice(f.title,"Type
");
3927 feedServerType = "file
";
3928 var feedServerHost = store.getTiddlerSlice(f.title,"URL
");
3930 feedServerHost = "";
3931 var feedServerWorkspace = store.getTiddlerSlice(f.title,"Workspace
");
3932 if(!feedServerWorkspace)
3933 feedServerWorkspace = "";
3934 var caption = f.title;
3935 if(serverType == feedServerType && serverHost == feedServerHost && serverWorkspace == feedServerWorkspace) {
3936 caption = config.commands.syncing.currServerMarker + caption;
3938 caption = config.commands.syncing.notCurrServerMarker + caption;
3940 btn = createTiddlyButton(createTiddlyElement(popup,"li
"),caption,null,config.commands.syncing.onChooseServer);
3941 btn.setAttribute("tiddler
",title);
3942 btn.setAttribute("server.type
",feedServerType);
3943 btn.setAttribute("server.host
",feedServerHost);
3944 btn.setAttribute("server.workspace
",feedServerWorkspace);
3948 config.commands.syncing.onChooseServer = function(e)
3950 var tiddler = this.getAttribute("tiddler
");
3951 var serverType = this.getAttribute("server.type
");
3953 store.addTiddlerFields(tiddler,{
3954 "server.type
": serverType,
3955 "server.host
": this.getAttribute("server.host
"),
3956 "server.workspace
": this.getAttribute("server.workspace
")
3959 store.setValue(tiddler,"server
",null);
3964 config.commands.fields.handlePopup = function(popup,title)
3966 var tiddler = store.fetchTiddler(title);
3970 store.forEachField(tiddler,function(tiddler,fieldName,value) {fields[fieldName] = value;},true);
3972 for(var t in fields) {
3973 items.push({field: t,value: fields[t]});
3975 items.sort(function(a,b) {return a.field < b.field ? -1 : (a.field == b.field ? 0 : +1);});
3976 if(items.length > 0)
3977 ListView.create(popup,items,this.listViewTemplate);
3979 createTiddlyElement(popup,"div
",null,null,this.emptyText);
3983 //-- Tiddler() object
3986 function Tiddler(title)
3990 this.modifier = null;
3991 this.created = new Date();
3992 this.modified = this.created;
3994 this.linksUpdated = false;
4000 Tiddler.prototype.getLinks = function()
4002 if(this.linksUpdated==false)
4007 // Returns the fields that are inherited in string field:"value
" field2:"value2
" format
4008 Tiddler.prototype.getInheritedFields = function()
4011 for(var i in this.fields) {
4012 if(i=="server.host
" || i=="server.workspace
" || i=="wikiformat
"|| i=="server.type
") {
4013 f[i] = this.fields[i];
4016 return String.encodeHashMap(f);
4019 // Increment the changeCount of a tiddler
4020 Tiddler.prototype.incChangeCount = function()
4022 var c = this.fields['changecount'];
4023 c = c ? parseInt(c,10) : 0;
4024 this.fields['changecount'] = String(c+1);
4027 // Clear the changeCount of a tiddler
4028 Tiddler.prototype.clearChangeCount = function()
4030 if(this.fields['changecount']) {
4031 delete this.fields['changecount'];
4035 Tiddler.prototype.doNotSave = function()
4037 return this.fields['doNotSave'];
4040 // Returns true if the tiddler has been updated since the tiddler was created or downloaded
4041 Tiddler.prototype.isTouched = function()
4043 var changeCount = this.fields['changecount'];
4044 if(changeCount === undefined)
4046 return changeCount > 0;
4049 // Return the tiddler as an RSS item
4050 Tiddler.prototype.toRssItem = function(uri)
4053 s.push("<title" + ">" + this.title.htmlEncode() + "</title" + ">");
4054 s.push("<description>" + wikifyStatic(this.text,null,this).htmlEncode() + "</description>");
4055 for(var t=0; t<this.tags.length; t++)
4056 s.push("<category>" + this.tags[t] + "</category>");
4057 s.push("<link>" + uri + "#
" + encodeURIComponent(String.encodeTiddlyLink(this.title)) + "</link>");
4058 s.push("<pubDate>" + this.modified.toGMTString() + "</pubDate>");
4059 return s.join("\n
");
4062 // Format the text for storage in an RSS item
4063 Tiddler.prototype.saveToRss = function(uri)
4065 return "<item>\n
" + this.toRssItem(uri) + "\n
</item>";
4068 // Change the text and other attributes of a tiddler
4069 Tiddler.prototype.set = function(title,text,modifier,modified,tags,created,fields)
4071 this.assign(title,text,modifier,modified,tags,created,fields);
4076 // Change the text and other attributes of a tiddler without triggered a tiddler.changed() call
4077 Tiddler.prototype.assign = function(title,text,modifier,modified,tags,created,fields)
4079 if(title != undefined)
4081 if(text != undefined)
4083 if(modifier != undefined)
4084 this.modifier = modifier;
4085 if(modified != undefined)
4086 this.modified = modified;
4087 if(created != undefined)
4088 this.created = created;
4089 if(fields != undefined)
4090 this.fields = fields;
4091 if(tags != undefined)
4092 this.tags = (typeof tags == "string
") ? tags.readBracketedList() : tags;
4093 else if(this.tags == undefined)
4098 // Get the tags for a tiddler as a string (space delimited, using [[brackets]] for tags containing spaces)
4099 Tiddler.prototype.getTags = function()
4101 return String.encodeTiddlyLinkList(this.tags);
4104 // Test if a tiddler carries a tag
4105 Tiddler.prototype.isTagged = function(tag)
4107 return this.tags.indexOf(tag) != -1;
4110 // Static method to convert "\n
" to newlines, "\s
" to "\
"
4111 Tiddler.unescapeLineBreaks = function(text)
4113 return text ? text.unescapeLineBreaks() : "";
4116 // Convert newlines to "\n
", "\
" to "\s
"
4117 Tiddler.prototype.escapeLineBreaks = function()
4119 return this.text.escapeLineBreaks();
4122 // Updates the secondary information (like links[] array) after a change to a tiddler
4123 Tiddler.prototype.changed = function()
4126 var t = this.autoLinkWikiWords() ? 0 : 1;
4127 var tiddlerLinkRegExp = t==0 ? config.textPrimitives.tiddlerAnyLinkRegExp : config.textPrimitives.tiddlerForcedLinkRegExp;
4128 tiddlerLinkRegExp.lastIndex = 0;
4129 var formatMatch = tiddlerLinkRegExp.exec(this.text);
4130 while(formatMatch) {
4131 var lastIndex = tiddlerLinkRegExp.lastIndex;
4132 if(t==0 && formatMatch[1] && formatMatch[1] != this.title) {
4134 if(formatMatch.index > 0) {
4135 var preRegExp = new RegExp(config.textPrimitives.unWikiLink+"|
"+config.textPrimitives.anyLetter,"mg
");
4136 preRegExp.lastIndex = formatMatch.index-1;
4137 var preMatch = preRegExp.exec(this.text);
4138 if(preMatch.index != formatMatch.index-1)
4139 this.links.pushUnique(formatMatch[1]);
4141 this.links.pushUnique(formatMatch[1]);
4144 else if(formatMatch[2-t] && !config.formatterHelpers.isExternalLink(formatMatch[3-t])) // titledBrackettedLink
4145 this.links.pushUnique(formatMatch[3-t]);
4146 else if(formatMatch[4-t] && formatMatch[4-t] != this.title) // brackettedLink
4147 this.links.pushUnique(formatMatch[4-t]);
4148 tiddlerLinkRegExp.lastIndex = lastIndex;
4149 formatMatch = tiddlerLinkRegExp.exec(this.text);
4151 this.linksUpdated = true;
4154 Tiddler.prototype.getSubtitle = function()
4156 var modifier = this.modifier;
4158 modifier = config.messages.subtitleUnknown;
4159 var modified = this.modified;
4161 modified = modified.toLocaleString();
4163 modified = config.messages.subtitleUnknown;
4164 return config.messages.tiddlerLinkTooltip.format([this.title,modifier,modified]);
4167 Tiddler.prototype.isReadOnly = function()
4172 Tiddler.prototype.autoLinkWikiWords = function()
4174 return !(this.isTagged("systemConfig
") || this.isTagged("excludeMissing
"));
4177 Tiddler.prototype.generateFingerprint = function()
4179 return "0x
" + Crypto.hexSha1Str(this.text);
4182 Tiddler.prototype.getServerType = function()
4184 var serverType = null;
4185 if(this.fields['server.type'])
4186 serverType = this.fields['server.type'];
4188 serverType = this.fields['wikiformat'];
4189 if(serverType && !config.adaptors[serverType])
4194 Tiddler.prototype.getAdaptor = function()
4196 var serverType = this.getServerType();
4197 return serverType ? new config.adaptors[serverType]() : null;
4201 //-- TiddlyWiki() object contains Tiddler()s
4204 function TiddlyWiki()
4206 var tiddlers = {}; // Hashmap by name of tiddlers
4207 this.tiddlersUpdated = false;
4208 this.namedNotifications = []; // Array of {name:,notify:} of notification functions
4209 this.notificationLevel = 0;
4210 this.slices = {}; // map tiddlerName->(map sliceName->sliceValue). Lazy.
4211 this.clear = function() {
4213 this.setDirty(false);
4215 this.fetchTiddler = function(title) {
4216 var t = tiddlers[title];
4217 return t instanceof Tiddler ? t : null;
4219 this.deleteTiddler = function(title) {
4220 delete this.slices[title];
4221 delete tiddlers[title];
4223 this.addTiddler = function(tiddler) {
4224 delete this.slices[tiddler.title];
4225 tiddlers[tiddler.title] = tiddler;
4227 this.forEachTiddler = function(callback) {
4228 for(var t in tiddlers) {
4229 var tiddler = tiddlers[t];
4230 if(tiddler instanceof Tiddler)
4231 callback.call(this,t,tiddler);
4236 TiddlyWiki.prototype.setDirty = function(dirty)
4241 TiddlyWiki.prototype.isDirty = function()
4246 TiddlyWiki.prototype.tiddlerExists = function(title)
4248 var t = this.fetchTiddler(title);
4249 return t != undefined;
4252 TiddlyWiki.prototype.isShadowTiddler = function(title)
4254 return typeof config.shadowTiddlers[title] == "string
";
4257 TiddlyWiki.prototype.createTiddler = function(title)
4259 var tiddler = this.fetchTiddler(title);
4261 tiddler = new Tiddler(title);
4262 this.addTiddler(tiddler);
4263 this.setDirty(true);
4268 TiddlyWiki.prototype.getTiddler = function(title)
4270 var t = this.fetchTiddler(title);
4277 TiddlyWiki.prototype.getTiddlerText = function(title,defaultText)
4281 var pos = title.indexOf(config.textPrimitives.sectionSeparator);
4284 section = title.substr(pos + config.textPrimitives.sectionSeparator.length);
4285 title = title.substr(0,pos);
4287 pos = title.indexOf(config.textPrimitives.sliceSeparator);
4289 var slice = this.getTiddlerSlice(title.substr(0,pos),title.substr(pos + config.textPrimitives.sliceSeparator.length));
4293 var tiddler = this.fetchTiddler(title);
4296 return tiddler.text;
4297 var re = new RegExp("(^!{
1,
6}
" + section.escapeRegExp() + "[ \t]*\n)
","mg
");
4299 var match = re.exec(tiddler.text);
4301 var t = tiddler.text.substr(match.index+match[1].length);
4304 match = re2.exec(t); //# search for the next heading
4306 t = t.substr(0,match.index-1);//# don't include final \n
4311 if(this.isShadowTiddler(title))
4312 return config.shadowTiddlers[title];
4313 if(defaultText != undefined)
4318 TiddlyWiki.prototype.getRecursiveTiddlerText = function(title,defaultText,depth)
4320 var bracketRegExp = new RegExp("(?:\\[\\[([^\\]]+)\\]\\])
","mg
");
4321 var text = this.getTiddlerText(title,null);
4327 var match = bracketRegExp.exec(text);
4329 textOut.push(text.substr(lastPos,match.index-lastPos));
4332 textOut.push(match[1]);
4334 textOut.push(this.getRecursiveTiddlerText(match[1],"[[
" + match[1] + "]]
",depth-1));
4336 lastPos = match.index + match[0].length;
4338 textOut.push(text.substr(lastPos));
4341 return textOut.join("");
4344 TiddlyWiki.prototype.slicesRE = /(?:^([\'\/]{0,2})~?([\.\w]+)\:\1\s*([^\n]+)\s*$)|(?:^\|([\'\/]{0,2})~?([\.\w]+)\:?\4\|\s*([^\|\n]+)\s*\|$)/gm;
4347 TiddlyWiki.prototype.calcAllSlices = function(title)
4350 var text = this.getTiddlerText(title,"");
4351 this.slicesRE.lastIndex = 0;
4352 var m = this.slicesRE.exec(text);
4355 slices[m[2]] = m[3];
4357 slices[m[5]] = m[6];
4358 m = this.slicesRE.exec(text);
4363 // Returns the slice of text of the given name
4364 TiddlyWiki.prototype.getTiddlerSlice = function(title,sliceName)
4366 var slices = this.slices[title];
4368 slices = this.calcAllSlices(title);
4369 this.slices[title] = slices;
4371 return slices[sliceName];
4374 // Build an hashmap of the specified named slices of a tiddler
4375 TiddlyWiki.prototype.getTiddlerSlices = function(title,sliceNames)
4378 for(var t=0; t<sliceNames.length; t++) {
4379 var slice = this.getTiddlerSlice(title,sliceNames[t]);
4381 r[sliceNames[t]] = slice;
4386 TiddlyWiki.prototype.suspendNotifications = function()
4388 this.notificationLevel--;
4391 TiddlyWiki.prototype.resumeNotifications = function()
4393 this.notificationLevel++;
4396 // Invoke the notification handlers for a particular tiddler
4397 TiddlyWiki.prototype.notify = function(title,doBlanket)
4399 if(!this.notificationLevel) {
4400 for(var t=0; t<this.namedNotifications.length; t++) {
4401 var n = this.namedNotifications[t];
4402 if((n.name == null && doBlanket) || (n.name == title))
4408 // Invoke the notification handlers for all tiddlers
4409 TiddlyWiki.prototype.notifyAll = function()
4411 if(!this.notificationLevel) {
4412 for(var t=0; t<this.namedNotifications.length; t++) {
4413 var n = this.namedNotifications[t];
4420 // Add a notification handler to a tiddler
4421 TiddlyWiki.prototype.addNotification = function(title,fn)
4423 for(var i=0; i<this.namedNotifications.length; i++) {
4424 if((this.namedNotifications[i].name == title) && (this.namedNotifications[i].notify == fn))
4427 this.namedNotifications.push({name: title, notify: fn});
4431 TiddlyWiki.prototype.removeTiddler = function(title)
4433 var tiddler = this.fetchTiddler(title);
4435 this.deleteTiddler(title);
4436 this.notify(title,true);
4437 this.setDirty(true);
4441 // Reset the sync status of a freshly synced tiddler
4442 TiddlyWiki.prototype.resetTiddler = function(title)
4444 var tiddler = this.fetchTiddler(title);
4446 tiddler.clearChangeCount();
4447 this.notify(title,true);
4448 this.setDirty(true);
4452 TiddlyWiki.prototype.setTiddlerTag = function(title,status,tag)
4454 var tiddler = this.fetchTiddler(title);
4456 var t = tiddler.tags.indexOf(tag);
4458 tiddler.tags.splice(t,1);
4460 tiddler.tags.push(tag);
4462 tiddler.incChangeCount(title);
4463 this.notify(title,true);
4464 this.setDirty(true);
4468 TiddlyWiki.prototype.addTiddlerFields = function(title,fields)
4470 var tiddler = this.fetchTiddler(title);
4473 merge(tiddler.fields,fields);
4475 tiddler.incChangeCount(title);
4476 this.notify(title,true);
4477 this.setDirty(true);
4480 TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created)
4482 var tiddler = this.fetchTiddler(title);
4484 created = created || tiddler.created; // Preserve created date
4485 this.deleteTiddler(title);
4487 created = created || modified;
4488 tiddler = new Tiddler();
4490 tiddler.set(newTitle,newBody,modifier,modified,tags,created,fields);
4491 this.addTiddler(tiddler);
4492 if(clearChangeCount)
4493 tiddler.clearChangeCount();
4495 tiddler.incChangeCount();
4496 if(title != newTitle)
4497 this.notify(title,true);
4498 this.notify(newTitle,true);
4499 this.setDirty(true);
4503 TiddlyWiki.prototype.incChangeCount = function(title)
4505 var tiddler = this.fetchTiddler(title);
4507 tiddler.incChangeCount();
4510 TiddlyWiki.prototype.getLoader = function()
4513 this.loader = new TW21Loader();
4517 TiddlyWiki.prototype.getSaver = function()
4520 this.saver = new TW21Saver();
4524 // Return all tiddlers formatted as an HTML string
4525 TiddlyWiki.prototype.allTiddlersAsHtml = function()
4527 return this.getSaver().externalize(store);
4530 // Load contents of a TiddlyWiki from an HTML DIV
4531 TiddlyWiki.prototype.loadFromDiv = function(src,idPrefix,noUpdate)
4533 this.idPrefix = idPrefix;
4534 var storeElem = (typeof src == "string
") ? document.getElementById(src) : src;
4537 var tiddlers = this.getLoader().loadTiddlers(this,storeElem.childNodes);
4538 this.setDirty(false);
4540 for(var i = 0;i<tiddlers.length; i++)
4541 tiddlers[i].changed();
4545 // Load contents of a TiddlyWiki from a string
4546 // Returns null if there's an error
4547 TiddlyWiki.prototype.importTiddlyWiki = function(text)
4549 var posDiv = locateStoreArea(text);
4552 var content = "<
" + "html
><
" + "body
>" + text.substring(posDiv[0],posDiv[1] + endSaveArea.length) + "<
" + "/body
><
" + "/html
>";
4553 // Create the iframe
4554 var iframe = document.createElement("iframe
");
4555 iframe.style.display = "none
";
4556 document.body.appendChild(iframe);
4557 var doc = iframe.document;
4558 if(iframe.contentDocument)
4559 doc = iframe.contentDocument; // For NS6
4560 else if(iframe.contentWindow)
4561 doc = iframe.contentWindow.document; // For IE5.5 and IE6
4562 // Put the content in the iframe
4564 doc.writeln(content);
4566 // Load the content into a TiddlyWiki() object
4567 var storeArea = doc.getElementById("storeArea
");
4568 this.loadFromDiv(storeArea,"store
");
4569 // Get rid of the iframe
4570 iframe.parentNode.removeChild(iframe);
4574 TiddlyWiki.prototype.updateTiddlers = function()
4576 this.tiddlersUpdated = true;
4577 this.forEachTiddler(function(title,tiddler) {
4582 // Return an array of tiddlers matching a search regular expression
4583 TiddlyWiki.prototype.search = function(searchRegExp,sortField,excludeTag,match)
4585 var candidates = this.reverseLookup("tags
",excludeTag,!!match);
4587 for(var t=0; t<candidates.length; t++) {
4588 if((candidates[t].title.search(searchRegExp) != -1) || (candidates[t].text.search(searchRegExp) != -1))
4589 results.push(candidates[t]);
4592 sortField = "title
";
4593 results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
4597 // Returns a list of all tags in use
4598 // excludeTag - if present, excludes tags that are themselves tagged with excludeTag
4599 // Returns an array of arrays where [tag][0] is the name of the tag and [tag][1] is the number of occurances
4600 TiddlyWiki.prototype.getTags = function(excludeTag)
4603 this.forEachTiddler(function(title,tiddler) {
4604 for(var g=0; g<tiddler.tags.length; g++) {
4605 var tag = tiddler.tags[g];
4607 for(var c=0; c<results.length; c++) {
4608 if(results[c][0] == tag) {
4613 if(n && excludeTag) {
4614 var t = this.fetchTiddler(tag);
4615 if(t && t.isTagged(excludeTag))
4619 results.push([tag,1]);
4622 results.sort(function(a,b) {return a[0].toLowerCase() < b[0].toLowerCase() ? -1 : (a[0].toLowerCase() == b[0].toLowerCase() ? 0 : +1);});
4626 // Return an array of the tiddlers that are tagged with a given tag
4627 TiddlyWiki.prototype.getTaggedTiddlers = function(tag,sortField)
4629 return this.reverseLookup("tags
",tag,true,sortField);
4632 // Return an array of the tiddlers that link to a given tiddler
4633 TiddlyWiki.prototype.getReferringTiddlers = function(title,unusedParameter,sortField)
4635 if(!this.tiddlersUpdated)
4636 this.updateTiddlers();
4637 return this.reverseLookup("links
",title,true,sortField);
4640 // Return an array of the tiddlers that do or do not have a specified entry in the specified storage array (ie, "links
" or "tags
")
4641 // lookupMatch == true to match tiddlers, false to exclude tiddlers
4642 TiddlyWiki.prototype.reverseLookup = function(lookupField,lookupValue,lookupMatch,sortField)
4645 this.forEachTiddler(function(title,tiddler) {
4646 var f = !lookupMatch;
4647 for(var lookup=0; lookup<tiddler[lookupField].length; lookup++) {
4648 if(tiddler[lookupField][lookup] == lookupValue)
4652 results.push(tiddler);
4655 sortField = "title
";
4656 results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
4660 // Return the tiddlers as a sorted array
4661 TiddlyWiki.prototype.getTiddlers = function(field,excludeTag)
4664 this.forEachTiddler(function(title,tiddler) {
4665 if(excludeTag == undefined || !tiddler.isTagged(excludeTag))
4666 results.push(tiddler);
4669 results.sort(function(a,b) {return a[field] < b[field] ? -1 : (a[field] == b[field] ? 0 : +1);});
4673 // Return array of names of tiddlers that are referred to but not defined
4674 TiddlyWiki.prototype.getMissingLinks = function(sortField)
4676 if(!this.tiddlersUpdated)
4677 this.updateTiddlers();
4679 this.forEachTiddler(function (title,tiddler) {
4680 if(tiddler.isTagged("excludeMissing
") || tiddler.isTagged("systemConfig
"))
4682 for(var n=0; n<tiddler.links.length;n++) {
4683 var link = tiddler.links[n];
4684 if(this.fetchTiddler(link) == null && !this.isShadowTiddler(link))
4685 results.pushUnique(link);
4692 // Return an array of names of tiddlers that are defined but not referred to
4693 TiddlyWiki.prototype.getOrphans = function()
4696 this.forEachTiddler(function (title,tiddler) {
4697 if(this.getReferringTiddlers(title).length == 0 && !tiddler.isTagged("excludeLists
"))
4698 results.push(title);
4704 // Return an array of names of all the shadow tiddlers
4705 TiddlyWiki.prototype.getShadowed = function()
4708 for(var t in config.shadowTiddlers) {
4709 if(typeof config.shadowTiddlers[t] == "string
")
4716 // Return an array of tiddlers that have been touched since they were downloaded or created
4717 TiddlyWiki.prototype.getTouched = function()
4720 this.forEachTiddler(function(title,tiddler) {
4721 if(tiddler.isTouched())
4722 results.push(tiddler);
4728 // Resolves a Tiddler reference or tiddler title into a Tiddler object, or null if it doesn't exist
4729 TiddlyWiki.prototype.resolveTiddler = function(tiddler)
4731 var t = (typeof tiddler == 'string') ? this.getTiddler(tiddler) : tiddler;
4732 return t instanceof Tiddler ? t : null;
4735 // Filter a list of tiddlers
4736 TiddlyWiki.prototype.filterTiddlers = function(filter)
4741 var re = /([^\s\[\]]+)|(?:\[([ \w]+)\[([^\]]+)\]\])|(?:\[\[([^\]]+)\]\])/mg;
4742 var match = re.exec(filter);
4744 if(match[1] || match[4]) {
4745 var title = match[1] || match[4];
4746 tiddler = this.fetchTiddler(title);
4748 results.pushUnique(tiddler);
4749 } else if(this.isShadowTiddler(title)) {
4750 tiddler = new Tiddler();
4751 tiddler.set(title,this.getTiddlerText(title));
4752 results.pushUnique(tiddler);
4754 } else if(match[2]) {
4757 var matched = this.getTaggedTiddlers(match[3]);
4758 for(var m = 0; m < matched.length; m++)
4759 results.pushUnique(matched[m]);
4762 results = this.sortTiddlers(results,match[3]);
4766 match = re.exec(filter);
4772 // Sort a list of tiddlers
4773 TiddlyWiki.prototype.sortTiddlers = function(tiddlers,field)
4776 switch(field.substr(0,1)) {
4779 // Note: this fall-through is intentional
4782 field = field.substr(1);
4785 if(TiddlyWiki.standardFieldAccess[field])
4786 tiddlers.sort(function(a,b) {return a[field] < b[field] ? -asc : (a[field] == b[field] ? 0 : asc);});
4788 tiddlers.sort(function(a,b) {return a.fields[field] < b.fields[field] ? -asc : (a.fields[field] == b.fields[field] ? 0 : +asc);});
4792 // Returns true if path is a valid field name (path),
4793 // i.e. a sequence of identifiers, separated by '.'
4794 TiddlyWiki.isValidFieldName = function(name)
4796 var match = /[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*/.exec(name);
4797 return match && (match[0] == name);
4800 // Throws an exception when name is not a valid field name.
4801 TiddlyWiki.checkFieldName = function(name)
4803 if(!TiddlyWiki.isValidFieldName(name))
4804 throw config.messages.invalidFieldName.format([name]);
4807 function StringFieldAccess(n,readOnly)
4809 this.set = readOnly ?
4810 function(t,v) {if(v != t[n]) throw config.messages.fieldCannotBeChanged.format([n]);} :
4811 function(t,v) {if(v != t[n]) {t[n] = v; return true;}};
4812 this.get = function(t) {return t[n];};
4815 function DateFieldAccess(n)
4817 this.set = function(t,v) {
4818 var d = v instanceof Date ? v : Date.convertFromYYYYMMDDHHMM(v);
4820 t[n] = d; return true;
4823 this.get = function(t) {return t[n].convertToYYYYMMDDHHMM();};
4826 function LinksFieldAccess(n)
4828 this.set = function(t,v) {
4829 var s = (typeof v == "string
") ? v.readBracketedList() : v;
4830 if(s.toString() != t[n].toString()) {
4831 t[n] = s; return true;
4834 this.get = function(t) {return String.encodeTiddlyLinkList(t[n]);};
4837 TiddlyWiki.standardFieldAccess = {
4838 // The set functions return true when setting the data has changed the value.
4839 "title
": new StringFieldAccess("title
",true),
4840 // Handle the "tiddler
" field name as the title
4841 "tiddler
": new StringFieldAccess("title
",true),
4842 "text
": new StringFieldAccess("text
"),
4843 "modifier
": new StringFieldAccess("modifier
"),
4844 "modified
": new DateFieldAccess("modified
"),
4845 "created
": new DateFieldAccess("created
"),
4846 "tags
": new LinksFieldAccess("tags
")
4849 TiddlyWiki.isStandardField = function(name)
4851 return TiddlyWiki.standardFieldAccess[name] != undefined;
4854 // Sets the value of the given field of the tiddler to the value.
4855 // Setting an ExtendedField's value to null or undefined removes the field.
4856 // Setting a namespace to undefined removes all fields of that namespace.
4857 // The fieldName is case-insensitive.
4858 // All values will be converted to a string value.
4859 TiddlyWiki.prototype.setValue = function(tiddler,fieldName,value)
4861 TiddlyWiki.checkFieldName(fieldName);
4862 var t = this.resolveTiddler(tiddler);
4865 fieldName = fieldName.toLowerCase();
4866 var isRemove = (value === undefined) || (value === null);
4867 var accessor = TiddlyWiki.standardFieldAccess[fieldName];
4870 // don't remove StandardFields
4872 var h = TiddlyWiki.standardFieldAccess[fieldName];
4876 var oldValue = t.fields[fieldName];
4878 if(oldValue !== undefined) {
4879 // deletes a single field
4880 delete t.fields[fieldName];
4882 // no concrete value is defined for the fieldName
4883 // so we guess this is a namespace path.
4884 // delete all fields in a namespace
4885 var re = new RegExp('^'+fieldName+'\\.');
4887 for(var n in t.fields) {
4897 // the "normal
" set case. value is defined (not null/undefined)
4898 // For convenience provide a nicer conversion Date->String
4899 value = value instanceof Date ? value.convertToYYYYMMDDHHMMSSMMM() : String(value);
4900 if(oldValue == value)
4902 t.fields[fieldName] = value;
4905 // When we are here the tiddler/store really was changed.
4906 this.notify(t.title,true);
4907 if(!fieldName.match(/^temp\./))
4908 this.setDirty(true);
4911 // Returns the value of the given field of the tiddler.
4912 // The fieldName is case-insensitive.
4913 // Will only return String values (or undefined).
4914 TiddlyWiki.prototype.getValue = function(tiddler,fieldName)
4916 var t = this.resolveTiddler(tiddler);
4919 fieldName = fieldName.toLowerCase();
4920 var accessor = TiddlyWiki.standardFieldAccess[fieldName];
4922 return accessor.get(t);
4924 return t.fields[fieldName];
4927 // Calls the callback function for every field in the tiddler.
4928 // When callback function returns a non-false value the iteration stops
4929 // and that value is returned.
4930 // The order of the fields is not defined.
4931 // @param callback a function(tiddler,fieldName,value).
4932 TiddlyWiki.prototype.forEachField = function(tiddler,callback,onlyExtendedFields)
4934 var t = this.resolveTiddler(tiddler);
4938 for(n in t.fields) {
4939 result = callback(t,n,t.fields[n]);
4943 if(onlyExtendedFields)
4945 for(n in TiddlyWiki.standardFieldAccess) {
4947 // even though the "title
" field can also be referenced through the name "tiddler
"
4948 // we only visit this field once.
4950 result = callback(t,n,TiddlyWiki.standardFieldAccess[n].get(t));
4958 //-- Story functions
4961 function Story(containerId,idPrefix)
4963 this.container = containerId;
4964 this.idPrefix = idPrefix;
4965 this.highlightRegExp = null;
4966 this.tiddlerId = function(title) {
4967 var id = this.idPrefix + title;
4968 return id==this.container ? this.idPrefix + "_
" + title : id;
4970 this.containerId = function() {
4971 return this.container;
4975 Story.prototype.getTiddler = function(title)
4977 return document.getElementById(this.tiddlerId(title));
4980 Story.prototype.getContainer = function()
4982 return document.getElementById(this.containerId());
4985 Story.prototype.forEachTiddler = function(fn)
4987 var place = this.getContainer();
4990 var e = place.firstChild;
4992 var n = e.nextSibling;
4993 var title = e.getAttribute("tiddler
");
4994 fn.call(this,title,e);
4999 Story.prototype.displayDefaultTiddlers = function()
5001 this.displayTiddlers(null,store.filterTiddlers(store.getTiddlerText("DefaultTiddlers
")));
5004 Story.prototype.displayTiddlers = function(srcElement,titles,template,animate,unused,customFields,toggle)
5006 for(var t = titles.length-1;t>=0;t--)
5007 this.displayTiddler(srcElement,titles[t],template,animate,unused,customFields);
5010 Story.prototype.displayTiddler = function(srcElement,tiddler,template,animate,unused,customFields,toggle,animationSrc)
5012 var title = (tiddler instanceof Tiddler) ? tiddler.title : tiddler;
5013 var tiddlerElem = this.getTiddler(title);
5016 this.closeTiddler(title,true);
5018 this.refreshTiddler(title,template,false,customFields);
5020 var place = this.getContainer();
5021 var before = this.positionTiddler(srcElement);
5022 tiddlerElem = this.createTiddler(place,before,title,template,customFields);
5024 if(animationSrc && typeof animationSrc !== "string
") {
5025 srcElement = animationSrc;
5027 if(srcElement && typeof srcElement !== "string
") {
5028 if(config.options.chkAnimate && (animate == undefined || animate == true) && anim && typeof Zoomer == "function
" && typeof Scroller == "function
")
5029 anim.startAnimating(new Zoomer(title,srcElement,tiddlerElem),new Scroller(tiddlerElem));
5031 window.scrollTo(0,ensureVisible(tiddlerElem));
5035 Story.prototype.positionTiddler = function(srcElement)
5037 var place = this.getContainer();
5039 if(typeof srcElement == "string
") {
5040 switch(srcElement) {
5042 before = place.firstChild;
5049 var after = this.findContainingTiddler(srcElement);
5051 before = place.firstChild;
5052 } else if(after.nextSibling) {
5053 before = after.nextSibling;
5054 if(before.nodeType != 1)
5061 Story.prototype.createTiddler = function(place,before,title,template,customFields)
5063 var tiddlerElem = createTiddlyElement(null,"div
",this.tiddlerId(title),"tiddler
");
5064 tiddlerElem.setAttribute("refresh
","tiddler
");
5066 tiddlerElem.setAttribute("tiddlyFields
",customFields);
5067 place.insertBefore(tiddlerElem,before);
5068 var defaultText = null;
5069 if(!store.tiddlerExists(title) && !store.isShadowTiddler(title))
5070 defaultText = this.loadMissingTiddler(title,customFields,tiddlerElem);
5071 this.refreshTiddler(title,template,false,customFields,defaultText);
5075 Story.prototype.loadMissingTiddler = function(title,fields,tiddlerElem)
5077 var tiddler = new Tiddler(title);
5078 tiddler.fields = typeof fields == "string
" ? fields.decodeHashMap() : (fields || {});
5079 var serverType = tiddler.getServerType();
5080 var host = tiddler.fields['server.host'];
5081 var workspace = tiddler.fields['server.workspace'];
5082 if(!serverType || !host)
5084 var sm = new SyncMachine(serverType,{
5086 return this.openHost(host,"openWorkspace
");
5088 openWorkspace: function() {
5089 return this.openWorkspace(workspace,"getTiddler
");
5091 getTiddler: function() {
5092 return this.getTiddler(title,"onGetTiddler
");
5094 onGetTiddler: function(context) {
5095 var tiddler = context.tiddler;
5096 if(tiddler && tiddler.text) {
5097 var downloaded = new Date();
5098 if(!tiddler.created)
5099 tiddler.created = downloaded;
5100 if(!tiddler.modified)
5101 tiddler.modified = tiddler.created;
5102 store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields,true,tiddler.created);
5108 error: function(message) {
5109 displayMessage("Error loading missing tiddler from %
0: %
1".format([host,message]));
5113 return config.messages.loadingMissingTiddler.format([title,serverType,host,workspace]);
5116 Story.prototype.chooseTemplateForTiddler = function(title,template)
5119 template = DEFAULT_VIEW_TEMPLATE;
5120 if(template == DEFAULT_VIEW_TEMPLATE || template == DEFAULT_EDIT_TEMPLATE)
5121 template = config.tiddlerTemplates[template];
5125 Story.prototype.getTemplateForTiddler = function(title,template,tiddler)
5127 return store.getRecursiveTiddlerText(template,null,10);
5130 Story.prototype.refreshTiddler = function(title,template,force,customFields,defaultText)
5132 var tiddlerElem = this.getTiddler(title);
5134 if(tiddlerElem.getAttribute("dirty
") == "true
" && !force)
5136 template = this.chooseTemplateForTiddler(title,template);
5137 var currTemplate = tiddlerElem.getAttribute("template
");
5138 if((template != currTemplate) || force) {
5139 var tiddler = store.getTiddler(title);
5141 tiddler = new Tiddler();
5142 if(store.isShadowTiddler(title)) {
5143 tiddler.set(title,store.getTiddlerText(title),config.views.wikified.shadowModifier,version.date,[],version.date);
5145 var text = template=="EditTemplate
" ?
5146 config.views.editor.defaultText.format([title]) :
5147 config.views.wikified.defaultText.format([title]);
5148 text = defaultText || text;
5149 var fields = customFields ? customFields.decodeHashMap() : null;
5150 tiddler.set(title,text,config.views.wikified.defaultModifier,version.date,[],version.date,fields);
5153 tiddlerElem.setAttribute("tags
",tiddler.tags.join(" "));
5154 tiddlerElem.setAttribute("tiddler
",title);
5155 tiddlerElem.setAttribute("template
",template);
5156 tiddlerElem.onmouseover = this.onTiddlerMouseOver;
5157 tiddlerElem.onmouseout = this.onTiddlerMouseOut;
5158 tiddlerElem.ondblclick = this.onTiddlerDblClick;
5159 tiddlerElem[window.event?"onkeydown
":"onkeypress
"] = this.onTiddlerKeyPress;
5160 tiddlerElem.innerHTML = this.getTemplateForTiddler(title,template,tiddler);
5161 applyHtmlMacros(tiddlerElem,tiddler);
5162 if(store.getTaggedTiddlers(title).length > 0)
5163 addClass(tiddlerElem,"isTag
");
5165 removeClass(tiddlerElem,"isTag
");
5166 if(store.tiddlerExists(title)) {
5167 removeClass(tiddlerElem,"shadow
");
5168 removeClass(tiddlerElem,"missing
");
5170 addClass(tiddlerElem,store.isShadowTiddler(title) ? "shadow
" : "missing
");
5173 this.addCustomFields(tiddlerElem,customFields);
5180 Story.prototype.addCustomFields = function(place,customFields)
5182 var fields = customFields.decodeHashMap();
5183 var w = document.createElement("div
");
5184 w.style.display = "none
";
5185 place.appendChild(w);
5186 for(var t in fields) {
5187 var e = document.createElement("input
");
5188 e.setAttribute("type
","text
");
5189 e.setAttribute("value
",fields[t]);
5191 e.setAttribute("edit
",t);
5195 Story.prototype.refreshAllTiddlers = function(force)
5197 var e = this.getContainer().firstChild;
5199 var template = e.getAttribute("template
");
5200 if(template && e.getAttribute("dirty
") != "true
") {
5201 this.refreshTiddler(e.getAttribute("tiddler
"),force ? null : template,true);
5207 Story.prototype.onTiddlerMouseOver = function(e)
5209 if(window.addClass instanceof Function)
5210 addClass(this,"selected
");
5213 Story.prototype.onTiddlerMouseOut = function(e)
5215 if(window.removeClass instanceof Function)
5216 removeClass(this,"selected
");
5219 Story.prototype.onTiddlerDblClick = function(ev)
5221 var e = ev || window.event;
5222 var target = resolveTarget(e);
5223 if(target && target.nodeName.toLowerCase() != "input
" && target.nodeName.toLowerCase() != "textarea
") {
5224 if(document.selection && document.selection.empty)
5225 document.selection.empty();
5226 config.macros.toolbar.invokeCommand(this,"defaultCommand
",e);
5227 e.cancelBubble = true;
5228 if(e.stopPropagation) e.stopPropagation();
5234 Story.prototype.onTiddlerKeyPress = function(ev)
5236 var e = ev || window.event;
5238 var consume = false;
5239 var title = this.getAttribute("tiddler
");
5240 var target = resolveTarget(e);
5243 if(config.options.chkInsertTabs && target.tagName.toLowerCase() == "textarea
") {
5244 replaceSelection(target,String.fromCharCode(9));
5247 if(config.isOpera) {
5248 target.onblur = function() {
5254 case 13: // Ctrl-Enter
5255 case 10: // Ctrl-Enter on IE PC
5256 case 77: // Ctrl-Enter is "M
" on some platforms
5259 config.macros.toolbar.invokeCommand(this,"defaultCommand
",e);
5265 config.macros.toolbar.invokeCommand(this,"cancelCommand
",e);
5269 e.cancelBubble = consume;
5271 if(e.stopPropagation) e.stopPropagation(); // Stop Propagation
5272 e.returnValue = true; // Cancel The Event in IE
5273 if(e.preventDefault ) e.preventDefault(); // Cancel The Event in Moz
5278 Story.prototype.getTiddlerField = function(title,field)
5280 var tiddlerElem = this.getTiddler(title);
5283 var children = tiddlerElem.getElementsByTagName("*
");
5284 for(var t=0; t<children.length; t++) {
5285 var c = children[t];
5286 if(c.tagName.toLowerCase() == "input
" || c.tagName.toLowerCase() == "textarea
") {
5289 if(c.getAttribute("edit
") == field)
5297 Story.prototype.focusTiddler = function(title,field)
5299 var e = this.getTiddlerField(title,field);
5306 Story.prototype.blurTiddler = function(title)
5308 var tiddlerElem = this.getTiddler(title);
5309 if(tiddlerElem && tiddlerElem.focus && tiddlerElem.blur) {
5310 tiddlerElem.focus();
5315 Story.prototype.setTiddlerField = function(title,tag,mode,field)
5317 var c = this.getTiddlerField(title,field);
5318 var tags = c.value.readBracketedList();
5319 tags.setItem(tag,mode);
5320 c.value = String.encodeTiddlyLinkList(tags);
5323 Story.prototype.setTiddlerTag = function(title,tag,mode)
5325 this.setTiddlerField(title,tag,mode,"tags
");
5328 Story.prototype.closeTiddler = function(title,animate,unused)
5330 var tiddlerElem = this.getTiddler(title);
5333 this.scrubTiddler(tiddlerElem);
5334 if(config.options.chkAnimate && animate && anim && typeof Slider == "function
")
5335 anim.startAnimating(new Slider(tiddlerElem,false,null,"all
"));
5337 removeNode(tiddlerElem);
5343 Story.prototype.scrubTiddler = function(tiddlerElem)
5345 tiddlerElem.id = null;
5348 Story.prototype.setDirty = function(title,dirty)
5350 var tiddlerElem = this.getTiddler(title);
5352 tiddlerElem.setAttribute("dirty
",dirty ? "true
" : "false
");
5355 Story.prototype.isDirty = function(title)
5357 var tiddlerElem = this.getTiddler(title);
5359 return tiddlerElem.getAttribute("dirty
") == "true
";
5363 Story.prototype.areAnyDirty = function()
5366 this.forEachTiddler(function(title,element) {
5367 if(this.isDirty(title))
5373 Story.prototype.closeAllTiddlers = function(exclude)
5376 this.forEachTiddler(function(title,element) {
5377 if((title != exclude) && element.getAttribute("dirty
") != "true
")
5378 this.closeTiddler(title);
5380 window.scrollTo(0,ensureVisible(this.container));
5383 Story.prototype.isEmpty = function()
5385 var place = this.getContainer();
5386 return place && place.firstChild == null;
5389 Story.prototype.search = function(text,useCaseSensitive,useRegExp)
5391 this.closeAllTiddlers();
5392 highlightHack = new RegExp(useRegExp ? text : text.escapeRegExp(),useCaseSensitive ? "mg
" : "img
");
5393 var matches = store.search(highlightHack,"title
","excludeSearch
");
5394 this.displayTiddlers(null,matches);
5395 highlightHack = null;
5396 var q = useRegExp ? "/
" : "'
";
5397 if(matches.length > 0)
5398 displayMessage(config.macros.search.successMsg.format([matches.length.toString(),q + text + q]));
5400 displayMessage(config.macros.search.failureMsg.format([q + text + q]));
5403 Story.prototype.findContainingTiddler = function(e)
5405 while(e && !hasClass(e,"tiddler
"))
5410 Story.prototype.gatherSaveFields = function(e,fields)
5412 if(e && e.getAttribute) {
5413 var f = e.getAttribute("edit
");
5415 fields[f] = e.value.replace(/\r/mg,"");
5416 if(e.hasChildNodes()) {
5417 var c = e.childNodes;
5418 for(var t=0; t<c.length; t++)
5419 this.gatherSaveFields(c[t],fields);
5424 Story.prototype.hasChanges = function(title)
5426 var e = this.getTiddler(title);
5429 this.gatherSaveFields(e,fields);
5430 var tiddler = store.fetchTiddler(title);
5433 for(var n in fields) {
5434 if(store.getValue(title,n) != fields[n])
5441 Story.prototype.saveTiddler = function(title,minorUpdate)
5443 var tiddlerElem = this.getTiddler(title);
5446 this.gatherSaveFields(tiddlerElem,fields);
5447 var newTitle = fields.title || title;
5448 if(!store.tiddlerExists(newTitle))
5449 newTitle = newTitle.trim();
5450 if(store.tiddlerExists(newTitle) && newTitle != title) {
5451 if(!confirm(config.messages.overwriteWarning.format([newTitle.toString()])))
5454 if(newTitle != title)
5455 this.closeTiddler(newTitle,false);
5456 tiddlerElem.id = this.tiddlerId(newTitle);
5457 tiddlerElem.setAttribute("tiddler
",newTitle);
5458 tiddlerElem.setAttribute("template
",DEFAULT_VIEW_TEMPLATE);
5459 tiddlerElem.setAttribute("dirty
","false
");
5460 if(config.options.chkForceMinorUpdate)
5461 minorUpdate = !minorUpdate;
5462 if(!store.tiddlerExists(newTitle))
5463 minorUpdate = false;
5464 var newDate = new Date();
5465 var extendedFields = store.tiddlerExists(newTitle) ? store.fetchTiddler(newTitle).fields : (newTitle!=title && store.tiddlerExists(title) ? store.fetchTiddler(title).fields : config.defaultCustomFields);
5466 for(var n in fields) {
5467 if(!TiddlyWiki.isStandardField(n))
5468 extendedFields[n] = fields[n];
5470 var tiddler = store.saveTiddler(title,newTitle,fields.text,minorUpdate ? undefined : config.options.txtUserName,minorUpdate ? undefined : newDate,fields.tags,extendedFields);
5471 autoSaveChanges(null,[tiddler]);
5477 Story.prototype.permaView = function()
5480 this.forEachTiddler(function(title,element) {
5481 links.push(String.encodeTiddlyLink(title));
5483 var t = encodeURIComponent(links.join(" "));
5486 if(window.location.hash != t)
5487 window.location.hash = t;
5490 Story.prototype.switchTheme = function(theme)
5495 var isAvailable = function(title) {
5496 var s = title ? title.indexOf(config.textPrimitives.sectionSeparator) : -1;
5498 title = title.substr(0,s);
5499 return store.tiddlerExists(title) || store.isShadowTiddler(title);
5502 var getSlice = function(theme,slice) {
5505 r = store.getTiddlerSlice(theme,slice+"ReadOnly
") || store.getTiddlerSlice(theme,"Web
"+slice);
5506 r = r || store.getTiddlerSlice(theme,slice);
5507 if(r && r.indexOf(config.textPrimitives.sectionSeparator)==0)
5509 return isAvailable(r) ? r : slice;
5512 var replaceNotification = function(i,name,theme,slice) {
5513 var newName = getSlice(theme,slice);
5514 if(name!=newName && store.namedNotifications[i].name==name) {
5515 store.namedNotifications[i].name = newName;
5521 var pt = config.refresherData.pageTemplate;
5522 var vi = DEFAULT_VIEW_TEMPLATE;
5523 var vt = config.tiddlerTemplates[vi];
5524 var ei = DEFAULT_EDIT_TEMPLATE;
5525 var et = config.tiddlerTemplates[ei];
5527 for(var i=0; i<config.notifyTiddlers.length; i++) {
5528 var name = config.notifyTiddlers[i].name;
5530 case "PageTemplate
":
5531 config.refresherData.pageTemplate = replaceNotification(i,config.refresherData.pageTemplate,theme,name);
5534 removeStyleSheet(config.refresherData.styleSheet);
5535 config.refresherData.styleSheet = replaceNotification(i,config.refresherData.styleSheet,theme,name);
5537 case "ColorPalette
":
5538 config.refresherData.colorPalette = replaceNotification(i,config.refresherData.colorPalette,theme,name);
5544 config.tiddlerTemplates[vi] = getSlice(theme,"ViewTemplate
");
5545 config.tiddlerTemplates[ei] = getSlice(theme,"EditTemplate
");
5547 if(config.refresherData.pageTemplate!=pt || config.tiddlerTemplates[vi]!=vt || config.tiddlerTemplates[ei]!=et) {
5549 this.refreshAllTiddlers(true);
5551 setStylesheet(store.getRecursiveTiddlerText(config.refresherData.styleSheet,"",10),config.refreshers.styleSheet);
5553 config.options.txtTheme = theme;
5554 saveOptionCookie("txtTheme
");
5577 var cmb = config.messages.backstage;
5578 this.area = document.getElementById("backstageArea
");
5579 this.toolbar = document.getElementById("backstageToolbar
");
5580 this.button = document.getElementById("backstageButton
");
5581 this.button.style.display = "block
";
5582 var t = cmb.open.text + " " + glyph("bentArrowLeft
");
5583 this.showButton = createTiddlyButton(this.button,t,cmb.open.tooltip,
5584 function(e) {backstage.show(); return false;},null,"backstageShow
");
5585 t = glyph("bentArrowRight
") + " " + cmb.close.text;
5586 this.hideButton = createTiddlyButton(this.button,t,cmb.close.tooltip,
5587 function(e) {backstage.hide(); return false;},null,"backstageHide
");
5588 this.cloak = document.getElementById("backstageCloak
");
5589 this.panel = document.getElementById("backstagePanel
");
5590 this.panelFooter = createTiddlyElement(this.panel,"div
",null,"backstagePanelFooter
");
5591 this.panelBody = createTiddlyElement(this.panel,"div
",null,"backstagePanelBody
");
5592 this.cloak.onmousedown = function(e) {backstage.switchTab(null);};
5593 createTiddlyText(this.toolbar,cmb.prompt);
5594 for(t=0; t<config.backstageTasks.length; t++) {
5595 var taskName = config.backstageTasks[t];
5596 var task = config.tasks[taskName];
5597 var handler = task.action ? this.onClickCommand : this.onClickTab;
5598 var text = task.text + (task.action ? "" : glyph("downTriangle
"));
5599 var btn = createTiddlyButton(this.toolbar,text,task.tooltip,handler,"backstageTab
");
5600 btn.setAttribute("task
",taskName);
5601 addClass(btn,task.action ? "backstageAction
" : "backstageTask
");
5603 this.content = document.getElementById("contentWrapper
");
5604 if(config.options.chkBackstage)
5610 isVisible: function() {
5611 return this.area ? this.area.style.display == "block
" : false;
5615 this.area.style.display = "block
";
5616 if(anim && config.options.chkAnimate) {
5617 backstage.toolbar.style.left = findWindowWidth() + "px
";
5618 var p = [{style: "left
", start: findWindowWidth(), end: 0, template: "%
0px
"}];
5619 anim.startAnimating(new Morpher(backstage.toolbar,config.animDuration,p));
5621 backstage.area.style.left = "0px
";
5623 this.showButton.style.display = "none
";
5624 this.hideButton.style.display = "block
";
5625 config.options.chkBackstage = true;
5626 saveOptionCookie("chkBackstage
");
5627 addClass(this.content,"backstageVisible
");
5631 if(this.currTabElem) {
5632 this.switchTab(null);
5634 backstage.toolbar.style.left = "0px
";
5635 if(anim && config.options.chkAnimate) {
5636 var p = [{style: "left
", start: 0, end: findWindowWidth(), template: "%
0px
"}];
5637 var c = function(element,properties) {backstage.area.style.display = "none
";};
5638 anim.startAnimating(new Morpher(backstage.toolbar,config.animDuration,p,c));
5640 this.area.style.display = "none
";
5642 this.showButton.style.display = "block
";
5643 this.hideButton.style.display = "none
";
5644 config.options.chkBackstage = false;
5645 saveOptionCookie("chkBackstage
");
5646 removeClass(this.content,"backstageVisible
");
5650 onClickCommand: function(e) {
5651 var task = config.tasks[this.getAttribute("task
")];
5652 displayMessage(task);
5654 backstage.switchTab(null);
5660 onClickTab: function(e) {
5661 backstage.switchTab(this.getAttribute("task
"));
5665 // Switch to a given tab, or none if null is passed
5666 switchTab: function(tabName) {
5668 var e = this.toolbar.firstChild;
5671 if(e.getAttribute && e.getAttribute("task
") == tabName)
5675 if(tabName == backstage.currTabName)
5677 if(backstage.currTabElem) {
5678 removeClass(this.currTabElem,"backstageSelTab
");
5680 if(tabElem && tabName) {
5681 backstage.preparePanel();
5682 addClass(tabElem,"backstageSelTab
");
5683 var task = config.tasks[tabName];
5684 wikify(task.content,backstage.panelBody,null,null);
5685 backstage.showPanel();
5686 } else if(backstage.currTabElem) {
5687 backstage.hidePanel();
5689 backstage.currTabName = tabName;
5690 backstage.currTabElem = tabElem;
5693 isPanelVisible: function() {
5694 return backstage.panel ? backstage.panel.style.display == "block
" : false;
5697 preparePanel: function() {
5698 backstage.cloak.style.height = findWindowHeight() + "px
";
5699 backstage.cloak.style.display = "block
";
5700 removeChildren(backstage.panelBody);
5701 return backstage.panelBody;
5704 showPanel: function() {
5705 backstage.panel.style.display = "block
";
5706 if(anim && config.options.chkAnimate) {
5707 backstage.panel.style.top = (-backstage.panel.offsetHeight) + "px
";
5708 var p = [{style: "top
", start: -backstage.panel.offsetHeight, end: 0, template: "%
0px
"}];
5709 anim.startAnimating(new Morpher(backstage.panel,config.animDuration,p),new Scroller(backstage.panel,false));
5711 backstage.panel.style.top = "0px
";
5713 return backstage.panelBody;
5716 hidePanel: function() {
5717 if(backstage.currTabElem)
5718 removeClass(backstage.currTabElem,"backstageSelTab
");
5719 backstage.currTabElem = null;
5720 backstage.currTabName = null;
5721 if(anim && config.options.chkAnimate) {
5723 {style: "top
", start: 0, end: -(backstage.panel.offsetHeight), template: "%
0px
"},
5724 {style: "display
", atEnd: "none
"}
5726 var c = function(element,properties) {backstage.cloak.style.display = "none
";};
5727 anim.startAnimating(new Morpher(backstage.panel,config.animDuration,p,c));
5729 backstage.panel.style.display = "none
";
5730 backstage.cloak.style.display = "none
";
5735 config.macros.backstage = {};
5737 config.macros.backstage.handler = function(place,macroName,params)
5739 var backstageTask = config.tasks[params[0]];
5741 createTiddlyButton(place,backstageTask.text,backstageTask.tooltip,function(e) {backstage.switchTab(params[0]); return false;});
5745 //-- ImportTiddlers macro
5748 config.macros.importTiddlers.handler = function(place,macroName,params,wikifier,paramString,tiddler)
5751 createTiddlyElement(place,"div
",null,"marked
",this.readOnlyWarning);
5754 var w = new Wizard();
5755 w.createWizard(place,this.wizardTitle);
5759 config.macros.importTiddlers.onCancel = function(e)
5761 var wizard = new Wizard(this);
5762 var place = wizard.clear();
5763 config.macros.importTiddlers.restart(wizard);
5767 config.macros.importTiddlers.onClose = function(e)
5769 backstage.hidePanel();
5773 config.macros.importTiddlers.restart = function(wizard)
5775 wizard.addStep(this.step1Title,this.step1Html);
5776 var s = wizard.getElement("selTypes
");
5777 for(var t in config.adaptors) {
5778 var e = createTiddlyElement(s,"option
",null,null,config.adaptors[t].serverLabel ? config.adaptors[t].serverLabel : t);
5781 if(config.defaultAdaptor)
5782 s.value = config.defaultAdaptor;
5783 s = wizard.getElement("selFeeds
");
5784 var feeds = this.getFeeds();
5786 e = createTiddlyElement(s,"option
",null,null,t);
5789 wizard.setValue("feeds
",feeds);
5790 s.onchange = config.macros.importTiddlers.onFeedChange;
5791 var fileInput = wizard.getElement("txtBrowse
");
5792 fileInput.onchange = config.macros.importTiddlers.onBrowseChange;
5793 fileInput.onkeyup = config.macros.importTiddlers.onBrowseChange;
5794 wizard.setButtons([{caption: this.openLabel, tooltip: this.openPrompt, onClick: config.macros.importTiddlers.onOpen}]);
5795 wizard.formElem.action = "javascript:;
";
5796 wizard.formElem.onsubmit = function() {
5797 if(this.txtPath.value.length)
5798 this.lastChild.firstChild.onclick();
5802 config.macros.importTiddlers.getFeeds = function()
5805 var tagged = store.getTaggedTiddlers("systemServer
","title
");
5806 for(var t=0; t<tagged.length; t++) {
5807 var title = tagged[t].title;
5808 var serverType = store.getTiddlerSlice(title,"Type
");
5810 serverType = "file
";
5811 feeds[title] = {title: title,
5812 url: store.getTiddlerSlice(title,"URL
"),
5813 workspace: store.getTiddlerSlice(title,"Workspace
"),
5814 workspaceList: store.getTiddlerSlice(title,"WorkspaceList
"),
5815 tiddlerFilter: store.getTiddlerSlice(title,"TiddlerFilter
"),
5816 serverType: serverType,
5817 description: store.getTiddlerSlice(title,"Description
")};
5822 config.macros.importTiddlers.onFeedChange = function(e)
5824 var wizard = new Wizard(this);
5825 var selTypes = wizard.getElement("selTypes
");
5826 var fileInput = wizard.getElement("txtPath
");
5827 var feeds = wizard.getValue("feeds
");
5828 var f = feeds[this.value];
5830 selTypes.value = f.serverType;
5831 fileInput.value = f.url;
5832 wizard.setValue("feedName
",f.serverType);
5833 wizard.setValue("feedHost
",f.url);
5834 wizard.setValue("feedWorkspace
",f.workspace);
5835 wizard.setValue("feedWorkspaceList
",f.workspaceList);
5836 wizard.setValue("feedTiddlerFilter
",f.tiddlerFilter);
5841 config.macros.importTiddlers.onBrowseChange = function(e)
5843 var wizard = new Wizard(this);
5844 var fileInput = wizard.getElement("txtPath
");
5845 fileInput.value = config.macros.importTiddlers.getURLFromLocalPath(this.value);
5846 var serverType = wizard.getElement("selTypes
");
5847 serverType.value = "file
";
5851 config.macros.importTiddlers.getURLFromLocalPath = function(v)
5855 v = v.replace(/\\/g,"/
"); // use "/
" for cross-platform consistency
5857 var t = v.split(":
");
5858 var p = t[1]||t[0]; // remove drive letter (if any)
5859 if (t[1] && (t[0]=="http
"||t[0]=="https
"||t[0]=="file
")) {
5861 } else if(p.substr(0,1)=="/
") {
5862 u = document.location.protocol + "//
" + document.location.hostname + (t[1] ? "/
" : "") + v;
5864 var c = document.location.href.replace(/\\/g,"/
");
5865 var pos = c.lastIndexOf("/
");
5867 c = c.substr(0,pos); // remove filename
5873 config.macros.importTiddlers.onOpen = function(e)
5875 var wizard = new Wizard(this);
5876 var fileInput = wizard.getElement("txtPath
");
5877 var url = fileInput.value;
5878 var serverType = wizard.getElement("selTypes
").value || config.defaultAdaptor;
5879 var adaptor = new config.adaptors[serverType]();
5880 wizard.setValue("adaptor
",adaptor);
5881 wizard.setValue("serverType
",serverType);
5882 wizard.setValue("host
",url);
5883 var ret = adaptor.openHost(url,null,wizard,config.macros.importTiddlers.onOpenHost);
5885 displayMessage(ret);
5886 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusOpenHost);
5890 config.macros.importTiddlers.onOpenHost = function(context,wizard)
5892 var adaptor = wizard.getValue("adaptor
");
5893 if(context.status !== true)
5894 displayMessage("Error in importTiddlers.onOpenHost:
" + context.statusText);
5895 var ret = adaptor.getWorkspaceList(context,wizard,config.macros.importTiddlers.onGetWorkspaceList);
5897 displayMessage(ret);
5898 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusGetWorkspaceList);
5901 config.macros.importTiddlers.onGetWorkspaceList = function(context,wizard)
5903 if(context.status !== true)
5904 displayMessage("Error in importTiddlers.onGetWorkspaceList:
" + context.statusText);
5905 wizard.setValue("context
",context);
5906 var workspace = wizard.getValue("feedWorkspace
");
5907 if(!workspace && context.workspaces.length==1)
5908 workspace = context.workspaces[0].title;
5910 var ret = context.adaptor.openWorkspace(workspace,context,wizard,config.macros.importTiddlers.onOpenWorkspace);
5912 displayMessage(ret);
5913 wizard.setValue("workspace
",workspace);
5914 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusOpenWorkspace);
5917 wizard.addStep(config.macros.importTiddlers.step2Title,config.macros.importTiddlers.step2Html);
5918 var s = wizard.getElement("selWorkspace
");
5919 s.onchange = config.macros.importTiddlers.onWorkspaceChange;
5920 for(var t=0; t<context.workspaces.length; t++) {
5921 var e = createTiddlyElement(s,"option
",null,null,context.workspaces[t].title);
5922 e.value = context.workspaces[t].title;
5924 var workspaceList = wizard.getValue("feedWorkspaceList
");
5926 var list = workspaceList.parseParams("workspace
",null,false,true);
5927 for(var n=1; n<list.length; n++) {
5928 if(context.workspaces.findByField("title
",list[n].value) == null) {
5929 e = createTiddlyElement(s,"option
",null,null,list[n].value);
5930 e.value = list[n].value;
5935 t = wizard.getElement("txtWorkspace
");
5936 t.value = workspace;
5938 wizard.setButtons([{caption: config.macros.importTiddlers.openLabel, tooltip: config.macros.importTiddlers.openPrompt, onClick: config.macros.importTiddlers.onChooseWorkspace}]);
5941 config.macros.importTiddlers.onWorkspaceChange = function(e)
5943 var wizard = new Wizard(this);
5944 var t = wizard.getElement("txtWorkspace
");
5945 t.value = this.value;
5946 this.selectedIndex = 0;
5950 config.macros.importTiddlers.onChooseWorkspace = function(e)
5952 var wizard = new Wizard(this);
5953 var adaptor = wizard.getValue("adaptor
");
5954 var workspace = wizard.getElement("txtWorkspace
").value;
5955 wizard.setValue("workspace
",workspace);
5956 var context = wizard.getValue("context
");
5957 var ret = adaptor.openWorkspace(workspace,context,wizard,config.macros.importTiddlers.onOpenWorkspace);
5959 displayMessage(ret);
5960 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusOpenWorkspace);
5964 config.macros.importTiddlers.onOpenWorkspace = function(context,wizard)
5966 if(context.status !== true)
5967 displayMessage("Error in importTiddlers.onOpenWorkspace:
" + context.statusText);
5968 var adaptor = wizard.getValue("adaptor
");
5969 var ret = adaptor.getTiddlerList(context,wizard,config.macros.importTiddlers.onGetTiddlerList,wizard.getValue("feedTiddlerFilter
"));
5971 displayMessage(ret);
5972 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusGetTiddlerList);
5975 config.macros.importTiddlers.onGetTiddlerList = function(context,wizard)
5977 if(context.status !== true) {
5978 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.errorGettingTiddlerList);
5981 // Extract data for the listview
5982 var listedTiddlers = [];
5983 if(context.tiddlers) {
5984 for(var n=0; n<context.tiddlers.length; n++) {
5985 var tiddler = context.tiddlers[n];
5986 listedTiddlers.push({
5987 title: tiddler.title,
5988 modified: tiddler.modified,
5989 modifier: tiddler.modifier,
5990 text: tiddler.text ? wikifyPlainText(tiddler.text,100) : "",
5992 size: tiddler.text ? tiddler.text.length : 0,
5997 listedTiddlers.sort(function(a,b) {return a.title < b.title ? -1 : (a.title == b.title ? 0 : +1);});
5998 // Display the listview
5999 wizard.addStep(config.macros.importTiddlers.step3Title,config.macros.importTiddlers.step3Html);
6000 var markList = wizard.getElement("markList
");
6001 var listWrapper = document.createElement("div
");
6002 markList.parentNode.insertBefore(listWrapper,markList);
6003 var listView = ListView.create(listWrapper,listedTiddlers,config.macros.importTiddlers.listViewTemplate);
6004 wizard.setValue("listView
",listView);
6005 var txtSaveTiddler = wizard.getElement("txtSaveTiddler
");
6006 txtSaveTiddler.value = config.macros.importTiddlers.generateSystemServerName(wizard);
6008 {caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel},
6009 {caption: config.macros.importTiddlers.importLabel, tooltip: config.macros.importTiddlers.importPrompt, onClick: config.macros.importTiddlers.doImport}
6013 config.macros.importTiddlers.generateSystemServerName = function(wizard)
6015 var serverType = wizard.getValue("serverType
");
6016 var host = wizard.getValue("host
");
6017 var workspace = wizard.getValue("workspace
");
6018 var pattern = config.macros.importTiddlers[workspace ? "systemServerNamePattern
" : "systemServerNamePatternNoWorkspace
"];
6019 return pattern.format([serverType,host,workspace]);
6022 config.macros.importTiddlers.saveServerTiddler = function(wizard)
6024 var txtSaveTiddler = wizard.getElement("txtSaveTiddler
").value;
6025 if(store.tiddlerExists(txtSaveTiddler)) {
6026 if(!confirm(config.macros.importTiddlers.confirmOverwriteSaveTiddler.format([txtSaveTiddler])))
6028 store.suspendNotifications();
6029 store.removeTiddler(txtSaveTiddler);
6030 store.resumeNotifications();
6032 var serverType = wizard.getValue("serverType
");
6033 var host = wizard.getValue("host
");
6034 var workspace = wizard.getValue("workspace
");
6035 var text = config.macros.importTiddlers.serverSaveTemplate.format([serverType,host,workspace]);
6036 store.saveTiddler(txtSaveTiddler,txtSaveTiddler,text,config.macros.importTiddlers.serverSaveModifier,new Date(),["systemServer
"]);
6039 config.macros.importTiddlers.doImport = function(e)
6041 var wizard = new Wizard(this);
6042 if(wizard.getElement("chkSave
").checked)
6043 config.macros.importTiddlers.saveServerTiddler(wizard);
6044 var chkSync = wizard.getElement("chkSync
").checked;
6045 wizard.setValue("sync
",chkSync);
6046 var listView = wizard.getValue("listView
");
6047 var rowNames = ListView.getSelectedRows(listView);
6048 var adaptor = wizard.getValue("adaptor
");
6051 for(t=0; t<rowNames.length; t++) {
6052 if(store.tiddlerExists(rowNames[t]))
6053 overwrite.push(rowNames[t]);
6055 if(overwrite.length > 0) {
6056 if(!confirm(config.macros.importTiddlers.confirmOverwriteText.format([overwrite.join(",
")])))
6059 wizard.addStep(config.macros.importTiddlers.step4Title.format([rowNames.length]),config.macros.importTiddlers.step4Html);
6060 for(t=0; t<rowNames.length; t++) {
6061 var link = document.createElement("div
");
6062 createTiddlyLink(link,rowNames[t],true);
6063 var place = wizard.getElement("markReport
");
6064 place.parentNode.insertBefore(link,place);
6066 wizard.setValue("remainingImports
",rowNames.length);
6068 {caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}
6069 ],config.macros.importTiddlers.statusDoingImport);
6070 for(t=0; t<rowNames.length; t++) {
6072 context.allowSynchronous = true;
6073 var inbound = adaptor.getTiddler(rowNames[t],context,wizard,config.macros.importTiddlers.onGetTiddler);
6078 config.macros.importTiddlers.onGetTiddler = function(context,wizard)
6081 displayMessage("Error in importTiddlers.onGetTiddler:
" + context.statusText);
6082 var tiddler = context.tiddler;
6083 store.suspendNotifications();
6084 store.saveTiddler(tiddler.title, tiddler.title, tiddler.text, tiddler.modifier, tiddler.modified, tiddler.tags, tiddler.fields, true, tiddler.created);
6085 if(!wizard.getValue("sync
")) {
6086 store.setValue(tiddler.title,'server',null);
6088 store.resumeNotifications();
6089 if(!context.isSynchronous)
6090 store.notify(tiddler.title,true);
6091 var remainingImports = wizard.getValue("remainingImports
")-1;
6092 wizard.setValue("remainingImports
",remainingImports);
6093 if(remainingImports == 0) {
6094 if(context.isSynchronous) {
6099 {caption: config.macros.importTiddlers.doneLabel, tooltip: config.macros.importTiddlers.donePrompt, onClick: config.macros.importTiddlers.onClose}
6100 ],config.macros.importTiddlers.statusDoneImport);
6109 config.macros.upgrade.handler = function(place)
6111 var w = new Wizard();
6112 w.createWizard(place,this.wizardTitle);
6113 w.addStep(this.step1Title,this.step1Html.format([this.source,this.source]));
6114 w.setButtons([{caption: this.upgradeLabel, tooltip: this.upgradePrompt, onClick: this.onClickUpgrade}]);
6117 config.macros.upgrade.onClickUpgrade = function(e)
6119 var me = config.macros.upgrade;
6120 var w = new Wizard(this);
6121 if(window.location.protocol != "file:
") {
6122 alert(me.errorCantUpgrade);
6125 if(story.areAnyDirty() || store.isDirty()) {
6126 alert(me.errorNotSaved);
6129 var localPath = getLocalPath(document.location.toString());
6130 var backupPath = getBackupPath(localPath,me.backupExtension);
6131 w.setValue("backupPath
",backupPath);
6132 w.setButtons([],me.statusPreparingBackup);
6133 var original = loadOriginal(localPath);
6134 w.setButtons([],me.statusSavingBackup);
6135 var backup = config.browser.isIE ? ieCopyFile(backupPath,localPath) : saveFile(backupPath,original);
6136 if(backup != true) {
6137 w.setButtons([],me.errorSavingBackup);
6138 alert(me.errorSavingBackup);
6141 w.setButtons([],me.statusLoadingCore);
6142 var load = loadRemoteFile(me.source,me.onLoadCore,w);
6143 if(typeof load == "string
") {
6144 w.setButtons([],me.errorLoadingCore);
6145 alert(me.errorLoadingCore);
6151 config.macros.upgrade.onLoadCore = function(status,params,responseText,url,xhr)
6153 var me = config.macros.upgrade;
6157 errMsg = me.errorLoadingCore;
6158 var newVer = me.extractVersion(responseText);
6160 errMsg = me.errorCoreFormat;
6162 w.setButtons([],errMsg);
6166 var onStartUpgrade = function(e) {
6167 w.setButtons([],me.statusSavingCore);
6168 var localPath = getLocalPath(document.location.toString());
6169 saveFile(localPath,responseText);
6170 w.setButtons([],me.statusReloadingCore);
6171 var backupPath = w.getValue("backupPath
");
6172 var newLoc = document.location.toString() + '?time=' + new Date().convertToYYYYMMDDHHMM() + '#upgrade:[[' + encodeURI(backupPath) + ']]';
6173 window.setTimeout(function () {window.location = newLoc;},10);
6175 var step2 = [me.step2Html_downgrade,me.step2Html_restore,me.step2Html_upgrade][compareVersions(version,newVer) + 1];
6176 w.addStep(me.step2Title,step2.format([formatVersion(newVer),formatVersion(version)]));
6177 w.setButtons([{caption: me.startLabel, tooltip: me.startPrompt, onClick: onStartUpgrade},{caption: me.cancelLabel, tooltip: me.cancelPrompt, onClick: me.onCancel}]);
6180 config.macros.upgrade.onCancel = function(e)
6182 var me = config.macros.upgrade;
6183 var w = new Wizard(this);
6184 w.addStep(me.step3Title,me.step3Html);
6189 config.macros.upgrade.extractVersion = function(upgradeFile)
6191 var re = /^var version = \{title: "([^
"]+)", major: (\d+), minor: (\d+), revision: (\d+)(, beta: (\d+)){
0,
1}, date: new Date\(
"([^"]+)
"\)/mg;
6192 var m = re.exec(upgradeFile);
6193 return m ? {title: m[1], major: m[2], minor: m[3], revision: m[4], beta: m[6], date: new Date(m[7])} : null;
6196 function upgradeFrom(path)
6198 var importStore = new TiddlyWiki();
6199 var tw = loadFile(path);
6200 if(window.netscape !== undefined)
6201 tw = convertUTF8ToUnicode(tw);
6202 importStore.importTiddlyWiki(tw);
6203 importStore.forEachTiddler(function(title,tiddler) {
6204 if(!store.getTiddler(title)) {
6205 store.addTiddler(tiddler);
6209 saveChanges(); //# To create appropriate Markup* sections
6210 alert(config.messages.upgradeDone.format([formatVersion()]));
6211 window.location = window.location.toString().substr(0,window.location.toString().lastIndexOf('?'));
6218 // Synchronisation handlers
6219 config.syncers = {};
6222 var currSync = null;
6225 config.macros.sync.handler = function(place,macroName,params,wikifier,paramString,tiddler)
6227 if(!wikifier.isStatic)
6228 this.startSync(place);
6231 config.macros.sync.cancelSync = function()
6236 config.macros.sync.startSync = function(place)
6239 config.macros.sync.cancelSync();
6241 currSync.syncList = this.getSyncableTiddlers();
6242 currSync.syncTasks = this.createSyncTasks(currSync.syncList);
6243 this.preProcessSyncableTiddlers(currSync.syncList);
6244 var wizard = new Wizard();
6245 currSync.wizard = wizard;
6246 wizard.createWizard(place,this.wizardTitle);
6247 wizard.addStep(this.step1Title,this.step1Html);
6248 var markList = wizard.getElement("markList
");
6249 var listWrapper = document.createElement("div
");
6250 markList.parentNode.insertBefore(listWrapper,markList);
6251 currSync.listView = ListView.create(listWrapper,currSync.syncList,this.listViewTemplate);
6252 this.processSyncableTiddlers(currSync.syncList);
6253 wizard.setButtons([{caption: this.syncLabel, tooltip: this.syncPrompt, onClick: this.doSync}]);
6256 config.macros.sync.getSyncableTiddlers = function()
6259 store.forEachTiddler(function(title,tiddler) {
6261 syncItem.serverType = tiddler.getServerType();
6262 syncItem.serverHost = tiddler.fields['server.host'];
6263 if(syncItem.serverType && syncItem.serverHost) {
6264 syncItem.serverWorkspace = tiddler.fields['server.workspace'];
6265 syncItem.tiddler = tiddler;
6266 syncItem.title = tiddler.title;
6267 syncItem.isTouched = tiddler.isTouched();
6268 syncItem.selected = syncItem.isTouched;
6269 syncItem.syncStatus = config.macros.sync.syncStatusList[syncItem.isTouched ? "changedLocally
" : "none
"];
6270 syncItem.status = syncItem.syncStatus.text;
6271 list.push(syncItem);
6274 list.sort(function(a,b) {return a.title < b.title ? -1 : (a.title == b.title ? 0 : +1);});
6278 config.macros.sync.preProcessSyncableTiddlers = function(syncList)
6280 for(var i=0; i<syncList.length; i++) {
6281 var si = syncList[i];
6282 si.serverUrl = si.syncTask.syncMachine.generateTiddlerInfo(si.tiddler).uri;
6286 config.macros.sync.processSyncableTiddlers = function(syncList)
6288 for(var i=0; i<syncList.length; i++) {
6289 var si = syncList[i];
6290 si.rowElement.style.display = si.syncStatus.display;
6291 if(si.syncStatus.className)
6292 si.rowElement.className = si.syncStatus.className;
6296 config.macros.sync.createSyncTasks = function(syncList)
6299 for(var i=0; i<syncList.length; i++) {
6300 var si = syncList[i];
6302 for(var j=0; j<syncTasks.length; j++) {
6303 var cst = syncTasks[j];
6304 if(si.serverType == cst.serverType && si.serverHost == cst.serverHost && si.serverWorkspace == cst.serverWorkspace)
6309 r.syncItems.push(si);
6311 si.syncTask = this.createSyncTask(si);
6312 syncTasks.push(si.syncTask);
6318 config.macros.sync.createSyncTask = function(syncItem)
6321 st.serverType = syncItem.serverType;
6322 st.serverHost = syncItem.serverHost;
6323 st.serverWorkspace = syncItem.serverWorkspace;
6324 st.syncItems = [syncItem];
6325 st.syncMachine = new SyncMachine(st.serverType,{
6327 return this.openHost(st.serverHost,"openWorkspace
");
6329 openWorkspace: function() {
6330 return this.openWorkspace(st.serverWorkspace,"getTiddlerList
");
6332 getTiddlerList: function() {
6333 return this.getTiddlerList("onGetTiddlerList
");
6335 onGetTiddlerList: function(context) {
6336 var tiddlers = context.tiddlers;
6337 for(var i=0; i<st.syncItems.length; i++) {
6338 var si = st.syncItems[i];
6339 var f = tiddlers.findByField("title
",si.title);
6341 if(tiddlers[f].fields['server.page.revision'] > si.tiddler.fields['server.page.revision']) {
6342 si.syncStatus = config.macros.sync.syncStatusList[si.isTouched ? 'changedBoth' : 'changedServer'];
6345 si.syncStatus = config.macros.sync.syncStatusList.notFound;
6347 config.macros.sync.updateSyncStatus(si);
6350 getTiddler: function(title) {
6351 return this.getTiddler(title,"onGetTiddler
");
6353 onGetTiddler: function(context) {
6354 var tiddler = context.tiddler;
6355 var syncItem = st.syncItems.findByField("title
",tiddler.title);
6356 if(syncItem !== null) {
6357 syncItem = st.syncItems[syncItem];
6358 store.saveTiddler(tiddler.title, tiddler.title, tiddler.text, tiddler.modifier, tiddler.modified, tiddler.tags, tiddler.fields, true, tiddler.created);
6359 syncItem.syncStatus = config.macros.sync.syncStatusList.gotFromServer;
6360 config.macros.sync.updateSyncStatus(syncItem);
6363 putTiddler: function(tiddler) {
6364 return this.putTiddler(tiddler,"onPutTiddler
");
6366 onPutTiddler: function(context) {
6367 var title = context.title;
6368 var syncItem = st.syncItems.findByField("title
",title);
6369 if(syncItem !== null) {
6370 syncItem = st.syncItems[syncItem];
6371 store.resetTiddler(title);
6372 if(context.status) {
6373 syncItem.syncStatus = config.macros.sync.syncStatusList.putToServer;
6374 config.macros.sync.updateSyncStatus(syncItem);
6379 st.syncMachine.go();
6383 config.macros.sync.updateSyncStatus = function(syncItem)
6385 var e = syncItem.colElements["status
"];
6387 createTiddlyText(e,syncItem.syncStatus.text);
6388 syncItem.rowElement.style.display = syncItem.syncStatus.display;
6389 if(syncItem.syncStatus.className)
6390 syncItem.rowElement.className = syncItem.syncStatus.className;
6393 config.macros.sync.doSync = function(e)
6395 var rowNames = ListView.getSelectedRows(currSync.listView);
6396 var sl = config.macros.sync.syncStatusList;
6397 for(var i=0; i<currSync.syncList.length; i++) {
6398 var si = currSync.syncList[i];
6399 if(rowNames.indexOf(si.title) != -1) {
6401 switch(si.syncStatus) {
6402 case sl.changedServer:
6403 r = si.syncTask.syncMachine.go("getTiddler
",si.title);
6406 case sl.changedLocally:
6407 case sl.changedBoth:
6408 r = si.syncTask.syncMachine.go("putTiddler
",si.tiddler);
6414 displayMessage("Error in doSync:
" + r);
6420 function SyncMachine(serverType,steps)
6422 this.serverType = serverType;
6423 this.adaptor = new config.adaptors[serverType]();
6427 SyncMachine.prototype.go = function(step,context)
6429 var r = context ? context.status : null;
6430 if(typeof r == "string
") {
6431 this.invokeError(r);
6434 var h = this.steps[step ? step : "start
"];
6437 r = h.call(this,context);
6438 if(typeof r == "string
")
6439 this.invokeError(r);
6443 SyncMachine.prototype.invokeError = function(message)
6445 if(this.steps.error)
6446 this.steps.error(message);
6449 SyncMachine.prototype.openHost = function(host,nextStep)
6452 return me.adaptor.openHost(host,null,null,function(context) {me.go(nextStep,context);});
6455 SyncMachine.prototype.getWorkspaceList = function(nextStep)
6458 return me.adaptor.getWorkspaceList(null,null,function(context) {me.go(nextStep,context);});
6461 SyncMachine.prototype.openWorkspace = function(workspace,nextStep)
6464 return me.adaptor.openWorkspace(workspace,null,null,function(context) {me.go(nextStep,context);});
6467 SyncMachine.prototype.getTiddlerList = function(nextStep)
6470 return me.adaptor.getTiddlerList(null,null,function(context) {me.go(nextStep,context);});
6473 SyncMachine.prototype.generateTiddlerInfo = function(tiddler)
6475 return this.adaptor.generateTiddlerInfo(tiddler);
6478 SyncMachine.prototype.getTiddler = function(title,nextStep)
6481 return me.adaptor.getTiddler(title,null,null,function(context) {me.go(nextStep,context);});
6484 SyncMachine.prototype.putTiddler = function(tiddler,nextStep)
6487 return me.adaptor.putTiddler(tiddler,null,null,function(context) {me.go(nextStep,context);});
6491 //-- Manager UI for groups of tiddlers
6494 config.macros.plugins.handler = function(place,macroName,params,wikifier,paramString)
6496 var wizard = new Wizard();
6497 wizard.createWizard(place,this.wizardTitle);
6498 wizard.addStep(this.step1Title,this.step1Html);
6499 var markList = wizard.getElement("markList
");
6500 var listWrapper = document.createElement("div
");
6501 markList.parentNode.insertBefore(listWrapper,markList);
6502 listWrapper.setAttribute("refresh
","macro
");
6503 listWrapper.setAttribute("macroName
","plugins
");
6504 listWrapper.setAttribute("params
",paramString);
6505 this.refresh(listWrapper,paramString);
6508 config.macros.plugins.refresh = function(listWrapper,params)
6510 var wizard = new Wizard(listWrapper);
6511 var selectedRows = [];
6512 ListView.forEachSelector(listWrapper,function(e,rowName) {
6514 selectedRows.push(e.getAttribute("rowName
"));
6516 removeChildren(listWrapper);
6517 params = params.parseParams("anon
");
6518 var plugins = installedPlugins.slice(0);
6520 var configTiddlers = store.getTaggedTiddlers("systemConfig
");
6521 for(t=0; t<configTiddlers.length; t++) {
6522 tiddler = configTiddlers[t];
6523 if(plugins.findByField("title
",tiddler.title) == null) {
6524 p = getPluginInfo(tiddler);
6526 p.log.splice(0,0,this.skippedText);
6530 for(t=0; t<plugins.length; t++) {
6532 p.size = p.tiddler.text ? p.tiddler.text.length : 0;
6533 p.forced = p.tiddler.isTagged("systemConfigForce
");
6534 p.disabled = p.tiddler.isTagged("systemConfigDisable
");
6535 p.Selected = selectedRows.indexOf(plugins[t].title) != -1;
6537 if(plugins.length == 0) {
6538 createTiddlyElement(listWrapper,"em
",null,null,this.noPluginText);
6539 wizard.setButtons([]);
6541 var listView = ListView.create(listWrapper,plugins,this.listViewTemplate,this.onSelectCommand);
6542 wizard.setValue("listView
",listView);
6544 {caption: config.macros.plugins.removeLabel, tooltip: config.macros.plugins.removePrompt, onClick: config.macros.plugins.doRemoveTag},
6545 {caption: config.macros.plugins.deleteLabel, tooltip: config.macros.plugins.deletePrompt, onClick: config.macros.plugins.doDelete}
6550 config.macros.plugins.doRemoveTag = function(e)
6552 var wizard = new Wizard(this);
6553 var listView = wizard.getValue("listView
");
6554 var rowNames = ListView.getSelectedRows(listView);
6555 if(rowNames.length == 0) {
6556 alert(config.messages.nothingSelected);
6558 for(var t=0; t<rowNames.length; t++)
6559 store.setTiddlerTag(rowNames[t],false,"systemConfig
");
6563 config.macros.plugins.doDelete = function(e)
6565 var wizard = new Wizard(this);
6566 var listView = wizard.getValue("listView
");
6567 var rowNames = ListView.getSelectedRows(listView);
6568 if(rowNames.length == 0) {
6569 alert(config.messages.nothingSelected);
6571 if(confirm(config.macros.plugins.confirmDeleteText.format([rowNames.join(",
")]))) {
6572 for(var t=0; t<rowNames.length; t++) {
6573 store.removeTiddler(rowNames[t]);
6574 story.closeTiddler(rowNames[t],true);
6584 function getMessageDiv()
6586 var msgArea = document.getElementById("messageArea
");
6589 if(!msgArea.hasChildNodes())
6590 createTiddlyButton(createTiddlyElement(msgArea,"div
",null,"messageToolbar
"),
6591 config.messages.messageClose.text,
6592 config.messages.messageClose.tooltip,
6594 msgArea.style.display = "block
";
6595 return createTiddlyElement(msgArea,"div
");
6598 function displayMessage(text,linkText)
6600 var e = getMessageDiv();
6606 var link = createTiddlyElement(e,"a
",null,null,text);
6607 link.href = linkText;
6608 link.target = "_blank
";
6610 e.appendChild(document.createTextNode(text));
6614 function clearMessage()
6616 var msgArea = document.getElementById("messageArea
");
6618 removeChildren(msgArea);
6619 msgArea.style.display = "none
";
6625 //-- Refresh mechanism
6628 config.notifyTiddlers = [
6629 {name: "StyleSheetLayout
", notify: refreshStyles},
6630 {name: "StyleSheetColors
", notify: refreshStyles},
6631 {name: "StyleSheet
", notify: refreshStyles},
6632 {name: "StyleSheetPrint
", notify: refreshStyles},
6633 {name: "PageTemplate
", notify: refreshPageTemplate},
6634 {name: "SiteTitle
", notify: refreshPageTitle},
6635 {name: "SiteSubtitle
", notify: refreshPageTitle},
6636 {name: "ColorPalette
", notify: refreshColorPalette},
6637 {name: null, notify: refreshDisplay}
6640 config.refreshers = {
6641 link: function(e,changeList)
6643 var title = e.getAttribute("tiddlyLink
");
6644 refreshTiddlyLink(e,title);
6648 tiddler: function(e,changeList)
6650 var title = e.getAttribute("tiddler
");
6651 var template = e.getAttribute("template
");
6652 if(changeList && changeList.indexOf(title) != -1 && !story.isDirty(title))
6653 story.refreshTiddler(title,template,true);
6655 refreshElements(e,changeList);
6659 content: function(e,changeList)
6661 var title = e.getAttribute("tiddler
");
6662 var force = e.getAttribute("force
");
6663 if(force != null || changeList == null || changeList.indexOf(title) != -1) {
6665 wikify(store.getTiddlerText(title,""),e,null,store.fetchTiddler(title));
6671 macro: function(e,changeList)
6673 var macro = e.getAttribute("macroName
");
6674 var params = e.getAttribute("params
");
6676 macro = config.macros[macro];
6677 if(macro && macro.refresh)
6678 macro.refresh(e,params);
6683 config.refresherData = {
6684 styleSheet: "StyleSheet
",
6685 defaultStyleSheet: "StyleSheet
",
6686 pageTemplate: "PageTemplate
",
6687 defaultPageTemplate: "PageTemplate
",
6688 colorPalette: "ColorPalette
",
6689 defaultColorPalette: "ColorPalette
"
6692 function refreshElements(root,changeList)
6694 var nodes = root.childNodes;
6695 for(var c=0; c<nodes.length; c++) {
6696 var e = nodes[c], type = null;
6697 if(e.getAttribute && (e.tagName ? e.tagName != "IFRAME
" : true))
6698 type = e.getAttribute("refresh
");
6699 var refresher = config.refreshers[type];
6700 var refreshed = false;
6701 if(refresher != undefined)
6702 refreshed = refresher(e,changeList);
6703 if(e.hasChildNodes() && !refreshed)
6704 refreshElements(e,changeList);
6708 function applyHtmlMacros(root,tiddler)
6710 var e = root.firstChild;
6712 var nextChild = e.nextSibling;
6713 if(e.getAttribute) {
6714 var macro = e.getAttribute("macro
");
6716 e.removeAttribute("macro
");
6718 var p = macro.indexOf(" ");
6720 params = macro.substr(p+1);
6721 macro = macro.substr(0,p);
6723 invokeMacro(e,macro,params,null,tiddler);
6726 if(e.hasChildNodes())
6727 applyHtmlMacros(e,tiddler);
6732 function refreshPageTemplate(title)
6734 var stash = createTiddlyElement(document.body,"div
");
6735 stash.style.display = "none
";
6736 var display = story.getContainer();
6739 nodes = display.childNodes;
6740 for(t=nodes.length-1; t>=0; t--)
6741 stash.appendChild(nodes[t]);
6743 var wrapper = document.getElementById("contentWrapper
");
6745 var isAvailable = function(title) {
6746 var s = title ? title.indexOf(config.textPrimitives.sectionSeparator) : -1;
6748 title = title.substr(0,s);
6749 return store.tiddlerExists(title) || store.isShadowTiddler(title);
6751 if(!title || !isAvailable(title))
6752 title = config.refresherData.pageTemplate;
6753 if(!isAvailable(title))
6754 title = config.refresherData.defaultPageTemplate; //# this one is always avaialable
6755 wrapper.innerHTML = store.getRecursiveTiddlerText(title,null,10);
6756 applyHtmlMacros(wrapper);
6757 refreshElements(wrapper);
6758 display = story.getContainer();
6759 removeChildren(display);
6761 display = createTiddlyElement(wrapper,"div
",story.containerId());
6762 nodes = stash.childNodes;
6763 for(t=nodes.length-1; t>=0; t--)
6764 display.appendChild(nodes[t]);
6768 function refreshDisplay(hint)
6770 if(typeof hint == "string
")
6772 var e = document.getElementById("contentWrapper
");
6773 refreshElements(e,hint);
6774 if(backstage.isPanelVisible()) {
6775 e = document.getElementById("backstage
");
6776 refreshElements(e,hint);
6780 function refreshPageTitle()
6782 document.title = getPageTitle();
6785 function getPageTitle()
6787 var st = wikifyPlain("SiteTitle
");
6788 var ss = wikifyPlain("SiteSubtitle
");
6789 return st + ((st == "" || ss == "") ? "" : " -
") + ss;
6792 function refreshStyles(title,doc)
6794 setStylesheet(title == null ? "" : store.getRecursiveTiddlerText(title,"",10),title,doc || document);
6797 function refreshColorPalette(title)
6803 function refreshAll()
6805 refreshPageTemplate();
6807 refreshStyles("StyleSheetLayout
");
6808 refreshStyles("StyleSheetColors
");
6809 refreshStyles(config.refresherData.styleSheet);
6810 refreshStyles("StyleSheetPrint
");
6817 config.optionHandlers = {
6819 get: function(name) {return encodeCookie(config.options[name].toString());},
6820 set: function(name,value) {config.options[name] = decodeCookie(value);}
6823 get: function(name) {return config.options[name] ? "true
" : "false
";},
6824 set: function(name,value) {config.options[name] = value == "true
";}
6828 function loadOptionsCookie()
6832 var cookies = document.cookie.split(";
");
6833 for(var c=0; c<cookies.length; c++) {
6834 var p = cookies[c].indexOf("=
");
6836 var name = cookies[c].substr(0,p).trim();
6837 var value = cookies[c].substr(p+1).trim();
6838 var optType = name.substr(0,3);
6839 if(config.optionHandlers[optType] && config.optionHandlers[optType].set)
6840 config.optionHandlers[optType].set(name,value);
6845 function saveOptionCookie(name)
6850 var optType = name.substr(0,3);
6851 if(config.optionHandlers[optType] && config.optionHandlers[optType].get)
6852 c += config.optionHandlers[optType].get(name);
6853 c += "; expires=Fri,
1 Jan
2038 12:
00:
00 UTC; path=/
";
6854 document.cookie = c;
6857 function encodeCookie(s)
6859 return escape(convertUnicodeToHtmlEntities(s));
6862 function decodeCookie(s)
6865 var re = /&#[0-9]{1,5};/g;
6866 return s.replace(re,function($0) {return String.fromCharCode(eval($0.replace(/[&#;]/g,"")));});
6870 config.macros.option.genericCreate = function(place,type,opt,className,desc)
6872 var typeInfo = config.macros.option.types[type];
6873 var c = document.createElement(typeInfo.elementType);
6874 if(typeInfo.typeValue)
6875 c.setAttribute("type
",typeInfo.typeValue);
6876 c[typeInfo.eventName] = typeInfo.onChange;
6877 c.setAttribute("option
",opt);
6878 c.className = className || typeInfo.className;
6879 if(config.optionsDesc[opt])
6880 c.setAttribute("title
",config.optionsDesc[opt]);
6881 place.appendChild(c);
6883 createTiddlyText(place,config.optionsDesc[opt] || opt);
6884 c[typeInfo.valueField] = config.options[opt];
6888 config.macros.option.genericOnChange = function(e)
6890 var opt = this.getAttribute("option
");
6892 var optType = opt.substr(0,3);
6893 var handler = config.macros.option.types[optType];
6894 if(handler.elementType && handler.valueField)
6895 config.macros.option.propagateOption(opt,handler.valueField,this[handler.valueField],handler.elementType,this);
6900 config.macros.option.types = {
6902 elementType: "input
",
6903 valueField: "value
",
6904 eventName: "onchange
",
6905 className: "txtOptionInput
",
6906 create: config.macros.option.genericCreate,
6907 onChange: config.macros.option.genericOnChange
6910 elementType: "input
",
6911 valueField: "checked
",
6912 eventName: "onclick
",
6913 className: "chkOptionInput
",
6914 typeValue: "checkbox
",
6915 create: config.macros.option.genericCreate,
6916 onChange: config.macros.option.genericOnChange
6920 config.macros.option.propagateOption = function(opt,valueField,value,elementType,elem)
6922 config.options[opt] = value;
6923 saveOptionCookie(opt);
6924 var nodes = document.getElementsByTagName(elementType);
6925 for(var t=0; t<nodes.length; t++) {
6926 var optNode = nodes[t].getAttribute("option
");
6927 if(opt == optNode && nodes[t]!=elem)
6928 nodes[t][valueField] = value;
6932 config.macros.option.handler = function(place,macroName,params,wikifier,paramString)
6934 params = paramString.parseParams("anon
",null,true,false,false);
6935 var opt = (params[1] && params[1].name == "anon
") ? params[1].value : getParam(params,"name
",null);
6936 var className = (params[2] && params[2].name == "anon
") ? params[2].value : getParam(params,"class
",null);
6937 var desc = getParam(params,"desc
","no
");
6938 var type = opt.substr(0,3);
6939 var h = config.macros.option.types[type];
6941 h.create(place,type,opt,className,desc);
6944 config.macros.options.handler = function(place,macroName,params,wikifier,paramString)
6946 params = paramString.parseParams("anon
",null,true,false,false);
6947 var showUnknown = getParam(params,"showUnknown
","no
");
6948 var wizard = new Wizard();
6949 wizard.createWizard(place,this.wizardTitle);
6950 wizard.addStep(this.step1Title,this.step1Html);
6951 var markList = wizard.getElement("markList
");
6952 var chkUnknown = wizard.getElement("chkUnknown
");
6953 chkUnknown.checked = showUnknown == "yes
";
6954 chkUnknown.onchange = this.onChangeUnknown;
6955 var listWrapper = document.createElement("div
");
6956 markList.parentNode.insertBefore(listWrapper,markList);
6957 wizard.setValue("listWrapper
",listWrapper);
6958 this.refreshOptions(listWrapper,showUnknown == "yes
");
6961 config.macros.options.refreshOptions = function(listWrapper,showUnknown)
6964 for(var n in config.options) {
6968 opt.lowlight = !config.optionsDesc[n];
6969 opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
6970 if(!opt.lowlight || showUnknown)
6973 opts.sort(function(a,b) {return a.name.substr(3) < b.name.substr(3) ? -1 : (a.name.substr(3) == b.name.substr(3) ? 0 : +1);});
6974 var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
6975 for(n=0; n<opts.length; n++) {
6976 var type = opts[n].name.substr(0,3);
6977 var h = config.macros.option.types[type];
6979 h.create(opts[n].colElements['option'],type,opts[n].name,null,"no
");
6984 config.macros.options.onChangeUnknown = function(e)
6986 var wizard = new Wizard(this);
6987 var listWrapper = wizard.getValue("listWrapper
");
6988 removeChildren(listWrapper);
6989 config.macros.options.refreshOptions(listWrapper,this.checked);
6997 var saveUsingSafari = false;
6999 var startSaveArea = '<div id="' + 'storeArea
">'; // Split up into two so that indexOf() of this source doesn't find it
7000 var endSaveArea = '</d' + 'iv>';
7002 // If there are unsaved changes, force the user to confirm before exitting
7003 function confirmExit()
7005 hadConfirmExit = true;
7006 if((store && store.isDirty && store.isDirty()) || (story && story.areAnyDirty && story.areAnyDirty()))
7007 return config.messages.confirmExit;
7010 // Give the user a chance to save changes before exitting
7011 function checkUnsavedChanges()
7013 if(store && store.isDirty && store.isDirty() && window.hadConfirmExit === false) {
7014 if(confirm(config.messages.unsavedChangesWarning))
7019 function updateLanguageAttribute(s)
7022 var mRE = /(<html(?:.*?)?)(?: xml:lang\="([a-z]+)
")?(?: lang\="([a-z]+)
")?>/;
7023 var m = mRE.exec(s);
7027 t += ' xml:lang="' + config.locale + '
"';
7029 t += ' lang="' + config.locale + '
"';
7031 s = s.substr(0,m.index) + t + s.substr(m.index+m[0].length);
7037 function updateMarkupBlock(s,blockName,tiddlerName)
7039 return s.replaceChunk(
7040 "<!--%0-START-->".format([blockName]),
7041 "<!--%0-END-->".format([blockName]),
7042 "\n
" + convertUnicodeToFileFormat(store.getRecursiveTiddlerText(tiddlerName,"")) + "\n
");
7045 function updateOriginal(original,posDiv,localPath)
7048 posDiv = locateStoreArea(original);
7050 alert(config.messages.invalidFileError.format([localPath]));
7053 var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n
" +
7054 convertUnicodeToFileFormat(store.allTiddlersAsHtml()) + "\n
" +
7055 original.substr(posDiv[1]);
7056 var newSiteTitle = convertUnicodeToFileFormat(getPageTitle()).htmlEncode();
7057 revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
7058 revised = updateLanguageAttribute(revised);
7059 revised = updateMarkupBlock(revised,"PRE-HEAD
","MarkupPreHead
");
7060 revised = updateMarkupBlock(revised,"POST-HEAD
","MarkupPostHead
");
7061 revised = updateMarkupBlock(revised,"PRE-BODY
","MarkupPreBody
");
7062 revised = updateMarkupBlock(revised,"POST-SCRIPT
","MarkupPostBody
");
7066 function locateStoreArea(original)
7068 // Locate the storeArea div's
7069 var posOpeningDiv = original.indexOf(startSaveArea);
7070 var limitClosingDiv = original.indexOf("<
"+"!--POST-STOREAREA--
"+">");
7071 if(limitClosingDiv == -1)
7072 limitClosingDiv = original.indexOf("<
"+"!--POST-BODY-START--
"+">");
7073 var posClosingDiv = original.lastIndexOf(endSaveArea,limitClosingDiv == -1 ? original.length : limitClosingDiv);
7074 return (posOpeningDiv != -1 && posClosingDiv != -1) ? [posOpeningDiv,posClosingDiv] : null;
7077 function autoSaveChanges(onlyIfDirty,tiddlers)
7079 if(config.options.chkAutoSave)
7080 saveChanges(onlyIfDirty,tiddlers);
7083 function loadOriginal(localPath)
7085 return loadFile(localPath);
7088 // Save this tiddlywiki with the pending changes
7089 function saveChanges(onlyIfDirty,tiddlers)
7091 if(onlyIfDirty && !store.isDirty())
7094 var t0 = new Date();
7095 var originalPath = document.location.toString();
7096 if(originalPath.substr(0,5) != "file:
") {
7097 alert(config.messages.notFileUrlError);
7098 if(store.tiddlerExists(config.messages.saveInstructions))
7099 story.displayTiddler(null,config.messages.saveInstructions);
7102 var localPath = getLocalPath(originalPath);
7103 var original = loadOriginal(localPath);
7104 if(original == null) {
7105 alert(config.messages.cantSaveError);
7106 if(store.tiddlerExists(config.messages.saveInstructions))
7107 story.displayTiddler(null,config.messages.saveInstructions);
7110 var posDiv = locateStoreArea(original);
7112 alert(config.messages.invalidFileError.format([localPath]));
7115 saveMain(localPath,original,posDiv);
7116 if(config.options.chkSaveBackups)
7117 saveBackup(localPath,original);
7118 if(config.options.chkSaveEmptyTemplate)
7119 saveEmpty(localPath,original,posDiv);
7120 if(config.options.chkGenerateAnRssFeed && saveRss instanceof Function)
7122 if(config.options.chkDisplayInstrumentation)
7123 displayMessage("saveChanges
" + (new Date()-t0) + " ms
");
7126 function saveMain(localPath,original,posDiv)
7130 var revised = updateOriginal(original,posDiv,localPath);
7131 save = saveFile(localPath,revised);
7136 displayMessage(config.messages.mainSaved,"file://
" + localPath);
7137 store.setDirty(false);
7139 alert(config.messages.mainFailed);
7143 function saveBackup(localPath,original)
7145 var backupPath = getBackupPath(localPath);
7146 var backup = copyFile(backupPath,localPath);
7148 backup = saveFile(backupPath,original);
7150 displayMessage(config.messages.backupSaved,"file://
" + backupPath);
7152 alert(config.messages.backupFailed);
7155 function saveEmpty(localPath,original,posDiv)
7158 if((p = localPath.lastIndexOf("/
")) != -1)
7159 emptyPath = localPath.substr(0,p) + "/
";
7160 else if((p = localPath.lastIndexOf("\\
")) != -1)
7161 emptyPath = localPath.substr(0,p) + "\\
";
7163 emptyPath = localPath + ".
";
7164 emptyPath += "empty.html
";
7165 var empty = original.substr(0,posDiv[0] + startSaveArea.length) + original.substr(posDiv[1]);
7166 var emptySave = saveFile(emptyPath,empty);
7168 displayMessage(config.messages.emptySaved,"file://
" + emptyPath);
7170 alert(config.messages.emptyFailed);
7173 function getLocalPath(origPath)
7175 var originalPath = convertUriToUTF8(origPath,config.options.txtFileSystemCharSet);
7176 // Remove any location or query part of the URL
7177 var argPos = originalPath.indexOf("?
");
7179 originalPath = originalPath.substr(0,argPos);
7180 var hashPos = originalPath.indexOf("#
");
7182 originalPath = originalPath.substr(0,hashPos);
7183 // Convert file://localhost/ to file:///
7184 if(originalPath.indexOf("file://localhost/
") == 0)
7185 originalPath = "file://
" + originalPath.substr(16);
7186 // Convert to a native file format
7188 if(originalPath.charAt(9) == ":
") // pc local file
7189 localPath = unescape(originalPath.substr(8)).replace(new RegExp("/
","g
"),"\\
");
7190 else if(originalPath.indexOf("file://///
") == 0) // FireFox pc network file
7191 localPath = "\\\\
" + unescape(originalPath.substr(10)).replace(new RegExp("/
","g
"),"\\
");
7192 else if(originalPath.indexOf("file:///
") == 0) // mac/unix local file
7193 localPath = unescape(originalPath.substr(7));
7194 else if(originalPath.indexOf("file:/
") == 0) // mac/unix local file
7195 localPath = unescape(originalPath.substr(5));
7196 else // pc network file
7197 localPath = "\\\\
" + unescape(originalPath.substr(7)).replace(new RegExp("/
","g
"),"\\
");
7201 function getBackupPath(localPath,title,extension)
7204 var dirPathPos = localPath.lastIndexOf("\\
");
7205 if(dirPathPos == -1) {
7206 dirPathPos = localPath.lastIndexOf("/
");
7209 var backupFolder = config.options.txtBackupFolder;
7210 if(!backupFolder || backupFolder == "")
7212 var backupPath = localPath.substr(0,dirPathPos) + slash + backupFolder + localPath.substr(dirPathPos);
7213 backupPath = backupPath.substr(0,backupPath.lastIndexOf(".
")) + ".
";
7215 backupPath += title.replace(/[\\\/\*\?\":<
> ]/g,
"_") +
".";
7216 backupPath += (new Date()).convertToYYYYMMDDHHMMSSMMM() +
"." + (extension ||
"html");
7224 function saveRss(localPath)
7226 var rssPath = localPath.substr(
0,localPath.lastIndexOf(
".")) +
".xml";
7227 if(saveFile(rssPath,convertUnicodeToFileFormat(generateRss())))
7228 displayMessage(config.messages.rssSaved,
"file://" + rssPath);
7230 alert(config.messages.rssFailed);
7233 function generateRss()
7237 var u = store.getTiddlerText(
"SiteUrl");
7238 // Assemble the header
7239 s.push(
"<" +
"?xml version=\"1.0\
"?" +
">");
7240 s.push(
"<rss version=\"2.0\
">");
7241 s.push(
"<channel>");
7242 s.push(
"<title" +
">" + wikifyPlain(
"SiteTitle").htmlEncode() +
"</title" +
">");
7244 s.push(
"<link>" + u.htmlEncode() +
"</link>");
7245 s.push(
"<description>" + wikifyPlain(
"SiteSubtitle").htmlEncode() +
"</description>");
7246 s.push(
"<language>" + config.locale +
"</language>");
7247 s.push(
"<copyright>Copyright " + d.getFullYear() +
" " + config.options.txtUserName.htmlEncode() +
"</copyright>");
7248 s.push(
"<pubDate>" + d.toGMTString() +
"</pubDate>");
7249 s.push(
"<lastBuildDate>" + d.toGMTString() +
"</lastBuildDate>");
7250 s.push(
"<docs>http://blogs.law.harvard.edu/tech/rss</docs>");
7251 s.push(
"<generator>TiddlyWiki " + formatVersion() +
"</generator>");
7253 var tiddlers = store.getTiddlers(
"modified",
"excludeLists");
7254 var n = config.numRssItems
> tiddlers.length ?
0 : tiddlers.length-config.numRssItems;
7255 for(var t=tiddlers.length-
1; t
>=n; t--) {
7256 s.push(
"<item>\n" + tiddlers[t].toRssItem(u) +
"\n</item>");
7259 s.push(
"</channel>");
7262 return s.join(
"\n");
7266 //-- Filesystem code
7269 function convertUTF8ToUnicode(u)
7271 return config.browser.isOpera || !window.netscape ? manualConvertUTF8ToUnicode(u) : mozConvertUTF8ToUnicode(u);
7274 function manualConvertUTF8ToUnicode(utf)
7281 while(src < utf.length) {
7282 b1 = utf.charCodeAt(src++);
7285 } else if(b1 <
0xE0) {
7286 b2 = utf.charCodeAt(src++);
7287 c = String.fromCharCode(((b1 &
0x1F) <<
6) | (b2 &
0x3F));
7288 uni = uni.substring(
0,dst++).concat(c,utf.substr(src));
7290 b2 = utf.charCodeAt(src++);
7291 b3 = utf.charCodeAt(src++);
7292 c = String.fromCharCode(((b1 &
0xF) <<
12) | ((b2 &
0x3F) <<
6) | (b3 &
0x3F));
7293 uni = uni.substring(
0,dst++).concat(c,utf.substr(src));
7299 function mozConvertUTF8ToUnicode(u)
7302 netscape.security.PrivilegeManager.enablePrivilege(
"UniversalXPConnect");
7303 var converter = Components.classes[
"@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
7304 converter.charset =
"UTF-8";
7306 return manualConvertUTF8ToUnicode(u);
7308 var s = converter.ConvertToUnicode(u);
7309 var fin = converter.Finish();
7310 return fin.length
> 0 ? s+fin : s;
7313 function convertUnicodeToFileFormat(s)
7315 return config.browser.isOpera || !window.netscape ? convertUnicodeToHtmlEntities(s) : mozConvertUnicodeToUTF8(s);
7318 function convertUnicodeToHtmlEntities(s)
7320 var re = /[^\u0000-\u007F]/g;
7321 return s.replace(re,function($
0) {return
"&#" + $
0.charCodeAt(
0).toString() +
";";});
7324 function convertUnicodeToUTF8(s)
7326 // return convertUnicodeToFileFormat to allow plugin migration
7327 return convertUnicodeToFileFormat(s);
7330 function manualConvertUnicodeToUTF8(s)
7332 return unescape(encodeURIComponent(s));
7335 function mozConvertUnicodeToUTF8(s)
7338 netscape.security.PrivilegeManager.enablePrivilege(
"UniversalXPConnect");
7339 var converter = Components.classes[
"@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
7340 converter.charset =
"UTF-8";
7342 return manualConvertUnicodeToUTF8(s);
7344 var u = converter.ConvertFromUnicode(s);
7345 var fin = converter.Finish();
7346 return fin.length
> 0 ? u + fin : u;
7349 function convertUriToUTF8(uri,charSet)
7351 if(window.netscape == undefined || charSet == undefined || charSet ==
"")
7354 netscape.security.PrivilegeManager.enablePrivilege(
"UniversalXPConnect");
7355 var converter = Components.classes[
"@mozilla.org/intl/utf8converterservice;1"].getService(Components.interfaces.nsIUTF8ConverterService);
7359 return converter.convertURISpecToUTF8(uri,charSet);
7362 function copyFile(dest,source)
7364 return config.browser.isIE ? ieCopyFile(dest,source) : false;
7367 function saveFile(fileUrl,content)
7369 var r = mozillaSaveFile(fileUrl,content);
7371 r = ieSaveFile(fileUrl,content);
7373 r = javaSaveFile(fileUrl,content);
7377 function loadFile(fileUrl)
7379 var r = mozillaLoadFile(fileUrl);
7380 if((r == null) || (r == false))
7381 r = ieLoadFile(fileUrl);
7382 if((r == null) || (r == false))
7383 r = javaLoadFile(fileUrl);
7387 function ieCreatePath(path)
7390 var fso = new ActiveXObject(
"Scripting.FileSystemObject");
7395 var pos = path.lastIndexOf(
"\\");
7397 path = path.substring(
0,pos+
1);
7400 var parent = fso.GetParentFolderName(path);
7401 while(parent && !fso.FolderExists(parent)) {
7403 parent = fso.GetParentFolderName(parent);
7406 for(i=scan.length-
1;i
>=
0;i--) {
7407 if(!fso.FolderExists(scan[i])) {
7408 fso.CreateFolder(scan[i]);
7414 // Returns null if it can't do it, false if there's an error, true if it saved OK
7415 function ieSaveFile(filePath,content)
7417 ieCreatePath(filePath);
7419 var fso = new ActiveXObject(
"Scripting.FileSystemObject");
7423 var file = fso.OpenTextFile(filePath,
2,-
1,
0);
7424 file.Write(content);
7429 // Returns null if it can't do it, false if there's an error, or a string of the content if successful
7430 function ieLoadFile(filePath)
7433 var fso = new ActiveXObject(
"Scripting.FileSystemObject");
7434 var file = fso.OpenTextFile(filePath,
1);
7435 var content = file.ReadAll();
7443 function ieCopyFile(dest,source)
7447 var fso = new ActiveXObject(
"Scripting.FileSystemObject");
7448 fso.GetFile(source).Copy(dest);
7455 // Returns null if it can't do it, false if there's an error, true if it saved OK
7456 function mozillaSaveFile(filePath,content)
7458 if(window.Components) {
7460 netscape.security.PrivilegeManager.enablePrivilege(
"UniversalXPConnect");
7461 var file = Components.classes[
"@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
7462 file.initWithPath(filePath);
7464 file.create(
0,
0664);
7465 var out = Components.classes[
"@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
7466 out.init(file,
0x20|
0x02,
00004,null);
7467 out.write(content,content.length);
7478 // Returns null if it can't do it, false if there's an error, or a string of the content if successful
7479 function mozillaLoadFile(filePath)
7481 if(window.Components) {
7483 netscape.security.PrivilegeManager.enablePrivilege(
"UniversalXPConnect");
7484 var file = Components.classes[
"@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
7485 file.initWithPath(filePath);
7488 var inputStream = Components.classes[
"@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
7489 inputStream.init(file,
0x01,
00004,null);
7490 var sInputStream = Components.classes[
"@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
7491 sInputStream.init(inputStream);
7492 var contents = sInputStream.read(sInputStream.available());
7493 sInputStream.close();
7494 inputStream.close();
7503 function javaUrlToFilename(url)
7505 var f =
"//localhost";
7506 if(url.indexOf(f) ==
0)
7507 return url.substring(f.length);
7508 var i = url.indexOf(
":");
7509 return i
> 0 ? url.substring(i-
1) : url;
7512 function javaSaveFile(filePath,content)
7515 if(document.applets[
"TiddlySaver"])
7516 return document.applets[
"TiddlySaver"].saveFile(javaUrlToFilename(filePath),
"UTF-8",content);
7520 var s = new java.io.PrintStream(new java.io.FileOutputStream(javaUrlToFilename(filePath)));
7529 function javaLoadFile(filePath)
7532 if(document.applets[
"TiddlySaver"])
7533 return String(document.applets[
"TiddlySaver"].loadFile(javaUrlToFilename(filePath),
"UTF-8"));
7538 var r = new java.io.BufferedReader(new java.io.FileReader(javaUrlToFilename(filePath)));
7540 while((line = r.readLine()) != null)
7541 content.push(new String(line));
7546 return content.join(
"\n");
7550 //-- Server adaptor base class
7553 function AdaptorBase()
7560 AdaptorBase.prototype.close = function()
7565 AdaptorBase.prototype.fullHostName = function(host)
7570 if(!host.match(/:\/\//))
7571 host = 'http://' + host;
7572 if(host.substr(host.length-
1) == '/')
7573 host = host.substr(
0,host.length-
1)
7577 AdaptorBase.minHostName = function(host)
7579 return host ? host.replace(/^http:\/\//,'').replace(/\/$/,'') : '';
7582 AdaptorBase.prototype.setContext = function(context,userParams,callback)
7584 if(!context) context = {};
7585 context.userParams = userParams;
7586 if(callback) context.callback = callback;
7587 context.adaptor = this;
7589 context.host = this.host;
7590 context.host = this.fullHostName(context.host);
7591 if(!context.workspace)
7592 context.workspace = this.workspace;
7596 // Open the specified host
7597 AdaptorBase.prototype.openHost = function(host,context,userParams,callback)
7600 context = this.setContext(context,userParams,callback);
7601 context.status = true;
7603 window.setTimeout(function() {context.callback(context,userParams);},
10);
7607 // Open the specified workspace
7608 AdaptorBase.prototype.openWorkspace = function(workspace,context,userParams,callback)
7610 this.workspace = workspace;
7611 context = this.setContext(context,userParams,callback);
7612 context.status = true;
7614 window.setTimeout(function() {callback(context,userParams);},
10);
7619 //-- Server adaptor for talking to static TiddlyWiki files
7622 function FileAdaptor()
7626 FileAdaptor.prototype = new AdaptorBase();
7628 FileAdaptor.serverType = 'file';
7629 FileAdaptor.serverLabel = 'TiddlyWiki';
7631 FileAdaptor.loadTiddlyWikiCallback = function(status,context,responseText,url,xhr)
7633 context.status = status;
7635 context.statusText =
"Error reading file";
7637 context.adaptor.store = new TiddlyWiki();
7638 if(!context.adaptor.store.importTiddlyWiki(responseText)) {
7639 context.statusText = config.messages.invalidFileError.format([url]);
7640 context.status = false;
7643 context.complete(context,context.userParams);
7646 // Get the list of workspaces on a given server
7647 FileAdaptor.prototype.getWorkspaceList = function(context,userParams,callback)
7649 context = this.setContext(context,userParams,callback);
7650 context.workspaces = [{title:
"(default)"}];
7651 context.status = true;
7653 window.setTimeout(function() {callback(context,userParams);},
10);
7657 // Gets the list of tiddlers within a given workspace
7658 FileAdaptor.prototype.getTiddlerList = function(context,userParams,callback,filter)
7660 context = this.setContext(context,userParams,callback);
7662 context.filter = filter;
7663 context.complete = FileAdaptor.getTiddlerListComplete;
7665 var ret = context.complete(context,context.userParams);
7667 ret = loadRemoteFile(context.host,FileAdaptor.loadTiddlyWikiCallback,context);
7668 if(typeof ret !=
"string")
7674 FileAdaptor.getTiddlerListComplete = function(context,userParams)
7676 if(context.status) {
7677 if(context.filter) {
7678 context.tiddlers = context.adaptor.store.filterTiddlers(context.filter);
7680 context.tiddlers = [];
7681 context.adaptor.store.forEachTiddler(function(title,tiddler) {context.tiddlers.push(tiddler);});
7683 for(var i=
0; i
<context.tiddlers.length; i++) {
7684 context.tiddlers[i].fields['server.type'] = FileAdaptor.serverType;
7685 context.tiddlers[i].fields['server.host'] = AdaptorBase.minHostName(context.host);
7686 context.tiddlers[i].fields['server.page.revision'] = context.tiddlers[i].modified.convertToYYYYMMDDHHMM();
7688 context.status = true;
7690 if(context.callback) {
7691 window.setTimeout(function() {context.callback(context,userParams);},
10);
7696 FileAdaptor.prototype.generateTiddlerInfo = function(tiddler)
7699 info.uri = tiddler.fields['server.host'] +
"#" + tiddler.title;
7703 // Retrieve a tiddler from a given workspace on a given server
7704 FileAdaptor.prototype.getTiddler = function(title,context,userParams,callback)
7706 context = this.setContext(context,userParams,callback);
7707 context.title = title;
7708 context.complete = FileAdaptor.getTiddlerComplete;
7709 return context.adaptor.store ?
7710 context.complete(context,context.userParams) :
7711 loadRemoteFile(context.host,FileAdaptor.loadTiddlyWikiCallback,context);
7714 FileAdaptor.getTiddlerComplete = function(context,userParams)
7716 var t = context.adaptor.store.fetchTiddler(context.title);
7717 t.fields['server.type'] = FileAdaptor.serverType;
7718 t.fields['server.host'] = AdaptorBase.minHostName(context.host);
7719 t.fields['server.page.revision'] = t.modified.convertToYYYYMMDDHHMM();
7720 context.tiddler = t;
7721 context.status = true;
7722 if(context.allowSynchronous) {
7723 context.isSynchronous = true;
7724 context.callback(context,userParams);
7726 window.setTimeout(function() {context.callback(context,userParams);},
10);
7731 FileAdaptor.prototype.close = function()
7737 config.adaptors[FileAdaptor.serverType] = FileAdaptor;
7739 config.defaultAdaptor = FileAdaptor.serverType;
7742 //-- Remote HTTP requests
7745 function loadRemoteFile(url,callback,params)
7747 return httpReq(
"GET",url,callback,params);
7750 function httpReq(type,url,callback,params,headers,data,contentType,username,password,allowCache)
7754 x = new XMLHttpRequest(); //# Modern
7757 x = new ActiveXObject(
"Msxml2.XMLHTTP"); //# IE
6
7762 return
"Can't create XMLHttpRequest object";
7763 x.onreadystatechange = function() {
7765 var status = x.status;
7769 if(x.readyState ==
4 && callback && (status !== undefined)) {
7770 if([
0,
200,
201,
204,
207].contains(status))
7771 callback(true,params,x.responseText,url,x);
7773 callback(false,params,null,url,x);
7774 x.onreadystatechange = function(){};
7778 if(window.Components && window.netscape && window.netscape.security && document.location.protocol.indexOf(
"http") == -
1)
7779 window.netscape.security.PrivilegeManager.enablePrivilege(
"UniversalBrowserRead");
7782 url = url + (url.indexOf(
"?") <
0 ?
"?" :
"&") +
"nocache=" + Math.random();
7783 x.open(type,url,true,username,password);
7785 x.setRequestHeader(
"Content-Type", contentType ||
"application/x-www-form-urlencoded");
7786 if(x.overrideMimeType)
7787 x.setRequestHeader(
"Connection",
"close");
7789 for(var n in headers)
7790 x.setRequestHeader(n,headers[n]);
7792 x.setRequestHeader(
"X-Requested-With",
"TiddlyWiki " + formatVersion());
7795 return exceptionText(ex);
7800 // included for compatibility
7801 function getXMLHttpRequest()
7804 var x = new XMLHttpRequest(); // Modern
7807 x = new ActiveXObject(
"Msxml2.XMLHTTP"); // IE
6
7815 // included for compatibility
7816 function doHttp(type,url,data,contentType,username,password,callback,params,headers,allowCache)
7818 return httpReq(type,url,callback,params,headers,data,contentType,username,password,allowCache);
7822 //-- TiddlyWiki-specific utility functions
7825 function formatVersion(v)
7828 return v.major +
"." + v.minor +
"." + v.revision + (v.beta ?
" (beta " + v.beta +
")" :
"");
7831 function compareVersions(v1,v2)
7833 var a = [
"major",
"minor",
"revision"];
7834 for(var i =
0; i
<a.length; i++) {
7835 var x1 = v1[a[i]] ||
0;
7836 var x2 = v2[a[i]] ||
0;
7842 x1 = v1.beta ||
9999;
7843 x2 = v2.beta ||
9999;
7846 return x1
> x2 ? -
1 :
0;
7849 function createTiddlyButton(parent,text,tooltip,action,className,id,accessKey,attribs)
7851 var btn = document.createElement(
"a");
7853 btn.onclick = action;
7854 btn.setAttribute(
"href",
"javascript:;");
7857 btn.setAttribute(
"title",tooltip);
7859 btn.appendChild(document.createTextNode(text));
7860 btn.className = className ||
"button";
7864 for(var i in attribs) {
7865 btn.setAttribute(i,attribs[i]);
7869 parent.appendChild(btn);
7871 btn.setAttribute(
"accessKey",accessKey);
7875 function createTiddlyLink(place,title,includeText,className,isStatic,linkedFromTiddler,noToggle)
7877 var text = includeText ? title : null;
7878 var i = getTiddlyLinkInfo(title,className);
7879 var btn = isStatic ? createExternalLink(place,store.getTiddlerText(
"SiteUrl",null) +
"#" + title) : createTiddlyButton(place,text,i.subTitle,onClickTiddlerLink,i.classes);
7881 btn.className += ' ' + className;
7882 btn.setAttribute(
"refresh",
"link");
7883 btn.setAttribute(
"tiddlyLink",title);
7885 btn.setAttribute(
"noToggle",
"true");
7886 if(linkedFromTiddler) {
7887 var fields = linkedFromTiddler.getInheritedFields();
7889 btn.setAttribute(
"tiddlyFields",fields);
7894 function refreshTiddlyLink(e,title)
7896 var i = getTiddlyLinkInfo(title,e.className);
7897 e.className = i.classes;
7898 e.title = i.subTitle;
7901 function getTiddlyLinkInfo(title,currClasses)
7903 var classes = currClasses ? currClasses.split(
" ") : [];
7904 classes.pushUnique(
"tiddlyLink");
7905 var tiddler = store.fetchTiddler(title);
7908 subTitle = tiddler.getSubtitle();
7909 classes.pushUnique(
"tiddlyLinkExisting");
7910 classes.remove(
"tiddlyLinkNonExisting");
7911 classes.remove(
"shadow");
7913 classes.remove(
"tiddlyLinkExisting");
7914 classes.pushUnique(
"tiddlyLinkNonExisting");
7915 if(store.isShadowTiddler(title)) {
7916 subTitle = config.messages.shadowedTiddlerToolTip.format([title]);
7917 classes.pushUnique(
"shadow");
7919 subTitle = config.messages.undefinedTiddlerToolTip.format([title]);
7920 classes.remove(
"shadow");
7923 if(typeof config.annotations[title]==
"string")
7924 subTitle = config.annotations[title];
7925 return {classes: classes.join(
" "),subTitle: subTitle};
7928 function createExternalLink(place,url)
7930 var link = document.createElement(
"a");
7931 link.className =
"externalLink";
7933 link.title = config.messages.externalLinkTooltip.format([url]);
7934 if(config.options.chkOpenInNewWindow)
7935 link.target =
"_blank";
7936 place.appendChild(link);
7940 // Event handler for clicking on a tiddly link
7941 function onClickTiddlerLink(ev)
7943 var e = ev || window.event;
7944 var target = resolveTarget(e);
7948 var noToggle = null;
7950 title = link.getAttribute(
"tiddlyLink");
7951 fields = link.getAttribute(
"tiddlyFields");
7952 noToggle = link.getAttribute(
"noToggle");
7953 link = link.parentNode;
7954 } while(title == null && link != null);
7955 if(!store.isShadowTiddler(title)) {
7956 var f = fields ? fields.decodeHashMap() : {};
7957 fields = String.encodeHashMap(merge(f,config.defaultCustomFields,true));
7960 var toggling = e.metaKey || e.ctrlKey;
7961 if(config.options.chkToggleLinks)
7962 toggling = !toggling;
7965 if(store.getTiddler(title))
7967 story.displayTiddler(target,title,null,true,null,fields,toggling);
7973 // Create a button for a tag with a popup listing all the tiddlers that it tags
7974 function createTagButton(place,tag,excludeTiddler,title,tooltip)
7976 var btn = createTiddlyButton(place,title||tag,(tooltip||config.views.wikified.tag.tooltip).format([tag]),onClickTag);
7977 btn.setAttribute(
"tag",tag);
7979 btn.setAttribute(
"tiddler",excludeTiddler);
7983 // Event handler for clicking on a tiddler tag
7984 function onClickTag(ev)
7986 var e = ev || window.event;
7987 var popup = Popup.create(this);
7988 var tag = this.getAttribute(
"tag");
7989 var title = this.getAttribute(
"tiddler");
7991 var tagged = store.getTaggedTiddlers(tag);
7994 for(r=
0;r
<tagged.length;r++) {
7995 if(tagged[r].title != title)
7996 titles.push(tagged[r].title);
7998 var lingo = config.views.wikified.tag;
7999 if(titles.length
> 0) {
8000 var openAll = createTiddlyButton(createTiddlyElement(popup,
"li"),lingo.openAllText.format([tag]),lingo.openAllTooltip,onClickTagOpenAll);
8001 openAll.setAttribute(
"tag",tag);
8002 createTiddlyElement(createTiddlyElement(popup,
"li",null,
"listBreak"),
"div");
8003 for(r=
0; r
<titles.length; r++) {
8004 createTiddlyLink(createTiddlyElement(popup,
"li"),titles[r],true);
8007 createTiddlyText(createTiddlyElement(popup,
"li",null,
"disabled"),lingo.popupNone.format([tag]));
8009 createTiddlyElement(createTiddlyElement(popup,
"li",null,
"listBreak"),
"div");
8010 var h = createTiddlyLink(createTiddlyElement(popup,
"li"),tag,false);
8011 createTiddlyText(h,lingo.openTag.format([tag]));
8014 e.cancelBubble = true;
8015 if(e.stopPropagation) e.stopPropagation();
8019 // Event handler for 'open all' on a tiddler popup
8020 function onClickTagOpenAll(ev)
8022 var tiddlers = store.getTaggedTiddlers(this.getAttribute(
"tag"));
8023 story.displayTiddlers(this,tiddlers);
8027 function onClickError(ev)
8029 var e = ev || window.event;
8030 var popup = Popup.create(this);
8031 var lines = this.getAttribute(
"errorText").split(
"\n");
8032 for(var t=
0; t
<lines.length; t++)
8033 createTiddlyElement(popup,
"li",null,null,lines[t]);
8035 e.cancelBubble = true;
8036 if(e.stopPropagation) e.stopPropagation();
8040 function createTiddlyDropDown(place,onchange,options,defaultValue)
8042 var sel = createTiddlyElement(place,
"select");
8043 sel.onchange = onchange;
8044 for(var t=
0; t
<options.length; t++) {
8045 var e = createTiddlyElement(sel,
"option",null,null,options[t].caption);
8046 e.value = options[t].name;
8047 if(options[t].name == defaultValue)
8053 function createTiddlyPopup(place,caption,tooltip,tiddler)
8056 createTiddlyLink(place,caption,true);
8057 var btn = createTiddlyButton(place,glyph(
"downArrow"),tooltip,onClickTiddlyPopup,
"tiddlerPopupButton");
8058 btn.tiddler = tiddler;
8060 createTiddlyText(place,caption);
8064 function onClickTiddlyPopup(ev)
8066 var e = ev || window.event;
8067 var tiddler = this.tiddler;
8069 var popup = Popup.create(this,
"div",
"popupTiddler");
8070 wikify(tiddler.text,popup,null,tiddler);
8073 if(e) e.cancelBubble = true;
8074 if(e && e.stopPropagation) e.stopPropagation();
8078 function createTiddlyError(place,title,text)
8080 var btn = createTiddlyButton(place,title,null,onClickError,
"errorButton");
8081 if(text) btn.setAttribute(
"errorText",text);
8084 function merge(dst,src,preserveExisting)
8087 if(!preserveExisting || dst[i] === undefined)
8093 // Returns a string containing the description of an exception, optionally prepended by a message
8094 function exceptionText(e,message)
8096 var s = e.description || e.toString();
8097 return message ?
"%0:\n%1".format([message,s]) : s;
8100 // Displays an alert of an exception description with optional message
8101 function showException(e,message)
8103 alert(exceptionText(e,message));
8106 function alertAndThrow(m)
8112 function glyph(name)
8114 var g = config.glyphs;
8115 var b = g.currBrowser;
8118 while(!g.browsers[b]() && b < g.browsers.length-
1)
8124 return g.codes[name][b];
8127 if(!window.console) {
8128 console = {log:function(message) {displayMessage(message);}};
8132 //- Animation engine
8137 this.running =
0; // Incremented at start of each animation, decremented afterwards. If zero, the interval timer is disabled
8138 this.timerID =
0; // ID of the timer used for animating
8139 this.animations = []; // List of animations in progress
8143 // Start animation engine
8144 Animator.prototype.startAnimating = function() //# Variable number of arguments
8146 for(var t=
0; t
<arguments.length; t++)
8147 this.animations.push(arguments[t]);
8148 if(this.running ==
0) {
8150 this.timerID = window.setInterval(function() {me.doAnimate(me);},
10);
8152 this.running += arguments.length;
8155 // Perform an animation engine tick, calling each of the known animation modules
8156 Animator.prototype.doAnimate = function(me)
8159 while(a < me.animations.length) {
8160 var animation = me.animations[a];
8161 if(animation.tick()) {
8164 me.animations.splice(a,
1);
8165 if(--me.running ==
0)
8166 window.clearInterval(me.timerID);
8171 Animator.slowInSlowOut = function(progress)
8173 return(
1-((Math.cos(progress * Math.PI)+
1)/
2));
8177 //-- Morpher animation
8180 // Animate a set of properties of an element
8181 function Morpher(element,duration,properties,callback)
8183 this.element = element;
8184 this.duration = duration;
8185 this.properties = properties;
8186 this.startTime = new Date();
8187 this.endTime = Number(this.startTime) + duration;
8188 this.callback = callback;
8193 Morpher.prototype.assignStyle = function(element,style,value)
8196 case
"-tw-vertScroll":
8197 window.scrollTo(findScrollX(),value);
8199 case
"-tw-horizScroll":
8200 window.scrollTo(value,findScrollY());
8203 element.style[style] = value;
8208 Morpher.prototype.stop = function()
8210 for(var t=
0; t
<this.properties.length; t++) {
8211 var p = this.properties[t];
8212 if(p.atEnd !== undefined) {
8213 this.assignStyle(this.element,p.style,p.atEnd);
8217 this.callback(this.element,this.properties);
8220 Morpher.prototype.tick = function()
8222 var currTime = Number(new Date());
8223 var progress = Animator.slowInSlowOut(Math.min(
1,(currTime-this.startTime)/this.duration));
8224 for(var t=
0; t
<this.properties.length; t++) {
8225 var p = this.properties[t];
8226 if(p.start !== undefined && p.end !== undefined) {
8227 var template = p.template ||
"%0";
8231 var v = p.start + (p.end-p.start) * progress;
8232 this.assignStyle(this.element,p.style,template.format([v]));
8239 if(currTime
>= this.endTime) {
8247 //-- Zoomer animation
8250 function Zoomer(text,startElement,targetElement,unused)
8252 var e = createTiddlyElement(document.body,
"div",null,
"zoomer");
8253 createTiddlyElement(e,
"div",null,null,text);
8254 var winWidth = findWindowWidth();
8255 var winHeight = findWindowHeight();
8257 {style: 'left', start: findPosX(startElement), end: findPosX(targetElement), template: '%
0px'},
8258 {style: 'top', start: findPosY(startElement), end: findPosY(targetElement), template: '%
0px'},
8259 {style: 'width', start: Math.min(startElement.scrollWidth,winWidth), end: Math.min(targetElement.scrollWidth,winWidth), template: '%
0px', atEnd: 'auto'},
8260 {style: 'height', start: Math.min(startElement.scrollHeight,winHeight), end: Math.min(targetElement.scrollHeight,winHeight), template: '%
0px', atEnd: 'auto'},
8261 {style: 'fontSize', start:
8, end:
24, template: '%
0pt'}
8263 var c = function(element,properties) {removeNode(element);};
8264 return new Morpher(e,config.animDuration,p,c);
8268 //-- Scroller animation
8271 function Scroller(targetElement)
8273 var p = [{style: '-tw-vertScroll', start: findScrollY(), end: ensureVisible(targetElement)}];
8274 return new Morpher(targetElement,config.animDuration,p);
8278 //-- Slider animation
8281 // deleteMode -
"none",
"all" [delete target element and it's children], [only]
"children" [but not the target element]
8282 function Slider(element,opening,unused,deleteMode)
8284 element.style.overflow = 'hidden';
8286 element.style.height = '
0px'; // Resolves a Firefox flashing bug
8287 element.style.display = 'block';
8288 var left = findPosX(element);
8289 var width = element.scrollWidth;
8290 var height = element.scrollHeight;
8291 var winWidth = findWindowWidth();
8295 p.push({style: 'height', start:
0, end: height, template: '%
0px', atEnd: 'auto'});
8296 p.push({style: 'opacity', start:
0, end:
1, template: '%
0'});
8297 p.push({style: 'filter', start:
0, end:
100, template: 'alpha(opacity:%
0)'});
8299 p.push({style: 'height', start: height, end:
0, template: '%
0px'});
8300 p.push({style: 'display', atEnd: 'none'});
8301 p.push({style: 'opacity', start:
1, end:
0, template: '%
0'});
8302 p.push({style: 'filter', start:
100, end:
0, template: 'alpha(opacity:%
0)'});
8303 switch(deleteMode) {
8305 c = function(element,properties) {removeNode(element);};
8308 c = function(element,properties) {removeChildren(element);};
8312 return new Morpher(element,config.animDuration,p,c);
8320 stack: [] // Array of objects with members root: and popup:
8323 Popup.create = function(root,elem,className)
8325 var stackPosition = this.find(root,
"popup");
8326 Popup.remove(stackPosition+
1);
8327 var popup = createTiddlyElement(document.body,elem ||
"ol",
"popup",className ||
"popup");
8328 popup.stackPosition = stackPosition;
8329 Popup.stack.push({root: root, popup: popup});
8333 Popup.onDocumentClick = function(ev)
8335 var e = ev || window.event;
8336 if(e.eventPhase == undefined)
8338 else if(e.eventPhase == Event.BUBBLING_PHASE || e.eventPhase == Event.AT_TARGET)
8343 Popup.show = function(valign,halign,offset)
8345 var curr = Popup.stack[Popup.stack.length-
1];
8346 this.place(curr.root,curr.popup,valign,halign,offset);
8347 addClass(curr.root,
"highlight");
8348 if(config.options.chkAnimate && anim && typeof Scroller ==
"function")
8349 anim.startAnimating(new Scroller(curr.popup));
8351 window.scrollTo(
0,ensureVisible(curr.popup));
8354 Popup.place = function(root,popup,valign,halign,offset)
8357 var offset = {x:
0,y:
0};
8358 if(popup.stackPosition
>=
0 && !valign && !halign) {
8359 offset.x = offset.x + root.offsetWidth;
8361 offset.x = (halign == 'right') ? offset.x + root.offsetWidth : offset.x;
8362 offset.y = (valign == 'top') ? offset.y : offset.y + root.offsetHeight;
8364 var rootLeft = findPosX(root);
8365 var rootTop = findPosY(root);
8366 var popupLeft = rootLeft + offset.x;
8367 var popupTop = rootTop + offset.y;
8368 var winWidth = findWindowWidth();
8369 if(popup.offsetWidth
> winWidth*
0.75)
8370 popup.style.width = winWidth*
0.75 +
"px";
8371 var popupWidth = popup.offsetWidth;
8372 var scrollWidth = winWidth - document.body.offsetWidth;
8373 if(popupLeft + popupWidth
> winWidth - scrollWidth -
1) {
8374 if(halign == 'right')
8375 popupLeft = popupLeft - root.offsetWidth - popupWidth;
8377 popupLeft = winWidth - popupWidth - scrollWidth -
1;
8379 popup.style.left = popupLeft +
"px";
8380 popup.style.top = popupTop +
"px";
8381 popup.style.display =
"block";
8384 Popup.find = function(e)
8387 for (var t=this.stack.length-
1; t
>=
0; t--) {
8388 if(isDescendant(e,this.stack[t].popup))
8394 Popup.remove = function(pos)
8396 if(!pos) var pos =
0;
8397 if(Popup.stack.length
> pos) {
8398 Popup.removeFrom(pos);
8402 Popup.removeFrom = function(from)
8404 for(var t=Popup.stack.length-
1; t
>=from; t--) {
8405 var p = Popup.stack[t];
8406 removeClass(p.root,
"highlight");
8407 removeNode(p.popup);
8409 Popup.stack = Popup.stack.slice(
0,from);
8416 function Wizard(elem)
8419 this.formElem = findRelated(elem,
"wizard",
"className");
8420 this.bodyElem = findRelated(this.formElem.firstChild,
"wizardBody",
"className",
"nextSibling");
8421 this.footElem = findRelated(this.formElem.firstChild,
"wizardFooter",
"className",
"nextSibling");
8423 this.formElem = null;
8424 this.bodyElem = null;
8425 this.footElem = null;
8429 Wizard.prototype.setValue = function(name,value)
8432 this.formElem[name] = value;
8435 Wizard.prototype.getValue = function(name)
8437 return this.formElem ? this.formElem[name] : null;
8440 Wizard.prototype.createWizard = function(place,title)
8442 this.formElem = createTiddlyElement(place,
"form",null,
"wizard");
8443 createTiddlyElement(this.formElem,
"h1",null,null,title);
8444 this.bodyElem = createTiddlyElement(this.formElem,
"div",null,
"wizardBody");
8445 this.footElem = createTiddlyElement(this.formElem,
"div",null,
"wizardFooter");
8448 Wizard.prototype.clear = function()
8450 removeChildren(this.bodyElem);
8453 Wizard.prototype.setButtons = function(buttonInfo,status)
8455 removeChildren(this.footElem);
8456 for(var t=
0; t
<buttonInfo.length; t++) {
8457 createTiddlyButton(this.footElem,buttonInfo[t].caption,buttonInfo[t].tooltip,buttonInfo[t].onClick);
8458 insertSpacer(this.footElem);
8460 if(typeof status ==
"string") {
8461 createTiddlyElement(this.footElem,
"span",null,
"status",status);
8465 Wizard.prototype.addStep = function(stepTitle,html)
8467 removeChildren(this.bodyElem);
8468 var w = createTiddlyElement(this.bodyElem,
"div");
8469 createTiddlyElement(w,
"h2",null,null,stepTitle);
8470 var step = createTiddlyElement(w,
"div",null,
"wizardStep");
8471 step.innerHTML = html;
8472 applyHtmlMacros(step,tiddler);
8475 Wizard.prototype.getElement = function(name)
8477 return this.formElem.elements[name];
8481 //-- ListView gadget
8486 // Create a listview
8487 ListView.create = function(place,listObject,listTemplate,callback,className)
8489 var table = createTiddlyElement(place,
"table",null,className ||
"listView twtable");
8490 var thead = createTiddlyElement(table,
"thead");
8491 var r = createTiddlyElement(thead,
"tr");
8492 for(var t=
0; t
<listTemplate.columns.length; t++) {
8493 var columnTemplate = listTemplate.columns[t];
8494 var c = createTiddlyElement(r,
"th");
8495 var colType = ListView.columnTypes[columnTemplate.type];
8496 if(colType && colType.createHeader) {
8497 colType.createHeader(c,columnTemplate,t);
8498 if(columnTemplate.className)
8499 addClass(c,columnTemplate.className);
8502 var tbody = createTiddlyElement(table,
"tbody");
8503 for(var rc=
0; rc
<listObject.length; rc++) {
8504 var rowObject = listObject[rc];
8505 r = createTiddlyElement(tbody,
"tr");
8506 for(c=
0; c
<listTemplate.rowClasses.length; c++) {
8507 if(rowObject[listTemplate.rowClasses[c].field])
8508 addClass(r,listTemplate.rowClasses[c].className);
8510 rowObject.rowElement = r;
8511 rowObject.colElements = {};
8512 for(var cc=
0; cc
<listTemplate.columns.length; cc++) {
8513 c = createTiddlyElement(r,
"td");
8514 columnTemplate = listTemplate.columns[cc];
8515 var field = columnTemplate.field;
8516 colType = ListView.columnTypes[columnTemplate.type];
8517 if(colType && colType.createItem) {
8518 colType.createItem(c,rowObject,field,columnTemplate,cc,rc);
8519 if(columnTemplate.className)
8520 addClass(c,columnTemplate.className);
8522 rowObject.colElements[field] = c;
8525 if(callback && listTemplate.actions)
8526 createTiddlyDropDown(place,ListView.getCommandHandler(callback),listTemplate.actions);
8527 if(callback && listTemplate.buttons) {
8528 for(t=
0; t
<listTemplate.buttons.length; t++) {
8529 var a = listTemplate.buttons[t];
8530 if(a && a.name !=
"")
8531 createTiddlyButton(place,a.caption,null,ListView.getCommandHandler(callback,a.name,a.allowEmptySelection));
8537 ListView.getCommandHandler = function(callback,name,allowEmptySelection)
8539 return function(e) {
8540 var view = findRelated(this,
"TABLE",null,
"previousSibling");
8542 ListView.forEachSelector(view,function(e,rowName) {
8544 tiddlers.push(rowName);
8546 if(tiddlers.length ==
0 && !allowEmptySelection) {
8547 alert(config.messages.nothingSelected);
8549 if(this.nodeName.toLowerCase() ==
"select") {
8550 callback(view,this.value,tiddlers);
8551 this.selectedIndex =
0;
8553 callback(view,name,tiddlers);
8559 // Invoke a callback for each selector checkbox in the listview
8560 ListView.forEachSelector = function(view,callback)
8562 var checkboxes = view.getElementsByTagName(
"input");
8564 for(var t=
0; t
<checkboxes.length; t++) {
8565 var cb = checkboxes[t];
8566 if(cb.getAttribute(
"type") ==
"checkbox") {
8567 var rn = cb.getAttribute(
"rowName");
8577 ListView.getSelectedRows = function(view)
8580 ListView.forEachSelector(view,function(e,rowName) {
8582 rowNames.push(rowName);
8587 ListView.columnTypes = {};
8589 ListView.columnTypes.String = {
8590 createHeader: function(place,columnTemplate,col)
8592 createTiddlyText(place,columnTemplate.title);
8594 createItem: function(place,listObject,field,columnTemplate,col,row)
8596 var v = listObject[field];
8598 createTiddlyText(place,v);
8602 ListView.columnTypes.WikiText = {
8603 createHeader: ListView.columnTypes.String.createHeader,
8604 createItem: function(place,listObject,field,columnTemplate,col,row)
8606 var v = listObject[field];
8608 wikify(v,place,null,null);
8612 ListView.columnTypes.Tiddler = {
8613 createHeader: ListView.columnTypes.String.createHeader,
8614 createItem: function(place,listObject,field,columnTemplate,col,row)
8616 var v = listObject[field];
8617 if(v != undefined && v.title)
8618 createTiddlyPopup(place,v.title,config.messages.listView.tiddlerTooltip,v);
8622 ListView.columnTypes.Size = {
8623 createHeader: ListView.columnTypes.String.createHeader,
8624 createItem: function(place,listObject,field,columnTemplate,col,row)
8626 var v = listObject[field];
8627 if(v != undefined) {
8629 while(t
<config.messages.sizeTemplates.length-
1 && v
<config.messages.sizeTemplates[t].unit)
8631 createTiddlyText(place,config.messages.sizeTemplates[t].template.format([Math.round(v/config.messages.sizeTemplates[t].unit)]));
8636 ListView.columnTypes.Link = {
8637 createHeader: ListView.columnTypes.String.createHeader,
8638 createItem: function(place,listObject,field,columnTemplate,col,row)
8640 var v = listObject[field];
8641 var c = columnTemplate.text;
8643 createTiddlyText(createExternalLink(place,v),c || v);
8647 ListView.columnTypes.Date = {
8648 createHeader: ListView.columnTypes.String.createHeader,
8649 createItem: function(place,listObject,field,columnTemplate,col,row)
8651 var v = listObject[field];
8653 createTiddlyText(place,v.formatString(columnTemplate.dateFormat));
8657 ListView.columnTypes.StringList = {
8658 createHeader: ListView.columnTypes.String.createHeader,
8659 createItem: function(place,listObject,field,columnTemplate,col,row)
8661 var v = listObject[field];
8662 if(v != undefined) {
8663 for(var t=
0; t
<v.length; t++) {
8664 createTiddlyText(place,v[t]);
8665 createTiddlyElement(place,
"br");
8671 ListView.columnTypes.Selector = {
8672 createHeader: function(place,columnTemplate,col)
8674 createTiddlyCheckbox(place,null,false,this.onHeaderChange);
8676 createItem: function(place,listObject,field,columnTemplate,col,row)
8678 var e = createTiddlyCheckbox(place,null,listObject[field],null);
8679 e.setAttribute(
"rowName",listObject[columnTemplate.rowName]);
8681 onHeaderChange: function(e)
8683 var state = this.checked;
8684 var view = findRelated(this,
"TABLE");
8687 ListView.forEachSelector(view,function(e,rowName) {
8693 ListView.columnTypes.Tags = {
8694 createHeader: ListView.columnTypes.String.createHeader,
8695 createItem: function(place,listObject,field,columnTemplate,col,row)
8697 var tags = listObject[field];
8698 createTiddlyText(place,String.encodeTiddlyLinkList(tags));
8702 ListView.columnTypes.Boolean = {
8703 createHeader: ListView.columnTypes.String.createHeader,
8704 createItem: function(place,listObject,field,columnTemplate,col,row)
8706 if(listObject[field] == true)
8707 createTiddlyText(place,columnTemplate.trueText);
8708 if(listObject[field] == false)
8709 createTiddlyText(place,columnTemplate.falseText);
8713 ListView.columnTypes.TagCheckbox = {
8714 createHeader: ListView.columnTypes.String.createHeader,
8715 createItem: function(place,listObject,field,columnTemplate,col,row)
8717 var e = createTiddlyCheckbox(place,null,listObject[field],this.onChange);
8718 e.setAttribute(
"tiddler",listObject.title);
8719 e.setAttribute(
"tag",columnTemplate.tag);
8721 onChange : function(e)
8723 var tag = this.getAttribute(
"tag");
8724 var tiddler = this.getAttribute(
"tiddler");
8725 store.setTiddlerTag(tiddler,this.checked,tag);
8729 ListView.columnTypes.TiddlerLink = {
8730 createHeader: ListView.columnTypes.String.createHeader,
8731 createItem: function(place,listObject,field,columnTemplate,col,row)
8733 var v = listObject[field];
8734 if(v != undefined) {
8735 var link = createTiddlyLink(place,listObject[columnTemplate.tiddlerLink],false,null);
8736 createTiddlyText(link,listObject[field]);
8742 //-- Augmented methods for the JavaScript Number(), Array(), String() and Date() objects
8745 // Clamp a number to a range
8746 Number.prototype.clamp = function(min,max)
8756 // Add indexOf function if browser does not support it
8757 if(!Array.indexOf) {
8758 Array.prototype.indexOf = function(item,from)
8762 for(var i=from; i
<this.length; i++) {
8763 if(this[i] === item)
8769 // Find an entry in a given field of the members of an array
8770 Array.prototype.findByField = function(field,value)
8772 for(var t=
0; t
<this.length; t++) {
8773 if(this[t][field] == value)
8779 // Return whether an entry exists in an array
8780 Array.prototype.contains = function(item)
8782 return this.indexOf(item) != -
1;
8785 // Adds, removes or toggles a particular value within an array
8786 // value - value to add
8787 // mode - +
1 to add value, -
1 to remove value,
0 to toggle it
8788 Array.prototype.setItem = function(value,mode)
8790 var p = this.indexOf(value);
8792 mode = (p == -
1) ? +
1 : -
1;
8796 } else if(mode == -
1) {
8802 // Return whether one of a list of values exists in an array
8803 Array.prototype.containsAny = function(items)
8805 for(var i=
0; i
<items.length; i++) {
8806 if(this.indexOf(items[i]) != -
1)
8812 // Return whether all of a list of values exists in an array
8813 Array.prototype.containsAll = function(items)
8815 for(var i =
0; i
<items.length; i++) {
8816 if(this.indexOf(items[i]) == -
1)
8822 // 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
8823 Array.prototype.pushUnique = function(item,unique)
8825 if(unique === false) {
8828 if(this.indexOf(item) == -
1)
8833 Array.prototype.remove = function(item)
8835 var p = this.indexOf(item);
8840 if(!Array.prototype.map) {
8841 Array.prototype.map = function(fn,thisObj)
8843 var scope = thisObj || window;
8845 for(var i=
0, j=this.length; i < j; ++i) {
8846 a.push(fn.call(scope,this[i],i,this));
8851 // Get characters from the right end of a string
8852 String.prototype.right = function(n)
8854 return n < this.length ? this.slice(this.length-n) : this;
8857 // Trim whitespace from both ends of a string
8858 String.prototype.trim = function()
8860 return this.replace(/^\s*|\s*$/g,
"");
8863 // Convert a string from a CSS style property name to a JavaScript style name (
"background-color" -
> "backgroundColor")
8864 String.prototype.unDash = function()
8866 var s = this.split(
"-");
8868 for(var t=
1; t
<s.length; t++)
8869 s[t] = s[t].substr(
0,
1).toUpperCase() + s[t].substr(
1);
8874 // Substitute substrings from an array into a format string that includes '%
1'-type specifiers
8875 String.prototype.format = function(substrings)
8877 var subRegExp = /(?:%(\d+))/mg;
8881 var match = subRegExp.exec(this);
8882 if(match && match[
1]) {
8883 if(match.index
> currPos)
8884 r.push(this.substring(currPos,match.index));
8885 r.push(substrings[parseInt(match[
1])]);
8886 currPos = subRegExp.lastIndex;
8889 if(currPos < this.length)
8890 r.push(this.substring(currPos,this.length));
8894 // Escape any special RegExp characters with that character preceded by a backslash
8895 String.prototype.escapeRegExp = function()
8897 var s =
"\\^$*+?()=!|,{}[].";
8899 for(var t=
0; t
<s.length; t++)
8900 c = c.replace(new RegExp(
"\\" + s.substr(t,
1),
"g"),
"\\" + s.substr(t,
1));
8904 // Convert
"\" to
"\s", newlines to
"\n" (and remove carriage returns)
8905 String.prototype.escapeLineBreaks = function()
8907 return this.replace(/\\/mg,
"\\s").replace(/\n/mg,
"\\n").replace(/\r/mg,
"");
8910 // Convert
"\n" to newlines,
"\b" to
" ",
"\s" to
"\" (and remove carriage returns)
8911 String.prototype.unescapeLineBreaks = function()
8913 return this.replace(/\\n/mg,
"\n").replace(/\\b/mg,
" ").replace(/\\s/mg,
"\\").replace(/\r/mg,
"");
8916 // Convert & to
"&", < to
"<",
> to
">" and
" to """
8917 String.prototype.htmlEncode = function()
8919 return this.replace(/&/mg,"&").replace(/</mg,"<").replace(/>/mg,">").replace(/\"/mg,
""");
8922 // Convert
"&" to &,
"<" to <,
">" to
> and
""" to
"
8923 String.prototype.htmlDecode = function()
8925 return this.replace(/</mg,"<
").replace(/>/mg,">").replace(/"/mg,"\
"").replace(/
&/mg,
"&");
8928 // Convert a string to it's JSON representation by encoding control characters, double quotes and backslash. See json.org
8929 String.prototype.toJSONString = function()
8940 var replaceFn = function(a,b) {
8945 return '\\u00' + Math.floor(c /
16).toString(
16) + (c %
16).toString(
16);
8947 if(/[
"\\\x00-\x1f]/.test(this))
8948 return '"' + this.replace(/([\x00-\x1f\\
"])/g,replaceFn) + '"';
8949 return '
"' + this + '"';
8952 // Parse a space-separated string of name:value parameters
8953 // The result is an array of objects:
8954 // result[
0] = object with a member for each parameter name, value of that member being an array of values
8955 // result[
1..n] = one object for each parameter, with 'name' and 'value' members
8956 String.prototype.parseParams = function(defaultName,defaultValue,allowEval,noNames,cascadeDefaults)
8958 var parseToken = function(match,p) {
8960 if(match[p]) // Double quoted
8962 else if(match[p+
1]) // Single quoted
8964 else if(match[p+
2]) // Double-square-bracket quoted
8966 else if(match[p+
3]) // Double-brace quoted
8972 throw
"Unable to evaluate {{" + match[p+
3] +
"}}: " + exceptionText(ex);
8974 else if(match[p+
4]) // Unquoted
8976 else if(match[p+
5]) // empty quote
8981 var dblQuote =
"(?:\"((?:(?:\\\\\
")|[^\"])+)\
")";
8982 var sngQuote =
"(?:'((?:(?:\\\\\')|[^'])+)')";
8983 var dblSquare =
"(?:\\[\\[((?:\\s|\\S)*?)\\]\\])";
8984 var dblBrace =
"(?:\\{\\{((?:\\s|\\S)*?)\\}\\})";
8985 var unQuoted = noNames ?
"([^\"'\\s]\\S*)
" : "([^\
"':\\s][^\\s:]*)";
8986 var emptyQuote =
"((?:\"\
")|(?:''))";
8987 var skipSpace =
"(?:\\s*)";
8988 var token =
"(?:" + dblQuote +
"|" + sngQuote +
"|" + dblSquare +
"|" + dblBrace +
"|" + unQuoted +
"|" + emptyQuote +
")";
8989 var re = noNames ? new RegExp(token,
"mg") : new RegExp(skipSpace + token + skipSpace +
"(?:(\\:)" + skipSpace + token +
")?",
"mg");
8992 var match = re.exec(this);
8994 var n = parseToken(match,
1);
8996 r.push({name:
"",value:n});
8998 var v = parseToken(match,
8);
8999 if(v == null && defaultName) {
9002 } else if(v == null && defaultValue) {
9005 r.push({name:n,value:v});
9006 if(cascadeDefaults) {
9013 // Summarise parameters into first element
9014 for(var t=
1; t
<r.length; t++) {
9016 r[
0][r[t].name].push(r[t].value);
9018 r[
0][r[t].name] = [r[t].value];
9023 // Process a string list of macro parameters into an array. Parameters can be quoted with
"", '',
9024 // [[]], {{ }} or left unquoted (and therefore space-separated). Double-braces {{}} results in
9025 // an *evaluated* parameter: e.g. {{config.options.txtUserName}} results in the current user's name.
9026 String.prototype.readMacroParams = function()
9028 var p = this.parseParams(
"list",null,true,true);
9030 for(var t=
1; t
<p.length; t++)
9035 // Process a string list of unique tiddler names into an array. Tiddler names that have spaces in them must be [[bracketed]]
9036 String.prototype.readBracketedList = function(unique)
9038 var p = this.parseParams(
"list",null,false,true);
9040 for(var t=
1; t
<p.length; t++) {
9042 n.pushUnique(p[t].value,unique);
9047 // Returns array with start and end index of chunk between given start and end marker, or undefined.
9048 String.prototype.getChunkRange = function(start,end)
9050 var s = this.indexOf(start);
9053 var e = this.indexOf(end,s);
9059 // Replace a chunk of a string given start and end markers
9060 String.prototype.replaceChunk = function(start,end,sub)
9062 var r = this.getChunkRange(start,end);
9063 return r ? this.substring(
0,r[
0]) + sub + this.substring(r[
1]) : this;
9066 // Returns a chunk of a string between start and end markers, or undefined
9067 String.prototype.getChunk = function(start,end)
9069 var r = this.getChunkRange(start,end);
9071 return this.substring(r[
0],r[
1]);
9075 // Static method to bracket a string with double square brackets if it contains a space
9076 String.encodeTiddlyLink = function(title)
9078 return title.indexOf(
" ") == -
1 ? title :
"[[" + title +
"]]";
9081 // Static method to encodeTiddlyLink for every item in an array and join them with spaces
9082 String.encodeTiddlyLinkList = function(list)
9086 for(var t=
0; t
<list.length; t++)
9087 results.push(String.encodeTiddlyLink(list[t]));
9088 return results.join(
" ");
9094 // Convert a string as a sequence of name:
"value" pairs into a hashmap
9095 String.prototype.decodeHashMap = function()
9097 var fields = this.parseParams(
"anon",
"",false);
9099 for(var t=
1; t
<fields.length; t++)
9100 r[fields[t].name] = fields[t].value;
9104 // Static method to encode a hashmap into a name:
"value"... string
9105 String.encodeHashMap = function(hashmap)
9108 for(var t in hashmap)
9109 r.push(t + ':
"' + hashmap[t] + '"');
9113 // Static method to left-pad a string with
0s to a certain width
9114 String.zeroPad = function(n,d)
9116 var s = n.toString();
9118 s =
"000000000000000000000000000".substr(
0,d-s.length) + s;
9122 String.prototype.startsWith = function(prefix)
9124 return !prefix || this.substring(
0,prefix.length) == prefix;
9127 // Returns the first value of the given named parameter.
9128 function getParam(params,name,defaultValue)
9131 return defaultValue;
9132 var p = params[
0][name];
9133 return p ? p[
0] : defaultValue;
9136 // Returns the first value of the given boolean named parameter.
9137 function getFlag(params,name,defaultValue)
9139 return !!getParam(params,name,defaultValue);
9142 // Substitute date components into a string
9143 Date.prototype.formatString = function(template)
9145 var t = template.replace(/
0hh12/g,String.zeroPad(this.getHours12(),
2));
9146 t = t.replace(/hh12/g,this.getHours12());
9147 t = t.replace(/
0hh/g,String.zeroPad(this.getHours(),
2));
9148 t = t.replace(/hh/g,this.getHours());
9149 t = t.replace(/mmm/g,config.messages.dates.shortMonths[this.getMonth()]);
9150 t = t.replace(/
0mm/g,String.zeroPad(this.getMinutes(),
2));
9151 t = t.replace(/mm/g,this.getMinutes());
9152 t = t.replace(/
0ss/g,String.zeroPad(this.getSeconds(),
2));
9153 t = t.replace(/ss/g,this.getSeconds());
9154 t = t.replace(/[ap]m/g,this.getAmPm().toLowerCase());
9155 t = t.replace(/[AP]M/g,this.getAmPm().toUpperCase());
9156 t = t.replace(/wYYYY/g,this.getYearForWeekNo());
9157 t = t.replace(/wYY/g,String.zeroPad(this.getYearForWeekNo()-
2000,
2));
9158 t = t.replace(/YYYY/g,this.getFullYear());
9159 t = t.replace(/YY/g,String.zeroPad(this.getFullYear()-
2000,
2));
9160 t = t.replace(/MMM/g,config.messages.dates.months[this.getMonth()]);
9161 t = t.replace(/
0MM/g,String.zeroPad(this.getMonth()+
1,
2));
9162 t = t.replace(/MM/g,this.getMonth()+
1);
9163 t = t.replace(/
0WW/g,String.zeroPad(this.getWeek(),
2));
9164 t = t.replace(/WW/g,this.getWeek());
9165 t = t.replace(/DDD/g,config.messages.dates.days[this.getDay()]);
9166 t = t.replace(/ddd/g,config.messages.dates.shortDays[this.getDay()]);
9167 t = t.replace(/
0DD/g,String.zeroPad(this.getDate(),
2));
9168 t = t.replace(/DDth/g,this.getDate()+this.daySuffix());
9169 t = t.replace(/DD/g,this.getDate());
9170 var tz = this.getTimezoneOffset();
9171 var atz = Math.abs(tz);
9172 t = t.replace(/TZD/g,(tz <
0 ? '+' : '-') + String.zeroPad(Math.floor(atz /
60),
2) + ':' + String.zeroPad(atz %
60,
2));
9173 t = t.replace(/\\/g,
"");
9177 Date.prototype.getWeek = function()
9179 var dt = new Date(this.getTime());
9180 var d = dt.getDay();
9181 if(d==
0) d=
7;// JavaScript Sun=
0, ISO Sun=
7
9182 dt.setTime(dt.getTime()+(
4-d)*
86400000);// shift day to Thurs of same week to calculate weekNo
9183 var n = Math.floor((dt.getTime()-new Date(dt.getFullYear(),
0,
1)+
3600000)/
86400000);
9184 return Math.floor(n/
7)+
1;
9187 Date.prototype.getYearForWeekNo = function()
9189 var dt = new Date(this.getTime());
9190 var d = dt.getDay();
9191 if(d==
0) d=
7;// JavaScript Sun=
0, ISO Sun=
7
9192 dt.setTime(dt.getTime()+(
4-d)*
86400000);// shift day to Thurs of same week
9193 return dt.getFullYear();
9196 Date.prototype.getHours12 = function()
9198 var h = this.getHours();
9199 return h
> 12 ? h-
12 : ( h
> 0 ? h :
12 );
9202 Date.prototype.getAmPm = function()
9204 return this.getHours()
>=
12 ? config.messages.dates.pm : config.messages.dates.am;
9207 Date.prototype.daySuffix = function()
9209 return config.messages.dates.daySuffixes[this.getDate()-
1];
9212 // Convert a date to local YYYYMMDDHHMM string format
9213 Date.prototype.convertToLocalYYYYMMDDHHMM = function()
9215 return this.getFullYear() + String.zeroPad(this.getMonth()+
1,
2) + String.zeroPad(this.getDate(),
2) + String.zeroPad(this.getHours(),
2) + String.zeroPad(this.getMinutes(),
2);
9218 // Convert a date to UTC YYYYMMDDHHMM string format
9219 Date.prototype.convertToYYYYMMDDHHMM = function()
9221 return this.getUTCFullYear() + String.zeroPad(this.getUTCMonth()+
1,
2) + String.zeroPad(this.getUTCDate(),
2) + String.zeroPad(this.getUTCHours(),
2) + String.zeroPad(this.getUTCMinutes(),
2);
9224 // Convert a date to UTC YYYYMMDD.HHMMSSMMM string format
9225 Date.prototype.convertToYYYYMMDDHHMMSSMMM = function()
9227 return this.getUTCFullYear() + 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);
9230 // Static method to create a date from a UTC YYYYMMDDHHMM format string
9231 Date.convertFromYYYYMMDDHHMM = function(d)
9233 var hh = d.substr(
8,
2) ||
"00";
9234 var mm = d.substr(
10,
2) ||
"00";
9235 return new Date(Date.UTC(parseInt(d.substr(
0,
4),
10),
9236 parseInt(d.substr(
4,
2),
10)-
1,
9237 parseInt(d.substr(
6,
2),
10),
9239 parseInt(mm,
10),
0,
0));
9243 //-- Crypto functions and associated conversion routines
9246 // Crypto 'namespace'
9247 function Crypto() {}
9249 // Convert a string to an array of big-endian
32-bit words
9250 Crypto.strToBe32s = function(str)
9253 var len=Math.floor(str.length/
4);
9255 for(i=
0, j=
0; i
<len; i++, j+=
4) {
9256 be[i]=((str.charCodeAt(j)&
0xff) <<
24)|((str.charCodeAt(j+
1)&
0xff) <<
16)|((str.charCodeAt(j+
2)&
0xff) <<
8)|(str.charCodeAt(j+
3)&
0xff);
9258 while(j
<str.length) {
9259 be[j
>>2] |= (str.charCodeAt(j)&
0xff)<<(
24-(j*
8)%
32);
9265 // Convert an array of big-endian
32-bit words to a string
9266 Crypto.be32sToStr = function(be)
9269 for(var i=
0;i
<be.length*
32;i+=
8) {
9270 str += String.fromCharCode((be[i
>>5]
>>>(
24-i%
32)) &
0xff);
9275 // Convert an array of big-endian
32-bit words to a hex string
9276 Crypto.be32sToHex = function(be)
9278 var hex='
0123456789ABCDEF';
9280 for(var i=
0;i
<be.length*
4;i++) {
9281 str += hex.charAt((be[i
>>2]
>>((
3-i%
4)*
8+
4))&
0xF) + hex.charAt((be[i
>>2]
>>((
3-i%
4)*
8))&
0xF);
9286 // Return, in hex, the SHA-
1 hash of a string
9287 Crypto.hexSha1Str = function(str)
9289 return Crypto.be32sToHex(Crypto.sha1Str(str));
9292 // Return the SHA-
1 hash of a string
9293 Crypto.sha1Str = function(str)
9295 return Crypto.sha1(Crypto.strToBe32s(str),str.length);
9298 // Calculate the SHA-
1 hash of an array of blen bytes of big-endian
32-bit words
9299 Crypto.sha1 = function(x,blen)
9301 // Add
32-bit integers, wrapping at
32 bits
9304 var lsw=(a&
0xFFFF)+(b&
0xFFFF);
9305 var msw=(a
>>16)+(b
>>16)+(lsw
>>16);
9306 return (msw<
<16)|(lsw&
0xFFFF);
9308 function AA(a,b,c,d,e)
9311 var lsw=(a&
0xFFFF)+(b&
0xFFFF)+(c&
0xFFFF)+(d&
0xFFFF)+(e&
0xFFFF);
9312 var msw=(a
>>16)+(b
>>16)+(c
>>16)+(d
>>16)+(e
>>16)+(lsw
>>16);
9313 return (msw<
<16)|(lsw&
0xFFFF);
9317 var n=w[j-
3]^w[j-
8]^w[j-
14]^w[j-
16];
9318 return (n
>>>31)|(n<
<1);
9322 x[len
>>5] |=
0x80 << (
24-len%
32);
9323 x[((len+
64>>9)<
<4)+
15]=len;
9324 var w=new Array(
80);
9337 for(var i=
0;i
<x.length;i+=
16) {
9347 t=AA(e,a,d^(b&(c^d)),w[j],k1);
9348 e=d; d=c; c=(b
>>>2)|(b<
<30); b=a; a=t; j++;
9352 t=AA(e,a,d^(b&(c^d)),w[j],k1);
9353 e=d; d=c; c=(b
>>>2)|(b<
<30); b=a; a=t; j++;
9357 t=AA(e,a,b^c^d,w[j],k2);
9358 e=d; d=c; c=(b
>>>2)|(b<
<30); b=a; a=t; j++;
9362 t=AA(e,a,(b&c)|(d&(b|c)),w[j],k3);
9363 e=d; d=c; c=(b
>>>2)|(b<
<30); b=a; a=t; j++;
9367 t=AA(e,a,b^c^d,w[j],k4);
9368 e=d; d=c; c=(b
>>>2)|(b<
<30); b=a; a=t; j++;
9376 return [h0,h1,h2,h3,h4];
9380 //-- RGB colour object
9383 // Construct an RGB colour object from a '#rrggbb', '#rgb' or 'rgb(n,n,n)' string or from separate r,g,b values
9389 if(typeof r ==
"string") {
9390 if(r.substr(
0,
1) ==
"#") {
9392 this.r = parseInt(r.substr(
1,
2),
16)/
255;
9393 this.g = parseInt(r.substr(
3,
2),
16)/
255;
9394 this.b = parseInt(r.substr(
5,
2),
16)/
255;
9396 this.r = parseInt(r.substr(
1,
1),
16)/
15;
9397 this.g = parseInt(r.substr(
2,
1),
16)/
15;
9398 this.b = parseInt(r.substr(
3,
1),
16)/
15;
9401 var rgbPattern = /rgb\s*\(\s*(\d{
1,
3})\s*,\s*(\d{
1,
3})\s*,\s*(\d{
1,
3})\s*\)/;
9402 var c = r.match(rgbPattern);
9404 this.r = parseInt(c[
1],
10)/
255;
9405 this.g = parseInt(c[
2],
10)/
255;
9406 this.b = parseInt(c[
3],
10)/
255;
9417 // Mixes this colour with another in a specified proportion
9418 // c = other colour to mix
9419 // f =
0.
.1 where
0 is this colour and
1 is the new colour
9420 // Returns an RGB object
9421 RGB.prototype.mix = function(c,f)
9423 return new RGB(this.r + (c.r-this.r) * f,this.g + (c.g-this.g) * f,this.b + (c.b-this.b) * f);
9426 // Return an rgb colour as a #rrggbb format hex string
9427 RGB.prototype.toString = function()
9429 return
"#" + (
"0" + Math.floor(this.r.clamp(
0,
1) *
255).toString(
16)).right(
2) +
9430 (
"0" + Math.floor(this.g.clamp(
0,
1) *
255).toString(
16)).right(
2) +
9431 (
"0" + Math.floor(this.b.clamp(
0,
1) *
255).toString(
16)).right(
2);
9435 //-- DOM utilities - many derived from www.quirksmode.org
9438 function drawGradient(place,horiz,locolors,hicolors)
9441 hicolors = locolors;
9442 for(var t=
0; t<=
100; t+=
2) {
9443 var bar = document.createElement(
"div");
9444 place.appendChild(bar);
9445 bar.style.position =
"absolute";
9446 bar.style.left = horiz ? t +
"%" :
0;
9447 bar.style.top = horiz ?
0 : t +
"%";
9448 bar.style.width = horiz ? (
101-t) +
"%" :
"100%";
9449 bar.style.height = horiz ?
"100%" : (
101-t) +
"%";
9450 bar.style.zIndex = -
1;
9451 var p = t/
100*(locolors.length-
1);
9452 bar.style.backgroundColor = hicolors[Math.floor(p)].mix(locolors[Math.ceil(p)],p-Math.floor(p)).toString();
9456 function createTiddlyText(parent,text)
9458 return parent.appendChild(document.createTextNode(text));
9461 function createTiddlyCheckbox(parent,caption,checked,onChange)
9463 var cb = document.createElement(
"input");
9464 cb.setAttribute(
"type",
"checkbox");
9465 cb.onclick = onChange;
9466 parent.appendChild(cb);
9467 cb.checked = checked;
9468 cb.className =
"chkOptionInput";
9470 wikify(caption,parent);
9474 function createTiddlyElement(parent,element,id,className,text,attribs)
9476 var e = document.createElement(element);
9477 if(className != null)
9478 e.className = className;
9480 e.setAttribute(
"id",id);
9482 e.appendChild(document.createTextNode(text));
9484 for(var n in attribs) {
9485 e.setAttribute(n,attribs[n]);
9489 parent.appendChild(e);
9493 function addEvent(obj,type,fn)
9495 if(obj.attachEvent) {
9496 obj['e'+type+fn] = fn;
9497 obj[type+fn] = function(){obj['e'+type+fn](window.event);};
9498 obj.attachEvent('on'+type,obj[type+fn]);
9500 obj.addEventListener(type,fn,false);
9504 function removeEvent(obj,type,fn)
9506 if(obj.detachEvent) {
9507 obj.detachEvent('on'+type,obj[type+fn]);
9508 obj[type+fn] = null;
9510 obj.removeEventListener(type,fn,false);
9514 function addClass(e,className)
9516 var currClass = e.className.split(
" ");
9517 if(currClass.indexOf(className) == -
1)
9518 e.className +=
" " + className;
9521 function removeClass(e,className)
9523 var currClass = e.className.split(
" ");
9524 var i = currClass.indexOf(className);
9526 currClass.splice(i,
1);
9527 i = currClass.indexOf(className);
9529 e.className = currClass.join(
" ");
9532 function hasClass(e,className)
9534 if(e.className && e.className.split(
" ").indexOf(className) != -
1) {
9540 // Find the closest relative with a given property value (property defaults to tagName, relative defaults to parentNode)
9541 function findRelated(e,value,name,relative)
9543 name = name ||
"tagName";
9544 relative = relative ||
"parentNode";
9545 if(name ==
"className") {
9546 while(e && !hasClass(e,value)) {
9550 while(e && e[name] != value) {
9557 // Resolve the target object of an event
9558 function resolveTarget(e)
9563 else if(e.srcElement)
9565 if(obj.nodeType ==
3) // defeat Safari bug
9566 obj = obj.parentNode;
9570 // Prevent an event from bubbling
9571 function stopEvent(e)
9573 var ev = e || window.event;
9574 ev.cancelBubble = true;
9575 if(ev.stopPropagation) ev.stopPropagation();
9579 // Return the content of an element as plain text with no formatting
9580 function getPlainText(e)
9585 else if(e.textContent)
9586 text = e.textContent;
9590 // Get the scroll position for window.scrollTo necessary to scroll a given element into view
9591 function ensureVisible(e)
9593 var posTop = findPosY(e);
9594 var posBot = posTop + e.offsetHeight;
9595 var winTop = findScrollY();
9596 var winHeight = findWindowHeight();
9597 var winBot = winTop + winHeight;
9598 if(posTop < winTop) {
9600 } else if(posBot
> winBot) {
9601 if(e.offsetHeight < winHeight)
9602 return posTop - (winHeight - e.offsetHeight);
9610 // Get the current width of the display window
9611 function findWindowWidth()
9613 return window.innerWidth || document.documentElement.clientWidth;
9616 // Get the current height of the display window
9617 function findWindowHeight()
9619 return window.innerHeight || document.documentElement.clientHeight;
9622 // Get the current horizontal page scroll position
9623 function findScrollX()
9625 return window.scrollX || document.documentElement.scrollLeft;
9628 // Get the current vertical page scroll position
9629 function findScrollY()
9631 return window.scrollY || document.documentElement.scrollTop;
9634 function findPosX(obj)
9637 while(obj.offsetParent) {
9638 curleft += obj.offsetLeft;
9639 obj = obj.offsetParent;
9644 function findPosY(obj)
9647 while(obj.offsetParent) {
9648 curtop += obj.offsetTop;
9649 obj = obj.offsetParent;
9654 // Blur a particular element
9655 function blurElement(e)
9657 if(e && e.focus && e.blur) {
9663 // Create a non-breaking space
9664 function insertSpacer(place)
9666 var e = document.createTextNode(String.fromCharCode(
160));
9668 place.appendChild(e);
9672 // Remove all children of a node
9673 function removeChildren(e)
9675 while(e && e.hasChildNodes())
9676 removeNode(e.firstChild);
9679 // Remove a node and all it's children
9680 function removeNode(e)
9683 e.parentNode.removeChild(e);
9686 // Remove any event handlers or non-primitve custom attributes
9687 function scrubNode(e)
9689 if(!config.browser.isIE)
9691 var att = e.attributes;
9693 for(var t=
0; t
<att.length; t++) {
9694 var n = att[t].name;
9695 if(n !== 'style' && (typeof e[n] === 'function' || (typeof e[n] === 'object' && e[n] != null))) {
9703 var c = e.firstChild;
9710 // Add a stylesheet, replacing any previous custom stylesheet
9711 function setStylesheet(s,id,doc)
9714 id =
"customStyleSheet";
9717 var n = doc.getElementById(id);
9718 if(doc.createStyleSheet) {
9719 // Test for IE's non-standard createStyleSheet method
9721 n.parentNode.removeChild(n);
9722 // This failed without the
9723 doc.getElementsByTagName(
"head")[
0].insertAdjacentHTML(
"beforeEnd",
" <style id='" + id +
"'>" + s +
"</style>");
9726 n.replaceChild(doc.createTextNode(s),n.firstChild);
9728 n = doc.createElement(
"style");
9729 n.type =
"text/css";
9731 n.appendChild(doc.createTextNode(s));
9732 doc.getElementsByTagName(
"head")[
0].appendChild(n);
9737 function removeStyleSheet(id)
9739 var e = document.getElementById(id);
9741 e.parentNode.removeChild(e);
9744 // Force the browser to do a document reflow when needed to workaround browser bugs
9745 function forceReflow()
9747 if(config.browser.isGecko) {
9748 setStylesheet(
"body {top:0px;margin-top:0px;}",
"forceReflow");
9749 setTimeout(function() {setStylesheet(
"",
"forceReflow");},
1);
9753 // Replace the current selection of a textarea or text input and scroll it into view
9754 function replaceSelection(e,text)
9756 if(e.setSelectionRange) {
9757 var oldpos = e.selectionStart;
9758 var isRange = e.selectionEnd
> e.selectionStart;
9759 e.value = e.value.substr(
0,e.selectionStart) + text + e.value.substr(e.selectionEnd);
9760 e.setSelectionRange(isRange ? oldpos : oldpos + text.length,oldpos + text.length);
9761 var linecount = e.value.split('\n').length;
9762 var thisline = e.value.substr(
0,e.selectionStart).split('\n').length-
1;
9763 e.scrollTop = Math.floor((thisline - e.rows /
2) * e.scrollHeight / linecount);
9764 } else if(document.selection) {
9765 var range = document.selection.createRange();
9766 if(range.parentElement() == e) {
9767 var isCollapsed = range.text ==
"";
9770 range.moveStart('character', -text.length);
9777 // Returns the text of the given (text) node, possibly merging subsequent text nodes
9778 function getNodeText(e)
9781 while(e && e.nodeName ==
"#text") {
9788 // Returns true if the element e has a given ancestor element
9789 function isDescendant(e,ancestor)
9800 //-- LoaderBase and SaverBase
9803 function LoaderBase() {}
9805 LoaderBase.prototype.loadTiddler = function(store,node,tiddlers)
9807 var title = this.getTitle(store,node);
9808 if(safeMode && store.isShadowTiddler(title))
9811 var tiddler = store.createTiddler(title);
9812 this.internalizeTiddler(store,tiddler,title,node);
9813 tiddlers.push(tiddler);
9817 LoaderBase.prototype.loadTiddlers = function(store,nodes)
9820 for(var t =
0; t < nodes.length; t++) {
9822 this.loadTiddler(store,nodes[t],tiddlers);
9824 showException(ex,config.messages.tiddlerLoadError.format([this.getTitle(store,nodes[t])]));
9830 function SaverBase() {}
9832 SaverBase.prototype.externalize = function(store)
9835 var tiddlers = store.getTiddlers(
"title");
9836 for(var t =
0; t < tiddlers.length; t++) {
9837 if(!tiddlers[t].doNotSave())
9838 results.push(this.externalizeTiddler(store, tiddlers[t]));
9840 return results.join(
"\n");
9844 //-- TW21Loader (inherits from LoaderBase)
9847 function TW21Loader() {}
9849 TW21Loader.prototype = new LoaderBase();
9851 TW21Loader.prototype.getTitle = function(store,node)
9854 if(node.getAttribute) {
9855 title = node.getAttribute(
"title");
9857 title = node.getAttribute(
"tiddler");
9859 if(!title && node.id) {
9860 var lenPrefix = store.idPrefix.length;
9861 if(node.id.substr(
0,lenPrefix) == store.idPrefix)
9862 title = node.id.substr(lenPrefix);
9867 TW21Loader.prototype.internalizeTiddler = function(store,tiddler,title,node)
9869 var e = node.firstChild;
9871 if(node.getAttribute(
"tiddler")) {
9872 text = getNodeText(e).unescapeLineBreaks();
9874 while(e.nodeName!=
"PRE" && e.nodeName!=
"pre") {
9877 text = e.innerHTML.replace(/\r/mg,
"").htmlDecode();
9879 var modifier = node.getAttribute(
"modifier");
9880 var c = node.getAttribute(
"created");
9881 var m = node.getAttribute(
"modified");
9882 var created = c ? Date.convertFromYYYYMMDDHHMM(c) : version.date;
9883 var modified = m ? Date.convertFromYYYYMMDDHHMM(m) : created;
9884 var tags = node.getAttribute(
"tags");
9886 var attrs = node.attributes;
9887 for(var i = attrs.length-
1; i
>=
0; i--) {
9888 var name = attrs[i].name;
9889 if(attrs[i].specified && !TiddlyWiki.isStandardField(name)) {
9890 fields[name] = attrs[i].value.unescapeLineBreaks();
9893 tiddler.assign(title,text,modifier,modified,tags,created,fields);
9898 //-- TW21Saver (inherits from SaverBase)
9901 function TW21Saver() {}
9903 TW21Saver.prototype = new SaverBase();
9905 TW21Saver.prototype.externalizeTiddler = function(store,tiddler)
9908 var extendedAttributes =
"";
9909 var usePre = config.options.chkUsePreForStorage;
9910 store.forEachField(tiddler,
9911 function(tiddler,fieldName,value) {
9912 // don't store stuff from the temp namespace
9913 if(typeof value !=
"string")
9915 if(!fieldName.match(/^temp\./))
9916 extendedAttributes += ' %
0=
"%1"'.format([fieldName,value.escapeLineBreaks().htmlEncode()]);
9918 var created = tiddler.created;
9919 var modified = tiddler.modified;
9920 var attributes = tiddler.modifier ? '
modifier=
"' + tiddler.modifier.htmlEncode() + '"' :
"";
9921 attributes += (usePre && created == version.date) ?
"" :'
created=
"' + created.convertToYYYYMMDDHHMM() + '"';
9922 attributes += (usePre && modified == created) ?
"" : '
modified=
"' + modified.convertToYYYYMMDDHHMM() +'"';
9923 var tags = tiddler.getTags();
9925 attributes += '
tags=
"' + tags.htmlEncode() + '"';
9926 return ('
<div %
0=
"%1"%
2%
3>%
4</'+'div
>').format([
9927 usePre ?
"title" :
"tiddler",
9928 tiddler.title.htmlEncode(),
9931 usePre ?
"\n<pre>" + tiddler.text.htmlEncode() +
"</pre>\n" : tiddler.text.escapeLineBreaks().htmlEncode()
9934 throw exceptionText(ex,config.messages.tiddlerSaveError.format([tiddler.title]));
9940 <script type=
"text/javascript">
9943 document
.write("<applet style='position:absolute;left:-1px' name='TiddlySaver' code='TiddlySaver.class' archive='TiddlySaver.jar' width='1' height='1'></applet>");
9946 <!--POST-SCRIPT-START-->
9948 <!--POST-SCRIPT-END-->