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=
"200810210428" changecount=
"258">
826 *Cycle plubble peers every
45-
60 mins
827 *Player names seem screwy -- bubba + fur = fur + fur?
828 *Do not allow connection unless Plubble can connect back
829 *Handle error loading a map from a peer that drops out
830 *camera rotation problem on Linux
832 *if this is a new plexus version, offer to toast the cache
833 *Pastry reconnect business (multiple root peers?)
834 *set CPPFLAGS -DTC in src/Makefile's call to src/enet/configure
835 *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)
836 *make updateMappingDiag() also display results for dumpents
837 *Diag panes for sauer and pastry cmds
838 *plexus profile editor in sauer (prepopulate wizard)
839 *combine swing windows
840 *only show diag tools until the first successful connect. Then make it an option.
842 *Monsters (phantom players)
844 *responsibility allocation
845 **maybe have a doc for each world that allocates responsibilities among the peers of a world
846 **when responsibilities or peers join or leave, responsibilities shuffle around
847 **we can store responsibility allocation in PAST
848 **might need a world-ping
849 *Create, destroy, and move objects (GUIDs)
852 **Save hook defined in map.groovy
853 **Save menu choice (which calls map's save hook)
855 **clean up obsolete maps
858 **use crosshairs for aiming when they are visible
859 **register sauer cmds for hit effects based on shooter and/or target
860 **show bio when you shoot someone with an examination gun
864 **Directory entry ownership by digital signatures (peers and groups) -- only an owner can change it
867 **specify box with attachment point, vector, and height
868 **specify driving position with attachment point
869 **driver can switch between player view and vehicle view
870 **physics interacts with box
871 **players can move around inside, fire grenades, etc.
872 *groovy libraries in the cloud
874 *DONE Detect double nat with upnp
875 *DONE Update costume list as thumbs arrive (don't wait for all of them before updating)
876 *DONE constumes getting reset to null (mr fixit) -- seems connected to costume uploading
877 *DONE when costume becomes null, sometimes it doesn't change it
878 *DONE fix case for md2 import
879 *DONE check response values to verify mappings succeed, remove upnp mappings during shutdown, remove cleanup call (make button?)
880 *DONE limbo name not showing up
882 *DONE Map name sometimes shows up as
"none
" when you are on a map
883 *DONE Blank costume no longer switches to Mr Fixit
884 *DONE players can taunt each other
885 *DONE Filter out Thumbs.db and
0 length files
886 *DONE drag selection with left button
887 *DONE cleanup connecting to existing sauer
888 *DONE remove date from directory properties so that dirs are not mistakenly unique
889 *DONE cloud
"power light
"
890 *DONE aiming/edit selection problem
891 *DONE Hidden worlds (not advertised, but part of the cloud)
892 *DONE Finish tutorial menus
893 *DONE [replaced by map.groovy] fix bug in saveGroovyData()
894 *DONE [replaced by map.groovy
& mapProps] persist JSON object as groovy file in map dir for metadata map development
895 *DONE allow turning off recoil for shots
896 *DONE allow user to restart plexus from sauer
897 *DONE cache player updates for your map so follow can teleport immediately
898 **DONE restore world when you restart sauer
899 *DONE pick random port in range
900 *DONE disable Launch Sauer until connected
901 *DONE test costume uploading
902 *DONE broadcast disconnect with shutting down
903 *DONE check java version on startup
904 *DONE remote guild names not showing
905 *DONE incoming cloud change notification to user
906 *DONE show feedback for unsuccessful port back-connection during ip discovery
907 *DONE Missing player problem -- when loading map, players clear out of sauer but not out of plexus id/name tables
908 *DONE Groovy version of build script
909 *DONE JSON format for cloud properties
910 *DONE (fluke?) on find map, couldn't find map with tume id instead of map id
911 *DONE upload/download progress notification
912 *DONE notification of droppage
914 *DONE Clean button in prep gui
915 *DONE options for cleanup on start/completely fresh start
917 *DONE left msg uses nodename instead of player name
918 *DONE switches remote player costume before d/l completed
919 *DONE download into temp dirs and only rename when completed
920 *DONE map member count is always
0
921 *DONE don't subscribe to map until successfully downloaded
923 *DONE Rework existing editing menu, much doesn't apply
924 *DONE decouple camera speed from players speed
925 *DONE Make players start off in Limbo
927 *DONE make LMB detach camera to orbit around the toon
928 *DONE make sauer load models from plexus folder
929 *DONE Global whispers
930 *DONE Map name on load screen
931 *DONE Make push handle regular maps (make manifest that places it in a subdir)
933 *DONE Replace map should broadcast
"switchmap
" pastry cmd
934 *DONE Message dialog: showmessage title text
935 *DONE make cursors load from plexus/dist
936 *DONE separate backup directory
937 *DONE Use UPnP to discover external IP address
938 *DONE delete player on peer disconnect
939 *DONE clean up players on world connection
940 *DONE Show connection count in world menu
941 *DONE Investigate occasional sauer/groovy lag on linux
942 *DONE Headless peer on Plubble
943 *DONE include UPnP to open external port in firewall
944 *DONE add tc_respawn command to put players back at spawn points
945 *DONE don't show original menu on startup
946 *DONE Hide Gun in First Person mode
947 *DONE Disable weapon swapping, disable shooting
948 *DONE Make Limbo map come up by default
949 *DONE Rename wowmode to tcmode
950 *DONE make cursor float above HUD
951 *DONE load limbo in sauer on startup
952 *DONE Show only filename on map thumbnails when loading
953 *DONE HUD add peer count
954 *DONE launch sauer in sep thread while connecting to pastry
955 *DONE add button to reload sauer
956 *DONE move commands from groovy init into a .cfg file
958 **DONE Check out out of PAST disk space errors
959 **DONE Global, shared directory for cloud
962 **DONE Presence topic for total online count
963 **DONE available world listing/selection from gui
966 **DONE Map connection count
975 **materials for sensors (use var to set material id) (callback set on material id)
976 **inventory mgmt (+ gui)
980 **dead monsters drop loot
981 **wizard guns (create monster, move, talk, paralyze, destroy)
982 **saved map chunks
</pre>
984 <div title=
"SiteSubtitle" modifier=
"Zot" created=
"200806171609" modified=
"200806191121" changecount=
"11">
985 <pre>The Killer App of the Future! //[[Reloaded]]//
</pre>
987 <div title=
"SiteTitle" modifier=
"Zot" created=
"200806171608" modified=
"200806191146" changecount=
"9">
988 <pre>[img[P2PMud|docs/images/p2pmud.gif]]
</pre>
990 <div title=
"StartingOut" modifier=
"BillBurdick" created=
"200807142041" modified=
"200809272113" changecount=
"12">
991 <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]]
992 *This repository is an Eclipse project, so you'll probably want Eclipse and the CDT feature for C/C++
993 Once you have the stuff, you need to:
995 *Copy/link sauer_client and scripts/autoexec.cfg into the top level of the sauerbraten directory (in the same directory as server.bat).
996 *Start the groovy command server in Eclipse (using the Cmd Server launcher)
997 *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)
998 !
<html
><span style=
"background: red
">HAVING PROBLEMS?
</span
></html
>
999 HowToReallyCleanAnEclipseProject
1000 EmergencyWorspaceReconstruction
</pre>
1002 <div title=
"StyleSheet" modifier=
"BillBurdick" created=
"200809280501" modified=
"200809280525" changecount=
"6">
1004 .cthulhu { text-decoration: none;
1006 font-family:
"arial
";
1008 font-weight: medium;
1009 background-color: #
282619;
1011 padding-bottom:
3px;
1013 .cmd.cthulhu { text-decoration: none;
1015 font-family:
"arial
";
1017 font-weight: medium;
1019 a.cthulhu:link { text-decoration: none;
1021 font-family:
"arial
";
1025 a.cthulhu:visited { text-decoration: none;
1027 font-family:
"arial
";
1033 <div title=
"ToDo" modifier=
"WRB" created=
"200806171610" modified=
"200807192045" changecount=
"5">
1040 Update docs to refer to new platform (Groovy/Sauerbraten) instead of old (Javascript/Mozilla)
1044 Drag and drop for browser images?
1047 master player -- invisible/intangible and does not transmit coords to the server
1049 puppet -- move according to instructions (default is frozen)
1050 normal -- moves with limitations (movement blockers)
1052 beacons: players can move as long as they remain within the radius of at least one authorized beacon
1053 This allows cell-style hand off
1054 walls: players can pass through authorized walls
1058 moveto id x y z -- sets id's movement target to location
1060 contexthook hook -- when active, execute hook on context change for player1
1061 usecontext on/off -- set whether player1's contexthook is active
1063 authorize id blocker
1064 unauthorize id blocker
1065 puppet id -- makes id move toward its movement target and if it's a player unyoke the controls
1066 unpuppet id -- makes id ignore its movement target and if it's a player, yoke the controls
1071 move -- shoot destination, then target(s)
1072 authorize -- shoot blocker, then target(s)
1073 unauthorize -- shoot blocker, then target(s)
1082 mastercommand cmd
</pre>
1084 <div title=
"Toys" modifier=
"BillBurdick" created=
"200808151458" changecount=
"1">
1085 <pre>launch gun -- sets velocity on opponents
1088 *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
1090 might be doable with only triggers and watcher code
1092 roman candles/wand of fireballs
1093 physics for wall splat
1095 monsters control assigned by DHT
1096 map sharing includes map.groovy file in addition to map.ogz and map.cfg
1098 *Each world gets a branch off the initial starting directory (a dir with a readme)
1099 *Git tree objects are stored in PAST, cached in a local Git repo, and extracted into a local directory
</pre>
1101 <div title=
"UseCases" modifier=
"BillBurdick" created=
"200807300630" modified=
"200807300650" changecount=
"3">
1103 ##player rolls over an item
1104 ##item adds to inventory
1105 ##player presses
"i
" key
1106 ##inventory menu pops up
1107 ##player chooses a different item from menu
1109 ##player chooses drop
1110 ##item leaves inventory and appears on map
1112 ##player approaches door
1113 ##door says
"locked
"
1114 ##player finds key and reapproaches door
1117 ##player enters shop
1118 ##shop keeper says
"welcome to the shop
"
1119 ##player switches to shopping gun
1120 ##items are visible on the counter
1121 ##player shoots item
1122 ##menu pops up and offers buy choice
1123 ##player selects
"buy
" and item gets added to inventory and gold is deducted
1124 ##player shoots shop keeper
1125 ##shop keeper menu pops up and offers to buy items
</pre>
1127 <div title=
"Zot" modifier=
"Zot" created=
"200806171649" changecount=
"1">
1128 <pre>Zot is one of the founding members of [[TEAM CTHULHU|http://teamcthulhu.com]]
</pre>
1131 <!--POST-STOREAREA-->
1132 <!--POST-BODY-START-->
1133 <!--POST-BODY-END-->
1134 <script id=
"jsArea" type=
"text/javascript">
1139 // * This code is designed to be readable but for compactness it only includes brief comments. You can see fuller comments
1140 // in the project Subversion repository at http://svn.tiddlywiki.org/Trunk/core/
1142 // * You should never need to modify this source code directly. TiddlyWiki is carefully designed to allow deep customisation
1143 // without changing the core code. Please consult the development group at http://groups.google.com/group/TiddlyWikiDev
1147 //-- Configuration repository
1150 // Miscellaneous options
1152 numRssItems:
20, // Number of items in the RSS feed
1153 animDuration:
400, // Duration of UI animations in milliseconds
1154 cascadeFast:
20, // Speed for cascade animations (higher == slower)
1155 cascadeSlow:
60, // Speed for EasterEgg cascade animations
1156 cascadeDepth:
5, // Depth of cascade animation
1157 locale:
"en" // W3C language tag
1160 // Hashmap of alternative parsers for the wikifier
1161 config.parsers = {};
1164 config.adaptors = {};
1165 config.defaultAdaptor = null;
1171 config.annotations = {};
1173 // Custom fields to be automatically added to new tiddlers
1174 config.defaultCustomFields = {};
1183 // Options that can be set in the options panel and/or cookies
1185 chkRegExpSearch: false,
1186 chkCaseSensitiveSearch: false,
1187 chkIncrementalSearch: true,
1189 chkSaveBackups: true,
1191 chkGenerateAnRssFeed: false,
1192 chkSaveEmptyTemplate: false,
1193 chkOpenInNewWindow: true,
1194 chkToggleLinks: false,
1195 chkHttpReadOnly: true,
1196 chkForceMinorUpdate: false,
1197 chkConfirmDelete: true,
1198 chkInsertTabs: false,
1199 chkUsePreForStorage: true, // Whether to use
<pre> format for storage
1200 chkDisplayInstrumentation: false,
1201 txtBackupFolder:
"",
1202 txtEditorFocus:
"text",
1203 txtMainTab:
"tabTimeline",
1204 txtMoreTab:
"moreTabAll",
1205 txtMaxEditRows:
"30",
1206 txtFileSystemCharSet:
"UTF-8",
1209 config.optionsDesc = {};
1211 // Default tiddler templates
1212 var DEFAULT_VIEW_TEMPLATE =
1;
1213 var DEFAULT_EDIT_TEMPLATE =
2;
1214 config.tiddlerTemplates = {
1219 // More messages (rather a legacy layout that should not really be like this)
1230 config.backstageTasks = [
"save",
"sync",
"importTask",
"tweak",
"upgrade",
"plugins"];
1232 // Macros; each has a 'handler' member that is inserted later
1236 search: {sizeTextbox:
15},
1262 view: {defaultView:
"text"},
1270 source:
"http://www.tiddlywiki.com/upgrade/",
1271 backupExtension:
"pre.core.upgrade"
1277 // Commands supported by the toolbar macro
1282 saveTiddler: {hideReadOnly: true},
1284 deleteTiddler: {hideReadOnly: true},
1286 references: {type:
"popup"},
1287 jump: {type:
"popup"},
1288 syncing: {type:
"popup"},
1289 fields: {type:
"popup"}
1292 // Browser detection... In a very few places, there's nothing else for it but to know what browser we're using.
1293 config.userAgent = navigator.userAgent.toLowerCase();
1295 isIE: config.userAgent.indexOf(
"msie") != -
1 && config.userAgent.indexOf(
"opera") == -
1,
1296 isGecko: config.userAgent.indexOf(
"gecko") != -
1,
1297 ieVersion: /MSIE (\d.\d)/i.exec(config.userAgent), // config.browser.ieVersion[
1], if it exists, will be the IE version string, eg
"6.0"
1298 isSafari: config.userAgent.indexOf(
"applewebkit") != -
1,
1299 isBadSafari: !((new RegExp(
"[\u0150\u0170]",
"g")).test(
"\u0150")),
1300 firefoxDate: /gecko\/(\d{
8})/i.exec(config.userAgent), // config.browser.firefoxDate[
1], if it exists, will be Firefox release date as
"YYYYMMDD"
1301 isOpera: config.userAgent.indexOf(
"opera") != -
1,
1302 isLinux: config.userAgent.indexOf(
"linux") != -
1,
1303 isUnix: config.userAgent.indexOf(
"x11") != -
1,
1304 isMac: config.userAgent.indexOf(
"mac") != -
1,
1305 isWindows: config.userAgent.indexOf(
"win") != -
1
1308 // Basic regular expressions
1309 config.textPrimitives = {
1310 upperLetter:
"[A-Z\u00c0-\u00de\u0150\u0170]",
1311 lowerLetter:
"[a-z0-9_\\-\u00df-\u00ff\u0151\u0171]",
1312 anyLetter:
"[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]",
1313 anyLetterStrict:
"[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]"
1315 if(config.browser.isBadSafari) {
1316 config.textPrimitives = {
1317 upperLetter:
"[A-Z\u00c0-\u00de]",
1318 lowerLetter:
"[a-z0-9_\\-\u00df-\u00ff]",
1319 anyLetter:
"[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff]",
1320 anyLetterStrict:
"[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff]"
1323 config.textPrimitives.sliceSeparator =
"::";
1324 config.textPrimitives.sectionSeparator =
"##";
1325 config.textPrimitives.urlPattern =
"(?:file|http|https|mailto|ftp|irc|news|data):[^\\s'\"]+(?:/|\\b)
";
1326 config.textPrimitives.unWikiLink = "~
";
1327 config.textPrimitives.wikiLink = "(?:(?:
" + config.textPrimitives.upperLetter + "+
" +
1328 config.textPrimitives.lowerLetter + "+
" +
1329 config.textPrimitives.upperLetter +
1330 config.textPrimitives.anyLetter + "*)|(?:
" +
1331 config.textPrimitives.upperLetter + "{
2,}
" +
1332 config.textPrimitives.lowerLetter + "+))
";
1334 config.textPrimitives.cssLookahead = "(?:(
" + config.textPrimitives.anyLetter + "+)\\(([^\\)\\|\\n]+)(?:\\):))|(?:(
" + config.textPrimitives.anyLetter + "+):([^;\\|\\n]+);)
";
1335 config.textPrimitives.cssLookaheadRegExp = new RegExp(config.textPrimitives.cssLookahead,"mg
");
1337 config.textPrimitives.brackettedLink = "\\[\\[([^\\]]+)\\]\\]
";
1338 config.textPrimitives.titledBrackettedLink = "\\[\\[([^\\[\\]\\|]+)\\|([^\\[\\]\\|]+)\\]\\]
";
1339 config.textPrimitives.tiddlerForcedLinkRegExp = new RegExp("(?:
" + config.textPrimitives.titledBrackettedLink + ")|(?:
" +
1340 config.textPrimitives.brackettedLink + ")|(?:
" +
1341 config.textPrimitives.urlPattern + ")
","mg
");
1342 config.textPrimitives.tiddlerAnyLinkRegExp = new RegExp("(
"+ config.textPrimitives.wikiLink + ")|(?:
" +
1343 config.textPrimitives.titledBrackettedLink + ")|(?:
" +
1344 config.textPrimitives.brackettedLink + ")|(?:
" +
1345 config.textPrimitives.urlPattern + ")
","mg
");
1349 function() {return config.browser.isIE;},
1350 function() {return true;}
1354 downTriangle: ["\u25BC
","\u25BE
"],
1355 downArrow: ["\u2193
","\u2193
"],
1356 bentArrowLeft: ["\u2190
","\u21A9
"],
1357 bentArrowRight: ["\u2192
","\u21AA
"]
1362 //-- Shadow tiddlers
1365 config.shadowTiddlers = {
1371 TabTimeline: '<<timeline>>',
1372 TabAll: '<<list all>>',
1373 TabTags: '<<allTags excludeLists>>',
1374 TabMoreMissing: '<<list missing>>',
1375 TabMoreOrphans: '<<list orphans>>',
1376 TabMoreShadowed: '<<list shadowed>>',
1377 AdvancedOptions: '<<options>>',
1378 PluginManager: '<<plugins>>',
1379 ToolbarCommands: '|~ViewToolbar|closeTiddler closeOthers +editTiddler > fields syncing permalink references jump|\n|~EditToolbar|+saveTiddler -cancelTiddler deleteTiddler|'
1383 //-- Translateable strings
1386 // Strings in "double quotes
" should be translated; strings in 'single quotes' should be left alone
1388 merge(config.options,{
1389 txtUserName: "YourName
"});
1391 merge(config.tasks,{
1392 save: {text: "save
", tooltip: "Save your changes to this TiddlyWiki
", action: saveChanges},
1393 sync: {text: "sync
", tooltip: "Synchronise changes with other TiddlyWiki files and servers
", content: '<<sync>>'},
1394 importTask: {text: "import
", tooltip: "Import tiddlers and plugins from other TiddlyWiki files and servers
", content: '<<importTiddlers>>'},
1395 tweak: {text: "tweak
", tooltip: "Tweak the appearance and behaviour of TiddlyWiki
", content: '<<options>>'},
1396 upgrade: {text: "upgrade
", tooltip: "Upgrade TiddlyWiki core code
", content: '<<upgrade>>'},
1397 plugins: {text: "plugins
", tooltip: "Manage installed plugins
", content: '<<plugins>>'}
1400 // Options that can be set in the options panel and/or cookies
1401 merge(config.optionsDesc,{
1402 txtUserName: "Username for signing your edits
",
1403 chkRegExpSearch: "Enable regular expressions for searches
",
1404 chkCaseSensitiveSearch: "Case-sensitive searching
",
1405 chkIncrementalSearch: "Incremental key-by-key searching
",
1406 chkAnimate: "Enable animations
",
1407 chkSaveBackups: "Keep backup file when saving changes
",
1408 chkAutoSave: "Automatically save changes
",
1409 chkGenerateAnRssFeed: "Generate an RSS feed when saving changes
",
1410 chkSaveEmptyTemplate: "Generate an empty template when saving changes
",
1411 chkOpenInNewWindow: "Open external links in a new window
",
1412 chkToggleLinks: "Clicking on links to open tiddlers causes them to close
",
1413 chkHttpReadOnly: "Hide editing features when viewed over HTTP
",
1414 chkForceMinorUpdate: "Don't update modifier username and date when editing tiddlers
",
1415 chkConfirmDelete: "Require confirmation before deleting tiddlers
",
1416 chkInsertTabs: "Use the tab key to insert tab characters instead of moving between fields
",
1417 txtBackupFolder: "Name of folder to use for backups
",
1418 txtMaxEditRows: "Maximum number of rows in edit boxes
",
1419 txtFileSystemCharSet: "Default character set for saving changes (Firefox/Mozilla only)
"});
1421 merge(config.messages,{
1422 customConfigError: "Problems were encountered loading plugins. See PluginManager for details
",
1423 pluginError: "Error: %
0",
1424 pluginDisabled: "Not executed because disabled via 'systemConfigDisable' tag
",
1425 pluginForced: "Executed because forced via 'systemConfigForce' tag
",
1426 pluginVersionError: "Not executed because this plugin needs a newer version of TiddlyWiki
",
1427 nothingSelected: "Nothing is selected. You must select one or more items first
",
1428 savedSnapshotError: "It appears that this TiddlyWiki has been incorrectly saved. Please see http://www.tiddlywiki.com/#DownloadSoftware for details
",
1429 subtitleUnknown: "(unknown)
",
1430 undefinedTiddlerToolTip: "The tiddler '%
0' doesn't yet exist
",
1431 shadowedTiddlerToolTip: "The tiddler '%
0' doesn't yet exist, but has a pre-defined shadow value
",
1432 tiddlerLinkTooltip: "%
0 - %
1, %
2",
1433 externalLinkTooltip: "External link to %
0",
1434 noTags: "There are no tagged tiddlers
",
1435 notFileUrlError: "You need to save this TiddlyWiki to a file before you can save changes
",
1436 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
",
1437 invalidFileError: "The original file '%
0' does not appear to be a valid TiddlyWiki
",
1438 backupSaved: "Backup saved
",
1439 backupFailed: "Failed to save backup file
",
1440 rssSaved: "RSS feed saved
",
1441 rssFailed: "Failed to save RSS feed file
",
1442 emptySaved: "Empty template saved
",
1443 emptyFailed: "Failed to save empty template file
",
1444 mainSaved: "Main TiddlyWiki file saved
",
1445 mainFailed: "Failed to save main TiddlyWiki file. Your changes have not been saved
",
1446 macroError: "Error in macro <<\%
0>>",
1447 macroErrorDetails: "Error while executing macro <<\%
0>>:\n%
1",
1448 missingMacro: "No such macro
",
1449 overwriteWarning: "A tiddler named '%
0' already exists. Choose OK to overwrite it
",
1450 unsavedChangesWarning: "WARNING! There are unsaved changes in TiddlyWiki\n\nChoose OK to save\nChoose CANCEL to discard
",
1451 confirmExit: "--------------------------------\n\nThere are unsaved changes in TiddlyWiki. If you continue you will lose those changes\n\n--------------------------------
",
1452 saveInstructions: "SaveChanges
",
1453 unsupportedTWFormat: "Unsupported TiddlyWiki format '%
0'
",
1454 tiddlerSaveError: "Error when saving tiddler '%
0'
",
1455 tiddlerLoadError: "Error when loading tiddler '%
0'
",
1456 wrongSaveFormat: "Cannot save with storage format '%
0'. Using standard format for save.
",
1457 invalidFieldName: "Invalid field name %
0",
1458 fieldCannotBeChanged: "Field '%
0' cannot be changed
",
1459 loadingMissingTiddler: "Attempting to retrieve the tiddler '%
0' from the '%
1' server at:\n\n'%
2' in the workspace '%
3'
",
1460 upgradeDone: "The upgrade to version %
0 is now complete\n\nClick 'OK' to reload the newly upgraded TiddlyWiki
"});
1462 merge(config.messages.messageClose,{
1464 tooltip: "close this message area
"});
1466 config.messages.backstage = {
1467 open: {text: "backstage
", tooltip: "Open the backstage area to perform authoring and editing tasks
"},
1468 close: {text: "close
", tooltip: "Close the backstage area
"},
1469 prompt: "backstage:
",
1471 edit: {text: "edit
", tooltip: "Edit the tiddler '%
0'
"}
1475 config.messages.listView = {
1476 tiddlerTooltip: "Click for the full text of this tiddler
",
1477 previewUnavailable: "(preview not available)
"
1480 config.messages.dates.months = ["January
", "February
", "March
", "April
", "May
", "June
", "July
", "August
", "September
", "October
", "November
","December
"];
1481 config.messages.dates.days = ["Sunday
", "Monday
", "Tuesday
", "Wednesday
", "Thursday
", "Friday
", "Saturday
"];
1482 config.messages.dates.shortMonths = ["Jan
", "Feb
", "Mar
", "Apr
", "May
", "Jun
", "Jul
", "Aug
", "Sep
", "Oct
", "Nov
", "Dec
"];
1483 config.messages.dates.shortDays = ["Sun
", "Mon
", "Tue
", "Wed
", "Thu
", "Fri
", "Sat
"];
1484 // suffixes for dates, eg "1st
","2nd
","3rd
"..."30th
","31st
"
1485 config.messages.dates.daySuffixes = ["st
","nd
","rd
","th
","th
","th
","th
","th
","th
","th
",
1486 "th
","th
","th
","th
","th
","th
","th
","th
","th
","th
",
1487 "st
","nd
","rd
","th
","th
","th
","th
","th
","th
","th
",
1489 config.messages.dates.am = "am
";
1490 config.messages.dates.pm = "pm
";
1492 merge(config.messages.tiddlerPopup,{
1495 merge(config.views.wikified.tag,{
1496 labelNoTags: "no tags
",
1497 labelTags: "tags:
",
1498 openTag: "Open tag '%
0'
",
1499 tooltip: "Show tiddlers tagged with '%
0'
",
1500 openAllText: "Open all
",
1501 openAllTooltip: "Open all of these tiddlers
",
1502 popupNone: "No other tiddlers tagged with '%
0'
"});
1504 merge(config.views.wikified,{
1505 defaultText: "The tiddler '%
0' doesn't yet exist. Double-click to create it
",
1506 defaultModifier: "(missing)
",
1507 shadowModifier: "(built-in shadow tiddler)
",
1508 dateFormat: "DD MMM YYYY
",
1509 createdPrompt: "created
"});
1511 merge(config.views.editor,{
1512 tagPrompt: "Type tags separated with spaces, [[use double square brackets]] if necessary, or add existing
",
1513 defaultText: "Type the text for '%
0'
"});
1515 merge(config.views.editor.tagChooser,{
1517 tooltip: "Choose existing tags to add to this tiddler
",
1518 popupNone: "There are no tags defined
",
1519 tagTooltip: "Add the tag '%
0'
"});
1521 merge(config.messages,{
1524 {unit: 1024*1024*1024, template: "%
0\u00a0GB
"},
1525 {unit: 1024*1024, template: "%
0\u00a0MB
"},
1526 {unit: 1024, template: "%
0\u00a0KB
"},
1527 {unit: 1, template: "%
0\u00a0B
"}
1530 merge(config.macros.search,{
1532 prompt: "Search this TiddlyWiki
",
1534 successMsg: "%
0 tiddlers found matching %
1",
1535 failureMsg: "No tiddlers found matching %
0"});
1537 merge(config.macros.tagging,{
1539 labelNotTag: "not tagging
",
1540 tooltip: "List of tiddlers tagged with '%
0'
"});
1542 merge(config.macros.timeline,{
1543 dateFormat: "DD MMM YYYY
"});
1545 merge(config.macros.allTags,{
1546 tooltip: "Show tiddlers tagged with '%
0'
",
1547 noTags: "There are no tagged tiddlers
"});
1549 config.macros.list.all.prompt = "All tiddlers in alphabetical order
";
1550 config.macros.list.missing.prompt = "Tiddlers that have links to them but are not defined
";
1551 config.macros.list.orphans.prompt = "Tiddlers that are not linked to from any other tiddlers
";
1552 config.macros.list.shadowed.prompt = "Tiddlers shadowed with default contents
";
1553 config.macros.list.touched.prompt = "Tiddlers that have been modified locally
";
1555 merge(config.macros.closeAll,{
1557 prompt: "Close all displayed tiddlers (except any that are being edited)
"});
1559 merge(config.macros.permaview,{
1561 prompt: "Link to an URL that retrieves all the currently displayed tiddlers
"});
1563 merge(config.macros.saveChanges,{
1564 label: "save changes
",
1565 prompt: "Save all tiddlers to create a new TiddlyWiki
",
1568 merge(config.macros.newTiddler,{
1569 label: "new tiddler
",
1570 prompt: "Create a new tiddler
",
1571 title: "New Tiddler
",
1574 merge(config.macros.newJournal,{
1575 label: "new journal
",
1576 prompt: "Create a new tiddler from the current date and time
",
1579 merge(config.macros.options,{
1580 wizardTitle: "Tweak advanced options
",
1581 step1Title: "These options are saved in cookies in your browser
",
1582 step1Html: "<input type='hidden' name='markList'
></input><br><input type='checkbox' checked='false' name='chkUnknown'
>Show unknown options
</input>",
1583 unknownDescription: "//(unknown)//
",
1586 {name: 'Option', field: 'option', title: "Option
", type: 'String'},
1587 {name: 'Description', field: 'description', title: "Description
", type: 'WikiText'},
1588 {name: 'Name', field: 'name', title: "Name
", type: 'String'}
1591 {className: 'lowlight', field: 'lowlight'}
1595 merge(config.macros.plugins,{
1596 wizardTitle: "Manage plugins
",
1597 step1Title: "Currently loaded plugins
",
1598 step1Html: "<input type='hidden' name='markList'
></input>", // DO NOT TRANSLATE
1599 skippedText: "(This plugin has not been executed because it was added since startup)
",
1600 noPluginText: "There are no plugins installed
",
1601 confirmDeleteText: "Are you sure you want to delete these plugins:\n\n%
0",
1602 removeLabel: "remove systemConfig tag
",
1603 removePrompt: "Remove systemConfig tag
",
1604 deleteLabel: "delete
",
1605 deletePrompt: "Delete these tiddlers forever
",
1608 {name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
1609 {name: 'Tiddler', field: 'tiddler', title: "Tiddler
", type: 'Tiddler'},
1610 {name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size
", type: 'Size'},
1611 {name: 'Forced', field: 'forced', title: "Forced
", tag: 'systemConfigForce', type: 'TagCheckbox'},
1612 {name: 'Disabled', field: 'disabled', title: "Disabled
", tag: 'systemConfigDisable', type: 'TagCheckbox'},
1613 {name: 'Executed', field: 'executed', title: "Loaded
", type: 'Boolean', trueText: "Yes
", falseText: "No
"},
1614 {name: 'Startup Time', field: 'startupTime', title: "Startup Time
", type: 'String'},
1615 {name: 'Error', field: 'error', title: "Status
", type: 'Boolean', trueText: "Error
", falseText: "OK
"},
1616 {name: 'Log', field: 'log', title: "Log
", type: 'StringList'}
1619 {className: 'error', field: 'error'},
1620 {className: 'warning', field: 'warning'}
1624 merge(config.macros.toolbar,{
1626 morePrompt: "Reveal further commands
"
1629 merge(config.macros.refreshDisplay,{
1631 prompt: "Redraw the entire TiddlyWiki display
"
1634 merge(config.macros.importTiddlers,{
1635 readOnlyWarning: "You cannot import into a read-only TiddlyWiki file. Try opening it from a file:// URL
",
1636 wizardTitle: "Import tiddlers from another file or server
",
1637 step1Title: "Step
1: Locate the server or TiddlyWiki file
",
1638 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>",
1640 openPrompt: "Open the connection to this file or server
",
1641 openError: "There were problems fetching the tiddlywiki file
",
1642 statusOpenHost: "Opening the host
",
1643 statusGetWorkspaceList: "Getting the list of available workspaces
",
1644 step2Title: "Step
2: Choose the workspace
",
1645 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>",
1646 cancelLabel: "cancel
",
1647 cancelPrompt: "Cancel this import
",
1648 statusOpenWorkspace: "Opening the workspace
",
1649 statusGetTiddlerList: "Getting the list of available tiddlers
",
1650 errorGettingTiddlerList: "Error getting list of tiddlers, click Cancel to try again
",
1651 step3Title: "Step
3: Choose the tiddlers to import
",
1652 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'
>",
1653 importLabel: "import
",
1654 importPrompt: "Import these tiddlers
",
1655 confirmOverwriteText: "Are you sure you want to overwrite these tiddlers:\n\n%
0",
1656 step4Title: "Step
4: Importing %
0 tiddler(s)
",
1657 step4Html: "<input type='hidden' name='markReport'
></input>", // DO NOT TRANSLATE
1659 donePrompt: "Close this wizard
",
1660 statusDoingImport: "Importing tiddlers
",
1661 statusDoneImport: "All tiddlers imported
",
1662 systemServerNamePattern: "%
2 on %
1",
1663 systemServerNamePatternNoWorkspace: "%
1",
1664 confirmOverwriteSaveTiddler: "The tiddler '%
0' already exists. Click 'OK' to overwrite it with the details of this server, or 'Cancel' to leave it unchanged
",
1665 serverSaveTemplate: "|''Type:''|%
0|\n|''URL:''|%
1|\n|''Workspace:''|%
2|\n\nThis tiddler was automatically created to record the details of this server
",
1666 serverSaveModifier: "(System)
",
1669 {name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
1670 {name: 'Tiddler', field: 'tiddler', title: "Tiddler
", type: 'Tiddler'},
1671 {name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size
", type: 'Size'},
1672 {name: 'Tags', field: 'tags', title: "Tags
", type: 'Tags'}
1678 merge(config.macros.upgrade,{
1679 wizardTitle: "Upgrade TiddlyWiki core code
",
1680 step1Title: "Update or repair this TiddlyWiki to the latest release
",
1681 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>",
1682 errorCantUpgrade: "Unable to upgrade this TiddlyWiki. You can only perform upgrades on TiddlyWiki files stored locally
",
1683 errorNotSaved: "You must save changes before you can perform an upgrade
",
1684 step2Title: "Confirm the upgrade details
",
1685 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
",
1686 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
",
1687 step2Html_upgrade: "You are about to upgrade to TiddlyWiki version %
0 from %
1",
1688 upgradeLabel: "upgrade
",
1689 upgradePrompt: "Prepare for the upgrade process
",
1690 statusPreparingBackup: "Preparing backup
",
1691 statusSavingBackup: "Saving backup file
",
1692 errorSavingBackup: "There was a problem saving the backup file
",
1693 statusLoadingCore: "Loading core code
",
1694 errorLoadingCore: "Error loading the core code
",
1695 errorCoreFormat: "Error with the new core code
",
1696 statusSavingCore: "Saving the new core code
",
1697 statusReloadingCore: "Reloading the new core code
",
1698 startLabel: "start
",
1699 startPrompt: "Start the upgrade process
",
1700 cancelLabel: "cancel
",
1701 cancelPrompt: "Cancel the upgrade process
",
1702 step3Title: "Upgrade cancelled
",
1703 step3Html: "You have cancelled the upgrade process
"
1706 merge(config.macros.sync,{
1709 {name: 'Selected', field: 'selected', rowName: 'title', type: 'Selector'},
1710 {name: 'Tiddler', field: 'tiddler', title: "Tiddler
", type: 'Tiddler'},
1711 {name: 'Server Type', field: 'serverType', title: "Server type
", type: 'String'},
1712 {name: 'Server Host', field: 'serverHost', title: "Server host
", type: 'String'},
1713 {name: 'Server Workspace', field: 'serverWorkspace', title: "Server workspace
", type: 'String'},
1714 {name: 'Status', field: 'status', title: "Synchronisation status
", type: 'String'},
1715 {name: 'Server URL', field: 'serverUrl', title: "Server URL
", text: "View
", type: 'Link'}
1720 {caption: "Sync these tiddlers
", name: 'sync'}
1722 wizardTitle: "Synchronize with external servers and files
",
1723 step1Title: "Choose the tiddlers you want to synchronize
",
1724 step1Html: "<input type='hidden' name='markList'
></input>", // DO NOT TRANSLATE
1726 syncPrompt: "Sync these tiddlers
",
1727 hasChanged: "Changed while unplugged
",
1728 hasNotChanged: "Unchanged while unplugged
",
1730 none: {text: "...
", display:null, className:'notChanged'},
1731 changedServer: {text: "Changed on server
", display:null, className:'changedServer'},
1732 changedLocally: {text: "Changed while unplugged
", display:null, className:'changedLocally'},
1733 changedBoth: {text: "Changed while unplugged and on server
", display:null, className:'changedBoth'},
1734 notFound: {text: "Not found on server
", display:null, className:'notFound'},
1735 putToServer: {text: "Saved update on server
", display:null, className:'putToServer'},
1736 gotFromServer: {text: "Retrieved update from server
", display:null, className:'gotFromServer'}
1740 merge(config.macros.annotations,{
1743 merge(config.commands.closeTiddler,{
1745 tooltip: "Close this tiddler
"});
1747 merge(config.commands.closeOthers,{
1748 text: "close others
",
1749 tooltip: "Close all other tiddlers
"});
1751 merge(config.commands.editTiddler,{
1753 tooltip: "Edit this tiddler
",
1754 readOnlyText: "view
",
1755 readOnlyTooltip: "View the source of this tiddler
"});
1757 merge(config.commands.saveTiddler,{
1759 tooltip: "Save changes to this tiddler
"});
1761 merge(config.commands.cancelTiddler,{
1763 tooltip: "Undo changes to this tiddler
",
1764 warning: "Are you sure you want to abandon your changes to '%
0'?
",
1765 readOnlyText: "done
",
1766 readOnlyTooltip: "View this tiddler normally
"});
1768 merge(config.commands.deleteTiddler,{
1770 tooltip: "Delete this tiddler
",
1771 warning: "Are you sure you want to delete '%
0'?
"});
1773 merge(config.commands.permalink,{
1775 tooltip: "Permalink for this tiddler
"});
1777 merge(config.commands.references,{
1779 tooltip: "Show tiddlers that link to this one
",
1780 popupNone: "No references
"});
1782 merge(config.commands.jump,{
1784 tooltip: "Jump to another open tiddler
"});
1786 merge(config.commands.syncing,{
1788 tooltip: "Control synchronisation of this tiddler with a server or external file
",
1789 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
1790 notCurrentlySyncing: "Not currently syncing
",
1791 captionUnSync: "Stop synchronising this tiddler
",
1792 chooseServer: "Synchronise this tiddler with another server:
",
1793 currServerMarker: "\u25cf
",
1794 notCurrServerMarker: " "});
1796 merge(config.commands.fields,{
1798 tooltip: "Show the extended fields of this tiddler
",
1799 emptyText: "There are no extended fields for this tiddler
",
1802 {name: 'Field', field: 'field', title: "Field
", type: 'String'},
1803 {name: 'Value', field: 'value', title: "Value
", type: 'String'}
1810 merge(config.shadowTiddlers,{
1811 DefaultTiddlers: "[[GettingStarted]]
",
1812 MainMenu: "[[GettingStarted]]
",
1813 SiteTitle: "My TiddlyWiki
",
1814 SiteSubtitle: "a reusable non-linear personal web notebook
",
1815 SiteUrl: "http://www.tiddlywiki.com/
",
1816 SideBarOptions: '<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal "DD MMM YYYY
" "journal
">><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel "options \u00bb
" "Change TiddlyWiki advanced options
">>',
1817 SideBarTabs: '<<tabs txtMainTab "Timeline
" "Timeline
" TabTimeline "All
" "All tiddlers
" TabAll "Tags
" "All tags
" TabTags "More
" "More lists
" TabMore>>',
1818 TabMore: '<<tabs txtMoreTab "Missing
" "Missing tiddlers
" TabMoreMissing "Orphans
" "Orphaned tiddlers
" TabMoreOrphans "Shadowed
" "Shadowed tiddlers
" TabMoreShadowed>>'
1821 merge(config.annotations,{
1822 AdvancedOptions: "This shadow tiddler provides access to several advanced options
",
1823 ColorPalette: "These values in this shadow tiddler determine the colour scheme of the ~TiddlyWiki user interface
",
1824 DefaultTiddlers: "The tiddlers listed in this shadow tiddler will be automatically displayed when ~TiddlyWiki starts up
",
1825 EditTemplate: "The HTML template in this shadow tiddler determines how tiddlers look while they are being edited
",
1826 GettingStarted: "This shadow tiddler provides basic usage instructions
",
1827 ImportTiddlers: "This shadow tiddler provides access to importing tiddlers
",
1828 MainMenu: "This shadow tiddler is used as the contents of the main menu in the left-hand column of the screen
",
1829 MarkupPreHead: "This tiddler is inserted at the top of the
<head> section of the TiddlyWiki HTML file
",
1830 MarkupPostHead: "This tiddler is inserted at the bottom of the
<head> section of the TiddlyWiki HTML file
",
1831 MarkupPreBody: "This tiddler is inserted at the top of the
<body> section of the TiddlyWiki HTML file
",
1832 MarkupPostBody: "This tiddler is inserted at the end of the
<body> section of the TiddlyWiki HTML file immediately after the script block
",
1833 OptionsPanel: "This shadow tiddler is used as the contents of the options panel slider in the right-hand sidebar
",
1834 PageTemplate: "The HTML template in this shadow tiddler determines the overall ~TiddlyWiki layout
",
1835 PluginManager: "This shadow tiddler provides access to the plugin manager
",
1836 SideBarOptions: "This shadow tiddler is used as the contents of the option panel in the right-hand sidebar
",
1837 SideBarTabs: "This shadow tiddler is used as the contents of the tabs panel in the right-hand sidebar
",
1838 SiteSubtitle: "This shadow tiddler is used as the second part of the page title
",
1839 SiteTitle: "This shadow tiddler is used as the first part of the page title
",
1840 SiteUrl: "This shadow tiddler should be set to the full target URL for publication
",
1841 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
",
1842 StyleSheet: "This tiddler can contain custom CSS definitions
",
1843 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
",
1844 StyleSheetLocale: "This shadow tiddler contains CSS definitions related to the translation locale
",
1845 StyleSheetPrint: "This shadow tiddler contains CSS definitions for printing
",
1846 TabAll: "This shadow tiddler contains the contents of the 'All' tab in the right-hand sidebar
",
1847 TabMore: "This shadow tiddler contains the contents of the 'More' tab in the right-hand sidebar
",
1848 TabMoreMissing: "This shadow tiddler contains the contents of the 'Missing' tab in the right-hand sidebar
",
1849 TabMoreOrphans: "This shadow tiddler contains the contents of the 'Orphans' tab in the right-hand sidebar
",
1850 TabMoreShadowed: "This shadow tiddler contains the contents of the 'Shadowed' tab in the right-hand sidebar
",
1851 TabTags: "This shadow tiddler contains the contents of the 'Tags' tab in the right-hand sidebar
",
1852 TabTimeline: "This shadow tiddler contains the contents of the 'Timeline' tab in the right-hand sidebar
",
1853 ToolbarCommands: "This shadow tiddler determines which commands are shown in tiddler toolbars
",
1854 ViewTemplate: "The HTML template in this shadow tiddler determines how tiddlers look
"
1861 var params = null; // Command line parameters
1862 var store = null; // TiddlyWiki storage
1863 var story = null; // Main story
1864 var formatter = null; // Default formatters for the wikifier
1865 var anim = typeof Animator == "function
" ? new Animator() : null; // Animation engine
1866 var readOnly = false; // Whether we're in readonly mode
1867 var highlightHack = null; // Embarrassing hack department...
1868 var hadConfirmExit = false; // Don't warn more than once
1869 var safeMode = false; // Disable all plugins and cookies
1870 var showBackstage; // Whether to include the backstage area
1871 var installedPlugins = []; // Information filled in when plugins are executed
1872 var startingUp = false; // Whether we're in the process of starting up
1873 var pluginInfo,tiddler; // Used to pass information to plugins in loadPlugins()
1875 // Whether to use the JavaSaver applet
1876 var useJavaSaver = (config.browser.isSafari || config.browser.isOpera) && (document.location.toString().substr(0,4) != "http
");
1881 var t10,t9,t8,t7,t6,t5,t4,t3,t2,t1,t0 = new Date();
1883 window.onbeforeunload = function(e) {if(window.confirmExit) return confirmExit();};
1884 params = getParameters();
1886 params = params.parseParams("open
",null,false);
1887 store = new TiddlyWiki();
1888 invokeParamifier(params,"oninit
");
1889 story = new Story("tiddlerDisplay
","tiddler
");
1890 addEvent(document,"click
",Popup.onDocumentClick);
1892 loadOptionsCookie();
1893 for(var s=0; s<config.notifyTiddlers.length; s++)
1894 store.addNotification(config.notifyTiddlers[s].name,config.notifyTiddlers[s].notify);
1896 loadShadowTiddlers();
1898 store.loadFromDiv("storeArea
","store
",true);
1900 invokeParamifier(params,"onload
");
1902 readOnly = (window.location.protocol == "file:
") ? false : config.options.chkHttpReadOnly;
1903 var pluginProblem = loadPlugins();
1905 formatter = new Formatter(config.formatters);
1906 invokeParamifier(params,"onconfig
");
1907 story.switchTheme(config.options.txtTheme);
1908 showBackstage = !readOnly;
1916 story.displayTiddler(null,"PluginManager
");
1917 displayMessage(config.messages.customConfigError);
1919 for(var m in config.macros) {
1920 if(config.macros[m].init)
1921 config.macros[m].init();
1927 if(config.options.chkDisplayInstrumentation) {
1928 displayMessage("LoadShadows
" + (t2-t1) + " ms
");
1929 displayMessage("LoadFromDiv
" + (t3-t2) + " ms
");
1930 displayMessage("LoadPlugins
" + (t5-t4) + " ms
");
1931 displayMessage("Notify
" + (t7-t6) + " ms
");
1932 displayMessage("Restart
" + (t8-t7) + " ms
");
1933 displayMessage("Macro init
" + (t9-t8) + " ms
");
1934 displayMessage("Total:
" + (t10-t0) + " ms
");
1942 invokeParamifier(params,"onstart
");
1943 if(story.isEmpty()) {
1944 story.displayDefaultTiddlers();
1946 window.scrollTo(0,0);
1951 var s = document.getElementById("saveTest
");
1952 if(s.hasChildNodes())
1953 alert(config.messages.savedSnapshotError);
1954 s.appendChild(document.createTextNode("savetest
"));
1957 function loadShadowTiddlers()
1959 var shadows = new TiddlyWiki();
1960 shadows.loadFromDiv("shadowArea
","shadows
",true);
1961 shadows.forEachTiddler(function(title,tiddler){config.shadowTiddlers[title] = tiddler.text;});
1965 function loadPlugins()
1969 var tiddlers = store.getTaggedTiddlers("systemConfig
");
1973 var nPlugins = tiddlers.length;
1974 installedPlugins = [];
1975 for(var i=0; i<nPlugins; i++) {
1976 var p = getPluginInfo(tiddlers[i]);
1977 installedPlugins[i] = p;
1985 var visit = function(p) {
1989 var reqs = p.Requires;
1991 reqs = reqs.readBracketedList();
1992 for(var i=0; i<reqs.length; i++)
1993 visit(map[reqs[i]]);
1997 for(i=0; i<nPlugins; i++)
1998 visit(installedPlugins[i]);
1999 for(i=0; i<toLoad.length; i++) {
2002 tiddler = p.tiddler;
2003 if(isPluginExecutable(p)) {
2004 if(isPluginEnabled(p)) {
2006 var startTime = new Date();
2009 window.eval(tiddler.text);
2012 p.log.push(config.messages.pluginError.format([exceptionText(ex)]));
2015 pluginInfo.startupTime = String((new Date()) - startTime) + "ms
";
2023 return nLoaded != nPlugins;
2026 function getPluginInfo(tiddler)
2028 var p = store.getTiddlerSlices(tiddler.title,["Name
","Description
","Version
","Requires
","CoreVersion
","Date
","Source
","Author
","License
","Browsers
"]);
2029 p.tiddler = tiddler;
2030 p.title = tiddler.title;
2035 // Check that a particular plugin is valid for execution
2036 function isPluginExecutable(plugin)
2038 if(plugin.tiddler.isTagged("systemConfigForce
")) {
2039 plugin.log.push(config.messages.pluginForced);
2042 if(plugin["CoreVersion
"]) {
2043 var coreVersion = plugin["CoreVersion
"].split(".
");
2044 var w = parseInt(coreVersion[0],10) - version.major;
2045 if(w == 0 && coreVersion[1])
2046 w = parseInt(coreVersion[1],10) - version.minor;
2047 if(w == 0 && coreVersion[2])
2048 w = parseInt(coreVersion[2],10) - version.revision;
2050 plugin.log.push(config.messages.pluginVersionError);
2057 function isPluginEnabled(plugin)
2059 if(plugin.tiddler.isTagged("systemConfigDisable
")) {
2060 plugin.log.push(config.messages.pluginDisabled);
2066 function invokeMacro(place,macro,params,wikifier,tiddler)
2069 var m = config.macros[macro];
2071 m.handler(place,macro,params.readMacroParams(),wikifier,params,tiddler);
2073 createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,config.messages.missingMacro]));
2075 createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,ex.toString()]));
2083 function getParameters()
2086 if(window.location.hash) {
2087 p = decodeURIComponent(window.location.hash.substr(1));
2088 if(config.browser.firefoxDate != null && config.browser.firefoxDate[1] < "20051111")
2089 p = convertUTF8ToUnicode(p);
2094 function invokeParamifier(params,handler)
2096 if(!params || params.length == undefined || params.length <= 1)
2098 for(var t=1; t<params.length; t++) {
2099 var p = config.paramifiers[params[t].name];
2100 if(p && p[handler] instanceof Function)
2101 p[handler](params[t].value);
2105 config.paramifiers = {};
2107 config.paramifiers.start = {
2108 oninit: function(v) {
2109 safeMode = v.toLowerCase() == "safe
";
2113 config.paramifiers.open = {
2114 onstart: function(v) {
2115 if(!readOnly || store.tiddlerExists(v) || store.isShadowTiddler(v))
2116 story.displayTiddler("bottom
",v,null,false,null);
2120 config.paramifiers.story = {
2121 onstart: function(v) {
2122 var list = store.getTiddlerText(v,"").parseParams("open
",null,false);
2123 invokeParamifier(list,"onstart
");
2127 config.paramifiers.search = {
2128 onstart: function(v) {
2129 story.search(v,false,false);
2133 config.paramifiers.searchRegExp = {
2134 onstart: function(v) {
2135 story.prototype.search(v,false,true);
2139 config.paramifiers.tag = {
2140 onstart: function(v) {
2141 story.displayTiddlers(null,store.filterTiddlers("[tag[
"+v+"]]
"),null,false,null);
2145 config.paramifiers.newTiddler = {
2146 onstart: function(v) {
2148 story.displayTiddler(null,v,DEFAULT_EDIT_TEMPLATE);
2149 story.focusTiddler(v,"text
");
2154 config.paramifiers.newJournal = {
2155 onstart: function(v) {
2157 var now = new Date();
2158 var title = now.formatString(v.trim());
2159 story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
2160 story.focusTiddler(title,"text
");
2165 config.paramifiers.readOnly = {
2166 onconfig: function(v) {
2167 var p = v.toLowerCase();
2168 readOnly = p == "yes
" ? true : (p == "no
" ? false : readOnly);
2172 config.paramifiers.theme = {
2173 onconfig: function(v) {
2174 story.switchTheme(v);
2178 config.paramifiers.upgrade = {
2179 onstart: function(v) {
2184 config.paramifiers.recent= {
2185 onstart: function(v) {
2187 var tiddlers=store.getTiddlers("modified
","excludeLists
").reverse();
2188 for(var i=0; i<v && i<tiddlers.length; i++)
2189 titles.push(tiddlers[i].title);
2190 story.displayTiddlers(null,titles);
2194 config.paramifiers.filter = {
2195 onstart: function(v) {
2196 story.displayTiddlers(null,store.filterTiddlers(v),null,false);
2201 //-- Formatter helpers
2204 function Formatter(formatters)
2206 this.formatters = [];
2208 for(var n=0; n<formatters.length; n++) {
2209 pattern.push("(
" + formatters[n].match + ")
");
2210 this.formatters.push(formatters[n]);
2212 this.formatterRegExp = new RegExp(pattern.join("|
"),"mg
");
2215 config.formatterHelpers = {
2217 createElementAndWikify: function(w)
2219 w.subWikifyTerm(createTiddlyElement(w.output,this.element),this.termRegExp);
2222 inlineCssHelper: function(w)
2225 config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
2226 var lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
2227 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
2229 if(lookaheadMatch[1]) {
2230 s = lookaheadMatch[1].unDash();
2231 v = lookaheadMatch[2];
2233 s = lookaheadMatch[3].unDash();
2234 v = lookaheadMatch[4];
2237 s = "backgroundColor
";
2238 styles.push({style: s, value: v});
2239 w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
2240 config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
2241 lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
2246 applyCssHelper: function(e,styles)
2248 for(var t=0; t< styles.length; t++) {
2250 e.style[styles[t].style] = styles[t].value;
2256 enclosedTextHelper: function(w)
2258 this.lookaheadRegExp.lastIndex = w.matchStart;
2259 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2260 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
2261 var text = lookaheadMatch[1];
2262 if(config.browser.isIE)
2263 text = text.replace(/\n/g,"\r
");
2264 createTiddlyElement(w.output,this.element,null,null,text);
2265 w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
2269 isExternalLink: function(link)
2271 if(store.tiddlerExists(link) || store.isShadowTiddler(link)) {
2274 var urlRegExp = new RegExp(config.textPrimitives.urlPattern,"mg
");
2275 if(urlRegExp.exec(link)) {
2278 if(link.indexOf(".
")!=-1 || link.indexOf("\\
")!=-1 || link.indexOf("/
")!=-1 || link.indexOf("#
")!=-1) {
2287 //-- Standard formatters
2290 config.formatters = [
2293 match: "^\\|(?:[^\\n]*)\\|(?:[fhck]?)$
",
2294 lookaheadRegExp: /^\|([^\n]*)\|([fhck]?)$/mg,
2295 rowTermRegExp: /(\|(?:[fhck]?)$\n?)/mg,
2296 cellRegExp: /(?:\|([^\n\|]*)\|)|(\|[fhck]?$\n?)/mg,
2297 cellTermRegExp: /((?:\x20*)\|)/mg,
2298 rowTypes: {"c
":"caption
", "h
":"thead
", "":"tbody
", "f
":"tfoot
"},
2299 handler: function(w)
2301 var table = createTiddlyElement(w.output,"table
",null,"twtable
");
2302 var prevColumns = [];
2303 var currRowType = null;
2306 w.nextMatch = w.matchStart;
2307 this.lookaheadRegExp.lastIndex = w.nextMatch;
2308 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2309 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
2310 var nextRowType = lookaheadMatch[2];
2311 if(nextRowType == "k
") {
2312 table.className = lookaheadMatch[1];
2313 w.nextMatch += lookaheadMatch[0].length+1;
2315 if(nextRowType != currRowType) {
2316 rowContainer = createTiddlyElement(table,this.rowTypes[nextRowType]);
2317 currRowType = nextRowType;
2319 if(currRowType == "c
") {
2322 if(rowContainer != table.firstChild)
2323 table.insertBefore(rowContainer,table.firstChild);
2324 rowContainer.setAttribute("align
",rowCount == 0?"top
":"bottom
");
2325 w.subWikifyTerm(rowContainer,this.rowTermRegExp);
2327 var theRow = createTiddlyElement(rowContainer,"tr
",null,(rowCount&1)?"oddRow
":"evenRow
");
2328 theRow.onmouseover = function() {addClass(this,"hoverRow
");};
2329 theRow.onmouseout = function() {removeClass(this,"hoverRow
");};
2330 this.rowHandler(w,theRow,prevColumns);
2334 this.lookaheadRegExp.lastIndex = w.nextMatch;
2335 lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2338 rowHandler: function(w,e,prevColumns)
2341 var colSpanCount = 1;
2342 var prevCell = null;
2343 this.cellRegExp.lastIndex = w.nextMatch;
2344 var cellMatch = this.cellRegExp.exec(w.source);
2345 while(cellMatch && cellMatch.index == w.nextMatch) {
2346 if(cellMatch[1] == "~
") {
2348 var last = prevColumns[col];
2350 last.rowSpanCount++;
2351 last.element.setAttribute("rowspan
",last.rowSpanCount);
2352 last.element.setAttribute("rowSpan
",last.rowSpanCount); // Needed for IE
2353 last.element.valign = "center
";
2355 w.nextMatch = this.cellRegExp.lastIndex-1;
2356 } else if(cellMatch[1] == ">") {
2359 w.nextMatch = this.cellRegExp.lastIndex-1;
2360 } else if(cellMatch[2]) {
2362 if(prevCell && colSpanCount > 1) {
2363 prevCell.setAttribute("colspan
",colSpanCount);
2364 prevCell.setAttribute("colSpan
",colSpanCount); // Needed for IE
2366 w.nextMatch = this.cellRegExp.lastIndex;
2371 var styles = config.formatterHelpers.inlineCssHelper(w);
2372 var spaceLeft = false;
2373 var chr = w.source.substr(w.nextMatch,1);
2377 chr = w.source.substr(w.nextMatch,1);
2381 cell = createTiddlyElement(e,"th
");
2384 cell = createTiddlyElement(e,"td
");
2387 prevColumns[col] = {rowSpanCount:1,element:cell};
2388 if(colSpanCount > 1) {
2389 cell.setAttribute("colspan
",colSpanCount);
2390 cell.setAttribute("colSpan
",colSpanCount); // Needed for IE
2393 config.formatterHelpers.applyCssHelper(cell,styles);
2394 w.subWikifyTerm(cell,this.cellTermRegExp);
2395 if(w.matchText.substr(w.matchText.length-2,1) == " ") // spaceRight
2396 cell.align = spaceLeft ? "center
" : "left
";
2398 cell.align = "right
";
2402 this.cellRegExp.lastIndex = w.nextMatch;
2403 cellMatch = this.cellRegExp.exec(w.source);
2411 termRegExp: /(\n)/mg,
2412 handler: function(w)
2414 w.subWikifyTerm(createTiddlyElement(w.output,"h
" + w.matchLength),this.termRegExp);
2420 match: "^(?:[\\*#;:]+)
",
2421 lookaheadRegExp: /^(?:(?:(\*)|(#)|(;)|(:))+)/mg,
2422 termRegExp: /(\n)/mg,
2423 handler: function(w)
2425 var stack = [w.output];
2426 var currLevel = 0, currType = null;
2427 var listLevel, listType, itemType, baseType;
2428 w.nextMatch = w.matchStart;
2429 this.lookaheadRegExp.lastIndex = w.nextMatch;
2430 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2431 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
2432 if(lookaheadMatch[1]) {
2435 } else if(lookaheadMatch[2]) {
2438 } else if(lookaheadMatch[3]) {
2441 } else if(lookaheadMatch[4]) {
2446 baseType = listType;
2447 listLevel = lookaheadMatch[0].length;
2448 w.nextMatch += lookaheadMatch[0].length;
2450 if(listLevel > currLevel) {
2451 for(t=currLevel; t<listLevel; t++) {
2452 var target = (currLevel == 0) ? stack[stack.length-1] : stack[stack.length-1].lastChild;
2453 stack.push(createTiddlyElement(target,listType));
2455 } else if(listType!=baseType && listLevel==1) {
2456 w.nextMatch -= lookaheadMatch[0].length;
2458 } else if(listLevel < currLevel) {
2459 for(t=currLevel; t>listLevel; t--)
2461 } else if(listLevel == currLevel && listType != currType) {
2463 stack.push(createTiddlyElement(stack[stack.length-1].lastChild,listType));
2465 currLevel = listLevel;
2466 currType = listType;
2467 var e = createTiddlyElement(stack[stack.length-1],itemType);
2468 w.subWikifyTerm(e,this.termRegExp);
2469 this.lookaheadRegExp.lastIndex = w.nextMatch;
2470 lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2476 name: "quoteByBlock
",
2478 termRegExp: /(^<<<(\n|$))/mg,
2479 element: "blockquote
",
2480 handler: config.formatterHelpers.createElementAndWikify
2484 name: "quoteByLine
",
2486 lookaheadRegExp: /^>+/mg,
2487 termRegExp: /(\n)/mg,
2488 element: "blockquote
",
2489 handler: function(w)
2491 var stack = [w.output];
2493 var newLevel = w.matchLength;
2496 if(newLevel > currLevel) {
2497 for(t=currLevel; t<newLevel; t++)
2498 stack.push(createTiddlyElement(stack[stack.length-1],this.element));
2499 } else if(newLevel < currLevel) {
2500 for(t=currLevel; t>newLevel; t--)
2503 currLevel = newLevel;
2504 w.subWikifyTerm(stack[stack.length-1],this.termRegExp);
2505 createTiddlyElement(stack[stack.length-1],"br
");
2506 this.lookaheadRegExp.lastIndex = w.nextMatch;
2507 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2508 var matched = lookaheadMatch && lookaheadMatch.index == w.nextMatch;
2510 newLevel = lookaheadMatch[0].length;
2511 w.nextMatch += lookaheadMatch[0].length;
2519 match: "^----+$\\n?
",
2520 handler: function(w)
2522 createTiddlyElement(w.output,"hr
");
2527 name: "monospacedByLine
",
2528 match: "^(?:/\\*\\{\\{\\{\\*/|\\{\\{\\{|//\\{\\{\\{|
<!--\\{\\{\\{-->)\\n
",
2530 handler: function(w)
2532 switch(w.matchText) {
2533 case "/*{{{*/\n
": // CSS
2534 this.lookaheadRegExp = /\/\*\{\{\{\*\/\n*((?:^[^\n]*\n)+?)(\n*^\/\*\}\}\}\*\/$\n?)/mg;
2536 case "{{{\n
": // monospaced block
2537 this.lookaheadRegExp = /^\{\{\{\n((?:^[^\n]*\n)+?)(^\}\}\}$\n?)/mg;
2539 case "//{{{\n
": // plugin
2540 this.lookaheadRegExp = /^\/\/\{\{\{\n\n*((?:^[^\n]*\n)+?)(\n*^\/\/\}\}\}$\n?)/mg;
2542 case "<!--{{{-->\n
": //template
2543 this.lookaheadRegExp = /<!--\{\{\{-->\n*((?:^[^\n]*\n)+?)(\n*^<!--\}\}\}-->$\n?)/mg;
2548 config.formatterHelpers.enclosedTextHelper.call(this,w);
2553 name: "wikifyComment
",
2554 match: "^(?:/\\*\\*\\*|
<!---)\\n",
2555 handler: function(w)
2557 var termRegExp = (w.matchText == "/***\n") ? (/(^\*\*\*\/\n)/mg) : (/(^--->\n)/mg);
2558 w.subWikifyTerm(w.output,termRegExp);
2565 lookaheadRegExp: /<<([^
>\s]+)(?:\s*)((?:[^
>]|(?:
>(?!
>)))*)
>>/mg,
2566 handler: function(w)
2568 this.lookaheadRegExp.lastIndex = w.matchStart;
2569 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2570 if(lookaheadMatch && lookaheadMatch.index == w.matchStart && lookaheadMatch[
1]) {
2571 w.nextMatch = this.lookaheadRegExp.lastIndex;
2572 invokeMacro(w.output,lookaheadMatch[
1],lookaheadMatch[
2],w,w.tiddler);
2580 lookaheadRegExp: /\[\[(.*?)(?:\|(~)?(.*?))?\]\]/mg,
2581 handler: function(w)
2583 this.lookaheadRegExp.lastIndex = w.matchStart;
2584 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2585 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
2587 var text = lookaheadMatch[
1];
2588 if(lookaheadMatch[
3]) {
2589 // Pretty bracketted link
2590 var link = lookaheadMatch[
3];
2591 e = (!lookaheadMatch[
2] && config.formatterHelpers.isExternalLink(link)) ?
2592 createExternalLink(w.output,link) : createTiddlyLink(w.output,decodeURIComponent(link),false,null,w.isStatic,w.tiddler);
2594 // Simple bracketted link
2595 e = createTiddlyLink(w.output,decodeURIComponent(text),false,null,w.isStatic,w.tiddler);
2597 createTiddlyText(e,text);
2598 w.nextMatch = this.lookaheadRegExp.lastIndex;
2605 match: config.textPrimitives.unWikiLink+
"?"+config.textPrimitives.wikiLink,
2606 handler: function(w)
2608 if(w.matchText.substr(
0,
1) == config.textPrimitives.unWikiLink) {
2609 w.outputText(w.output,w.matchStart+
1,w.nextMatch);
2612 if(w.matchStart
> 0) {
2613 var preRegExp = new RegExp(config.textPrimitives.anyLetterStrict,
"mg");
2614 preRegExp.lastIndex = w.matchStart-
1;
2615 var preMatch = preRegExp.exec(w.source);
2616 if(preMatch.index == w.matchStart-
1) {
2617 w.outputText(w.output,w.matchStart,w.nextMatch);
2621 if(w.autoLinkWikiWords || store.isShadowTiddler(w.matchText)) {
2622 var link = createTiddlyLink(w.output,w.matchText,false,null,w.isStatic,w.tiddler);
2623 w.outputText(link,w.matchStart,w.nextMatch);
2625 w.outputText(w.output,w.matchStart,w.nextMatch);
2632 match: config.textPrimitives.urlPattern,
2633 handler: function(w)
2635 w.outputText(createExternalLink(w.output,w.matchText),w.matchStart,w.nextMatch);
2641 match:
"\\[[<>]?[Ii][Mm][Gg]\\[",
2642 lookaheadRegExp: /\[([<]?)(
>?)[Ii][Mm][Gg]\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg,
2643 handler: function(w)
2645 this.lookaheadRegExp.lastIndex = w.matchStart;
2646 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2647 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
2649 if(lookaheadMatch[
5]) {
2650 var link = lookaheadMatch[
5];
2651 e = config.formatterHelpers.isExternalLink(link) ? createExternalLink(w.output,link) : createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
2652 addClass(e,
"imageLink");
2654 var img = createTiddlyElement(e,
"img");
2655 if(lookaheadMatch[
1])
2657 else if(lookaheadMatch[
2])
2658 img.align =
"right";
2659 if(lookaheadMatch[
3]) {
2660 img.title = lookaheadMatch[
3];
2661 img.setAttribute(
"alt",lookaheadMatch[
3]);
2663 img.src = lookaheadMatch[
4];
2664 w.nextMatch = this.lookaheadRegExp.lastIndex;
2671 match:
"<[Hh][Tt][Mm][Ll]>",
2672 lookaheadRegExp: /<[Hh][Tt][Mm][Ll]
>((?:.|\n)*?)<\/[Hh][Tt][Mm][Ll]
>/mg,
2673 handler: function(w)
2675 this.lookaheadRegExp.lastIndex = w.matchStart;
2676 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2677 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
2678 createTiddlyElement(w.output,
"span").innerHTML = lookaheadMatch[
1];
2679 w.nextMatch = this.lookaheadRegExp.lastIndex;
2685 name:
"commentByBlock",
2687 lookaheadRegExp: /\/%((?:.|\n)*?)%\//mg,
2688 handler: function(w)
2690 this.lookaheadRegExp.lastIndex = w.matchStart;
2691 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2692 if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
2693 w.nextMatch = this.lookaheadRegExp.lastIndex;
2698 name:
"characterFormat",
2699 match:
"''|//|__|\\^\\^|~~|--(?!\\s|$)|\\{\\{\\{",
2700 handler: function(w)
2702 switch(w.matchText) {
2704 w.subWikifyTerm(w.output.appendChild(document.createElement(
"strong")),/('')/mg);
2707 w.subWikifyTerm(createTiddlyElement(w.output,
"em"),/(\/\/)/mg);
2710 w.subWikifyTerm(createTiddlyElement(w.output,
"u"),/(__)/mg);
2713 w.subWikifyTerm(createTiddlyElement(w.output,
"sup"),/(\^\^)/mg);
2716 w.subWikifyTerm(createTiddlyElement(w.output,
"sub"),/(~~)/mg);
2719 w.subWikifyTerm(createTiddlyElement(w.output,
"strike"),/(--)/mg);
2722 var lookaheadRegExp = /\{\{\{((?:.|\n)*?)\}\}\}/mg;
2723 lookaheadRegExp.lastIndex = w.matchStart;
2724 var lookaheadMatch = lookaheadRegExp.exec(w.source);
2725 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
2726 createTiddlyElement(w.output,
"code",null,null,lookaheadMatch[
1]);
2727 w.nextMatch = lookaheadRegExp.lastIndex;
2735 name:
"customFormat",
2737 handler: function(w)
2739 switch(w.matchText) {
2741 var e = createTiddlyElement(w.output,
"span");
2742 var styles = config.formatterHelpers.inlineCssHelper(w);
2743 if(styles.length ==
0)
2744 e.className =
"marked";
2746 config.formatterHelpers.applyCssHelper(e,styles);
2747 w.subWikifyTerm(e,/(@@)/mg);
2750 var lookaheadRegExp = /\{\{[\s]*([\w]+[\s\w]*)[\s]*\{(\n?)/mg;
2751 lookaheadRegExp.lastIndex = w.matchStart;
2752 var lookaheadMatch = lookaheadRegExp.exec(w.source);
2753 if(lookaheadMatch) {
2754 w.nextMatch = lookaheadRegExp.lastIndex;
2755 e = createTiddlyElement(w.output,lookaheadMatch[
2] ==
"\n" ?
"div" :
"span",null,lookaheadMatch[
1]);
2756 w.subWikifyTerm(e,/(\}\}\})/mg);
2766 handler: function(w)
2768 createTiddlyElement(w.output,
"span").innerHTML =
"—";
2774 match:
"\\n|<br ?/?>",
2775 handler: function(w)
2777 createTiddlyElement(w.output,
"br");
2783 match:
"\\\"{
3}|
<nowiki>",
2784 lookaheadRegExp: /(?:\"{
3}|
<nowiki>)((?:.|\n)*?)(?:\
"{3}|<\/nowiki>)/mg,
2785 handler: function(w)
2787 this.lookaheadRegExp.lastIndex = w.matchStart;
2788 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2789 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
2790 createTiddlyElement(w.output,"span
",null,null,lookaheadMatch[1]);
2791 w.nextMatch = this.lookaheadRegExp.lastIndex;
2797 name: "htmlEntitiesEncoding
",
2798 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};)
",
2799 handler: function(w)
2801 createTiddlyElement(w.output,"span
").innerHTML = w.matchText;
2811 function getParser(tiddler,format)
2815 format = tiddler.fields["wikiformat
"];
2818 for(i in config.parsers) {
2819 if(format == config.parsers[i].format)
2820 return config.parsers[i];
2823 for(i in config.parsers) {
2824 if(tiddler.isTagged(config.parsers[i].formatTag))
2825 return config.parsers[i];
2832 function wikify(source,output,highlightRegExp,tiddler)
2835 var wikifier = new Wikifier(source,getParser(tiddler),highlightRegExp,tiddler);
2836 var t0 = new Date();
2837 wikifier.subWikify(output);
2838 if(tiddler && config.options.chkDisplayInstrumentation)
2839 displayMessage("wikify:
" +tiddler.title+ " in
" + (new Date()-t0) + " ms
");
2843 function wikifyStatic(source,highlightRegExp,tiddler,format)
2845 var e = createTiddlyElement(document.body,"pre
");
2846 e.style.display = "none
";
2848 if(source && source != "") {
2850 tiddler = new Tiddler("temp
");
2851 var wikifier = new Wikifier(source,getParser(tiddler,format),highlightRegExp,tiddler);
2852 wikifier.isStatic = true;
2853 wikifier.subWikify(e);
2860 function wikifyPlain(title,theStore,limit)
2864 if(theStore.tiddlerExists(title) || theStore.isShadowTiddler(title)) {
2865 return wikifyPlainText(theStore.getTiddlerText(title),limit,tiddler);
2871 function wikifyPlainText(text,limit,tiddler)
2874 text = text.substr(0,limit);
2875 var wikifier = new Wikifier(text,formatter,null,tiddler);
2876 return wikifier.wikifyPlain();
2879 function highlightify(source,output,highlightRegExp,tiddler)
2882 var wikifier = new Wikifier(source,formatter,highlightRegExp,tiddler);
2883 wikifier.outputText(output,0,source.length);
2887 function Wikifier(source,formatter,highlightRegExp,tiddler)
2889 this.source = source;
2891 this.formatter = formatter;
2893 this.autoLinkWikiWords = tiddler && tiddler.autoLinkWikiWords() == false ? false : true;
2894 this.highlightRegExp = highlightRegExp;
2895 this.highlightMatch = null;
2896 this.isStatic = false;
2897 if(highlightRegExp) {
2898 highlightRegExp.lastIndex = 0;
2899 this.highlightMatch = highlightRegExp.exec(source);
2901 this.tiddler = tiddler;
2904 Wikifier.prototype.wikifyPlain = function()
2906 var e = createTiddlyElement(document.body,"div
");
2907 e.style.display = "none
";
2909 var text = getPlainText(e);
2914 Wikifier.prototype.subWikify = function(output,terminator)
2918 this.subWikifyTerm(output,new RegExp("(
" + terminator + ")
","mg
"));
2920 this.subWikifyUnterm(output);
2926 Wikifier.prototype.subWikifyUnterm = function(output)
2928 var oldOutput = this.output;
2929 this.output = output;
2930 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
2931 var formatterMatch = this.formatter.formatterRegExp.exec(this.source);
2932 while(formatterMatch) {
2933 // Output any text before the match
2934 if(formatterMatch.index > this.nextMatch)
2935 this.outputText(this.output,this.nextMatch,formatterMatch.index);
2936 // Set the match parameters for the handler
2937 this.matchStart = formatterMatch.index;
2938 this.matchLength = formatterMatch[0].length;
2939 this.matchText = formatterMatch[0];
2940 this.nextMatch = this.formatter.formatterRegExp.lastIndex;
2941 for(var t=1; t<formatterMatch.length; t++) {
2942 if(formatterMatch[t]) {
2943 this.formatter.formatters[t-1].handler(this);
2944 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
2948 formatterMatch = this.formatter.formatterRegExp.exec(this.source);
2950 if(this.nextMatch < this.source.length) {
2951 this.outputText(this.output,this.nextMatch,this.source.length);
2952 this.nextMatch = this.source.length;
2954 this.output = oldOutput;
2957 Wikifier.prototype.subWikifyTerm = function(output,terminatorRegExp)
2959 var oldOutput = this.output;
2960 this.output = output;
2961 terminatorRegExp.lastIndex = this.nextMatch;
2962 var terminatorMatch = terminatorRegExp.exec(this.source);
2963 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
2964 var formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
2965 while(terminatorMatch || formatterMatch) {
2966 if(terminatorMatch && (!formatterMatch || terminatorMatch.index <= formatterMatch.index)) {
2967 if(terminatorMatch.index > this.nextMatch)
2968 this.outputText(this.output,this.nextMatch,terminatorMatch.index);
2969 this.matchText = terminatorMatch[1];
2970 this.matchLength = terminatorMatch[1].length;
2971 this.matchStart = terminatorMatch.index;
2972 this.nextMatch = this.matchStart + this.matchLength;
2973 this.output = oldOutput;
2976 if(formatterMatch.index > this.nextMatch)
2977 this.outputText(this.output,this.nextMatch,formatterMatch.index);
2978 this.matchStart = formatterMatch.index;
2979 this.matchLength = formatterMatch[0].length;
2980 this.matchText = formatterMatch[0];
2981 this.nextMatch = this.formatter.formatterRegExp.lastIndex;
2982 for(var t=1; t<formatterMatch.length; t++) {
2983 if(formatterMatch[t]) {
2984 this.formatter.formatters[t-1].handler(this);
2985 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
2989 terminatorRegExp.lastIndex = this.nextMatch;
2990 terminatorMatch = terminatorRegExp.exec(this.source);
2991 formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
2993 if(this.nextMatch < this.source.length) {
2994 this.outputText(this.output,this.nextMatch,this.source.length);
2995 this.nextMatch = this.source.length;
2997 this.output = oldOutput;
3000 Wikifier.prototype.outputText = function(place,startPos,endPos)
3002 while(this.highlightMatch && (this.highlightRegExp.lastIndex > startPos) && (this.highlightMatch.index < endPos) && (startPos < endPos)) {
3003 if(this.highlightMatch.index > startPos) {
3004 createTiddlyText(place,this.source.substring(startPos,this.highlightMatch.index));
3005 startPos = this.highlightMatch.index;
3007 var highlightEnd = Math.min(this.highlightRegExp.lastIndex,endPos);
3008 var theHighlight = createTiddlyElement(place,"span
",null,"highlight
",this.source.substring(startPos,highlightEnd));
3009 startPos = highlightEnd;
3010 if(startPos >= this.highlightRegExp.lastIndex)
3011 this.highlightMatch = this.highlightRegExp.exec(this.source);
3013 if(startPos < endPos) {
3014 createTiddlyText(place,this.source.substring(startPos,endPos));
3019 //-- Macro definitions
3022 config.macros.today.handler = function(place,macroName,params)
3024 var now = new Date();
3025 var text = params[0] ? now.formatString(params[0].trim()) : now.toLocaleString();
3026 createTiddlyElement(place,"span
",null,null,text);
3029 config.macros.version.handler = function(place)
3031 createTiddlyElement(place,"span
",null,null,formatVersion());
3034 config.macros.list.handler = function(place,macroName,params)
3036 var type = params[0] || "all
";
3037 var list = document.createElement("ul
");
3038 place.appendChild(list);
3039 if(this[type].prompt)
3040 createTiddlyElement(list,"li
",null,"listTitle
",this[type].prompt);
3042 if(this[type].handler)
3043 results = this[type].handler(params);
3044 for(var t = 0; t < results.length; t++) {
3045 var li = document.createElement("li
");
3046 list.appendChild(li);
3047 createTiddlyLink(li,typeof results[t] == "string
" ? results[t] : results[t].title,true);
3051 config.macros.list.all.handler = function(params)
3053 return store.reverseLookup("tags
","excludeLists
",false,"title
");
3056 config.macros.list.missing.handler = function(params)
3058 return store.getMissingLinks();
3061 config.macros.list.orphans.handler = function(params)
3063 return store.getOrphans();
3066 config.macros.list.shadowed.handler = function(params)
3068 return store.getShadowed();
3071 config.macros.list.touched.handler = function(params)
3073 return store.getTouched();
3076 config.macros.list.filter.handler = function(params)
3078 var filter = params[1];
3081 var tiddlers = store.filterTiddlers(filter);
3082 for(var t=0; t<tiddlers.length; t++)
3083 results.push(tiddlers[t].title);
3088 config.macros.allTags.handler = function(place,macroName,params)
3090 var tags = store.getTags(params[0]);
3091 var ul = createTiddlyElement(place,"ul
");
3092 if(tags.length == 0)
3093 createTiddlyElement(ul,"li
",null,"listTitle
",this.noTags);
3094 for(var t=0; t<tags.length; t++) {
3095 var title = tags[t][0];
3096 var info = getTiddlyLinkInfo(title);
3097 var li = createTiddlyElement(ul,"li
");
3098 var btn = createTiddlyButton(li,title + " (
" + tags[t][1] + ")
",this.tooltip.format([title]),onClickTag,info.classes);
3099 btn.setAttribute("tag
",title);
3100 btn.setAttribute("refresh
","link
");
3101 btn.setAttribute("tiddlyLink
",title);
3105 config.macros.timeline.handler = function(place,macroName,params)
3107 var field = params[0] || "modified
";
3108 var tiddlers = store.reverseLookup("tags
","excludeLists
",false,field);
3110 var last = params[1] ? tiddlers.length-Math.min(tiddlers.length,parseInt(params[1])) : 0;
3111 var dateFormat = params[2] || this.dateFormat;
3112 for(var t=tiddlers.length-1; t>=last; t--) {
3113 var tiddler = tiddlers[t];
3114 var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
3115 if(theDay != lastDay) {
3116 var ul = document.createElement("ul
");
3117 place.appendChild(ul);
3118 createTiddlyElement(ul,"li
",null,"listTitle
",tiddler[field].formatString(dateFormat));
3121 createTiddlyElement(ul,"li
",null,"listLink
").appendChild(createTiddlyLink(place,tiddler.title,true));
3125 config.macros.tiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3127 params = paramString.parseParams("name
",null,true,false,true);
3128 var names = params[0]["name
"];
3129 var tiddlerName = names[0];
3130 var className = names[1] || null;
3131 var args = params[0]["with
"];
3132 var wrapper = createTiddlyElement(place,"span
",null,className);
3134 wrapper.setAttribute("refresh
","content
");
3135 wrapper.setAttribute("tiddler
",tiddlerName);
3137 var text = store.getTiddlerText(tiddlerName);
3139 var stack = config.macros.tiddler.tiddlerStack;
3140 if(stack.indexOf(tiddlerName) !== -1)
3142 stack.push(tiddlerName);
3144 var n = args ? Math.min(args.length,9) : 0;
3145 for(var i=0; i<n; i++) {
3146 var placeholderRE = new RegExp("\\$
" + (i + 1),"mg
");
3147 text = text.replace(placeholderRE,args[i]);
3149 config.macros.tiddler.renderText(wrapper,text,tiddlerName,params);
3156 config.macros.tiddler.renderText = function(place,text,tiddlerName,params)
3158 wikify(text,place,null,store.getTiddler(tiddlerName));
3161 config.macros.tiddler.tiddlerStack = [];
3163 config.macros.tag.handler = function(place,macroName,params)
3165 createTagButton(place,params[0],null,params[1],params[2]);
3168 config.macros.tags.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3170 params = paramString.parseParams("anon
",null,true,false,false);
3171 var ul = createTiddlyElement(place,"ul
");
3172 var title = getParam(params,"anon
","");
3173 if(title && store.tiddlerExists(title))
3174 tiddler = store.getTiddler(title);
3175 var sep = getParam(params,"sep
"," ");
3176 var lingo = config.views.wikified.tag;
3177 var prompt = tiddler.tags.length == 0 ? lingo.labelNoTags : lingo.labelTags;
3178 createTiddlyElement(ul,"li
",null,"listTitle
",prompt.format([tiddler.title]));
3179 for(var t=0; t<tiddler.tags.length; t++) {
3180 createTagButton(createTiddlyElement(ul,"li
"),tiddler.tags[t],tiddler.title);
3181 if(t<tiddler.tags.length-1)
3182 createTiddlyText(ul,sep);
3186 config.macros.tagging.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3188 params = paramString.parseParams("anon
",null,true,false,false);
3189 var ul = createTiddlyElement(place,"ul
");
3190 var title = getParam(params,"anon
","");
3191 if(title == "" && tiddler instanceof Tiddler)
3192 title = tiddler.title;
3193 var sep = getParam(params,"sep
"," ");
3194 ul.setAttribute("title
",this.tooltip.format([title]));
3195 var tagged = store.getTaggedTiddlers(title);
3196 var prompt = tagged.length == 0 ? this.labelNotTag : this.label;
3197 createTiddlyElement(ul,"li
",null,"listTitle
",prompt.format([title,tagged.length]));
3198 for(var t=0; t<tagged.length; t++) {
3199 createTiddlyLink(createTiddlyElement(ul,"li
"),tagged[t].title,true);
3200 if(t<tagged.length-1)
3201 createTiddlyText(ul,sep);
3205 config.macros.closeAll.handler = function(place)
3207 createTiddlyButton(place,this.label,this.prompt,this.onClick);
3210 config.macros.closeAll.onClick = function(e)
3212 story.closeAllTiddlers();
3216 config.macros.permaview.handler = function(place)
3218 createTiddlyButton(place,this.label,this.prompt,this.onClick);
3221 config.macros.permaview.onClick = function(e)
3227 config.macros.saveChanges.handler = function(place,macroName,params)
3230 createTiddlyButton(place,params[0] || this.label,params[1] || this.prompt,this.onClick,null,null,this.accessKey);
3233 config.macros.saveChanges.onClick = function(e)
3239 config.macros.slider.onClickSlider = function(ev)
3241 var e = ev || window.event;
3242 var n = this.nextSibling;
3243 var cookie = n.getAttribute("cookie
");
3244 var isOpen = n.style.display != "none
";
3245 if(config.options.chkAnimate && anim && typeof Slider == "function
")
3246 anim.startAnimating(new Slider(n,!isOpen,null,"none
"));
3248 n.style.display = isOpen ? "none
" : "block
";
3249 config.options[cookie] = !isOpen;
3250 saveOptionCookie(cookie);
3254 config.macros.slider.createSlider = function(place,cookie,title,tooltip)
3256 var c = cookie || "";
3257 var btn = createTiddlyButton(place,title,tooltip,this.onClickSlider);
3258 var panel = createTiddlyElement(null,"div
",null,"sliderPanel
");
3259 panel.setAttribute("cookie
",c);
3260 panel.style.display = config.options[c] ? "block
" : "none
";
3261 place.appendChild(panel);
3265 config.macros.slider.handler = function(place,macroName,params)
3267 var panel = this.createSlider(place,params[0],params[2],params[3]);
3268 var text = store.getTiddlerText(params[1]);
3269 panel.setAttribute("refresh
","content
");
3270 panel.setAttribute("tiddler
",params[1]);
3272 wikify(text,panel,null,store.getTiddler(params[1]));
3275 // <<gradient [[tiddler name]] vert|horiz rgb rgb rgb rgb... >>
3276 config.macros.gradient.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3278 var panel = wikifier ? createTiddlyElement(place,"div
",null,"gradient
") : place;
3279 panel.style.position = "relative
";
3280 panel.style.overflow = "hidden
";
3281 panel.style.zIndex = "0";
3283 var styles = config.formatterHelpers.inlineCssHelper(wikifier);
3284 config.formatterHelpers.applyCssHelper(panel,styles);
3286 params = paramString.parseParams("color
");
3287 var locolors = [], hicolors = [];
3288 for(var t=2; t<params.length; t++) {
3289 var c = new RGB(params[t].value);
3290 if(params[t].name == "snap
") {
3291 hicolors[hicolors.length-1] = c;
3297 drawGradient(panel,params[1].value != "vert
",locolors,hicolors);
3299 wikifier.subWikify(panel,">>");
3301 panel.style.height = "100%
";
3302 panel.style.width = "100%
";
3306 config.macros.message.handler = function(place,macroName,params)
3309 var names = params[0].split(".
");
3310 var lookupMessage = function(root,nameIndex) {
3311 if(names[nameIndex] in root) {
3312 if(nameIndex < names.length-1)
3313 return (lookupMessage(root[names[nameIndex]],nameIndex+1));
3315 return root[names[nameIndex]];
3319 var m = lookupMessage(config,0);
3321 m = lookupMessage(window,0);
3322 createTiddlyText(place,m.toString().format(params.splice(1)));
3327 config.macros.view.views = {
3328 text: function(value,place,params,wikifier,paramString,tiddler) {
3329 highlightify(value,place,highlightHack,tiddler);
3331 link: function(value,place,params,wikifier,paramString,tiddler) {
3332 createTiddlyLink(place,value,true);
3334 wikified: function(value,place,params,wikifier,paramString,tiddler) {
3336 value=params[2].unescapeLineBreaks().format([value]);
3337 wikify(value,place,highlightHack,tiddler);
3339 date: function(value,place,params,wikifier,paramString,tiddler) {
3340 value = Date.convertFromYYYYMMDDHHMM(value);
3341 createTiddlyText(place,value.formatString(params[2] ? params[2] : config.views.wikified.dateFormat));
3345 config.macros.view.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3347 if((tiddler instanceof Tiddler) && params[0]) {
3348 var value = store.getValue(tiddler,params[0]);
3350 var type = params[1] || config.macros.view.defaultView;
3351 var handler = config.macros.view.views[type];
3353 handler(value,place,params,wikifier,paramString,tiddler);
3358 config.macros.edit.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3360 var field = params[0];
3361 var rows = params[1] || 0;
3362 var defVal = params[2] || '';
3363 if((tiddler instanceof Tiddler) && field) {
3364 story.setDirty(tiddler.title,true);
3366 if(field != "text
" && !rows) {
3367 e = createTiddlyElement(null,"input
");
3368 if(tiddler.isReadOnly())
3369 e.setAttribute("readOnly
","readOnly
");
3370 e.setAttribute("edit
",field);
3371 e.setAttribute("type
","text
");
3372 e.value = store.getValue(tiddler,field) || defVal;
3373 e.setAttribute("size
","40");
3374 e.setAttribute("autocomplete
","off
");
3375 place.appendChild(e);
3377 var wrapper1 = createTiddlyElement(null,"fieldset
",null,"fieldsetFix
");
3378 var wrapper2 = createTiddlyElement(wrapper1,"div
");
3379 e = createTiddlyElement(wrapper2,"textarea
");
3380 if(tiddler.isReadOnly())
3381 e.setAttribute("readOnly
","readOnly
");
3382 e.value = v = store.getValue(tiddler,field) || defVal;
3384 var lines = v.match(/\n/mg);
3385 var maxLines = Math.max(parseInt(config.options.txtMaxEditRows),5);
3386 if(lines != null && lines.length > rows)
3387 rows = lines.length + 5;
3388 rows = Math.min(rows,maxLines);
3389 e.setAttribute("rows
",rows);
3390 e.setAttribute("edit
",field);
3391 place.appendChild(wrapper1);
3397 config.macros.tagChooser.onClick = function(ev)
3399 var e = ev || window.event;
3400 var lingo = config.views.editor.tagChooser;
3401 var popup = Popup.create(this);
3402 var tags = store.getTags("excludeLists
");
3403 if(tags.length == 0)
3404 createTiddlyText(createTiddlyElement(popup,"li
"),lingo.popupNone);
3405 for(var t=0; t<tags.length; t++) {
3406 var tag = createTiddlyButton(createTiddlyElement(popup,"li
"),tags[t][0],lingo.tagTooltip.format([tags[t][0]]),config.macros.tagChooser.onTagClick);
3407 tag.setAttribute("tag
",tags[t][0]);
3408 tag.setAttribute("tiddler
",this.getAttribute("tiddler
"));
3411 e.cancelBubble = true;
3412 if(e.stopPropagation) e.stopPropagation();
3416 config.macros.tagChooser.onTagClick = function(ev)
3418 var e = ev || window.event;
3419 if(e.metaKey || e.ctrlKey) stopEvent(e); //# keep popup open on CTRL-click
3420 var tag = this.getAttribute("tag
");
3421 var title = this.getAttribute("tiddler
");
3423 story.setTiddlerTag(title,tag,0);
3427 config.macros.tagChooser.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3429 if(tiddler instanceof Tiddler) {
3430 var lingo = config.views.editor.tagChooser;
3431 var btn = createTiddlyButton(place,lingo.text,lingo.tooltip,this.onClick);
3432 btn.setAttribute("tiddler
",tiddler.title);
3436 config.macros.refreshDisplay.handler = function(place)
3438 createTiddlyButton(place,this.label,this.prompt,this.onClick);
3441 config.macros.refreshDisplay.onClick = function(e)
3447 config.macros.annotations.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3449 var title = tiddler ? tiddler.title : null;
3450 var a = title ? config.annotations[title] : null;
3451 if(!tiddler || !title || !a)
3453 var text = a.format([title]);
3454 wikify(text,createTiddlyElement(place,"div
",null,"annotation
"),null,tiddler);
3458 //-- NewTiddler and NewJournal macros
3461 config.macros.newTiddler.createNewTiddlerButton = function(place,title,params,label,prompt,accessKey,newFocus,isJournal)
3464 for(var t=1; t<params.length; t++) {
3465 if((params[t].name == "anon
" && t != 1) || (params[t].name == "tag
"))
3466 tags.push(params[t].value);
3468 label = getParam(params,"label
",label);
3469 prompt = getParam(params,"prompt
",prompt);
3470 accessKey = getParam(params,"accessKey
",accessKey);
3471 newFocus = getParam(params,"focus
",newFocus);
3472 var customFields = getParam(params,"fields
","");
3473 if(!customFields && !store.isShadowTiddler(title))
3474 customFields = String.encodeHashMap(config.defaultCustomFields);
3475 var btn = createTiddlyButton(place,label,prompt,this.onClickNewTiddler,null,null,accessKey);
3476 btn.setAttribute("newTitle
",title);
3477 btn.setAttribute("isJournal
",isJournal ? "true
" : "false
");
3479 btn.setAttribute("params
",tags.join("|
"));
3480 btn.setAttribute("newFocus
",newFocus);
3481 btn.setAttribute("newTemplate
",getParam(params,"template
",DEFAULT_EDIT_TEMPLATE));
3482 if(customFields !== "")
3483 btn.setAttribute("customFields
",customFields);
3484 var text = getParam(params,"text
");
3485 if(text !== undefined)
3486 btn.setAttribute("newText
",text);
3490 config.macros.newTiddler.onClickNewTiddler = function()
3492 var title = this.getAttribute("newTitle
");
3493 if(this.getAttribute("isJournal
") == "true
") {
3494 title = new Date().formatString(title.trim());
3496 var params = this.getAttribute("params
");
3497 var tags = params ? params.split("|
") : [];
3498 var focus = this.getAttribute("newFocus
");
3499 var template = this.getAttribute("newTemplate
");
3500 var customFields = this.getAttribute("customFields
");
3501 if(!customFields && !store.isShadowTiddler(title))
3502 customFields = String.encodeHashMap(config.defaultCustomFields);
3503 story.displayTiddler(null,title,template,false,null,null);
3504 var tiddlerElem = story.getTiddler(title);
3506 story.addCustomFields(tiddlerElem,customFields);
3507 var text = this.getAttribute("newText
");
3508 if(typeof text == "string
")
3509 story.getTiddlerField(title,"text
").value = text.format([title]);
3510 for(var t=0;t<tags.length;t++)
3511 story.setTiddlerTag(title,tags[t],+1);
3512 story.focusTiddler(title,focus);
3516 config.macros.newTiddler.handler = function(place,macroName,params,wikifier,paramString)
3519 params = paramString.parseParams("anon
",null,true,false,false);
3520 var title = params[1] && params[1].name == "anon
" ? params[1].value : this.title;
3521 title = getParam(params,"title
",title);
3522 this.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"title
",false);
3526 config.macros.newJournal.handler = function(place,macroName,params,wikifier,paramString)
3529 params = paramString.parseParams("anon
",null,true,false,false);
3530 var title = params[1] && params[1].name == "anon
" ? params[1].value : config.macros.timeline.dateFormat;
3531 title = getParam(params,"title
",title);
3532 config.macros.newTiddler.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"text
",true);
3540 config.macros.search.handler = function(place,macroName,params)
3542 var searchTimeout = null;
3543 var btn = createTiddlyButton(place,this.label,this.prompt,this.onClick,"searchButton
");
3544 var txt = createTiddlyElement(place,"input
",null,"txtOptionInput searchField
");
3546 txt.value = params[0];
3547 txt.onkeyup = this.onKeyPress;
3548 txt.onfocus = this.onFocus;
3549 txt.setAttribute("size
",this.sizeTextbox);
3550 txt.setAttribute("accessKey
",this.accessKey);
3551 txt.setAttribute("autocomplete
","off
");
3552 txt.setAttribute("lastSearchText
","");
3553 if(config.browser.isSafari) {
3554 txt.setAttribute("type
","search
");
3555 txt.setAttribute("results
","5");
3557 txt.setAttribute("type
","text
");
3561 // Global because there's only ever one outstanding incremental search timer
3562 config.macros.search.timeout = null;
3564 config.macros.search.doSearch = function(txt)
3566 if(txt.value.length > 0) {
3567 story.search(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch);
3568 txt.setAttribute("lastSearchText
",txt.value);
3572 config.macros.search.onClick = function(e)
3574 config.macros.search.doSearch(this.nextSibling);
3578 config.macros.search.onKeyPress = function(ev)
3580 var e = ev || window.event;
3582 case 13: // Ctrl-Enter
3583 case 10: // Ctrl-Enter on IE PC
3584 config.macros.search.doSearch(this);
3591 if(config.options.chkIncrementalSearch) {
3592 if(this.value.length > 2) {
3593 if(this.value != this.getAttribute("lastSearchText
")) {
3594 if(config.macros.search.timeout)
3595 clearTimeout(config.macros.search.timeout);
3597 config.macros.search.timeout = setTimeout(function() {config.macros.search.doSearch(txt);},500);
3600 if(config.macros.search.timeout)
3601 clearTimeout(config.macros.search.timeout);
3606 config.macros.search.onFocus = function(e)
3615 config.macros.tabs.handler = function(place,macroName,params)
3617 var cookie = params[0];
3618 var numTabs = (params.length-1)/3;
3619 var wrapper = createTiddlyElement(null,"div
",null,"tabsetWrapper
" + cookie);
3620 var tabset = createTiddlyElement(wrapper,"div
",null,"tabset
");
3621 tabset.setAttribute("cookie
",cookie);
3622 var validTab = false;
3623 for(var t=0; t<numTabs; t++) {
3624 var label = params[t*3+1];
3625 var prompt = params[t*3+2];
3626 var content = params[t*3+3];
3627 var tab = createTiddlyButton(tabset,label,prompt,this.onClickTab,"tab tabUnselected
");
3628 tab.setAttribute("tab
",label);
3629 tab.setAttribute("content
",content);
3631 if(config.options[cookie] == label)
3635 config.options[cookie] = params[1];
3636 place.appendChild(wrapper);
3637 this.switchTab(tabset,config.options[cookie]);
3640 config.macros.tabs.onClickTab = function(e)
3642 config.macros.tabs.switchTab(this.parentNode,this.getAttribute("tab
"));
3646 config.macros.tabs.switchTab = function(tabset,tab)
3648 var cookie = tabset.getAttribute("cookie
");
3650 var nodes = tabset.childNodes;
3651 for(var t=0; t<nodes.length; t++) {
3652 if(nodes[t].getAttribute && nodes[t].getAttribute("tab
") == tab) {
3654 theTab.className = "tab tabSelected
";
3656 nodes[t].className = "tab tabUnselected
";
3660 if(tabset.nextSibling && tabset.nextSibling.className == "tabContents
")
3661 removeNode(tabset.nextSibling);
3662 var tabContent = createTiddlyElement(null,"div
",null,"tabContents
");
3663 tabset.parentNode.insertBefore(tabContent,tabset.nextSibling);
3664 var contentTitle = theTab.getAttribute("content
");
3665 wikify(store.getTiddlerText(contentTitle),tabContent,null,store.getTiddler(contentTitle));
3667 config.options[cookie] = tab;
3668 saveOptionCookie(cookie);
3674 //-- Tiddler toolbar
3677 // Create a toolbar command button
3678 config.macros.toolbar.createCommand = function(place,commandName,tiddler,className)
3680 if(typeof commandName != "string
") {
3682 for(var t in config.commands) {
3683 if(config.commands[t] == commandName)
3688 if((tiddler instanceof Tiddler) && (typeof commandName == "string
")) {
3689 var command = config.commands[commandName];
3690 if(command.isEnabled ? command.isEnabled(tiddler) : this.isCommandEnabled(command,tiddler)) {
3691 var text = command.getText ? command.getText(tiddler) : this.getCommandText(command,tiddler);
3692 var tooltip = command.getTooltip ? command.getTooltip(tiddler) : this.getCommandTooltip(command,tiddler);
3694 switch(command.type) {
3696 cmd = this.onClickPopup;
3700 cmd = this.onClickCommand;
3703 var btn = createTiddlyButton(null,text,tooltip,cmd);
3704 btn.setAttribute("commandName
",commandName);
3705 btn.setAttribute("tiddler
",tiddler.title);
3707 addClass(btn,className);
3708 place.appendChild(btn);
3713 config.macros.toolbar.isCommandEnabled = function(command,tiddler)
3715 var title = tiddler.title;
3716 var ro = tiddler.isReadOnly();
3717 var shadow = store.isShadowTiddler(title) && !store.tiddlerExists(title);
3718 return (!ro || (ro && !command.hideReadOnly)) && !(shadow && command.hideShadow);
3721 config.macros.toolbar.getCommandText = function(command,tiddler)
3723 return tiddler.isReadOnly() && command.readOnlyText || command.text;
3726 config.macros.toolbar.getCommandTooltip = function(command,tiddler)
3728 return tiddler.isReadOnly() && command.readOnlyTooltip || command.tooltip;
3731 config.macros.toolbar.onClickCommand = function(ev)
3733 var e = ev || window.event;
3734 e.cancelBubble = true;
3735 if(e.stopPropagation) e.stopPropagation();
3736 var command = config.commands[this.getAttribute("commandName
")];
3737 return command.handler(e,this,this.getAttribute("tiddler
"));
3740 config.macros.toolbar.onClickPopup = function(ev)
3742 var e = ev || window.event;
3743 e.cancelBubble = true;
3744 if(e.stopPropagation) e.stopPropagation();
3745 var popup = Popup.create(this);
3746 var command = config.commands[this.getAttribute("commandName
")];
3747 var title = this.getAttribute("tiddler
");
3748 var tiddler = store.fetchTiddler(title);
3749 popup.setAttribute("tiddler
",title);
3750 command.handlePopup(popup,title);
3755 // Invoke the first command encountered from a given place that is tagged with a specified class
3756 config.macros.toolbar.invokeCommand = function(place,className,event)
3758 var children = place.getElementsByTagName("a
");
3759 for(var t=0; t<children.length; t++) {
3760 var c = children[t];
3761 if(hasClass(c,className) && c.getAttribute && c.getAttribute("commandName
")) {
3762 if(c.onclick instanceof Function)
3763 c.onclick.call(c,event);
3769 config.macros.toolbar.onClickMore = function(ev)
3771 var e = this.nextSibling;
3772 e.style.display = "inline
";
3777 config.macros.toolbar.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3779 for(var t=0; t<params.length; t++) {
3783 var btn = createTiddlyButton(place,this.moreLabel,this.morePrompt,config.macros.toolbar.onClickMore);
3784 addClass(btn,"moreCommand
");
3785 var e = createTiddlyElement(place,"span
",null,"moreCommand
");
3786 e.style.display = "none
";
3791 switch(c.substr(0,1)) {
3793 className = "defaultCommand
";
3797 className = "cancelCommand
";
3801 if(c in config.commands)
3802 this.createCommand(place,c,tiddler,className);
3809 //-- Menu and toolbar commands
3812 config.commands.closeTiddler.handler = function(event,src,title)
3814 if(story.isDirty(title) && !readOnly) {
3815 if(!confirm(config.commands.cancelTiddler.warning.format([title])))
3818 story.setDirty(title,false);
3819 story.closeTiddler(title,true);
3823 config.commands.closeOthers.handler = function(event,src,title)
3825 story.closeAllTiddlers(title);
3829 config.commands.editTiddler.handler = function(event,src,title)
3832 var tiddlerElem = story.getTiddler(title);
3833 var fields = tiddlerElem.getAttribute("tiddlyFields
");
3834 story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE,false,null,fields);
3835 story.focusTiddler(title,config.options.txtEditorFocus||"text
");
3839 config.commands.saveTiddler.handler = function(event,src,title)
3841 var newTitle = story.saveTiddler(title,event.shiftKey);
3843 story.displayTiddler(null,newTitle);
3847 config.commands.cancelTiddler.handler = function(event,src,title)
3849 if(story.hasChanges(title) && !readOnly) {
3850 if(!confirm(this.warning.format([title])))
3853 story.setDirty(title,false);
3854 story.displayTiddler(null,title);
3858 config.commands.deleteTiddler.handler = function(event,src,title)
3860 var deleteIt = true;
3861 if(config.options.chkConfirmDelete)
3862 deleteIt = confirm(this.warning.format([title]));
3864 store.removeTiddler(title);
3865 story.closeTiddler(title,true);
3871 config.commands.permalink.handler = function(event,src,title)
3873 var t = encodeURIComponent(String.encodeTiddlyLink(title));
3874 if(window.location.hash != t)
3875 window.location.hash = t;
3879 config.commands.references.handlePopup = function(popup,title)
3881 var references = store.getReferringTiddlers(title);
3883 for(var r=0; r<references.length; r++) {
3884 if(references[r].title != title && !references[r].isTagged("excludeLists
")) {
3885 createTiddlyLink(createTiddlyElement(popup,"li
"),references[r].title,true);
3890 createTiddlyText(createTiddlyElement(popup,"li
",null,"disabled
"),this.popupNone);
3893 config.commands.jump.handlePopup = function(popup,title)
3895 story.forEachTiddler(function(title,element) {
3896 createTiddlyLink(createTiddlyElement(popup,"li
"),title,true,null,false,null,true);
3900 config.commands.syncing.handlePopup = function(popup,title)
3902 var tiddler = store.fetchTiddler(title);
3905 var serverType = tiddler.getServerType();
3906 var serverHost = tiddler.fields['server.host'];
3907 var serverWorkspace = tiddler.fields['server.workspace'];
3908 if(!serverWorkspace)
3909 serverWorkspace = "";
3911 var e = createTiddlyElement(popup,"li
",null,"popupMessage
");
3912 e.innerHTML = config.commands.syncing.currentlySyncing.format([serverType,serverHost,serverWorkspace]);
3914 createTiddlyElement(popup,"li
",null,"popupMessage
",config.commands.syncing.notCurrentlySyncing);
3917 createTiddlyElement(createTiddlyElement(popup,"li
",null,"listBreak
"),"div
");
3918 var btn = createTiddlyButton(createTiddlyElement(popup,"li
"),this.captionUnSync,null,config.commands.syncing.onChooseServer);
3919 btn.setAttribute("tiddler
",title);
3920 btn.setAttribute("server.type
","");
3922 createTiddlyElement(createTiddlyElement(popup,"li
",null,"listBreak
"),"div
");
3923 createTiddlyElement(popup,"li
",null,"popupMessage
",config.commands.syncing.chooseServer);
3924 var feeds = store.getTaggedTiddlers("systemServer
","title
");
3925 for(var t=0; t<feeds.length; t++) {
3927 var feedServerType = store.getTiddlerSlice(f.title,"Type
");
3929 feedServerType = "file
";
3930 var feedServerHost = store.getTiddlerSlice(f.title,"URL
");
3932 feedServerHost = "";
3933 var feedServerWorkspace = store.getTiddlerSlice(f.title,"Workspace
");
3934 if(!feedServerWorkspace)
3935 feedServerWorkspace = "";
3936 var caption = f.title;
3937 if(serverType == feedServerType && serverHost == feedServerHost && serverWorkspace == feedServerWorkspace) {
3938 caption = config.commands.syncing.currServerMarker + caption;
3940 caption = config.commands.syncing.notCurrServerMarker + caption;
3942 btn = createTiddlyButton(createTiddlyElement(popup,"li
"),caption,null,config.commands.syncing.onChooseServer);
3943 btn.setAttribute("tiddler
",title);
3944 btn.setAttribute("server.type
",feedServerType);
3945 btn.setAttribute("server.host
",feedServerHost);
3946 btn.setAttribute("server.workspace
",feedServerWorkspace);
3950 config.commands.syncing.onChooseServer = function(e)
3952 var tiddler = this.getAttribute("tiddler
");
3953 var serverType = this.getAttribute("server.type
");
3955 store.addTiddlerFields(tiddler,{
3956 "server.type
": serverType,
3957 "server.host
": this.getAttribute("server.host
"),
3958 "server.workspace
": this.getAttribute("server.workspace
")
3961 store.setValue(tiddler,"server
",null);
3966 config.commands.fields.handlePopup = function(popup,title)
3968 var tiddler = store.fetchTiddler(title);
3972 store.forEachField(tiddler,function(tiddler,fieldName,value) {fields[fieldName] = value;},true);
3974 for(var t in fields) {
3975 items.push({field: t,value: fields[t]});
3977 items.sort(function(a,b) {return a.field < b.field ? -1 : (a.field == b.field ? 0 : +1);});
3978 if(items.length > 0)
3979 ListView.create(popup,items,this.listViewTemplate);
3981 createTiddlyElement(popup,"div
",null,null,this.emptyText);
3985 //-- Tiddler() object
3988 function Tiddler(title)
3992 this.modifier = null;
3993 this.created = new Date();
3994 this.modified = this.created;
3996 this.linksUpdated = false;
4002 Tiddler.prototype.getLinks = function()
4004 if(this.linksUpdated==false)
4009 // Returns the fields that are inherited in string field:"value
" field2:"value2
" format
4010 Tiddler.prototype.getInheritedFields = function()
4013 for(var i in this.fields) {
4014 if(i=="server.host
" || i=="server.workspace
" || i=="wikiformat
"|| i=="server.type
") {
4015 f[i] = this.fields[i];
4018 return String.encodeHashMap(f);
4021 // Increment the changeCount of a tiddler
4022 Tiddler.prototype.incChangeCount = function()
4024 var c = this.fields['changecount'];
4025 c = c ? parseInt(c,10) : 0;
4026 this.fields['changecount'] = String(c+1);
4029 // Clear the changeCount of a tiddler
4030 Tiddler.prototype.clearChangeCount = function()
4032 if(this.fields['changecount']) {
4033 delete this.fields['changecount'];
4037 Tiddler.prototype.doNotSave = function()
4039 return this.fields['doNotSave'];
4042 // Returns true if the tiddler has been updated since the tiddler was created or downloaded
4043 Tiddler.prototype.isTouched = function()
4045 var changeCount = this.fields['changecount'];
4046 if(changeCount === undefined)
4048 return changeCount > 0;
4051 // Return the tiddler as an RSS item
4052 Tiddler.prototype.toRssItem = function(uri)
4055 s.push("<title" + ">" + this.title.htmlEncode() + "</title" + ">");
4056 s.push("<description>" + wikifyStatic(this.text,null,this).htmlEncode() + "</description>");
4057 for(var t=0; t<this.tags.length; t++)
4058 s.push("<category>" + this.tags[t] + "</category>");
4059 s.push("<link>" + uri + "#
" + encodeURIComponent(String.encodeTiddlyLink(this.title)) + "</link>");
4060 s.push("<pubDate>" + this.modified.toGMTString() + "</pubDate>");
4061 return s.join("\n
");
4064 // Format the text for storage in an RSS item
4065 Tiddler.prototype.saveToRss = function(uri)
4067 return "<item>\n
" + this.toRssItem(uri) + "\n
</item>";
4070 // Change the text and other attributes of a tiddler
4071 Tiddler.prototype.set = function(title,text,modifier,modified,tags,created,fields)
4073 this.assign(title,text,modifier,modified,tags,created,fields);
4078 // Change the text and other attributes of a tiddler without triggered a tiddler.changed() call
4079 Tiddler.prototype.assign = function(title,text,modifier,modified,tags,created,fields)
4081 if(title != undefined)
4083 if(text != undefined)
4085 if(modifier != undefined)
4086 this.modifier = modifier;
4087 if(modified != undefined)
4088 this.modified = modified;
4089 if(created != undefined)
4090 this.created = created;
4091 if(fields != undefined)
4092 this.fields = fields;
4093 if(tags != undefined)
4094 this.tags = (typeof tags == "string
") ? tags.readBracketedList() : tags;
4095 else if(this.tags == undefined)
4100 // Get the tags for a tiddler as a string (space delimited, using [[brackets]] for tags containing spaces)
4101 Tiddler.prototype.getTags = function()
4103 return String.encodeTiddlyLinkList(this.tags);
4106 // Test if a tiddler carries a tag
4107 Tiddler.prototype.isTagged = function(tag)
4109 return this.tags.indexOf(tag) != -1;
4112 // Static method to convert "\n
" to newlines, "\s
" to "\
"
4113 Tiddler.unescapeLineBreaks = function(text)
4115 return text ? text.unescapeLineBreaks() : "";
4118 // Convert newlines to "\n
", "\
" to "\s
"
4119 Tiddler.prototype.escapeLineBreaks = function()
4121 return this.text.escapeLineBreaks();
4124 // Updates the secondary information (like links[] array) after a change to a tiddler
4125 Tiddler.prototype.changed = function()
4128 var t = this.autoLinkWikiWords() ? 0 : 1;
4129 var tiddlerLinkRegExp = t==0 ? config.textPrimitives.tiddlerAnyLinkRegExp : config.textPrimitives.tiddlerForcedLinkRegExp;
4130 tiddlerLinkRegExp.lastIndex = 0;
4131 var formatMatch = tiddlerLinkRegExp.exec(this.text);
4132 while(formatMatch) {
4133 var lastIndex = tiddlerLinkRegExp.lastIndex;
4134 if(t==0 && formatMatch[1] && formatMatch[1] != this.title) {
4136 if(formatMatch.index > 0) {
4137 var preRegExp = new RegExp(config.textPrimitives.unWikiLink+"|
"+config.textPrimitives.anyLetter,"mg
");
4138 preRegExp.lastIndex = formatMatch.index-1;
4139 var preMatch = preRegExp.exec(this.text);
4140 if(preMatch.index != formatMatch.index-1)
4141 this.links.pushUnique(formatMatch[1]);
4143 this.links.pushUnique(formatMatch[1]);
4146 else if(formatMatch[2-t] && !config.formatterHelpers.isExternalLink(formatMatch[3-t])) // titledBrackettedLink
4147 this.links.pushUnique(formatMatch[3-t]);
4148 else if(formatMatch[4-t] && formatMatch[4-t] != this.title) // brackettedLink
4149 this.links.pushUnique(formatMatch[4-t]);
4150 tiddlerLinkRegExp.lastIndex = lastIndex;
4151 formatMatch = tiddlerLinkRegExp.exec(this.text);
4153 this.linksUpdated = true;
4156 Tiddler.prototype.getSubtitle = function()
4158 var modifier = this.modifier;
4160 modifier = config.messages.subtitleUnknown;
4161 var modified = this.modified;
4163 modified = modified.toLocaleString();
4165 modified = config.messages.subtitleUnknown;
4166 return config.messages.tiddlerLinkTooltip.format([this.title,modifier,modified]);
4169 Tiddler.prototype.isReadOnly = function()
4174 Tiddler.prototype.autoLinkWikiWords = function()
4176 return !(this.isTagged("systemConfig
") || this.isTagged("excludeMissing
"));
4179 Tiddler.prototype.generateFingerprint = function()
4181 return "0x
" + Crypto.hexSha1Str(this.text);
4184 Tiddler.prototype.getServerType = function()
4186 var serverType = null;
4187 if(this.fields['server.type'])
4188 serverType = this.fields['server.type'];
4190 serverType = this.fields['wikiformat'];
4191 if(serverType && !config.adaptors[serverType])
4196 Tiddler.prototype.getAdaptor = function()
4198 var serverType = this.getServerType();
4199 return serverType ? new config.adaptors[serverType]() : null;
4203 //-- TiddlyWiki() object contains Tiddler()s
4206 function TiddlyWiki()
4208 var tiddlers = {}; // Hashmap by name of tiddlers
4209 this.tiddlersUpdated = false;
4210 this.namedNotifications = []; // Array of {name:,notify:} of notification functions
4211 this.notificationLevel = 0;
4212 this.slices = {}; // map tiddlerName->(map sliceName->sliceValue). Lazy.
4213 this.clear = function() {
4215 this.setDirty(false);
4217 this.fetchTiddler = function(title) {
4218 var t = tiddlers[title];
4219 return t instanceof Tiddler ? t : null;
4221 this.deleteTiddler = function(title) {
4222 delete this.slices[title];
4223 delete tiddlers[title];
4225 this.addTiddler = function(tiddler) {
4226 delete this.slices[tiddler.title];
4227 tiddlers[tiddler.title] = tiddler;
4229 this.forEachTiddler = function(callback) {
4230 for(var t in tiddlers) {
4231 var tiddler = tiddlers[t];
4232 if(tiddler instanceof Tiddler)
4233 callback.call(this,t,tiddler);
4238 TiddlyWiki.prototype.setDirty = function(dirty)
4243 TiddlyWiki.prototype.isDirty = function()
4248 TiddlyWiki.prototype.tiddlerExists = function(title)
4250 var t = this.fetchTiddler(title);
4251 return t != undefined;
4254 TiddlyWiki.prototype.isShadowTiddler = function(title)
4256 return typeof config.shadowTiddlers[title] == "string
";
4259 TiddlyWiki.prototype.createTiddler = function(title)
4261 var tiddler = this.fetchTiddler(title);
4263 tiddler = new Tiddler(title);
4264 this.addTiddler(tiddler);
4265 this.setDirty(true);
4270 TiddlyWiki.prototype.getTiddler = function(title)
4272 var t = this.fetchTiddler(title);
4279 TiddlyWiki.prototype.getTiddlerText = function(title,defaultText)
4283 var pos = title.indexOf(config.textPrimitives.sectionSeparator);
4286 section = title.substr(pos + config.textPrimitives.sectionSeparator.length);
4287 title = title.substr(0,pos);
4289 pos = title.indexOf(config.textPrimitives.sliceSeparator);
4291 var slice = this.getTiddlerSlice(title.substr(0,pos),title.substr(pos + config.textPrimitives.sliceSeparator.length));
4295 var tiddler = this.fetchTiddler(title);
4298 return tiddler.text;
4299 var re = new RegExp("(^!{
1,
6}
" + section.escapeRegExp() + "[ \t]*\n)
","mg
");
4301 var match = re.exec(tiddler.text);
4303 var t = tiddler.text.substr(match.index+match[1].length);
4306 match = re2.exec(t); //# search for the next heading
4308 t = t.substr(0,match.index-1);//# don't include final \n
4313 if(this.isShadowTiddler(title))
4314 return config.shadowTiddlers[title];
4315 if(defaultText != undefined)
4320 TiddlyWiki.prototype.getRecursiveTiddlerText = function(title,defaultText,depth)
4322 var bracketRegExp = new RegExp("(?:\\[\\[([^\\]]+)\\]\\])
","mg
");
4323 var text = this.getTiddlerText(title,null);
4329 var match = bracketRegExp.exec(text);
4331 textOut.push(text.substr(lastPos,match.index-lastPos));
4334 textOut.push(match[1]);
4336 textOut.push(this.getRecursiveTiddlerText(match[1],"[[
" + match[1] + "]]
",depth-1));
4338 lastPos = match.index + match[0].length;
4340 textOut.push(text.substr(lastPos));
4343 return textOut.join("");
4346 TiddlyWiki.prototype.slicesRE = /(?:^([\'\/]{0,2})~?([\.\w]+)\:\1\s*([^\n]+)\s*$)|(?:^\|([\'\/]{0,2})~?([\.\w]+)\:?\4\|\s*([^\|\n]+)\s*\|$)/gm;
4349 TiddlyWiki.prototype.calcAllSlices = function(title)
4352 var text = this.getTiddlerText(title,"");
4353 this.slicesRE.lastIndex = 0;
4354 var m = this.slicesRE.exec(text);
4357 slices[m[2]] = m[3];
4359 slices[m[5]] = m[6];
4360 m = this.slicesRE.exec(text);
4365 // Returns the slice of text of the given name
4366 TiddlyWiki.prototype.getTiddlerSlice = function(title,sliceName)
4368 var slices = this.slices[title];
4370 slices = this.calcAllSlices(title);
4371 this.slices[title] = slices;
4373 return slices[sliceName];
4376 // Build an hashmap of the specified named slices of a tiddler
4377 TiddlyWiki.prototype.getTiddlerSlices = function(title,sliceNames)
4380 for(var t=0; t<sliceNames.length; t++) {
4381 var slice = this.getTiddlerSlice(title,sliceNames[t]);
4383 r[sliceNames[t]] = slice;
4388 TiddlyWiki.prototype.suspendNotifications = function()
4390 this.notificationLevel--;
4393 TiddlyWiki.prototype.resumeNotifications = function()
4395 this.notificationLevel++;
4398 // Invoke the notification handlers for a particular tiddler
4399 TiddlyWiki.prototype.notify = function(title,doBlanket)
4401 if(!this.notificationLevel) {
4402 for(var t=0; t<this.namedNotifications.length; t++) {
4403 var n = this.namedNotifications[t];
4404 if((n.name == null && doBlanket) || (n.name == title))
4410 // Invoke the notification handlers for all tiddlers
4411 TiddlyWiki.prototype.notifyAll = function()
4413 if(!this.notificationLevel) {
4414 for(var t=0; t<this.namedNotifications.length; t++) {
4415 var n = this.namedNotifications[t];
4422 // Add a notification handler to a tiddler
4423 TiddlyWiki.prototype.addNotification = function(title,fn)
4425 for(var i=0; i<this.namedNotifications.length; i++) {
4426 if((this.namedNotifications[i].name == title) && (this.namedNotifications[i].notify == fn))
4429 this.namedNotifications.push({name: title, notify: fn});
4433 TiddlyWiki.prototype.removeTiddler = function(title)
4435 var tiddler = this.fetchTiddler(title);
4437 this.deleteTiddler(title);
4438 this.notify(title,true);
4439 this.setDirty(true);
4443 // Reset the sync status of a freshly synced tiddler
4444 TiddlyWiki.prototype.resetTiddler = function(title)
4446 var tiddler = this.fetchTiddler(title);
4448 tiddler.clearChangeCount();
4449 this.notify(title,true);
4450 this.setDirty(true);
4454 TiddlyWiki.prototype.setTiddlerTag = function(title,status,tag)
4456 var tiddler = this.fetchTiddler(title);
4458 var t = tiddler.tags.indexOf(tag);
4460 tiddler.tags.splice(t,1);
4462 tiddler.tags.push(tag);
4464 tiddler.incChangeCount(title);
4465 this.notify(title,true);
4466 this.setDirty(true);
4470 TiddlyWiki.prototype.addTiddlerFields = function(title,fields)
4472 var tiddler = this.fetchTiddler(title);
4475 merge(tiddler.fields,fields);
4477 tiddler.incChangeCount(title);
4478 this.notify(title,true);
4479 this.setDirty(true);
4482 TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created)
4484 var tiddler = this.fetchTiddler(title);
4486 created = created || tiddler.created; // Preserve created date
4487 this.deleteTiddler(title);
4489 created = created || modified;
4490 tiddler = new Tiddler();
4492 tiddler.set(newTitle,newBody,modifier,modified,tags,created,fields);
4493 this.addTiddler(tiddler);
4494 if(clearChangeCount)
4495 tiddler.clearChangeCount();
4497 tiddler.incChangeCount();
4498 if(title != newTitle)
4499 this.notify(title,true);
4500 this.notify(newTitle,true);
4501 this.setDirty(true);
4505 TiddlyWiki.prototype.incChangeCount = function(title)
4507 var tiddler = this.fetchTiddler(title);
4509 tiddler.incChangeCount();
4512 TiddlyWiki.prototype.getLoader = function()
4515 this.loader = new TW21Loader();
4519 TiddlyWiki.prototype.getSaver = function()
4522 this.saver = new TW21Saver();
4526 // Return all tiddlers formatted as an HTML string
4527 TiddlyWiki.prototype.allTiddlersAsHtml = function()
4529 return this.getSaver().externalize(store);
4532 // Load contents of a TiddlyWiki from an HTML DIV
4533 TiddlyWiki.prototype.loadFromDiv = function(src,idPrefix,noUpdate)
4535 this.idPrefix = idPrefix;
4536 var storeElem = (typeof src == "string
") ? document.getElementById(src) : src;
4539 var tiddlers = this.getLoader().loadTiddlers(this,storeElem.childNodes);
4540 this.setDirty(false);
4542 for(var i = 0;i<tiddlers.length; i++)
4543 tiddlers[i].changed();
4547 // Load contents of a TiddlyWiki from a string
4548 // Returns null if there's an error
4549 TiddlyWiki.prototype.importTiddlyWiki = function(text)
4551 var posDiv = locateStoreArea(text);
4554 var content = "<
" + "html
><
" + "body
>" + text.substring(posDiv[0],posDiv[1] + endSaveArea.length) + "<
" + "/body
><
" + "/html
>";
4555 // Create the iframe
4556 var iframe = document.createElement("iframe
");
4557 iframe.style.display = "none
";
4558 document.body.appendChild(iframe);
4559 var doc = iframe.document;
4560 if(iframe.contentDocument)
4561 doc = iframe.contentDocument; // For NS6
4562 else if(iframe.contentWindow)
4563 doc = iframe.contentWindow.document; // For IE5.5 and IE6
4564 // Put the content in the iframe
4566 doc.writeln(content);
4568 // Load the content into a TiddlyWiki() object
4569 var storeArea = doc.getElementById("storeArea
");
4570 this.loadFromDiv(storeArea,"store
");
4571 // Get rid of the iframe
4572 iframe.parentNode.removeChild(iframe);
4576 TiddlyWiki.prototype.updateTiddlers = function()
4578 this.tiddlersUpdated = true;
4579 this.forEachTiddler(function(title,tiddler) {
4584 // Return an array of tiddlers matching a search regular expression
4585 TiddlyWiki.prototype.search = function(searchRegExp,sortField,excludeTag,match)
4587 var candidates = this.reverseLookup("tags
",excludeTag,!!match);
4589 for(var t=0; t<candidates.length; t++) {
4590 if((candidates[t].title.search(searchRegExp) != -1) || (candidates[t].text.search(searchRegExp) != -1))
4591 results.push(candidates[t]);
4594 sortField = "title
";
4595 results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
4599 // Returns a list of all tags in use
4600 // excludeTag - if present, excludes tags that are themselves tagged with excludeTag
4601 // Returns an array of arrays where [tag][0] is the name of the tag and [tag][1] is the number of occurances
4602 TiddlyWiki.prototype.getTags = function(excludeTag)
4605 this.forEachTiddler(function(title,tiddler) {
4606 for(var g=0; g<tiddler.tags.length; g++) {
4607 var tag = tiddler.tags[g];
4609 for(var c=0; c<results.length; c++) {
4610 if(results[c][0] == tag) {
4615 if(n && excludeTag) {
4616 var t = this.fetchTiddler(tag);
4617 if(t && t.isTagged(excludeTag))
4621 results.push([tag,1]);
4624 results.sort(function(a,b) {return a[0].toLowerCase() < b[0].toLowerCase() ? -1 : (a[0].toLowerCase() == b[0].toLowerCase() ? 0 : +1);});
4628 // Return an array of the tiddlers that are tagged with a given tag
4629 TiddlyWiki.prototype.getTaggedTiddlers = function(tag,sortField)
4631 return this.reverseLookup("tags
",tag,true,sortField);
4634 // Return an array of the tiddlers that link to a given tiddler
4635 TiddlyWiki.prototype.getReferringTiddlers = function(title,unusedParameter,sortField)
4637 if(!this.tiddlersUpdated)
4638 this.updateTiddlers();
4639 return this.reverseLookup("links
",title,true,sortField);
4642 // Return an array of the tiddlers that do or do not have a specified entry in the specified storage array (ie, "links
" or "tags
")
4643 // lookupMatch == true to match tiddlers, false to exclude tiddlers
4644 TiddlyWiki.prototype.reverseLookup = function(lookupField,lookupValue,lookupMatch,sortField)
4647 this.forEachTiddler(function(title,tiddler) {
4648 var f = !lookupMatch;
4649 for(var lookup=0; lookup<tiddler[lookupField].length; lookup++) {
4650 if(tiddler[lookupField][lookup] == lookupValue)
4654 results.push(tiddler);
4657 sortField = "title
";
4658 results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
4662 // Return the tiddlers as a sorted array
4663 TiddlyWiki.prototype.getTiddlers = function(field,excludeTag)
4666 this.forEachTiddler(function(title,tiddler) {
4667 if(excludeTag == undefined || !tiddler.isTagged(excludeTag))
4668 results.push(tiddler);
4671 results.sort(function(a,b) {return a[field] < b[field] ? -1 : (a[field] == b[field] ? 0 : +1);});
4675 // Return array of names of tiddlers that are referred to but not defined
4676 TiddlyWiki.prototype.getMissingLinks = function(sortField)
4678 if(!this.tiddlersUpdated)
4679 this.updateTiddlers();
4681 this.forEachTiddler(function (title,tiddler) {
4682 if(tiddler.isTagged("excludeMissing
") || tiddler.isTagged("systemConfig
"))
4684 for(var n=0; n<tiddler.links.length;n++) {
4685 var link = tiddler.links[n];
4686 if(this.fetchTiddler(link) == null && !this.isShadowTiddler(link))
4687 results.pushUnique(link);
4694 // Return an array of names of tiddlers that are defined but not referred to
4695 TiddlyWiki.prototype.getOrphans = function()
4698 this.forEachTiddler(function (title,tiddler) {
4699 if(this.getReferringTiddlers(title).length == 0 && !tiddler.isTagged("excludeLists
"))
4700 results.push(title);
4706 // Return an array of names of all the shadow tiddlers
4707 TiddlyWiki.prototype.getShadowed = function()
4710 for(var t in config.shadowTiddlers) {
4711 if(typeof config.shadowTiddlers[t] == "string
")
4718 // Return an array of tiddlers that have been touched since they were downloaded or created
4719 TiddlyWiki.prototype.getTouched = function()
4722 this.forEachTiddler(function(title,tiddler) {
4723 if(tiddler.isTouched())
4724 results.push(tiddler);
4730 // Resolves a Tiddler reference or tiddler title into a Tiddler object, or null if it doesn't exist
4731 TiddlyWiki.prototype.resolveTiddler = function(tiddler)
4733 var t = (typeof tiddler == 'string') ? this.getTiddler(tiddler) : tiddler;
4734 return t instanceof Tiddler ? t : null;
4737 // Filter a list of tiddlers
4738 TiddlyWiki.prototype.filterTiddlers = function(filter)
4743 var re = /([^\s\[\]]+)|(?:\[([ \w]+)\[([^\]]+)\]\])|(?:\[\[([^\]]+)\]\])/mg;
4744 var match = re.exec(filter);
4746 if(match[1] || match[4]) {
4747 var title = match[1] || match[4];
4748 tiddler = this.fetchTiddler(title);
4750 results.pushUnique(tiddler);
4751 } else if(this.isShadowTiddler(title)) {
4752 tiddler = new Tiddler();
4753 tiddler.set(title,this.getTiddlerText(title));
4754 results.pushUnique(tiddler);
4756 } else if(match[2]) {
4759 var matched = this.getTaggedTiddlers(match[3]);
4760 for(var m = 0; m < matched.length; m++)
4761 results.pushUnique(matched[m]);
4764 results = this.sortTiddlers(results,match[3]);
4768 match = re.exec(filter);
4774 // Sort a list of tiddlers
4775 TiddlyWiki.prototype.sortTiddlers = function(tiddlers,field)
4778 switch(field.substr(0,1)) {
4781 // Note: this fall-through is intentional
4784 field = field.substr(1);
4787 if(TiddlyWiki.standardFieldAccess[field])
4788 tiddlers.sort(function(a,b) {return a[field] < b[field] ? -asc : (a[field] == b[field] ? 0 : asc);});
4790 tiddlers.sort(function(a,b) {return a.fields[field] < b.fields[field] ? -asc : (a.fields[field] == b.fields[field] ? 0 : +asc);});
4794 // Returns true if path is a valid field name (path),
4795 // i.e. a sequence of identifiers, separated by '.'
4796 TiddlyWiki.isValidFieldName = function(name)
4798 var match = /[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*/.exec(name);
4799 return match && (match[0] == name);
4802 // Throws an exception when name is not a valid field name.
4803 TiddlyWiki.checkFieldName = function(name)
4805 if(!TiddlyWiki.isValidFieldName(name))
4806 throw config.messages.invalidFieldName.format([name]);
4809 function StringFieldAccess(n,readOnly)
4811 this.set = readOnly ?
4812 function(t,v) {if(v != t[n]) throw config.messages.fieldCannotBeChanged.format([n]);} :
4813 function(t,v) {if(v != t[n]) {t[n] = v; return true;}};
4814 this.get = function(t) {return t[n];};
4817 function DateFieldAccess(n)
4819 this.set = function(t,v) {
4820 var d = v instanceof Date ? v : Date.convertFromYYYYMMDDHHMM(v);
4822 t[n] = d; return true;
4825 this.get = function(t) {return t[n].convertToYYYYMMDDHHMM();};
4828 function LinksFieldAccess(n)
4830 this.set = function(t,v) {
4831 var s = (typeof v == "string
") ? v.readBracketedList() : v;
4832 if(s.toString() != t[n].toString()) {
4833 t[n] = s; return true;
4836 this.get = function(t) {return String.encodeTiddlyLinkList(t[n]);};
4839 TiddlyWiki.standardFieldAccess = {
4840 // The set functions return true when setting the data has changed the value.
4841 "title
": new StringFieldAccess("title
",true),
4842 // Handle the "tiddler
" field name as the title
4843 "tiddler
": new StringFieldAccess("title
",true),
4844 "text
": new StringFieldAccess("text
"),
4845 "modifier
": new StringFieldAccess("modifier
"),
4846 "modified
": new DateFieldAccess("modified
"),
4847 "created
": new DateFieldAccess("created
"),
4848 "tags
": new LinksFieldAccess("tags
")
4851 TiddlyWiki.isStandardField = function(name)
4853 return TiddlyWiki.standardFieldAccess[name] != undefined;
4856 // Sets the value of the given field of the tiddler to the value.
4857 // Setting an ExtendedField's value to null or undefined removes the field.
4858 // Setting a namespace to undefined removes all fields of that namespace.
4859 // The fieldName is case-insensitive.
4860 // All values will be converted to a string value.
4861 TiddlyWiki.prototype.setValue = function(tiddler,fieldName,value)
4863 TiddlyWiki.checkFieldName(fieldName);
4864 var t = this.resolveTiddler(tiddler);
4867 fieldName = fieldName.toLowerCase();
4868 var isRemove = (value === undefined) || (value === null);
4869 var accessor = TiddlyWiki.standardFieldAccess[fieldName];
4872 // don't remove StandardFields
4874 var h = TiddlyWiki.standardFieldAccess[fieldName];
4878 var oldValue = t.fields[fieldName];
4880 if(oldValue !== undefined) {
4881 // deletes a single field
4882 delete t.fields[fieldName];
4884 // no concrete value is defined for the fieldName
4885 // so we guess this is a namespace path.
4886 // delete all fields in a namespace
4887 var re = new RegExp('^'+fieldName+'\\.');
4889 for(var n in t.fields) {
4899 // the "normal
" set case. value is defined (not null/undefined)
4900 // For convenience provide a nicer conversion Date->String
4901 value = value instanceof Date ? value.convertToYYYYMMDDHHMMSSMMM() : String(value);
4902 if(oldValue == value)
4904 t.fields[fieldName] = value;
4907 // When we are here the tiddler/store really was changed.
4908 this.notify(t.title,true);
4909 if(!fieldName.match(/^temp\./))
4910 this.setDirty(true);
4913 // Returns the value of the given field of the tiddler.
4914 // The fieldName is case-insensitive.
4915 // Will only return String values (or undefined).
4916 TiddlyWiki.prototype.getValue = function(tiddler,fieldName)
4918 var t = this.resolveTiddler(tiddler);
4921 fieldName = fieldName.toLowerCase();
4922 var accessor = TiddlyWiki.standardFieldAccess[fieldName];
4924 return accessor.get(t);
4926 return t.fields[fieldName];
4929 // Calls the callback function for every field in the tiddler.
4930 // When callback function returns a non-false value the iteration stops
4931 // and that value is returned.
4932 // The order of the fields is not defined.
4933 // @param callback a function(tiddler,fieldName,value).
4934 TiddlyWiki.prototype.forEachField = function(tiddler,callback,onlyExtendedFields)
4936 var t = this.resolveTiddler(tiddler);
4940 for(n in t.fields) {
4941 result = callback(t,n,t.fields[n]);
4945 if(onlyExtendedFields)
4947 for(n in TiddlyWiki.standardFieldAccess) {
4949 // even though the "title
" field can also be referenced through the name "tiddler
"
4950 // we only visit this field once.
4952 result = callback(t,n,TiddlyWiki.standardFieldAccess[n].get(t));
4960 //-- Story functions
4963 function Story(containerId,idPrefix)
4965 this.container = containerId;
4966 this.idPrefix = idPrefix;
4967 this.highlightRegExp = null;
4968 this.tiddlerId = function(title) {
4969 var id = this.idPrefix + title;
4970 return id==this.container ? this.idPrefix + "_
" + title : id;
4972 this.containerId = function() {
4973 return this.container;
4977 Story.prototype.getTiddler = function(title)
4979 return document.getElementById(this.tiddlerId(title));
4982 Story.prototype.getContainer = function()
4984 return document.getElementById(this.containerId());
4987 Story.prototype.forEachTiddler = function(fn)
4989 var place = this.getContainer();
4992 var e = place.firstChild;
4994 var n = e.nextSibling;
4995 var title = e.getAttribute("tiddler
");
4996 fn.call(this,title,e);
5001 Story.prototype.displayDefaultTiddlers = function()
5003 this.displayTiddlers(null,store.filterTiddlers(store.getTiddlerText("DefaultTiddlers
")));
5006 Story.prototype.displayTiddlers = function(srcElement,titles,template,animate,unused,customFields,toggle)
5008 for(var t = titles.length-1;t>=0;t--)
5009 this.displayTiddler(srcElement,titles[t],template,animate,unused,customFields);
5012 Story.prototype.displayTiddler = function(srcElement,tiddler,template,animate,unused,customFields,toggle,animationSrc)
5014 var title = (tiddler instanceof Tiddler) ? tiddler.title : tiddler;
5015 var tiddlerElem = this.getTiddler(title);
5018 this.closeTiddler(title,true);
5020 this.refreshTiddler(title,template,false,customFields);
5022 var place = this.getContainer();
5023 var before = this.positionTiddler(srcElement);
5024 tiddlerElem = this.createTiddler(place,before,title,template,customFields);
5026 if(animationSrc && typeof animationSrc !== "string
") {
5027 srcElement = animationSrc;
5029 if(srcElement && typeof srcElement !== "string
") {
5030 if(config.options.chkAnimate && (animate == undefined || animate == true) && anim && typeof Zoomer == "function
" && typeof Scroller == "function
")
5031 anim.startAnimating(new Zoomer(title,srcElement,tiddlerElem),new Scroller(tiddlerElem));
5033 window.scrollTo(0,ensureVisible(tiddlerElem));
5037 Story.prototype.positionTiddler = function(srcElement)
5039 var place = this.getContainer();
5041 if(typeof srcElement == "string
") {
5042 switch(srcElement) {
5044 before = place.firstChild;
5051 var after = this.findContainingTiddler(srcElement);
5053 before = place.firstChild;
5054 } else if(after.nextSibling) {
5055 before = after.nextSibling;
5056 if(before.nodeType != 1)
5063 Story.prototype.createTiddler = function(place,before,title,template,customFields)
5065 var tiddlerElem = createTiddlyElement(null,"div
",this.tiddlerId(title),"tiddler
");
5066 tiddlerElem.setAttribute("refresh
","tiddler
");
5068 tiddlerElem.setAttribute("tiddlyFields
",customFields);
5069 place.insertBefore(tiddlerElem,before);
5070 var defaultText = null;
5071 if(!store.tiddlerExists(title) && !store.isShadowTiddler(title))
5072 defaultText = this.loadMissingTiddler(title,customFields,tiddlerElem);
5073 this.refreshTiddler(title,template,false,customFields,defaultText);
5077 Story.prototype.loadMissingTiddler = function(title,fields,tiddlerElem)
5079 var tiddler = new Tiddler(title);
5080 tiddler.fields = typeof fields == "string
" ? fields.decodeHashMap() : (fields || {});
5081 var serverType = tiddler.getServerType();
5082 var host = tiddler.fields['server.host'];
5083 var workspace = tiddler.fields['server.workspace'];
5084 if(!serverType || !host)
5086 var sm = new SyncMachine(serverType,{
5088 return this.openHost(host,"openWorkspace
");
5090 openWorkspace: function() {
5091 return this.openWorkspace(workspace,"getTiddler
");
5093 getTiddler: function() {
5094 return this.getTiddler(title,"onGetTiddler
");
5096 onGetTiddler: function(context) {
5097 var tiddler = context.tiddler;
5098 if(tiddler && tiddler.text) {
5099 var downloaded = new Date();
5100 if(!tiddler.created)
5101 tiddler.created = downloaded;
5102 if(!tiddler.modified)
5103 tiddler.modified = tiddler.created;
5104 store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields,true,tiddler.created);
5110 error: function(message) {
5111 displayMessage("Error loading missing tiddler from %
0: %
1".format([host,message]));
5115 return config.messages.loadingMissingTiddler.format([title,serverType,host,workspace]);
5118 Story.prototype.chooseTemplateForTiddler = function(title,template)
5121 template = DEFAULT_VIEW_TEMPLATE;
5122 if(template == DEFAULT_VIEW_TEMPLATE || template == DEFAULT_EDIT_TEMPLATE)
5123 template = config.tiddlerTemplates[template];
5127 Story.prototype.getTemplateForTiddler = function(title,template,tiddler)
5129 return store.getRecursiveTiddlerText(template,null,10);
5132 Story.prototype.refreshTiddler = function(title,template,force,customFields,defaultText)
5134 var tiddlerElem = this.getTiddler(title);
5136 if(tiddlerElem.getAttribute("dirty
") == "true
" && !force)
5138 template = this.chooseTemplateForTiddler(title,template);
5139 var currTemplate = tiddlerElem.getAttribute("template
");
5140 if((template != currTemplate) || force) {
5141 var tiddler = store.getTiddler(title);
5143 tiddler = new Tiddler();
5144 if(store.isShadowTiddler(title)) {
5145 tiddler.set(title,store.getTiddlerText(title),config.views.wikified.shadowModifier,version.date,[],version.date);
5147 var text = template=="EditTemplate
" ?
5148 config.views.editor.defaultText.format([title]) :
5149 config.views.wikified.defaultText.format([title]);
5150 text = defaultText || text;
5151 var fields = customFields ? customFields.decodeHashMap() : null;
5152 tiddler.set(title,text,config.views.wikified.defaultModifier,version.date,[],version.date,fields);
5155 tiddlerElem.setAttribute("tags
",tiddler.tags.join(" "));
5156 tiddlerElem.setAttribute("tiddler
",title);
5157 tiddlerElem.setAttribute("template
",template);
5158 tiddlerElem.onmouseover = this.onTiddlerMouseOver;
5159 tiddlerElem.onmouseout = this.onTiddlerMouseOut;
5160 tiddlerElem.ondblclick = this.onTiddlerDblClick;
5161 tiddlerElem[window.event?"onkeydown
":"onkeypress
"] = this.onTiddlerKeyPress;
5162 tiddlerElem.innerHTML = this.getTemplateForTiddler(title,template,tiddler);
5163 applyHtmlMacros(tiddlerElem,tiddler);
5164 if(store.getTaggedTiddlers(title).length > 0)
5165 addClass(tiddlerElem,"isTag
");
5167 removeClass(tiddlerElem,"isTag
");
5168 if(store.tiddlerExists(title)) {
5169 removeClass(tiddlerElem,"shadow
");
5170 removeClass(tiddlerElem,"missing
");
5172 addClass(tiddlerElem,store.isShadowTiddler(title) ? "shadow
" : "missing
");
5175 this.addCustomFields(tiddlerElem,customFields);
5182 Story.prototype.addCustomFields = function(place,customFields)
5184 var fields = customFields.decodeHashMap();
5185 var w = document.createElement("div
");
5186 w.style.display = "none
";
5187 place.appendChild(w);
5188 for(var t in fields) {
5189 var e = document.createElement("input
");
5190 e.setAttribute("type
","text
");
5191 e.setAttribute("value
",fields[t]);
5193 e.setAttribute("edit
",t);
5197 Story.prototype.refreshAllTiddlers = function(force)
5199 var e = this.getContainer().firstChild;
5201 var template = e.getAttribute("template
");
5202 if(template && e.getAttribute("dirty
") != "true
") {
5203 this.refreshTiddler(e.getAttribute("tiddler
"),force ? null : template,true);
5209 Story.prototype.onTiddlerMouseOver = function(e)
5211 if(window.addClass instanceof Function)
5212 addClass(this,"selected
");
5215 Story.prototype.onTiddlerMouseOut = function(e)
5217 if(window.removeClass instanceof Function)
5218 removeClass(this,"selected
");
5221 Story.prototype.onTiddlerDblClick = function(ev)
5223 var e = ev || window.event;
5224 var target = resolveTarget(e);
5225 if(target && target.nodeName.toLowerCase() != "input
" && target.nodeName.toLowerCase() != "textarea
") {
5226 if(document.selection && document.selection.empty)
5227 document.selection.empty();
5228 config.macros.toolbar.invokeCommand(this,"defaultCommand
",e);
5229 e.cancelBubble = true;
5230 if(e.stopPropagation) e.stopPropagation();
5236 Story.prototype.onTiddlerKeyPress = function(ev)
5238 var e = ev || window.event;
5240 var consume = false;
5241 var title = this.getAttribute("tiddler
");
5242 var target = resolveTarget(e);
5245 if(config.options.chkInsertTabs && target.tagName.toLowerCase() == "textarea
") {
5246 replaceSelection(target,String.fromCharCode(9));
5249 if(config.isOpera) {
5250 target.onblur = function() {
5256 case 13: // Ctrl-Enter
5257 case 10: // Ctrl-Enter on IE PC
5258 case 77: // Ctrl-Enter is "M
" on some platforms
5261 config.macros.toolbar.invokeCommand(this,"defaultCommand
",e);
5267 config.macros.toolbar.invokeCommand(this,"cancelCommand
",e);
5271 e.cancelBubble = consume;
5273 if(e.stopPropagation) e.stopPropagation(); // Stop Propagation
5274 e.returnValue = true; // Cancel The Event in IE
5275 if(e.preventDefault ) e.preventDefault(); // Cancel The Event in Moz
5280 Story.prototype.getTiddlerField = function(title,field)
5282 var tiddlerElem = this.getTiddler(title);
5285 var children = tiddlerElem.getElementsByTagName("*
");
5286 for(var t=0; t<children.length; t++) {
5287 var c = children[t];
5288 if(c.tagName.toLowerCase() == "input
" || c.tagName.toLowerCase() == "textarea
") {
5291 if(c.getAttribute("edit
") == field)
5299 Story.prototype.focusTiddler = function(title,field)
5301 var e = this.getTiddlerField(title,field);
5308 Story.prototype.blurTiddler = function(title)
5310 var tiddlerElem = this.getTiddler(title);
5311 if(tiddlerElem && tiddlerElem.focus && tiddlerElem.blur) {
5312 tiddlerElem.focus();
5317 Story.prototype.setTiddlerField = function(title,tag,mode,field)
5319 var c = this.getTiddlerField(title,field);
5320 var tags = c.value.readBracketedList();
5321 tags.setItem(tag,mode);
5322 c.value = String.encodeTiddlyLinkList(tags);
5325 Story.prototype.setTiddlerTag = function(title,tag,mode)
5327 this.setTiddlerField(title,tag,mode,"tags
");
5330 Story.prototype.closeTiddler = function(title,animate,unused)
5332 var tiddlerElem = this.getTiddler(title);
5335 this.scrubTiddler(tiddlerElem);
5336 if(config.options.chkAnimate && animate && anim && typeof Slider == "function
")
5337 anim.startAnimating(new Slider(tiddlerElem,false,null,"all
"));
5339 removeNode(tiddlerElem);
5345 Story.prototype.scrubTiddler = function(tiddlerElem)
5347 tiddlerElem.id = null;
5350 Story.prototype.setDirty = function(title,dirty)
5352 var tiddlerElem = this.getTiddler(title);
5354 tiddlerElem.setAttribute("dirty
",dirty ? "true
" : "false
");
5357 Story.prototype.isDirty = function(title)
5359 var tiddlerElem = this.getTiddler(title);
5361 return tiddlerElem.getAttribute("dirty
") == "true
";
5365 Story.prototype.areAnyDirty = function()
5368 this.forEachTiddler(function(title,element) {
5369 if(this.isDirty(title))
5375 Story.prototype.closeAllTiddlers = function(exclude)
5378 this.forEachTiddler(function(title,element) {
5379 if((title != exclude) && element.getAttribute("dirty
") != "true
")
5380 this.closeTiddler(title);
5382 window.scrollTo(0,ensureVisible(this.container));
5385 Story.prototype.isEmpty = function()
5387 var place = this.getContainer();
5388 return place && place.firstChild == null;
5391 Story.prototype.search = function(text,useCaseSensitive,useRegExp)
5393 this.closeAllTiddlers();
5394 highlightHack = new RegExp(useRegExp ? text : text.escapeRegExp(),useCaseSensitive ? "mg
" : "img
");
5395 var matches = store.search(highlightHack,"title
","excludeSearch
");
5396 this.displayTiddlers(null,matches);
5397 highlightHack = null;
5398 var q = useRegExp ? "/
" : "'
";
5399 if(matches.length > 0)
5400 displayMessage(config.macros.search.successMsg.format([matches.length.toString(),q + text + q]));
5402 displayMessage(config.macros.search.failureMsg.format([q + text + q]));
5405 Story.prototype.findContainingTiddler = function(e)
5407 while(e && !hasClass(e,"tiddler
"))
5412 Story.prototype.gatherSaveFields = function(e,fields)
5414 if(e && e.getAttribute) {
5415 var f = e.getAttribute("edit
");
5417 fields[f] = e.value.replace(/\r/mg,"");
5418 if(e.hasChildNodes()) {
5419 var c = e.childNodes;
5420 for(var t=0; t<c.length; t++)
5421 this.gatherSaveFields(c[t],fields);
5426 Story.prototype.hasChanges = function(title)
5428 var e = this.getTiddler(title);
5431 this.gatherSaveFields(e,fields);
5432 var tiddler = store.fetchTiddler(title);
5435 for(var n in fields) {
5436 if(store.getValue(title,n) != fields[n])
5443 Story.prototype.saveTiddler = function(title,minorUpdate)
5445 var tiddlerElem = this.getTiddler(title);
5448 this.gatherSaveFields(tiddlerElem,fields);
5449 var newTitle = fields.title || title;
5450 if(!store.tiddlerExists(newTitle))
5451 newTitle = newTitle.trim();
5452 if(store.tiddlerExists(newTitle) && newTitle != title) {
5453 if(!confirm(config.messages.overwriteWarning.format([newTitle.toString()])))
5456 if(newTitle != title)
5457 this.closeTiddler(newTitle,false);
5458 tiddlerElem.id = this.tiddlerId(newTitle);
5459 tiddlerElem.setAttribute("tiddler
",newTitle);
5460 tiddlerElem.setAttribute("template
",DEFAULT_VIEW_TEMPLATE);
5461 tiddlerElem.setAttribute("dirty
","false
");
5462 if(config.options.chkForceMinorUpdate)
5463 minorUpdate = !minorUpdate;
5464 if(!store.tiddlerExists(newTitle))
5465 minorUpdate = false;
5466 var newDate = new Date();
5467 var extendedFields = store.tiddlerExists(newTitle) ? store.fetchTiddler(newTitle).fields : (newTitle!=title && store.tiddlerExists(title) ? store.fetchTiddler(title).fields : config.defaultCustomFields);
5468 for(var n in fields) {
5469 if(!TiddlyWiki.isStandardField(n))
5470 extendedFields[n] = fields[n];
5472 var tiddler = store.saveTiddler(title,newTitle,fields.text,minorUpdate ? undefined : config.options.txtUserName,minorUpdate ? undefined : newDate,fields.tags,extendedFields);
5473 autoSaveChanges(null,[tiddler]);
5479 Story.prototype.permaView = function()
5482 this.forEachTiddler(function(title,element) {
5483 links.push(String.encodeTiddlyLink(title));
5485 var t = encodeURIComponent(links.join(" "));
5488 if(window.location.hash != t)
5489 window.location.hash = t;
5492 Story.prototype.switchTheme = function(theme)
5497 var isAvailable = function(title) {
5498 var s = title ? title.indexOf(config.textPrimitives.sectionSeparator) : -1;
5500 title = title.substr(0,s);
5501 return store.tiddlerExists(title) || store.isShadowTiddler(title);
5504 var getSlice = function(theme,slice) {
5507 r = store.getTiddlerSlice(theme,slice+"ReadOnly
") || store.getTiddlerSlice(theme,"Web
"+slice);
5508 r = r || store.getTiddlerSlice(theme,slice);
5509 if(r && r.indexOf(config.textPrimitives.sectionSeparator)==0)
5511 return isAvailable(r) ? r : slice;
5514 var replaceNotification = function(i,name,theme,slice) {
5515 var newName = getSlice(theme,slice);
5516 if(name!=newName && store.namedNotifications[i].name==name) {
5517 store.namedNotifications[i].name = newName;
5523 var pt = config.refresherData.pageTemplate;
5524 var vi = DEFAULT_VIEW_TEMPLATE;
5525 var vt = config.tiddlerTemplates[vi];
5526 var ei = DEFAULT_EDIT_TEMPLATE;
5527 var et = config.tiddlerTemplates[ei];
5529 for(var i=0; i<config.notifyTiddlers.length; i++) {
5530 var name = config.notifyTiddlers[i].name;
5532 case "PageTemplate
":
5533 config.refresherData.pageTemplate = replaceNotification(i,config.refresherData.pageTemplate,theme,name);
5536 removeStyleSheet(config.refresherData.styleSheet);
5537 config.refresherData.styleSheet = replaceNotification(i,config.refresherData.styleSheet,theme,name);
5539 case "ColorPalette
":
5540 config.refresherData.colorPalette = replaceNotification(i,config.refresherData.colorPalette,theme,name);
5546 config.tiddlerTemplates[vi] = getSlice(theme,"ViewTemplate
");
5547 config.tiddlerTemplates[ei] = getSlice(theme,"EditTemplate
");
5549 if(config.refresherData.pageTemplate!=pt || config.tiddlerTemplates[vi]!=vt || config.tiddlerTemplates[ei]!=et) {
5551 this.refreshAllTiddlers(true);
5553 setStylesheet(store.getRecursiveTiddlerText(config.refresherData.styleSheet,"",10),config.refreshers.styleSheet);
5555 config.options.txtTheme = theme;
5556 saveOptionCookie("txtTheme
");
5579 var cmb = config.messages.backstage;
5580 this.area = document.getElementById("backstageArea
");
5581 this.toolbar = document.getElementById("backstageToolbar
");
5582 this.button = document.getElementById("backstageButton
");
5583 this.button.style.display = "block
";
5584 var t = cmb.open.text + " " + glyph("bentArrowLeft
");
5585 this.showButton = createTiddlyButton(this.button,t,cmb.open.tooltip,
5586 function(e) {backstage.show(); return false;},null,"backstageShow
");
5587 t = glyph("bentArrowRight
") + " " + cmb.close.text;
5588 this.hideButton = createTiddlyButton(this.button,t,cmb.close.tooltip,
5589 function(e) {backstage.hide(); return false;},null,"backstageHide
");
5590 this.cloak = document.getElementById("backstageCloak
");
5591 this.panel = document.getElementById("backstagePanel
");
5592 this.panelFooter = createTiddlyElement(this.panel,"div
",null,"backstagePanelFooter
");
5593 this.panelBody = createTiddlyElement(this.panel,"div
",null,"backstagePanelBody
");
5594 this.cloak.onmousedown = function(e) {backstage.switchTab(null);};
5595 createTiddlyText(this.toolbar,cmb.prompt);
5596 for(t=0; t<config.backstageTasks.length; t++) {
5597 var taskName = config.backstageTasks[t];
5598 var task = config.tasks[taskName];
5599 var handler = task.action ? this.onClickCommand : this.onClickTab;
5600 var text = task.text + (task.action ? "" : glyph("downTriangle
"));
5601 var btn = createTiddlyButton(this.toolbar,text,task.tooltip,handler,"backstageTab
");
5602 btn.setAttribute("task
",taskName);
5603 addClass(btn,task.action ? "backstageAction
" : "backstageTask
");
5605 this.content = document.getElementById("contentWrapper
");
5606 if(config.options.chkBackstage)
5612 isVisible: function() {
5613 return this.area ? this.area.style.display == "block
" : false;
5617 this.area.style.display = "block
";
5618 if(anim && config.options.chkAnimate) {
5619 backstage.toolbar.style.left = findWindowWidth() + "px
";
5620 var p = [{style: "left
", start: findWindowWidth(), end: 0, template: "%
0px
"}];
5621 anim.startAnimating(new Morpher(backstage.toolbar,config.animDuration,p));
5623 backstage.area.style.left = "0px
";
5625 this.showButton.style.display = "none
";
5626 this.hideButton.style.display = "block
";
5627 config.options.chkBackstage = true;
5628 saveOptionCookie("chkBackstage
");
5629 addClass(this.content,"backstageVisible
");
5633 if(this.currTabElem) {
5634 this.switchTab(null);
5636 backstage.toolbar.style.left = "0px
";
5637 if(anim && config.options.chkAnimate) {
5638 var p = [{style: "left
", start: 0, end: findWindowWidth(), template: "%
0px
"}];
5639 var c = function(element,properties) {backstage.area.style.display = "none
";};
5640 anim.startAnimating(new Morpher(backstage.toolbar,config.animDuration,p,c));
5642 this.area.style.display = "none
";
5644 this.showButton.style.display = "block
";
5645 this.hideButton.style.display = "none
";
5646 config.options.chkBackstage = false;
5647 saveOptionCookie("chkBackstage
");
5648 removeClass(this.content,"backstageVisible
");
5652 onClickCommand: function(e) {
5653 var task = config.tasks[this.getAttribute("task
")];
5654 displayMessage(task);
5656 backstage.switchTab(null);
5662 onClickTab: function(e) {
5663 backstage.switchTab(this.getAttribute("task
"));
5667 // Switch to a given tab, or none if null is passed
5668 switchTab: function(tabName) {
5670 var e = this.toolbar.firstChild;
5673 if(e.getAttribute && e.getAttribute("task
") == tabName)
5677 if(tabName == backstage.currTabName)
5679 if(backstage.currTabElem) {
5680 removeClass(this.currTabElem,"backstageSelTab
");
5682 if(tabElem && tabName) {
5683 backstage.preparePanel();
5684 addClass(tabElem,"backstageSelTab
");
5685 var task = config.tasks[tabName];
5686 wikify(task.content,backstage.panelBody,null,null);
5687 backstage.showPanel();
5688 } else if(backstage.currTabElem) {
5689 backstage.hidePanel();
5691 backstage.currTabName = tabName;
5692 backstage.currTabElem = tabElem;
5695 isPanelVisible: function() {
5696 return backstage.panel ? backstage.panel.style.display == "block
" : false;
5699 preparePanel: function() {
5700 backstage.cloak.style.height = findWindowHeight() + "px
";
5701 backstage.cloak.style.display = "block
";
5702 removeChildren(backstage.panelBody);
5703 return backstage.panelBody;
5706 showPanel: function() {
5707 backstage.panel.style.display = "block
";
5708 if(anim && config.options.chkAnimate) {
5709 backstage.panel.style.top = (-backstage.panel.offsetHeight) + "px
";
5710 var p = [{style: "top
", start: -backstage.panel.offsetHeight, end: 0, template: "%
0px
"}];
5711 anim.startAnimating(new Morpher(backstage.panel,config.animDuration,p),new Scroller(backstage.panel,false));
5713 backstage.panel.style.top = "0px
";
5715 return backstage.panelBody;
5718 hidePanel: function() {
5719 if(backstage.currTabElem)
5720 removeClass(backstage.currTabElem,"backstageSelTab
");
5721 backstage.currTabElem = null;
5722 backstage.currTabName = null;
5723 if(anim && config.options.chkAnimate) {
5725 {style: "top
", start: 0, end: -(backstage.panel.offsetHeight), template: "%
0px
"},
5726 {style: "display
", atEnd: "none
"}
5728 var c = function(element,properties) {backstage.cloak.style.display = "none
";};
5729 anim.startAnimating(new Morpher(backstage.panel,config.animDuration,p,c));
5731 backstage.panel.style.display = "none
";
5732 backstage.cloak.style.display = "none
";
5737 config.macros.backstage = {};
5739 config.macros.backstage.handler = function(place,macroName,params)
5741 var backstageTask = config.tasks[params[0]];
5743 createTiddlyButton(place,backstageTask.text,backstageTask.tooltip,function(e) {backstage.switchTab(params[0]); return false;});
5747 //-- ImportTiddlers macro
5750 config.macros.importTiddlers.handler = function(place,macroName,params,wikifier,paramString,tiddler)
5753 createTiddlyElement(place,"div
",null,"marked
",this.readOnlyWarning);
5756 var w = new Wizard();
5757 w.createWizard(place,this.wizardTitle);
5761 config.macros.importTiddlers.onCancel = function(e)
5763 var wizard = new Wizard(this);
5764 var place = wizard.clear();
5765 config.macros.importTiddlers.restart(wizard);
5769 config.macros.importTiddlers.onClose = function(e)
5771 backstage.hidePanel();
5775 config.macros.importTiddlers.restart = function(wizard)
5777 wizard.addStep(this.step1Title,this.step1Html);
5778 var s = wizard.getElement("selTypes
");
5779 for(var t in config.adaptors) {
5780 var e = createTiddlyElement(s,"option
",null,null,config.adaptors[t].serverLabel ? config.adaptors[t].serverLabel : t);
5783 if(config.defaultAdaptor)
5784 s.value = config.defaultAdaptor;
5785 s = wizard.getElement("selFeeds
");
5786 var feeds = this.getFeeds();
5788 e = createTiddlyElement(s,"option
",null,null,t);
5791 wizard.setValue("feeds
",feeds);
5792 s.onchange = config.macros.importTiddlers.onFeedChange;
5793 var fileInput = wizard.getElement("txtBrowse
");
5794 fileInput.onchange = config.macros.importTiddlers.onBrowseChange;
5795 fileInput.onkeyup = config.macros.importTiddlers.onBrowseChange;
5796 wizard.setButtons([{caption: this.openLabel, tooltip: this.openPrompt, onClick: config.macros.importTiddlers.onOpen}]);
5797 wizard.formElem.action = "javascript:;
";
5798 wizard.formElem.onsubmit = function() {
5799 if(this.txtPath.value.length)
5800 this.lastChild.firstChild.onclick();
5804 config.macros.importTiddlers.getFeeds = function()
5807 var tagged = store.getTaggedTiddlers("systemServer
","title
");
5808 for(var t=0; t<tagged.length; t++) {
5809 var title = tagged[t].title;
5810 var serverType = store.getTiddlerSlice(title,"Type
");
5812 serverType = "file
";
5813 feeds[title] = {title: title,
5814 url: store.getTiddlerSlice(title,"URL
"),
5815 workspace: store.getTiddlerSlice(title,"Workspace
"),
5816 workspaceList: store.getTiddlerSlice(title,"WorkspaceList
"),
5817 tiddlerFilter: store.getTiddlerSlice(title,"TiddlerFilter
"),
5818 serverType: serverType,
5819 description: store.getTiddlerSlice(title,"Description
")};
5824 config.macros.importTiddlers.onFeedChange = function(e)
5826 var wizard = new Wizard(this);
5827 var selTypes = wizard.getElement("selTypes
");
5828 var fileInput = wizard.getElement("txtPath
");
5829 var feeds = wizard.getValue("feeds
");
5830 var f = feeds[this.value];
5832 selTypes.value = f.serverType;
5833 fileInput.value = f.url;
5834 wizard.setValue("feedName
",f.serverType);
5835 wizard.setValue("feedHost
",f.url);
5836 wizard.setValue("feedWorkspace
",f.workspace);
5837 wizard.setValue("feedWorkspaceList
",f.workspaceList);
5838 wizard.setValue("feedTiddlerFilter
",f.tiddlerFilter);
5843 config.macros.importTiddlers.onBrowseChange = function(e)
5845 var wizard = new Wizard(this);
5846 var fileInput = wizard.getElement("txtPath
");
5847 fileInput.value = config.macros.importTiddlers.getURLFromLocalPath(this.value);
5848 var serverType = wizard.getElement("selTypes
");
5849 serverType.value = "file
";
5853 config.macros.importTiddlers.getURLFromLocalPath = function(v)
5857 v = v.replace(/\\/g,"/
"); // use "/
" for cross-platform consistency
5859 var t = v.split(":
");
5860 var p = t[1]||t[0]; // remove drive letter (if any)
5861 if (t[1] && (t[0]=="http
"||t[0]=="https
"||t[0]=="file
")) {
5863 } else if(p.substr(0,1)=="/
") {
5864 u = document.location.protocol + "//
" + document.location.hostname + (t[1] ? "/
" : "") + v;
5866 var c = document.location.href.replace(/\\/g,"/
");
5867 var pos = c.lastIndexOf("/
");
5869 c = c.substr(0,pos); // remove filename
5875 config.macros.importTiddlers.onOpen = function(e)
5877 var wizard = new Wizard(this);
5878 var fileInput = wizard.getElement("txtPath
");
5879 var url = fileInput.value;
5880 var serverType = wizard.getElement("selTypes
").value || config.defaultAdaptor;
5881 var adaptor = new config.adaptors[serverType]();
5882 wizard.setValue("adaptor
",adaptor);
5883 wizard.setValue("serverType
",serverType);
5884 wizard.setValue("host
",url);
5885 var ret = adaptor.openHost(url,null,wizard,config.macros.importTiddlers.onOpenHost);
5887 displayMessage(ret);
5888 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusOpenHost);
5892 config.macros.importTiddlers.onOpenHost = function(context,wizard)
5894 var adaptor = wizard.getValue("adaptor
");
5895 if(context.status !== true)
5896 displayMessage("Error in importTiddlers.onOpenHost:
" + context.statusText);
5897 var ret = adaptor.getWorkspaceList(context,wizard,config.macros.importTiddlers.onGetWorkspaceList);
5899 displayMessage(ret);
5900 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusGetWorkspaceList);
5903 config.macros.importTiddlers.onGetWorkspaceList = function(context,wizard)
5905 if(context.status !== true)
5906 displayMessage("Error in importTiddlers.onGetWorkspaceList:
" + context.statusText);
5907 wizard.setValue("context
",context);
5908 var workspace = wizard.getValue("feedWorkspace
");
5909 if(!workspace && context.workspaces.length==1)
5910 workspace = context.workspaces[0].title;
5912 var ret = context.adaptor.openWorkspace(workspace,context,wizard,config.macros.importTiddlers.onOpenWorkspace);
5914 displayMessage(ret);
5915 wizard.setValue("workspace
",workspace);
5916 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusOpenWorkspace);
5919 wizard.addStep(config.macros.importTiddlers.step2Title,config.macros.importTiddlers.step2Html);
5920 var s = wizard.getElement("selWorkspace
");
5921 s.onchange = config.macros.importTiddlers.onWorkspaceChange;
5922 for(var t=0; t<context.workspaces.length; t++) {
5923 var e = createTiddlyElement(s,"option
",null,null,context.workspaces[t].title);
5924 e.value = context.workspaces[t].title;
5926 var workspaceList = wizard.getValue("feedWorkspaceList
");
5928 var list = workspaceList.parseParams("workspace
",null,false,true);
5929 for(var n=1; n<list.length; n++) {
5930 if(context.workspaces.findByField("title
",list[n].value) == null) {
5931 e = createTiddlyElement(s,"option
",null,null,list[n].value);
5932 e.value = list[n].value;
5937 t = wizard.getElement("txtWorkspace
");
5938 t.value = workspace;
5940 wizard.setButtons([{caption: config.macros.importTiddlers.openLabel, tooltip: config.macros.importTiddlers.openPrompt, onClick: config.macros.importTiddlers.onChooseWorkspace}]);
5943 config.macros.importTiddlers.onWorkspaceChange = function(e)
5945 var wizard = new Wizard(this);
5946 var t = wizard.getElement("txtWorkspace
");
5947 t.value = this.value;
5948 this.selectedIndex = 0;
5952 config.macros.importTiddlers.onChooseWorkspace = function(e)
5954 var wizard = new Wizard(this);
5955 var adaptor = wizard.getValue("adaptor
");
5956 var workspace = wizard.getElement("txtWorkspace
").value;
5957 wizard.setValue("workspace
",workspace);
5958 var context = wizard.getValue("context
");
5959 var ret = adaptor.openWorkspace(workspace,context,wizard,config.macros.importTiddlers.onOpenWorkspace);
5961 displayMessage(ret);
5962 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusOpenWorkspace);
5966 config.macros.importTiddlers.onOpenWorkspace = function(context,wizard)
5968 if(context.status !== true)
5969 displayMessage("Error in importTiddlers.onOpenWorkspace:
" + context.statusText);
5970 var adaptor = wizard.getValue("adaptor
");
5971 var ret = adaptor.getTiddlerList(context,wizard,config.macros.importTiddlers.onGetTiddlerList,wizard.getValue("feedTiddlerFilter
"));
5973 displayMessage(ret);
5974 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusGetTiddlerList);
5977 config.macros.importTiddlers.onGetTiddlerList = function(context,wizard)
5979 if(context.status !== true) {
5980 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.errorGettingTiddlerList);
5983 // Extract data for the listview
5984 var listedTiddlers = [];
5985 if(context.tiddlers) {
5986 for(var n=0; n<context.tiddlers.length; n++) {
5987 var tiddler = context.tiddlers[n];
5988 listedTiddlers.push({
5989 title: tiddler.title,
5990 modified: tiddler.modified,
5991 modifier: tiddler.modifier,
5992 text: tiddler.text ? wikifyPlainText(tiddler.text,100) : "",
5994 size: tiddler.text ? tiddler.text.length : 0,
5999 listedTiddlers.sort(function(a,b) {return a.title < b.title ? -1 : (a.title == b.title ? 0 : +1);});
6000 // Display the listview
6001 wizard.addStep(config.macros.importTiddlers.step3Title,config.macros.importTiddlers.step3Html);
6002 var markList = wizard.getElement("markList
");
6003 var listWrapper = document.createElement("div
");
6004 markList.parentNode.insertBefore(listWrapper,markList);
6005 var listView = ListView.create(listWrapper,listedTiddlers,config.macros.importTiddlers.listViewTemplate);
6006 wizard.setValue("listView
",listView);
6007 var txtSaveTiddler = wizard.getElement("txtSaveTiddler
");
6008 txtSaveTiddler.value = config.macros.importTiddlers.generateSystemServerName(wizard);
6010 {caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel},
6011 {caption: config.macros.importTiddlers.importLabel, tooltip: config.macros.importTiddlers.importPrompt, onClick: config.macros.importTiddlers.doImport}
6015 config.macros.importTiddlers.generateSystemServerName = function(wizard)
6017 var serverType = wizard.getValue("serverType
");
6018 var host = wizard.getValue("host
");
6019 var workspace = wizard.getValue("workspace
");
6020 var pattern = config.macros.importTiddlers[workspace ? "systemServerNamePattern
" : "systemServerNamePatternNoWorkspace
"];
6021 return pattern.format([serverType,host,workspace]);
6024 config.macros.importTiddlers.saveServerTiddler = function(wizard)
6026 var txtSaveTiddler = wizard.getElement("txtSaveTiddler
").value;
6027 if(store.tiddlerExists(txtSaveTiddler)) {
6028 if(!confirm(config.macros.importTiddlers.confirmOverwriteSaveTiddler.format([txtSaveTiddler])))
6030 store.suspendNotifications();
6031 store.removeTiddler(txtSaveTiddler);
6032 store.resumeNotifications();
6034 var serverType = wizard.getValue("serverType
");
6035 var host = wizard.getValue("host
");
6036 var workspace = wizard.getValue("workspace
");
6037 var text = config.macros.importTiddlers.serverSaveTemplate.format([serverType,host,workspace]);
6038 store.saveTiddler(txtSaveTiddler,txtSaveTiddler,text,config.macros.importTiddlers.serverSaveModifier,new Date(),["systemServer
"]);
6041 config.macros.importTiddlers.doImport = function(e)
6043 var wizard = new Wizard(this);
6044 if(wizard.getElement("chkSave
").checked)
6045 config.macros.importTiddlers.saveServerTiddler(wizard);
6046 var chkSync = wizard.getElement("chkSync
").checked;
6047 wizard.setValue("sync
",chkSync);
6048 var listView = wizard.getValue("listView
");
6049 var rowNames = ListView.getSelectedRows(listView);
6050 var adaptor = wizard.getValue("adaptor
");
6053 for(t=0; t<rowNames.length; t++) {
6054 if(store.tiddlerExists(rowNames[t]))
6055 overwrite.push(rowNames[t]);
6057 if(overwrite.length > 0) {
6058 if(!confirm(config.macros.importTiddlers.confirmOverwriteText.format([overwrite.join(",
")])))
6061 wizard.addStep(config.macros.importTiddlers.step4Title.format([rowNames.length]),config.macros.importTiddlers.step4Html);
6062 for(t=0; t<rowNames.length; t++) {
6063 var link = document.createElement("div
");
6064 createTiddlyLink(link,rowNames[t],true);
6065 var place = wizard.getElement("markReport
");
6066 place.parentNode.insertBefore(link,place);
6068 wizard.setValue("remainingImports
",rowNames.length);
6070 {caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}
6071 ],config.macros.importTiddlers.statusDoingImport);
6072 for(t=0; t<rowNames.length; t++) {
6074 context.allowSynchronous = true;
6075 var inbound = adaptor.getTiddler(rowNames[t],context,wizard,config.macros.importTiddlers.onGetTiddler);
6080 config.macros.importTiddlers.onGetTiddler = function(context,wizard)
6083 displayMessage("Error in importTiddlers.onGetTiddler:
" + context.statusText);
6084 var tiddler = context.tiddler;
6085 store.suspendNotifications();
6086 store.saveTiddler(tiddler.title, tiddler.title, tiddler.text, tiddler.modifier, tiddler.modified, tiddler.tags, tiddler.fields, true, tiddler.created);
6087 if(!wizard.getValue("sync
")) {
6088 store.setValue(tiddler.title,'server',null);
6090 store.resumeNotifications();
6091 if(!context.isSynchronous)
6092 store.notify(tiddler.title,true);
6093 var remainingImports = wizard.getValue("remainingImports
")-1;
6094 wizard.setValue("remainingImports
",remainingImports);
6095 if(remainingImports == 0) {
6096 if(context.isSynchronous) {
6101 {caption: config.macros.importTiddlers.doneLabel, tooltip: config.macros.importTiddlers.donePrompt, onClick: config.macros.importTiddlers.onClose}
6102 ],config.macros.importTiddlers.statusDoneImport);
6111 config.macros.upgrade.handler = function(place)
6113 var w = new Wizard();
6114 w.createWizard(place,this.wizardTitle);
6115 w.addStep(this.step1Title,this.step1Html.format([this.source,this.source]));
6116 w.setButtons([{caption: this.upgradeLabel, tooltip: this.upgradePrompt, onClick: this.onClickUpgrade}]);
6119 config.macros.upgrade.onClickUpgrade = function(e)
6121 var me = config.macros.upgrade;
6122 var w = new Wizard(this);
6123 if(window.location.protocol != "file:
") {
6124 alert(me.errorCantUpgrade);
6127 if(story.areAnyDirty() || store.isDirty()) {
6128 alert(me.errorNotSaved);
6131 var localPath = getLocalPath(document.location.toString());
6132 var backupPath = getBackupPath(localPath,me.backupExtension);
6133 w.setValue("backupPath
",backupPath);
6134 w.setButtons([],me.statusPreparingBackup);
6135 var original = loadOriginal(localPath);
6136 w.setButtons([],me.statusSavingBackup);
6137 var backup = config.browser.isIE ? ieCopyFile(backupPath,localPath) : saveFile(backupPath,original);
6138 if(backup != true) {
6139 w.setButtons([],me.errorSavingBackup);
6140 alert(me.errorSavingBackup);
6143 w.setButtons([],me.statusLoadingCore);
6144 var load = loadRemoteFile(me.source,me.onLoadCore,w);
6145 if(typeof load == "string
") {
6146 w.setButtons([],me.errorLoadingCore);
6147 alert(me.errorLoadingCore);
6153 config.macros.upgrade.onLoadCore = function(status,params,responseText,url,xhr)
6155 var me = config.macros.upgrade;
6159 errMsg = me.errorLoadingCore;
6160 var newVer = me.extractVersion(responseText);
6162 errMsg = me.errorCoreFormat;
6164 w.setButtons([],errMsg);
6168 var onStartUpgrade = function(e) {
6169 w.setButtons([],me.statusSavingCore);
6170 var localPath = getLocalPath(document.location.toString());
6171 saveFile(localPath,responseText);
6172 w.setButtons([],me.statusReloadingCore);
6173 var backupPath = w.getValue("backupPath
");
6174 var newLoc = document.location.toString() + '?time=' + new Date().convertToYYYYMMDDHHMM() + '#upgrade:[[' + encodeURI(backupPath) + ']]';
6175 window.setTimeout(function () {window.location = newLoc;},10);
6177 var step2 = [me.step2Html_downgrade,me.step2Html_restore,me.step2Html_upgrade][compareVersions(version,newVer) + 1];
6178 w.addStep(me.step2Title,step2.format([formatVersion(newVer),formatVersion(version)]));
6179 w.setButtons([{caption: me.startLabel, tooltip: me.startPrompt, onClick: onStartUpgrade},{caption: me.cancelLabel, tooltip: me.cancelPrompt, onClick: me.onCancel}]);
6182 config.macros.upgrade.onCancel = function(e)
6184 var me = config.macros.upgrade;
6185 var w = new Wizard(this);
6186 w.addStep(me.step3Title,me.step3Html);
6191 config.macros.upgrade.extractVersion = function(upgradeFile)
6193 var re = /^var version = \{title: "([^
"]+)", major: (\d+), minor: (\d+), revision: (\d+)(, beta: (\d+)){
0,
1}, date: new Date\(
"([^"]+)
"\)/mg;
6194 var m = re.exec(upgradeFile);
6195 return m ? {title: m[1], major: m[2], minor: m[3], revision: m[4], beta: m[6], date: new Date(m[7])} : null;
6198 function upgradeFrom(path)
6200 var importStore = new TiddlyWiki();
6201 var tw = loadFile(path);
6202 if(window.netscape !== undefined)
6203 tw = convertUTF8ToUnicode(tw);
6204 importStore.importTiddlyWiki(tw);
6205 importStore.forEachTiddler(function(title,tiddler) {
6206 if(!store.getTiddler(title)) {
6207 store.addTiddler(tiddler);
6211 saveChanges(); //# To create appropriate Markup* sections
6212 alert(config.messages.upgradeDone.format([formatVersion()]));
6213 window.location = window.location.toString().substr(0,window.location.toString().lastIndexOf('?'));
6220 // Synchronisation handlers
6221 config.syncers = {};
6224 var currSync = null;
6227 config.macros.sync.handler = function(place,macroName,params,wikifier,paramString,tiddler)
6229 if(!wikifier.isStatic)
6230 this.startSync(place);
6233 config.macros.sync.cancelSync = function()
6238 config.macros.sync.startSync = function(place)
6241 config.macros.sync.cancelSync();
6243 currSync.syncList = this.getSyncableTiddlers();
6244 currSync.syncTasks = this.createSyncTasks(currSync.syncList);
6245 this.preProcessSyncableTiddlers(currSync.syncList);
6246 var wizard = new Wizard();
6247 currSync.wizard = wizard;
6248 wizard.createWizard(place,this.wizardTitle);
6249 wizard.addStep(this.step1Title,this.step1Html);
6250 var markList = wizard.getElement("markList
");
6251 var listWrapper = document.createElement("div
");
6252 markList.parentNode.insertBefore(listWrapper,markList);
6253 currSync.listView = ListView.create(listWrapper,currSync.syncList,this.listViewTemplate);
6254 this.processSyncableTiddlers(currSync.syncList);
6255 wizard.setButtons([{caption: this.syncLabel, tooltip: this.syncPrompt, onClick: this.doSync}]);
6258 config.macros.sync.getSyncableTiddlers = function()
6261 store.forEachTiddler(function(title,tiddler) {
6263 syncItem.serverType = tiddler.getServerType();
6264 syncItem.serverHost = tiddler.fields['server.host'];
6265 if(syncItem.serverType && syncItem.serverHost) {
6266 syncItem.serverWorkspace = tiddler.fields['server.workspace'];
6267 syncItem.tiddler = tiddler;
6268 syncItem.title = tiddler.title;
6269 syncItem.isTouched = tiddler.isTouched();
6270 syncItem.selected = syncItem.isTouched;
6271 syncItem.syncStatus = config.macros.sync.syncStatusList[syncItem.isTouched ? "changedLocally
" : "none
"];
6272 syncItem.status = syncItem.syncStatus.text;
6273 list.push(syncItem);
6276 list.sort(function(a,b) {return a.title < b.title ? -1 : (a.title == b.title ? 0 : +1);});
6280 config.macros.sync.preProcessSyncableTiddlers = function(syncList)
6282 for(var i=0; i<syncList.length; i++) {
6283 var si = syncList[i];
6284 si.serverUrl = si.syncTask.syncMachine.generateTiddlerInfo(si.tiddler).uri;
6288 config.macros.sync.processSyncableTiddlers = function(syncList)
6290 for(var i=0; i<syncList.length; i++) {
6291 var si = syncList[i];
6292 si.rowElement.style.display = si.syncStatus.display;
6293 if(si.syncStatus.className)
6294 si.rowElement.className = si.syncStatus.className;
6298 config.macros.sync.createSyncTasks = function(syncList)
6301 for(var i=0; i<syncList.length; i++) {
6302 var si = syncList[i];
6304 for(var j=0; j<syncTasks.length; j++) {
6305 var cst = syncTasks[j];
6306 if(si.serverType == cst.serverType && si.serverHost == cst.serverHost && si.serverWorkspace == cst.serverWorkspace)
6311 r.syncItems.push(si);
6313 si.syncTask = this.createSyncTask(si);
6314 syncTasks.push(si.syncTask);
6320 config.macros.sync.createSyncTask = function(syncItem)
6323 st.serverType = syncItem.serverType;
6324 st.serverHost = syncItem.serverHost;
6325 st.serverWorkspace = syncItem.serverWorkspace;
6326 st.syncItems = [syncItem];
6327 st.syncMachine = new SyncMachine(st.serverType,{
6329 return this.openHost(st.serverHost,"openWorkspace
");
6331 openWorkspace: function() {
6332 return this.openWorkspace(st.serverWorkspace,"getTiddlerList
");
6334 getTiddlerList: function() {
6335 return this.getTiddlerList("onGetTiddlerList
");
6337 onGetTiddlerList: function(context) {
6338 var tiddlers = context.tiddlers;
6339 for(var i=0; i<st.syncItems.length; i++) {
6340 var si = st.syncItems[i];
6341 var f = tiddlers.findByField("title
",si.title);
6343 if(tiddlers[f].fields['server.page.revision'] > si.tiddler.fields['server.page.revision']) {
6344 si.syncStatus = config.macros.sync.syncStatusList[si.isTouched ? 'changedBoth' : 'changedServer'];
6347 si.syncStatus = config.macros.sync.syncStatusList.notFound;
6349 config.macros.sync.updateSyncStatus(si);
6352 getTiddler: function(title) {
6353 return this.getTiddler(title,"onGetTiddler
");
6355 onGetTiddler: function(context) {
6356 var tiddler = context.tiddler;
6357 var syncItem = st.syncItems.findByField("title
",tiddler.title);
6358 if(syncItem !== null) {
6359 syncItem = st.syncItems[syncItem];
6360 store.saveTiddler(tiddler.title, tiddler.title, tiddler.text, tiddler.modifier, tiddler.modified, tiddler.tags, tiddler.fields, true, tiddler.created);
6361 syncItem.syncStatus = config.macros.sync.syncStatusList.gotFromServer;
6362 config.macros.sync.updateSyncStatus(syncItem);
6365 putTiddler: function(tiddler) {
6366 return this.putTiddler(tiddler,"onPutTiddler
");
6368 onPutTiddler: function(context) {
6369 var title = context.title;
6370 var syncItem = st.syncItems.findByField("title
",title);
6371 if(syncItem !== null) {
6372 syncItem = st.syncItems[syncItem];
6373 store.resetTiddler(title);
6374 if(context.status) {
6375 syncItem.syncStatus = config.macros.sync.syncStatusList.putToServer;
6376 config.macros.sync.updateSyncStatus(syncItem);
6381 st.syncMachine.go();
6385 config.macros.sync.updateSyncStatus = function(syncItem)
6387 var e = syncItem.colElements["status
"];
6389 createTiddlyText(e,syncItem.syncStatus.text);
6390 syncItem.rowElement.style.display = syncItem.syncStatus.display;
6391 if(syncItem.syncStatus.className)
6392 syncItem.rowElement.className = syncItem.syncStatus.className;
6395 config.macros.sync.doSync = function(e)
6397 var rowNames = ListView.getSelectedRows(currSync.listView);
6398 var sl = config.macros.sync.syncStatusList;
6399 for(var i=0; i<currSync.syncList.length; i++) {
6400 var si = currSync.syncList[i];
6401 if(rowNames.indexOf(si.title) != -1) {
6403 switch(si.syncStatus) {
6404 case sl.changedServer:
6405 r = si.syncTask.syncMachine.go("getTiddler
",si.title);
6408 case sl.changedLocally:
6409 case sl.changedBoth:
6410 r = si.syncTask.syncMachine.go("putTiddler
",si.tiddler);
6416 displayMessage("Error in doSync:
" + r);
6422 function SyncMachine(serverType,steps)
6424 this.serverType = serverType;
6425 this.adaptor = new config.adaptors[serverType]();
6429 SyncMachine.prototype.go = function(step,context)
6431 var r = context ? context.status : null;
6432 if(typeof r == "string
") {
6433 this.invokeError(r);
6436 var h = this.steps[step ? step : "start
"];
6439 r = h.call(this,context);
6440 if(typeof r == "string
")
6441 this.invokeError(r);
6445 SyncMachine.prototype.invokeError = function(message)
6447 if(this.steps.error)
6448 this.steps.error(message);
6451 SyncMachine.prototype.openHost = function(host,nextStep)
6454 return me.adaptor.openHost(host,null,null,function(context) {me.go(nextStep,context);});
6457 SyncMachine.prototype.getWorkspaceList = function(nextStep)
6460 return me.adaptor.getWorkspaceList(null,null,function(context) {me.go(nextStep,context);});
6463 SyncMachine.prototype.openWorkspace = function(workspace,nextStep)
6466 return me.adaptor.openWorkspace(workspace,null,null,function(context) {me.go(nextStep,context);});
6469 SyncMachine.prototype.getTiddlerList = function(nextStep)
6472 return me.adaptor.getTiddlerList(null,null,function(context) {me.go(nextStep,context);});
6475 SyncMachine.prototype.generateTiddlerInfo = function(tiddler)
6477 return this.adaptor.generateTiddlerInfo(tiddler);
6480 SyncMachine.prototype.getTiddler = function(title,nextStep)
6483 return me.adaptor.getTiddler(title,null,null,function(context) {me.go(nextStep,context);});
6486 SyncMachine.prototype.putTiddler = function(tiddler,nextStep)
6489 return me.adaptor.putTiddler(tiddler,null,null,function(context) {me.go(nextStep,context);});
6493 //-- Manager UI for groups of tiddlers
6496 config.macros.plugins.handler = function(place,macroName,params,wikifier,paramString)
6498 var wizard = new Wizard();
6499 wizard.createWizard(place,this.wizardTitle);
6500 wizard.addStep(this.step1Title,this.step1Html);
6501 var markList = wizard.getElement("markList
");
6502 var listWrapper = document.createElement("div
");
6503 markList.parentNode.insertBefore(listWrapper,markList);
6504 listWrapper.setAttribute("refresh
","macro
");
6505 listWrapper.setAttribute("macroName
","plugins
");
6506 listWrapper.setAttribute("params
",paramString);
6507 this.refresh(listWrapper,paramString);
6510 config.macros.plugins.refresh = function(listWrapper,params)
6512 var wizard = new Wizard(listWrapper);
6513 var selectedRows = [];
6514 ListView.forEachSelector(listWrapper,function(e,rowName) {
6516 selectedRows.push(e.getAttribute("rowName
"));
6518 removeChildren(listWrapper);
6519 params = params.parseParams("anon
");
6520 var plugins = installedPlugins.slice(0);
6522 var configTiddlers = store.getTaggedTiddlers("systemConfig
");
6523 for(t=0; t<configTiddlers.length; t++) {
6524 tiddler = configTiddlers[t];
6525 if(plugins.findByField("title
",tiddler.title) == null) {
6526 p = getPluginInfo(tiddler);
6528 p.log.splice(0,0,this.skippedText);
6532 for(t=0; t<plugins.length; t++) {
6534 p.size = p.tiddler.text ? p.tiddler.text.length : 0;
6535 p.forced = p.tiddler.isTagged("systemConfigForce
");
6536 p.disabled = p.tiddler.isTagged("systemConfigDisable
");
6537 p.Selected = selectedRows.indexOf(plugins[t].title) != -1;
6539 if(plugins.length == 0) {
6540 createTiddlyElement(listWrapper,"em
",null,null,this.noPluginText);
6541 wizard.setButtons([]);
6543 var listView = ListView.create(listWrapper,plugins,this.listViewTemplate,this.onSelectCommand);
6544 wizard.setValue("listView
",listView);
6546 {caption: config.macros.plugins.removeLabel, tooltip: config.macros.plugins.removePrompt, onClick: config.macros.plugins.doRemoveTag},
6547 {caption: config.macros.plugins.deleteLabel, tooltip: config.macros.plugins.deletePrompt, onClick: config.macros.plugins.doDelete}
6552 config.macros.plugins.doRemoveTag = function(e)
6554 var wizard = new Wizard(this);
6555 var listView = wizard.getValue("listView
");
6556 var rowNames = ListView.getSelectedRows(listView);
6557 if(rowNames.length == 0) {
6558 alert(config.messages.nothingSelected);
6560 for(var t=0; t<rowNames.length; t++)
6561 store.setTiddlerTag(rowNames[t],false,"systemConfig
");
6565 config.macros.plugins.doDelete = function(e)
6567 var wizard = new Wizard(this);
6568 var listView = wizard.getValue("listView
");
6569 var rowNames = ListView.getSelectedRows(listView);
6570 if(rowNames.length == 0) {
6571 alert(config.messages.nothingSelected);
6573 if(confirm(config.macros.plugins.confirmDeleteText.format([rowNames.join(",
")]))) {
6574 for(var t=0; t<rowNames.length; t++) {
6575 store.removeTiddler(rowNames[t]);
6576 story.closeTiddler(rowNames[t],true);
6586 function getMessageDiv()
6588 var msgArea = document.getElementById("messageArea
");
6591 if(!msgArea.hasChildNodes())
6592 createTiddlyButton(createTiddlyElement(msgArea,"div
",null,"messageToolbar
"),
6593 config.messages.messageClose.text,
6594 config.messages.messageClose.tooltip,
6596 msgArea.style.display = "block
";
6597 return createTiddlyElement(msgArea,"div
");
6600 function displayMessage(text,linkText)
6602 var e = getMessageDiv();
6608 var link = createTiddlyElement(e,"a
",null,null,text);
6609 link.href = linkText;
6610 link.target = "_blank
";
6612 e.appendChild(document.createTextNode(text));
6616 function clearMessage()
6618 var msgArea = document.getElementById("messageArea
");
6620 removeChildren(msgArea);
6621 msgArea.style.display = "none
";
6627 //-- Refresh mechanism
6630 config.notifyTiddlers = [
6631 {name: "StyleSheetLayout
", notify: refreshStyles},
6632 {name: "StyleSheetColors
", notify: refreshStyles},
6633 {name: "StyleSheet
", notify: refreshStyles},
6634 {name: "StyleSheetPrint
", notify: refreshStyles},
6635 {name: "PageTemplate
", notify: refreshPageTemplate},
6636 {name: "SiteTitle
", notify: refreshPageTitle},
6637 {name: "SiteSubtitle
", notify: refreshPageTitle},
6638 {name: "ColorPalette
", notify: refreshColorPalette},
6639 {name: null, notify: refreshDisplay}
6642 config.refreshers = {
6643 link: function(e,changeList)
6645 var title = e.getAttribute("tiddlyLink
");
6646 refreshTiddlyLink(e,title);
6650 tiddler: function(e,changeList)
6652 var title = e.getAttribute("tiddler
");
6653 var template = e.getAttribute("template
");
6654 if(changeList && changeList.indexOf(title) != -1 && !story.isDirty(title))
6655 story.refreshTiddler(title,template,true);
6657 refreshElements(e,changeList);
6661 content: function(e,changeList)
6663 var title = e.getAttribute("tiddler
");
6664 var force = e.getAttribute("force
");
6665 if(force != null || changeList == null || changeList.indexOf(title) != -1) {
6667 wikify(store.getTiddlerText(title,""),e,null,store.fetchTiddler(title));
6673 macro: function(e,changeList)
6675 var macro = e.getAttribute("macroName
");
6676 var params = e.getAttribute("params
");
6678 macro = config.macros[macro];
6679 if(macro && macro.refresh)
6680 macro.refresh(e,params);
6685 config.refresherData = {
6686 styleSheet: "StyleSheet
",
6687 defaultStyleSheet: "StyleSheet
",
6688 pageTemplate: "PageTemplate
",
6689 defaultPageTemplate: "PageTemplate
",
6690 colorPalette: "ColorPalette
",
6691 defaultColorPalette: "ColorPalette
"
6694 function refreshElements(root,changeList)
6696 var nodes = root.childNodes;
6697 for(var c=0; c<nodes.length; c++) {
6698 var e = nodes[c], type = null;
6699 if(e.getAttribute && (e.tagName ? e.tagName != "IFRAME
" : true))
6700 type = e.getAttribute("refresh
");
6701 var refresher = config.refreshers[type];
6702 var refreshed = false;
6703 if(refresher != undefined)
6704 refreshed = refresher(e,changeList);
6705 if(e.hasChildNodes() && !refreshed)
6706 refreshElements(e,changeList);
6710 function applyHtmlMacros(root,tiddler)
6712 var e = root.firstChild;
6714 var nextChild = e.nextSibling;
6715 if(e.getAttribute) {
6716 var macro = e.getAttribute("macro
");
6718 e.removeAttribute("macro
");
6720 var p = macro.indexOf(" ");
6722 params = macro.substr(p+1);
6723 macro = macro.substr(0,p);
6725 invokeMacro(e,macro,params,null,tiddler);
6728 if(e.hasChildNodes())
6729 applyHtmlMacros(e,tiddler);
6734 function refreshPageTemplate(title)
6736 var stash = createTiddlyElement(document.body,"div
");
6737 stash.style.display = "none
";
6738 var display = story.getContainer();
6741 nodes = display.childNodes;
6742 for(t=nodes.length-1; t>=0; t--)
6743 stash.appendChild(nodes[t]);
6745 var wrapper = document.getElementById("contentWrapper
");
6747 var isAvailable = function(title) {
6748 var s = title ? title.indexOf(config.textPrimitives.sectionSeparator) : -1;
6750 title = title.substr(0,s);
6751 return store.tiddlerExists(title) || store.isShadowTiddler(title);
6753 if(!title || !isAvailable(title))
6754 title = config.refresherData.pageTemplate;
6755 if(!isAvailable(title))
6756 title = config.refresherData.defaultPageTemplate; //# this one is always avaialable
6757 wrapper.innerHTML = store.getRecursiveTiddlerText(title,null,10);
6758 applyHtmlMacros(wrapper);
6759 refreshElements(wrapper);
6760 display = story.getContainer();
6761 removeChildren(display);
6763 display = createTiddlyElement(wrapper,"div
",story.containerId());
6764 nodes = stash.childNodes;
6765 for(t=nodes.length-1; t>=0; t--)
6766 display.appendChild(nodes[t]);
6770 function refreshDisplay(hint)
6772 if(typeof hint == "string
")
6774 var e = document.getElementById("contentWrapper
");
6775 refreshElements(e,hint);
6776 if(backstage.isPanelVisible()) {
6777 e = document.getElementById("backstage
");
6778 refreshElements(e,hint);
6782 function refreshPageTitle()
6784 document.title = getPageTitle();
6787 function getPageTitle()
6789 var st = wikifyPlain("SiteTitle
");
6790 var ss = wikifyPlain("SiteSubtitle
");
6791 return st + ((st == "" || ss == "") ? "" : " -
") + ss;
6794 function refreshStyles(title,doc)
6796 setStylesheet(title == null ? "" : store.getRecursiveTiddlerText(title,"",10),title,doc || document);
6799 function refreshColorPalette(title)
6805 function refreshAll()
6807 refreshPageTemplate();
6809 refreshStyles("StyleSheetLayout
");
6810 refreshStyles("StyleSheetColors
");
6811 refreshStyles(config.refresherData.styleSheet);
6812 refreshStyles("StyleSheetPrint
");
6819 config.optionHandlers = {
6821 get: function(name) {return encodeCookie(config.options[name].toString());},
6822 set: function(name,value) {config.options[name] = decodeCookie(value);}
6825 get: function(name) {return config.options[name] ? "true
" : "false
";},
6826 set: function(name,value) {config.options[name] = value == "true
";}
6830 function loadOptionsCookie()
6834 var cookies = document.cookie.split(";
");
6835 for(var c=0; c<cookies.length; c++) {
6836 var p = cookies[c].indexOf("=
");
6838 var name = cookies[c].substr(0,p).trim();
6839 var value = cookies[c].substr(p+1).trim();
6840 var optType = name.substr(0,3);
6841 if(config.optionHandlers[optType] && config.optionHandlers[optType].set)
6842 config.optionHandlers[optType].set(name,value);
6847 function saveOptionCookie(name)
6852 var optType = name.substr(0,3);
6853 if(config.optionHandlers[optType] && config.optionHandlers[optType].get)
6854 c += config.optionHandlers[optType].get(name);
6855 c += "; expires=Fri,
1 Jan
2038 12:
00:
00 UTC; path=/
";
6856 document.cookie = c;
6859 function encodeCookie(s)
6861 return escape(convertUnicodeToHtmlEntities(s));
6864 function decodeCookie(s)
6867 var re = /&#[0-9]{1,5};/g;
6868 return s.replace(re,function($0) {return String.fromCharCode(eval($0.replace(/[&#;]/g,"")));});
6872 config.macros.option.genericCreate = function(place,type,opt,className,desc)
6874 var typeInfo = config.macros.option.types[type];
6875 var c = document.createElement(typeInfo.elementType);
6876 if(typeInfo.typeValue)
6877 c.setAttribute("type
",typeInfo.typeValue);
6878 c[typeInfo.eventName] = typeInfo.onChange;
6879 c.setAttribute("option
",opt);
6880 c.className = className || typeInfo.className;
6881 if(config.optionsDesc[opt])
6882 c.setAttribute("title
",config.optionsDesc[opt]);
6883 place.appendChild(c);
6885 createTiddlyText(place,config.optionsDesc[opt] || opt);
6886 c[typeInfo.valueField] = config.options[opt];
6890 config.macros.option.genericOnChange = function(e)
6892 var opt = this.getAttribute("option
");
6894 var optType = opt.substr(0,3);
6895 var handler = config.macros.option.types[optType];
6896 if(handler.elementType && handler.valueField)
6897 config.macros.option.propagateOption(opt,handler.valueField,this[handler.valueField],handler.elementType,this);
6902 config.macros.option.types = {
6904 elementType: "input
",
6905 valueField: "value
",
6906 eventName: "onchange
",
6907 className: "txtOptionInput
",
6908 create: config.macros.option.genericCreate,
6909 onChange: config.macros.option.genericOnChange
6912 elementType: "input
",
6913 valueField: "checked
",
6914 eventName: "onclick
",
6915 className: "chkOptionInput
",
6916 typeValue: "checkbox
",
6917 create: config.macros.option.genericCreate,
6918 onChange: config.macros.option.genericOnChange
6922 config.macros.option.propagateOption = function(opt,valueField,value,elementType,elem)
6924 config.options[opt] = value;
6925 saveOptionCookie(opt);
6926 var nodes = document.getElementsByTagName(elementType);
6927 for(var t=0; t<nodes.length; t++) {
6928 var optNode = nodes[t].getAttribute("option
");
6929 if(opt == optNode && nodes[t]!=elem)
6930 nodes[t][valueField] = value;
6934 config.macros.option.handler = function(place,macroName,params,wikifier,paramString)
6936 params = paramString.parseParams("anon
",null,true,false,false);
6937 var opt = (params[1] && params[1].name == "anon
") ? params[1].value : getParam(params,"name
",null);
6938 var className = (params[2] && params[2].name == "anon
") ? params[2].value : getParam(params,"class
",null);
6939 var desc = getParam(params,"desc
","no
");
6940 var type = opt.substr(0,3);
6941 var h = config.macros.option.types[type];
6943 h.create(place,type,opt,className,desc);
6946 config.macros.options.handler = function(place,macroName,params,wikifier,paramString)
6948 params = paramString.parseParams("anon
",null,true,false,false);
6949 var showUnknown = getParam(params,"showUnknown
","no
");
6950 var wizard = new Wizard();
6951 wizard.createWizard(place,this.wizardTitle);
6952 wizard.addStep(this.step1Title,this.step1Html);
6953 var markList = wizard.getElement("markList
");
6954 var chkUnknown = wizard.getElement("chkUnknown
");
6955 chkUnknown.checked = showUnknown == "yes
";
6956 chkUnknown.onchange = this.onChangeUnknown;
6957 var listWrapper = document.createElement("div
");
6958 markList.parentNode.insertBefore(listWrapper,markList);
6959 wizard.setValue("listWrapper
",listWrapper);
6960 this.refreshOptions(listWrapper,showUnknown == "yes
");
6963 config.macros.options.refreshOptions = function(listWrapper,showUnknown)
6966 for(var n in config.options) {
6970 opt.lowlight = !config.optionsDesc[n];
6971 opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
6972 if(!opt.lowlight || showUnknown)
6975 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);});
6976 var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
6977 for(n=0; n<opts.length; n++) {
6978 var type = opts[n].name.substr(0,3);
6979 var h = config.macros.option.types[type];
6981 h.create(opts[n].colElements['option'],type,opts[n].name,null,"no
");
6986 config.macros.options.onChangeUnknown = function(e)
6988 var wizard = new Wizard(this);
6989 var listWrapper = wizard.getValue("listWrapper
");
6990 removeChildren(listWrapper);
6991 config.macros.options.refreshOptions(listWrapper,this.checked);
6999 var saveUsingSafari = false;
7001 var startSaveArea = '<div id="' + 'storeArea
">'; // Split up into two so that indexOf() of this source doesn't find it
7002 var endSaveArea = '</d' + 'iv>';
7004 // If there are unsaved changes, force the user to confirm before exitting
7005 function confirmExit()
7007 hadConfirmExit = true;
7008 if((store && store.isDirty && store.isDirty()) || (story && story.areAnyDirty && story.areAnyDirty()))
7009 return config.messages.confirmExit;
7012 // Give the user a chance to save changes before exitting
7013 function checkUnsavedChanges()
7015 if(store && store.isDirty && store.isDirty() && window.hadConfirmExit === false) {
7016 if(confirm(config.messages.unsavedChangesWarning))
7021 function updateLanguageAttribute(s)
7024 var mRE = /(<html(?:.*?)?)(?: xml:lang\="([a-z]+)
")?(?: lang\="([a-z]+)
")?>/;
7025 var m = mRE.exec(s);
7029 t += ' xml:lang="' + config.locale + '
"';
7031 t += ' lang="' + config.locale + '
"';
7033 s = s.substr(0,m.index) + t + s.substr(m.index+m[0].length);
7039 function updateMarkupBlock(s,blockName,tiddlerName)
7041 return s.replaceChunk(
7042 "<!--%0-START-->".format([blockName]),
7043 "<!--%0-END-->".format([blockName]),
7044 "\n
" + convertUnicodeToFileFormat(store.getRecursiveTiddlerText(tiddlerName,"")) + "\n
");
7047 function updateOriginal(original,posDiv,localPath)
7050 posDiv = locateStoreArea(original);
7052 alert(config.messages.invalidFileError.format([localPath]));
7055 var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n
" +
7056 convertUnicodeToFileFormat(store.allTiddlersAsHtml()) + "\n
" +
7057 original.substr(posDiv[1]);
7058 var newSiteTitle = convertUnicodeToFileFormat(getPageTitle()).htmlEncode();
7059 revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
7060 revised = updateLanguageAttribute(revised);
7061 revised = updateMarkupBlock(revised,"PRE-HEAD
","MarkupPreHead
");
7062 revised = updateMarkupBlock(revised,"POST-HEAD
","MarkupPostHead
");
7063 revised = updateMarkupBlock(revised,"PRE-BODY
","MarkupPreBody
");
7064 revised = updateMarkupBlock(revised,"POST-SCRIPT
","MarkupPostBody
");
7068 function locateStoreArea(original)
7070 // Locate the storeArea div's
7071 var posOpeningDiv = original.indexOf(startSaveArea);
7072 var limitClosingDiv = original.indexOf("<
"+"!--POST-STOREAREA--
"+">");
7073 if(limitClosingDiv == -1)
7074 limitClosingDiv = original.indexOf("<
"+"!--POST-BODY-START--
"+">");
7075 var posClosingDiv = original.lastIndexOf(endSaveArea,limitClosingDiv == -1 ? original.length : limitClosingDiv);
7076 return (posOpeningDiv != -1 && posClosingDiv != -1) ? [posOpeningDiv,posClosingDiv] : null;
7079 function autoSaveChanges(onlyIfDirty,tiddlers)
7081 if(config.options.chkAutoSave)
7082 saveChanges(onlyIfDirty,tiddlers);
7085 function loadOriginal(localPath)
7087 return loadFile(localPath);
7090 // Save this tiddlywiki with the pending changes
7091 function saveChanges(onlyIfDirty,tiddlers)
7093 if(onlyIfDirty && !store.isDirty())
7096 var t0 = new Date();
7097 var originalPath = document.location.toString();
7098 if(originalPath.substr(0,5) != "file:
") {
7099 alert(config.messages.notFileUrlError);
7100 if(store.tiddlerExists(config.messages.saveInstructions))
7101 story.displayTiddler(null,config.messages.saveInstructions);
7104 var localPath = getLocalPath(originalPath);
7105 var original = loadOriginal(localPath);
7106 if(original == null) {
7107 alert(config.messages.cantSaveError);
7108 if(store.tiddlerExists(config.messages.saveInstructions))
7109 story.displayTiddler(null,config.messages.saveInstructions);
7112 var posDiv = locateStoreArea(original);
7114 alert(config.messages.invalidFileError.format([localPath]));
7117 saveMain(localPath,original,posDiv);
7118 if(config.options.chkSaveBackups)
7119 saveBackup(localPath,original);
7120 if(config.options.chkSaveEmptyTemplate)
7121 saveEmpty(localPath,original,posDiv);
7122 if(config.options.chkGenerateAnRssFeed && saveRss instanceof Function)
7124 if(config.options.chkDisplayInstrumentation)
7125 displayMessage("saveChanges
" + (new Date()-t0) + " ms
");
7128 function saveMain(localPath,original,posDiv)
7132 var revised = updateOriginal(original,posDiv,localPath);
7133 save = saveFile(localPath,revised);
7138 displayMessage(config.messages.mainSaved,"file://
" + localPath);
7139 store.setDirty(false);
7141 alert(config.messages.mainFailed);
7145 function saveBackup(localPath,original)
7147 var backupPath = getBackupPath(localPath);
7148 var backup = copyFile(backupPath,localPath);
7150 backup = saveFile(backupPath,original);
7152 displayMessage(config.messages.backupSaved,"file://
" + backupPath);
7154 alert(config.messages.backupFailed);
7157 function saveEmpty(localPath,original,posDiv)
7160 if((p = localPath.lastIndexOf("/
")) != -1)
7161 emptyPath = localPath.substr(0,p) + "/
";
7162 else if((p = localPath.lastIndexOf("\\
")) != -1)
7163 emptyPath = localPath.substr(0,p) + "\\
";
7165 emptyPath = localPath + ".
";
7166 emptyPath += "empty.html
";
7167 var empty = original.substr(0,posDiv[0] + startSaveArea.length) + original.substr(posDiv[1]);
7168 var emptySave = saveFile(emptyPath,empty);
7170 displayMessage(config.messages.emptySaved,"file://
" + emptyPath);
7172 alert(config.messages.emptyFailed);
7175 function getLocalPath(origPath)
7177 var originalPath = convertUriToUTF8(origPath,config.options.txtFileSystemCharSet);
7178 // Remove any location or query part of the URL
7179 var argPos = originalPath.indexOf("?
");
7181 originalPath = originalPath.substr(0,argPos);
7182 var hashPos = originalPath.indexOf("#
");
7184 originalPath = originalPath.substr(0,hashPos);
7185 // Convert file://localhost/ to file:///
7186 if(originalPath.indexOf("file://localhost/
") == 0)
7187 originalPath = "file://
" + originalPath.substr(16);
7188 // Convert to a native file format
7190 if(originalPath.charAt(9) == ":
") // pc local file
7191 localPath = unescape(originalPath.substr(8)).replace(new RegExp("/
","g
"),"\\
");
7192 else if(originalPath.indexOf("file://///
") == 0) // FireFox pc network file
7193 localPath = "\\\\
" + unescape(originalPath.substr(10)).replace(new RegExp("/
","g
"),"\\
");
7194 else if(originalPath.indexOf("file:///
") == 0) // mac/unix local file
7195 localPath = unescape(originalPath.substr(7));
7196 else if(originalPath.indexOf("file:/
") == 0) // mac/unix local file
7197 localPath = unescape(originalPath.substr(5));
7198 else // pc network file
7199 localPath = "\\\\
" + unescape(originalPath.substr(7)).replace(new RegExp("/
","g
"),"\\
");
7203 function getBackupPath(localPath,title,extension)
7206 var dirPathPos = localPath.lastIndexOf("\\
");
7207 if(dirPathPos == -1) {
7208 dirPathPos = localPath.lastIndexOf("/
");
7211 var backupFolder = config.options.txtBackupFolder;
7212 if(!backupFolder || backupFolder == "")
7214 var backupPath = localPath.substr(0,dirPathPos) + slash + backupFolder + localPath.substr(dirPathPos);
7215 backupPath = backupPath.substr(0,backupPath.lastIndexOf(".
")) + ".
";
7217 backupPath += title.replace(/[\\\/\*\?\":<
> ]/g,
"_") +
".";
7218 backupPath += (new Date()).convertToYYYYMMDDHHMMSSMMM() +
"." + (extension ||
"html");
7226 function saveRss(localPath)
7228 var rssPath = localPath.substr(
0,localPath.lastIndexOf(
".")) +
".xml";
7229 if(saveFile(rssPath,convertUnicodeToFileFormat(generateRss())))
7230 displayMessage(config.messages.rssSaved,
"file://" + rssPath);
7232 alert(config.messages.rssFailed);
7235 function generateRss()
7239 var u = store.getTiddlerText(
"SiteUrl");
7240 // Assemble the header
7241 s.push(
"<" +
"?xml version=\"1.0\
"?" +
">");
7242 s.push(
"<rss version=\"2.0\
">");
7243 s.push(
"<channel>");
7244 s.push(
"<title" +
">" + wikifyPlain(
"SiteTitle").htmlEncode() +
"</title" +
">");
7246 s.push(
"<link>" + u.htmlEncode() +
"</link>");
7247 s.push(
"<description>" + wikifyPlain(
"SiteSubtitle").htmlEncode() +
"</description>");
7248 s.push(
"<language>" + config.locale +
"</language>");
7249 s.push(
"<copyright>Copyright " + d.getFullYear() +
" " + config.options.txtUserName.htmlEncode() +
"</copyright>");
7250 s.push(
"<pubDate>" + d.toGMTString() +
"</pubDate>");
7251 s.push(
"<lastBuildDate>" + d.toGMTString() +
"</lastBuildDate>");
7252 s.push(
"<docs>http://blogs.law.harvard.edu/tech/rss</docs>");
7253 s.push(
"<generator>TiddlyWiki " + formatVersion() +
"</generator>");
7255 var tiddlers = store.getTiddlers(
"modified",
"excludeLists");
7256 var n = config.numRssItems
> tiddlers.length ?
0 : tiddlers.length-config.numRssItems;
7257 for(var t=tiddlers.length-
1; t
>=n; t--) {
7258 s.push(
"<item>\n" + tiddlers[t].toRssItem(u) +
"\n</item>");
7261 s.push(
"</channel>");
7264 return s.join(
"\n");
7268 //-- Filesystem code
7271 function convertUTF8ToUnicode(u)
7273 return config.browser.isOpera || !window.netscape ? manualConvertUTF8ToUnicode(u) : mozConvertUTF8ToUnicode(u);
7276 function manualConvertUTF8ToUnicode(utf)
7283 while(src < utf.length) {
7284 b1 = utf.charCodeAt(src++);
7287 } else if(b1 <
0xE0) {
7288 b2 = utf.charCodeAt(src++);
7289 c = String.fromCharCode(((b1 &
0x1F) <<
6) | (b2 &
0x3F));
7290 uni = uni.substring(
0,dst++).concat(c,utf.substr(src));
7292 b2 = utf.charCodeAt(src++);
7293 b3 = utf.charCodeAt(src++);
7294 c = String.fromCharCode(((b1 &
0xF) <<
12) | ((b2 &
0x3F) <<
6) | (b3 &
0x3F));
7295 uni = uni.substring(
0,dst++).concat(c,utf.substr(src));
7301 function mozConvertUTF8ToUnicode(u)
7304 netscape.security.PrivilegeManager.enablePrivilege(
"UniversalXPConnect");
7305 var converter = Components.classes[
"@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
7306 converter.charset =
"UTF-8";
7308 return manualConvertUTF8ToUnicode(u);
7310 var s = converter.ConvertToUnicode(u);
7311 var fin = converter.Finish();
7312 return fin.length
> 0 ? s+fin : s;
7315 function convertUnicodeToFileFormat(s)
7317 return config.browser.isOpera || !window.netscape ? convertUnicodeToHtmlEntities(s) : mozConvertUnicodeToUTF8(s);
7320 function convertUnicodeToHtmlEntities(s)
7322 var re = /[^\u0000-\u007F]/g;
7323 return s.replace(re,function($
0) {return
"&#" + $
0.charCodeAt(
0).toString() +
";";});
7326 function convertUnicodeToUTF8(s)
7328 // return convertUnicodeToFileFormat to allow plugin migration
7329 return convertUnicodeToFileFormat(s);
7332 function manualConvertUnicodeToUTF8(s)
7334 return unescape(encodeURIComponent(s));
7337 function mozConvertUnicodeToUTF8(s)
7340 netscape.security.PrivilegeManager.enablePrivilege(
"UniversalXPConnect");
7341 var converter = Components.classes[
"@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
7342 converter.charset =
"UTF-8";
7344 return manualConvertUnicodeToUTF8(s);
7346 var u = converter.ConvertFromUnicode(s);
7347 var fin = converter.Finish();
7348 return fin.length
> 0 ? u + fin : u;
7351 function convertUriToUTF8(uri,charSet)
7353 if(window.netscape == undefined || charSet == undefined || charSet ==
"")
7356 netscape.security.PrivilegeManager.enablePrivilege(
"UniversalXPConnect");
7357 var converter = Components.classes[
"@mozilla.org/intl/utf8converterservice;1"].getService(Components.interfaces.nsIUTF8ConverterService);
7361 return converter.convertURISpecToUTF8(uri,charSet);
7364 function copyFile(dest,source)
7366 return config.browser.isIE ? ieCopyFile(dest,source) : false;
7369 function saveFile(fileUrl,content)
7371 var r = mozillaSaveFile(fileUrl,content);
7373 r = ieSaveFile(fileUrl,content);
7375 r = javaSaveFile(fileUrl,content);
7379 function loadFile(fileUrl)
7381 var r = mozillaLoadFile(fileUrl);
7382 if((r == null) || (r == false))
7383 r = ieLoadFile(fileUrl);
7384 if((r == null) || (r == false))
7385 r = javaLoadFile(fileUrl);
7389 function ieCreatePath(path)
7392 var fso = new ActiveXObject(
"Scripting.FileSystemObject");
7397 var pos = path.lastIndexOf(
"\\");
7399 path = path.substring(
0,pos+
1);
7402 var parent = fso.GetParentFolderName(path);
7403 while(parent && !fso.FolderExists(parent)) {
7405 parent = fso.GetParentFolderName(parent);
7408 for(i=scan.length-
1;i
>=
0;i--) {
7409 if(!fso.FolderExists(scan[i])) {
7410 fso.CreateFolder(scan[i]);
7416 // Returns null if it can't do it, false if there's an error, true if it saved OK
7417 function ieSaveFile(filePath,content)
7419 ieCreatePath(filePath);
7421 var fso = new ActiveXObject(
"Scripting.FileSystemObject");
7425 var file = fso.OpenTextFile(filePath,
2,-
1,
0);
7426 file.Write(content);
7431 // Returns null if it can't do it, false if there's an error, or a string of the content if successful
7432 function ieLoadFile(filePath)
7435 var fso = new ActiveXObject(
"Scripting.FileSystemObject");
7436 var file = fso.OpenTextFile(filePath,
1);
7437 var content = file.ReadAll();
7445 function ieCopyFile(dest,source)
7449 var fso = new ActiveXObject(
"Scripting.FileSystemObject");
7450 fso.GetFile(source).Copy(dest);
7457 // Returns null if it can't do it, false if there's an error, true if it saved OK
7458 function mozillaSaveFile(filePath,content)
7460 if(window.Components) {
7462 netscape.security.PrivilegeManager.enablePrivilege(
"UniversalXPConnect");
7463 var file = Components.classes[
"@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
7464 file.initWithPath(filePath);
7466 file.create(
0,
0664);
7467 var out = Components.classes[
"@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
7468 out.init(file,
0x20|
0x02,
00004,null);
7469 out.write(content,content.length);
7480 // Returns null if it can't do it, false if there's an error, or a string of the content if successful
7481 function mozillaLoadFile(filePath)
7483 if(window.Components) {
7485 netscape.security.PrivilegeManager.enablePrivilege(
"UniversalXPConnect");
7486 var file = Components.classes[
"@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
7487 file.initWithPath(filePath);
7490 var inputStream = Components.classes[
"@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
7491 inputStream.init(file,
0x01,
00004,null);
7492 var sInputStream = Components.classes[
"@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
7493 sInputStream.init(inputStream);
7494 var contents = sInputStream.read(sInputStream.available());
7495 sInputStream.close();
7496 inputStream.close();
7505 function javaUrlToFilename(url)
7507 var f =
"//localhost";
7508 if(url.indexOf(f) ==
0)
7509 return url.substring(f.length);
7510 var i = url.indexOf(
":");
7511 return i
> 0 ? url.substring(i-
1) : url;
7514 function javaSaveFile(filePath,content)
7517 if(document.applets[
"TiddlySaver"])
7518 return document.applets[
"TiddlySaver"].saveFile(javaUrlToFilename(filePath),
"UTF-8",content);
7522 var s = new java.io.PrintStream(new java.io.FileOutputStream(javaUrlToFilename(filePath)));
7531 function javaLoadFile(filePath)
7534 if(document.applets[
"TiddlySaver"])
7535 return String(document.applets[
"TiddlySaver"].loadFile(javaUrlToFilename(filePath),
"UTF-8"));
7540 var r = new java.io.BufferedReader(new java.io.FileReader(javaUrlToFilename(filePath)));
7542 while((line = r.readLine()) != null)
7543 content.push(new String(line));
7548 return content.join(
"\n");
7552 //-- Server adaptor base class
7555 function AdaptorBase()
7562 AdaptorBase.prototype.close = function()
7567 AdaptorBase.prototype.fullHostName = function(host)
7572 if(!host.match(/:\/\//))
7573 host = 'http://' + host;
7574 if(host.substr(host.length-
1) == '/')
7575 host = host.substr(
0,host.length-
1)
7579 AdaptorBase.minHostName = function(host)
7581 return host ? host.replace(/^http:\/\//,'').replace(/\/$/,'') : '';
7584 AdaptorBase.prototype.setContext = function(context,userParams,callback)
7586 if(!context) context = {};
7587 context.userParams = userParams;
7588 if(callback) context.callback = callback;
7589 context.adaptor = this;
7591 context.host = this.host;
7592 context.host = this.fullHostName(context.host);
7593 if(!context.workspace)
7594 context.workspace = this.workspace;
7598 // Open the specified host
7599 AdaptorBase.prototype.openHost = function(host,context,userParams,callback)
7602 context = this.setContext(context,userParams,callback);
7603 context.status = true;
7605 window.setTimeout(function() {context.callback(context,userParams);},
10);
7609 // Open the specified workspace
7610 AdaptorBase.prototype.openWorkspace = function(workspace,context,userParams,callback)
7612 this.workspace = workspace;
7613 context = this.setContext(context,userParams,callback);
7614 context.status = true;
7616 window.setTimeout(function() {callback(context,userParams);},
10);
7621 //-- Server adaptor for talking to static TiddlyWiki files
7624 function FileAdaptor()
7628 FileAdaptor.prototype = new AdaptorBase();
7630 FileAdaptor.serverType = 'file';
7631 FileAdaptor.serverLabel = 'TiddlyWiki';
7633 FileAdaptor.loadTiddlyWikiCallback = function(status,context,responseText,url,xhr)
7635 context.status = status;
7637 context.statusText =
"Error reading file";
7639 context.adaptor.store = new TiddlyWiki();
7640 if(!context.adaptor.store.importTiddlyWiki(responseText)) {
7641 context.statusText = config.messages.invalidFileError.format([url]);
7642 context.status = false;
7645 context.complete(context,context.userParams);
7648 // Get the list of workspaces on a given server
7649 FileAdaptor.prototype.getWorkspaceList = function(context,userParams,callback)
7651 context = this.setContext(context,userParams,callback);
7652 context.workspaces = [{title:
"(default)"}];
7653 context.status = true;
7655 window.setTimeout(function() {callback(context,userParams);},
10);
7659 // Gets the list of tiddlers within a given workspace
7660 FileAdaptor.prototype.getTiddlerList = function(context,userParams,callback,filter)
7662 context = this.setContext(context,userParams,callback);
7664 context.filter = filter;
7665 context.complete = FileAdaptor.getTiddlerListComplete;
7667 var ret = context.complete(context,context.userParams);
7669 ret = loadRemoteFile(context.host,FileAdaptor.loadTiddlyWikiCallback,context);
7670 if(typeof ret !=
"string")
7676 FileAdaptor.getTiddlerListComplete = function(context,userParams)
7678 if(context.status) {
7679 if(context.filter) {
7680 context.tiddlers = context.adaptor.store.filterTiddlers(context.filter);
7682 context.tiddlers = [];
7683 context.adaptor.store.forEachTiddler(function(title,tiddler) {context.tiddlers.push(tiddler);});
7685 for(var i=
0; i
<context.tiddlers.length; i++) {
7686 context.tiddlers[i].fields['server.type'] = FileAdaptor.serverType;
7687 context.tiddlers[i].fields['server.host'] = AdaptorBase.minHostName(context.host);
7688 context.tiddlers[i].fields['server.page.revision'] = context.tiddlers[i].modified.convertToYYYYMMDDHHMM();
7690 context.status = true;
7692 if(context.callback) {
7693 window.setTimeout(function() {context.callback(context,userParams);},
10);
7698 FileAdaptor.prototype.generateTiddlerInfo = function(tiddler)
7701 info.uri = tiddler.fields['server.host'] +
"#" + tiddler.title;
7705 // Retrieve a tiddler from a given workspace on a given server
7706 FileAdaptor.prototype.getTiddler = function(title,context,userParams,callback)
7708 context = this.setContext(context,userParams,callback);
7709 context.title = title;
7710 context.complete = FileAdaptor.getTiddlerComplete;
7711 return context.adaptor.store ?
7712 context.complete(context,context.userParams) :
7713 loadRemoteFile(context.host,FileAdaptor.loadTiddlyWikiCallback,context);
7716 FileAdaptor.getTiddlerComplete = function(context,userParams)
7718 var t = context.adaptor.store.fetchTiddler(context.title);
7719 t.fields['server.type'] = FileAdaptor.serverType;
7720 t.fields['server.host'] = AdaptorBase.minHostName(context.host);
7721 t.fields['server.page.revision'] = t.modified.convertToYYYYMMDDHHMM();
7722 context.tiddler = t;
7723 context.status = true;
7724 if(context.allowSynchronous) {
7725 context.isSynchronous = true;
7726 context.callback(context,userParams);
7728 window.setTimeout(function() {context.callback(context,userParams);},
10);
7733 FileAdaptor.prototype.close = function()
7739 config.adaptors[FileAdaptor.serverType] = FileAdaptor;
7741 config.defaultAdaptor = FileAdaptor.serverType;
7744 //-- Remote HTTP requests
7747 function loadRemoteFile(url,callback,params)
7749 return httpReq(
"GET",url,callback,params);
7752 function httpReq(type,url,callback,params,headers,data,contentType,username,password,allowCache)
7756 x = new XMLHttpRequest(); //# Modern
7759 x = new ActiveXObject(
"Msxml2.XMLHTTP"); //# IE
6
7764 return
"Can't create XMLHttpRequest object";
7765 x.onreadystatechange = function() {
7767 var status = x.status;
7771 if(x.readyState ==
4 && callback && (status !== undefined)) {
7772 if([
0,
200,
201,
204,
207].contains(status))
7773 callback(true,params,x.responseText,url,x);
7775 callback(false,params,null,url,x);
7776 x.onreadystatechange = function(){};
7780 if(window.Components && window.netscape && window.netscape.security && document.location.protocol.indexOf(
"http") == -
1)
7781 window.netscape.security.PrivilegeManager.enablePrivilege(
"UniversalBrowserRead");
7784 url = url + (url.indexOf(
"?") <
0 ?
"?" :
"&") +
"nocache=" + Math.random();
7785 x.open(type,url,true,username,password);
7787 x.setRequestHeader(
"Content-Type", contentType ||
"application/x-www-form-urlencoded");
7788 if(x.overrideMimeType)
7789 x.setRequestHeader(
"Connection",
"close");
7791 for(var n in headers)
7792 x.setRequestHeader(n,headers[n]);
7794 x.setRequestHeader(
"X-Requested-With",
"TiddlyWiki " + formatVersion());
7797 return exceptionText(ex);
7802 // included for compatibility
7803 function getXMLHttpRequest()
7806 var x = new XMLHttpRequest(); // Modern
7809 x = new ActiveXObject(
"Msxml2.XMLHTTP"); // IE
6
7817 // included for compatibility
7818 function doHttp(type,url,data,contentType,username,password,callback,params,headers,allowCache)
7820 return httpReq(type,url,callback,params,headers,data,contentType,username,password,allowCache);
7824 //-- TiddlyWiki-specific utility functions
7827 function formatVersion(v)
7830 return v.major +
"." + v.minor +
"." + v.revision + (v.beta ?
" (beta " + v.beta +
")" :
"");
7833 function compareVersions(v1,v2)
7835 var a = [
"major",
"minor",
"revision"];
7836 for(var i =
0; i
<a.length; i++) {
7837 var x1 = v1[a[i]] ||
0;
7838 var x2 = v2[a[i]] ||
0;
7844 x1 = v1.beta ||
9999;
7845 x2 = v2.beta ||
9999;
7848 return x1
> x2 ? -
1 :
0;
7851 function createTiddlyButton(parent,text,tooltip,action,className,id,accessKey,attribs)
7853 var btn = document.createElement(
"a");
7855 btn.onclick = action;
7856 btn.setAttribute(
"href",
"javascript:;");
7859 btn.setAttribute(
"title",tooltip);
7861 btn.appendChild(document.createTextNode(text));
7862 btn.className = className ||
"button";
7866 for(var i in attribs) {
7867 btn.setAttribute(i,attribs[i]);
7871 parent.appendChild(btn);
7873 btn.setAttribute(
"accessKey",accessKey);
7877 function createTiddlyLink(place,title,includeText,className,isStatic,linkedFromTiddler,noToggle)
7879 var text = includeText ? title : null;
7880 var i = getTiddlyLinkInfo(title,className);
7881 var btn = isStatic ? createExternalLink(place,store.getTiddlerText(
"SiteUrl",null) +
"#" + title) : createTiddlyButton(place,text,i.subTitle,onClickTiddlerLink,i.classes);
7883 btn.className += ' ' + className;
7884 btn.setAttribute(
"refresh",
"link");
7885 btn.setAttribute(
"tiddlyLink",title);
7887 btn.setAttribute(
"noToggle",
"true");
7888 if(linkedFromTiddler) {
7889 var fields = linkedFromTiddler.getInheritedFields();
7891 btn.setAttribute(
"tiddlyFields",fields);
7896 function refreshTiddlyLink(e,title)
7898 var i = getTiddlyLinkInfo(title,e.className);
7899 e.className = i.classes;
7900 e.title = i.subTitle;
7903 function getTiddlyLinkInfo(title,currClasses)
7905 var classes = currClasses ? currClasses.split(
" ") : [];
7906 classes.pushUnique(
"tiddlyLink");
7907 var tiddler = store.fetchTiddler(title);
7910 subTitle = tiddler.getSubtitle();
7911 classes.pushUnique(
"tiddlyLinkExisting");
7912 classes.remove(
"tiddlyLinkNonExisting");
7913 classes.remove(
"shadow");
7915 classes.remove(
"tiddlyLinkExisting");
7916 classes.pushUnique(
"tiddlyLinkNonExisting");
7917 if(store.isShadowTiddler(title)) {
7918 subTitle = config.messages.shadowedTiddlerToolTip.format([title]);
7919 classes.pushUnique(
"shadow");
7921 subTitle = config.messages.undefinedTiddlerToolTip.format([title]);
7922 classes.remove(
"shadow");
7925 if(typeof config.annotations[title]==
"string")
7926 subTitle = config.annotations[title];
7927 return {classes: classes.join(
" "),subTitle: subTitle};
7930 function createExternalLink(place,url)
7932 var link = document.createElement(
"a");
7933 link.className =
"externalLink";
7935 link.title = config.messages.externalLinkTooltip.format([url]);
7936 if(config.options.chkOpenInNewWindow)
7937 link.target =
"_blank";
7938 place.appendChild(link);
7942 // Event handler for clicking on a tiddly link
7943 function onClickTiddlerLink(ev)
7945 var e = ev || window.event;
7946 var target = resolveTarget(e);
7950 var noToggle = null;
7952 title = link.getAttribute(
"tiddlyLink");
7953 fields = link.getAttribute(
"tiddlyFields");
7954 noToggle = link.getAttribute(
"noToggle");
7955 link = link.parentNode;
7956 } while(title == null && link != null);
7957 if(!store.isShadowTiddler(title)) {
7958 var f = fields ? fields.decodeHashMap() : {};
7959 fields = String.encodeHashMap(merge(f,config.defaultCustomFields,true));
7962 var toggling = e.metaKey || e.ctrlKey;
7963 if(config.options.chkToggleLinks)
7964 toggling = !toggling;
7967 if(store.getTiddler(title))
7969 story.displayTiddler(target,title,null,true,null,fields,toggling);
7975 // Create a button for a tag with a popup listing all the tiddlers that it tags
7976 function createTagButton(place,tag,excludeTiddler,title,tooltip)
7978 var btn = createTiddlyButton(place,title||tag,(tooltip||config.views.wikified.tag.tooltip).format([tag]),onClickTag);
7979 btn.setAttribute(
"tag",tag);
7981 btn.setAttribute(
"tiddler",excludeTiddler);
7985 // Event handler for clicking on a tiddler tag
7986 function onClickTag(ev)
7988 var e = ev || window.event;
7989 var popup = Popup.create(this);
7990 var tag = this.getAttribute(
"tag");
7991 var title = this.getAttribute(
"tiddler");
7993 var tagged = store.getTaggedTiddlers(tag);
7996 for(r=
0;r
<tagged.length;r++) {
7997 if(tagged[r].title != title)
7998 titles.push(tagged[r].title);
8000 var lingo = config.views.wikified.tag;
8001 if(titles.length
> 0) {
8002 var openAll = createTiddlyButton(createTiddlyElement(popup,
"li"),lingo.openAllText.format([tag]),lingo.openAllTooltip,onClickTagOpenAll);
8003 openAll.setAttribute(
"tag",tag);
8004 createTiddlyElement(createTiddlyElement(popup,
"li",null,
"listBreak"),
"div");
8005 for(r=
0; r
<titles.length; r++) {
8006 createTiddlyLink(createTiddlyElement(popup,
"li"),titles[r],true);
8009 createTiddlyText(createTiddlyElement(popup,
"li",null,
"disabled"),lingo.popupNone.format([tag]));
8011 createTiddlyElement(createTiddlyElement(popup,
"li",null,
"listBreak"),
"div");
8012 var h = createTiddlyLink(createTiddlyElement(popup,
"li"),tag,false);
8013 createTiddlyText(h,lingo.openTag.format([tag]));
8016 e.cancelBubble = true;
8017 if(e.stopPropagation) e.stopPropagation();
8021 // Event handler for 'open all' on a tiddler popup
8022 function onClickTagOpenAll(ev)
8024 var tiddlers = store.getTaggedTiddlers(this.getAttribute(
"tag"));
8025 story.displayTiddlers(this,tiddlers);
8029 function onClickError(ev)
8031 var e = ev || window.event;
8032 var popup = Popup.create(this);
8033 var lines = this.getAttribute(
"errorText").split(
"\n");
8034 for(var t=
0; t
<lines.length; t++)
8035 createTiddlyElement(popup,
"li",null,null,lines[t]);
8037 e.cancelBubble = true;
8038 if(e.stopPropagation) e.stopPropagation();
8042 function createTiddlyDropDown(place,onchange,options,defaultValue)
8044 var sel = createTiddlyElement(place,
"select");
8045 sel.onchange = onchange;
8046 for(var t=
0; t
<options.length; t++) {
8047 var e = createTiddlyElement(sel,
"option",null,null,options[t].caption);
8048 e.value = options[t].name;
8049 if(options[t].name == defaultValue)
8055 function createTiddlyPopup(place,caption,tooltip,tiddler)
8058 createTiddlyLink(place,caption,true);
8059 var btn = createTiddlyButton(place,glyph(
"downArrow"),tooltip,onClickTiddlyPopup,
"tiddlerPopupButton");
8060 btn.tiddler = tiddler;
8062 createTiddlyText(place,caption);
8066 function onClickTiddlyPopup(ev)
8068 var e = ev || window.event;
8069 var tiddler = this.tiddler;
8071 var popup = Popup.create(this,
"div",
"popupTiddler");
8072 wikify(tiddler.text,popup,null,tiddler);
8075 if(e) e.cancelBubble = true;
8076 if(e && e.stopPropagation) e.stopPropagation();
8080 function createTiddlyError(place,title,text)
8082 var btn = createTiddlyButton(place,title,null,onClickError,
"errorButton");
8083 if(text) btn.setAttribute(
"errorText",text);
8086 function merge(dst,src,preserveExisting)
8089 if(!preserveExisting || dst[i] === undefined)
8095 // Returns a string containing the description of an exception, optionally prepended by a message
8096 function exceptionText(e,message)
8098 var s = e.description || e.toString();
8099 return message ?
"%0:\n%1".format([message,s]) : s;
8102 // Displays an alert of an exception description with optional message
8103 function showException(e,message)
8105 alert(exceptionText(e,message));
8108 function alertAndThrow(m)
8114 function glyph(name)
8116 var g = config.glyphs;
8117 var b = g.currBrowser;
8120 while(!g.browsers[b]() && b < g.browsers.length-
1)
8126 return g.codes[name][b];
8129 if(!window.console) {
8130 console = {log:function(message) {displayMessage(message);}};
8134 //- Animation engine
8139 this.running =
0; // Incremented at start of each animation, decremented afterwards. If zero, the interval timer is disabled
8140 this.timerID =
0; // ID of the timer used for animating
8141 this.animations = []; // List of animations in progress
8145 // Start animation engine
8146 Animator.prototype.startAnimating = function() //# Variable number of arguments
8148 for(var t=
0; t
<arguments.length; t++)
8149 this.animations.push(arguments[t]);
8150 if(this.running ==
0) {
8152 this.timerID = window.setInterval(function() {me.doAnimate(me);},
10);
8154 this.running += arguments.length;
8157 // Perform an animation engine tick, calling each of the known animation modules
8158 Animator.prototype.doAnimate = function(me)
8161 while(a < me.animations.length) {
8162 var animation = me.animations[a];
8163 if(animation.tick()) {
8166 me.animations.splice(a,
1);
8167 if(--me.running ==
0)
8168 window.clearInterval(me.timerID);
8173 Animator.slowInSlowOut = function(progress)
8175 return(
1-((Math.cos(progress * Math.PI)+
1)/
2));
8179 //-- Morpher animation
8182 // Animate a set of properties of an element
8183 function Morpher(element,duration,properties,callback)
8185 this.element = element;
8186 this.duration = duration;
8187 this.properties = properties;
8188 this.startTime = new Date();
8189 this.endTime = Number(this.startTime) + duration;
8190 this.callback = callback;
8195 Morpher.prototype.assignStyle = function(element,style,value)
8198 case
"-tw-vertScroll":
8199 window.scrollTo(findScrollX(),value);
8201 case
"-tw-horizScroll":
8202 window.scrollTo(value,findScrollY());
8205 element.style[style] = value;
8210 Morpher.prototype.stop = function()
8212 for(var t=
0; t
<this.properties.length; t++) {
8213 var p = this.properties[t];
8214 if(p.atEnd !== undefined) {
8215 this.assignStyle(this.element,p.style,p.atEnd);
8219 this.callback(this.element,this.properties);
8222 Morpher.prototype.tick = function()
8224 var currTime = Number(new Date());
8225 var progress = Animator.slowInSlowOut(Math.min(
1,(currTime-this.startTime)/this.duration));
8226 for(var t=
0; t
<this.properties.length; t++) {
8227 var p = this.properties[t];
8228 if(p.start !== undefined && p.end !== undefined) {
8229 var template = p.template ||
"%0";
8233 var v = p.start + (p.end-p.start) * progress;
8234 this.assignStyle(this.element,p.style,template.format([v]));
8241 if(currTime
>= this.endTime) {
8249 //-- Zoomer animation
8252 function Zoomer(text,startElement,targetElement,unused)
8254 var e = createTiddlyElement(document.body,
"div",null,
"zoomer");
8255 createTiddlyElement(e,
"div",null,null,text);
8256 var winWidth = findWindowWidth();
8257 var winHeight = findWindowHeight();
8259 {style: 'left', start: findPosX(startElement), end: findPosX(targetElement), template: '%
0px'},
8260 {style: 'top', start: findPosY(startElement), end: findPosY(targetElement), template: '%
0px'},
8261 {style: 'width', start: Math.min(startElement.scrollWidth,winWidth), end: Math.min(targetElement.scrollWidth,winWidth), template: '%
0px', atEnd: 'auto'},
8262 {style: 'height', start: Math.min(startElement.scrollHeight,winHeight), end: Math.min(targetElement.scrollHeight,winHeight), template: '%
0px', atEnd: 'auto'},
8263 {style: 'fontSize', start:
8, end:
24, template: '%
0pt'}
8265 var c = function(element,properties) {removeNode(element);};
8266 return new Morpher(e,config.animDuration,p,c);
8270 //-- Scroller animation
8273 function Scroller(targetElement)
8275 var p = [{style: '-tw-vertScroll', start: findScrollY(), end: ensureVisible(targetElement)}];
8276 return new Morpher(targetElement,config.animDuration,p);
8280 //-- Slider animation
8283 // deleteMode -
"none",
"all" [delete target element and it's children], [only]
"children" [but not the target element]
8284 function Slider(element,opening,unused,deleteMode)
8286 element.style.overflow = 'hidden';
8288 element.style.height = '
0px'; // Resolves a Firefox flashing bug
8289 element.style.display = 'block';
8290 var left = findPosX(element);
8291 var width = element.scrollWidth;
8292 var height = element.scrollHeight;
8293 var winWidth = findWindowWidth();
8297 p.push({style: 'height', start:
0, end: height, template: '%
0px', atEnd: 'auto'});
8298 p.push({style: 'opacity', start:
0, end:
1, template: '%
0'});
8299 p.push({style: 'filter', start:
0, end:
100, template: 'alpha(opacity:%
0)'});
8301 p.push({style: 'height', start: height, end:
0, template: '%
0px'});
8302 p.push({style: 'display', atEnd: 'none'});
8303 p.push({style: 'opacity', start:
1, end:
0, template: '%
0'});
8304 p.push({style: 'filter', start:
100, end:
0, template: 'alpha(opacity:%
0)'});
8305 switch(deleteMode) {
8307 c = function(element,properties) {removeNode(element);};
8310 c = function(element,properties) {removeChildren(element);};
8314 return new Morpher(element,config.animDuration,p,c);
8322 stack: [] // Array of objects with members root: and popup:
8325 Popup.create = function(root,elem,className)
8327 var stackPosition = this.find(root,
"popup");
8328 Popup.remove(stackPosition+
1);
8329 var popup = createTiddlyElement(document.body,elem ||
"ol",
"popup",className ||
"popup");
8330 popup.stackPosition = stackPosition;
8331 Popup.stack.push({root: root, popup: popup});
8335 Popup.onDocumentClick = function(ev)
8337 var e = ev || window.event;
8338 if(e.eventPhase == undefined)
8340 else if(e.eventPhase == Event.BUBBLING_PHASE || e.eventPhase == Event.AT_TARGET)
8345 Popup.show = function(valign,halign,offset)
8347 var curr = Popup.stack[Popup.stack.length-
1];
8348 this.place(curr.root,curr.popup,valign,halign,offset);
8349 addClass(curr.root,
"highlight");
8350 if(config.options.chkAnimate && anim && typeof Scroller ==
"function")
8351 anim.startAnimating(new Scroller(curr.popup));
8353 window.scrollTo(
0,ensureVisible(curr.popup));
8356 Popup.place = function(root,popup,valign,halign,offset)
8359 var offset = {x:
0,y:
0};
8360 if(popup.stackPosition
>=
0 && !valign && !halign) {
8361 offset.x = offset.x + root.offsetWidth;
8363 offset.x = (halign == 'right') ? offset.x + root.offsetWidth : offset.x;
8364 offset.y = (valign == 'top') ? offset.y : offset.y + root.offsetHeight;
8366 var rootLeft = findPosX(root);
8367 var rootTop = findPosY(root);
8368 var popupLeft = rootLeft + offset.x;
8369 var popupTop = rootTop + offset.y;
8370 var winWidth = findWindowWidth();
8371 if(popup.offsetWidth
> winWidth*
0.75)
8372 popup.style.width = winWidth*
0.75 +
"px";
8373 var popupWidth = popup.offsetWidth;
8374 var scrollWidth = winWidth - document.body.offsetWidth;
8375 if(popupLeft + popupWidth
> winWidth - scrollWidth -
1) {
8376 if(halign == 'right')
8377 popupLeft = popupLeft - root.offsetWidth - popupWidth;
8379 popupLeft = winWidth - popupWidth - scrollWidth -
1;
8381 popup.style.left = popupLeft +
"px";
8382 popup.style.top = popupTop +
"px";
8383 popup.style.display =
"block";
8386 Popup.find = function(e)
8389 for (var t=this.stack.length-
1; t
>=
0; t--) {
8390 if(isDescendant(e,this.stack[t].popup))
8396 Popup.remove = function(pos)
8398 if(!pos) var pos =
0;
8399 if(Popup.stack.length
> pos) {
8400 Popup.removeFrom(pos);
8404 Popup.removeFrom = function(from)
8406 for(var t=Popup.stack.length-
1; t
>=from; t--) {
8407 var p = Popup.stack[t];
8408 removeClass(p.root,
"highlight");
8409 removeNode(p.popup);
8411 Popup.stack = Popup.stack.slice(
0,from);
8418 function Wizard(elem)
8421 this.formElem = findRelated(elem,
"wizard",
"className");
8422 this.bodyElem = findRelated(this.formElem.firstChild,
"wizardBody",
"className",
"nextSibling");
8423 this.footElem = findRelated(this.formElem.firstChild,
"wizardFooter",
"className",
"nextSibling");
8425 this.formElem = null;
8426 this.bodyElem = null;
8427 this.footElem = null;
8431 Wizard.prototype.setValue = function(name,value)
8434 this.formElem[name] = value;
8437 Wizard.prototype.getValue = function(name)
8439 return this.formElem ? this.formElem[name] : null;
8442 Wizard.prototype.createWizard = function(place,title)
8444 this.formElem = createTiddlyElement(place,
"form",null,
"wizard");
8445 createTiddlyElement(this.formElem,
"h1",null,null,title);
8446 this.bodyElem = createTiddlyElement(this.formElem,
"div",null,
"wizardBody");
8447 this.footElem = createTiddlyElement(this.formElem,
"div",null,
"wizardFooter");
8450 Wizard.prototype.clear = function()
8452 removeChildren(this.bodyElem);
8455 Wizard.prototype.setButtons = function(buttonInfo,status)
8457 removeChildren(this.footElem);
8458 for(var t=
0; t
<buttonInfo.length; t++) {
8459 createTiddlyButton(this.footElem,buttonInfo[t].caption,buttonInfo[t].tooltip,buttonInfo[t].onClick);
8460 insertSpacer(this.footElem);
8462 if(typeof status ==
"string") {
8463 createTiddlyElement(this.footElem,
"span",null,
"status",status);
8467 Wizard.prototype.addStep = function(stepTitle,html)
8469 removeChildren(this.bodyElem);
8470 var w = createTiddlyElement(this.bodyElem,
"div");
8471 createTiddlyElement(w,
"h2",null,null,stepTitle);
8472 var step = createTiddlyElement(w,
"div",null,
"wizardStep");
8473 step.innerHTML = html;
8474 applyHtmlMacros(step,tiddler);
8477 Wizard.prototype.getElement = function(name)
8479 return this.formElem.elements[name];
8483 //-- ListView gadget
8488 // Create a listview
8489 ListView.create = function(place,listObject,listTemplate,callback,className)
8491 var table = createTiddlyElement(place,
"table",null,className ||
"listView twtable");
8492 var thead = createTiddlyElement(table,
"thead");
8493 var r = createTiddlyElement(thead,
"tr");
8494 for(var t=
0; t
<listTemplate.columns.length; t++) {
8495 var columnTemplate = listTemplate.columns[t];
8496 var c = createTiddlyElement(r,
"th");
8497 var colType = ListView.columnTypes[columnTemplate.type];
8498 if(colType && colType.createHeader) {
8499 colType.createHeader(c,columnTemplate,t);
8500 if(columnTemplate.className)
8501 addClass(c,columnTemplate.className);
8504 var tbody = createTiddlyElement(table,
"tbody");
8505 for(var rc=
0; rc
<listObject.length; rc++) {
8506 var rowObject = listObject[rc];
8507 r = createTiddlyElement(tbody,
"tr");
8508 for(c=
0; c
<listTemplate.rowClasses.length; c++) {
8509 if(rowObject[listTemplate.rowClasses[c].field])
8510 addClass(r,listTemplate.rowClasses[c].className);
8512 rowObject.rowElement = r;
8513 rowObject.colElements = {};
8514 for(var cc=
0; cc
<listTemplate.columns.length; cc++) {
8515 c = createTiddlyElement(r,
"td");
8516 columnTemplate = listTemplate.columns[cc];
8517 var field = columnTemplate.field;
8518 colType = ListView.columnTypes[columnTemplate.type];
8519 if(colType && colType.createItem) {
8520 colType.createItem(c,rowObject,field,columnTemplate,cc,rc);
8521 if(columnTemplate.className)
8522 addClass(c,columnTemplate.className);
8524 rowObject.colElements[field] = c;
8527 if(callback && listTemplate.actions)
8528 createTiddlyDropDown(place,ListView.getCommandHandler(callback),listTemplate.actions);
8529 if(callback && listTemplate.buttons) {
8530 for(t=
0; t
<listTemplate.buttons.length; t++) {
8531 var a = listTemplate.buttons[t];
8532 if(a && a.name !=
"")
8533 createTiddlyButton(place,a.caption,null,ListView.getCommandHandler(callback,a.name,a.allowEmptySelection));
8539 ListView.getCommandHandler = function(callback,name,allowEmptySelection)
8541 return function(e) {
8542 var view = findRelated(this,
"TABLE",null,
"previousSibling");
8544 ListView.forEachSelector(view,function(e,rowName) {
8546 tiddlers.push(rowName);
8548 if(tiddlers.length ==
0 && !allowEmptySelection) {
8549 alert(config.messages.nothingSelected);
8551 if(this.nodeName.toLowerCase() ==
"select") {
8552 callback(view,this.value,tiddlers);
8553 this.selectedIndex =
0;
8555 callback(view,name,tiddlers);
8561 // Invoke a callback for each selector checkbox in the listview
8562 ListView.forEachSelector = function(view,callback)
8564 var checkboxes = view.getElementsByTagName(
"input");
8566 for(var t=
0; t
<checkboxes.length; t++) {
8567 var cb = checkboxes[t];
8568 if(cb.getAttribute(
"type") ==
"checkbox") {
8569 var rn = cb.getAttribute(
"rowName");
8579 ListView.getSelectedRows = function(view)
8582 ListView.forEachSelector(view,function(e,rowName) {
8584 rowNames.push(rowName);
8589 ListView.columnTypes = {};
8591 ListView.columnTypes.String = {
8592 createHeader: function(place,columnTemplate,col)
8594 createTiddlyText(place,columnTemplate.title);
8596 createItem: function(place,listObject,field,columnTemplate,col,row)
8598 var v = listObject[field];
8600 createTiddlyText(place,v);
8604 ListView.columnTypes.WikiText = {
8605 createHeader: ListView.columnTypes.String.createHeader,
8606 createItem: function(place,listObject,field,columnTemplate,col,row)
8608 var v = listObject[field];
8610 wikify(v,place,null,null);
8614 ListView.columnTypes.Tiddler = {
8615 createHeader: ListView.columnTypes.String.createHeader,
8616 createItem: function(place,listObject,field,columnTemplate,col,row)
8618 var v = listObject[field];
8619 if(v != undefined && v.title)
8620 createTiddlyPopup(place,v.title,config.messages.listView.tiddlerTooltip,v);
8624 ListView.columnTypes.Size = {
8625 createHeader: ListView.columnTypes.String.createHeader,
8626 createItem: function(place,listObject,field,columnTemplate,col,row)
8628 var v = listObject[field];
8629 if(v != undefined) {
8631 while(t
<config.messages.sizeTemplates.length-
1 && v
<config.messages.sizeTemplates[t].unit)
8633 createTiddlyText(place,config.messages.sizeTemplates[t].template.format([Math.round(v/config.messages.sizeTemplates[t].unit)]));
8638 ListView.columnTypes.Link = {
8639 createHeader: ListView.columnTypes.String.createHeader,
8640 createItem: function(place,listObject,field,columnTemplate,col,row)
8642 var v = listObject[field];
8643 var c = columnTemplate.text;
8645 createTiddlyText(createExternalLink(place,v),c || v);
8649 ListView.columnTypes.Date = {
8650 createHeader: ListView.columnTypes.String.createHeader,
8651 createItem: function(place,listObject,field,columnTemplate,col,row)
8653 var v = listObject[field];
8655 createTiddlyText(place,v.formatString(columnTemplate.dateFormat));
8659 ListView.columnTypes.StringList = {
8660 createHeader: ListView.columnTypes.String.createHeader,
8661 createItem: function(place,listObject,field,columnTemplate,col,row)
8663 var v = listObject[field];
8664 if(v != undefined) {
8665 for(var t=
0; t
<v.length; t++) {
8666 createTiddlyText(place,v[t]);
8667 createTiddlyElement(place,
"br");
8673 ListView.columnTypes.Selector = {
8674 createHeader: function(place,columnTemplate,col)
8676 createTiddlyCheckbox(place,null,false,this.onHeaderChange);
8678 createItem: function(place,listObject,field,columnTemplate,col,row)
8680 var e = createTiddlyCheckbox(place,null,listObject[field],null);
8681 e.setAttribute(
"rowName",listObject[columnTemplate.rowName]);
8683 onHeaderChange: function(e)
8685 var state = this.checked;
8686 var view = findRelated(this,
"TABLE");
8689 ListView.forEachSelector(view,function(e,rowName) {
8695 ListView.columnTypes.Tags = {
8696 createHeader: ListView.columnTypes.String.createHeader,
8697 createItem: function(place,listObject,field,columnTemplate,col,row)
8699 var tags = listObject[field];
8700 createTiddlyText(place,String.encodeTiddlyLinkList(tags));
8704 ListView.columnTypes.Boolean = {
8705 createHeader: ListView.columnTypes.String.createHeader,
8706 createItem: function(place,listObject,field,columnTemplate,col,row)
8708 if(listObject[field] == true)
8709 createTiddlyText(place,columnTemplate.trueText);
8710 if(listObject[field] == false)
8711 createTiddlyText(place,columnTemplate.falseText);
8715 ListView.columnTypes.TagCheckbox = {
8716 createHeader: ListView.columnTypes.String.createHeader,
8717 createItem: function(place,listObject,field,columnTemplate,col,row)
8719 var e = createTiddlyCheckbox(place,null,listObject[field],this.onChange);
8720 e.setAttribute(
"tiddler",listObject.title);
8721 e.setAttribute(
"tag",columnTemplate.tag);
8723 onChange : function(e)
8725 var tag = this.getAttribute(
"tag");
8726 var tiddler = this.getAttribute(
"tiddler");
8727 store.setTiddlerTag(tiddler,this.checked,tag);
8731 ListView.columnTypes.TiddlerLink = {
8732 createHeader: ListView.columnTypes.String.createHeader,
8733 createItem: function(place,listObject,field,columnTemplate,col,row)
8735 var v = listObject[field];
8736 if(v != undefined) {
8737 var link = createTiddlyLink(place,listObject[columnTemplate.tiddlerLink],false,null);
8738 createTiddlyText(link,listObject[field]);
8744 //-- Augmented methods for the JavaScript Number(), Array(), String() and Date() objects
8747 // Clamp a number to a range
8748 Number.prototype.clamp = function(min,max)
8758 // Add indexOf function if browser does not support it
8759 if(!Array.indexOf) {
8760 Array.prototype.indexOf = function(item,from)
8764 for(var i=from; i
<this.length; i++) {
8765 if(this[i] === item)
8771 // Find an entry in a given field of the members of an array
8772 Array.prototype.findByField = function(field,value)
8774 for(var t=
0; t
<this.length; t++) {
8775 if(this[t][field] == value)
8781 // Return whether an entry exists in an array
8782 Array.prototype.contains = function(item)
8784 return this.indexOf(item) != -
1;
8787 // Adds, removes or toggles a particular value within an array
8788 // value - value to add
8789 // mode - +
1 to add value, -
1 to remove value,
0 to toggle it
8790 Array.prototype.setItem = function(value,mode)
8792 var p = this.indexOf(value);
8794 mode = (p == -
1) ? +
1 : -
1;
8798 } else if(mode == -
1) {
8804 // Return whether one of a list of values exists in an array
8805 Array.prototype.containsAny = function(items)
8807 for(var i=
0; i
<items.length; i++) {
8808 if(this.indexOf(items[i]) != -
1)
8814 // Return whether all of a list of values exists in an array
8815 Array.prototype.containsAll = function(items)
8817 for(var i =
0; i
<items.length; i++) {
8818 if(this.indexOf(items[i]) == -
1)
8824 // 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
8825 Array.prototype.pushUnique = function(item,unique)
8827 if(unique === false) {
8830 if(this.indexOf(item) == -
1)
8835 Array.prototype.remove = function(item)
8837 var p = this.indexOf(item);
8842 if(!Array.prototype.map) {
8843 Array.prototype.map = function(fn,thisObj)
8845 var scope = thisObj || window;
8847 for(var i=
0, j=this.length; i < j; ++i) {
8848 a.push(fn.call(scope,this[i],i,this));
8853 // Get characters from the right end of a string
8854 String.prototype.right = function(n)
8856 return n < this.length ? this.slice(this.length-n) : this;
8859 // Trim whitespace from both ends of a string
8860 String.prototype.trim = function()
8862 return this.replace(/^\s*|\s*$/g,
"");
8865 // Convert a string from a CSS style property name to a JavaScript style name (
"background-color" -
> "backgroundColor")
8866 String.prototype.unDash = function()
8868 var s = this.split(
"-");
8870 for(var t=
1; t
<s.length; t++)
8871 s[t] = s[t].substr(
0,
1).toUpperCase() + s[t].substr(
1);
8876 // Substitute substrings from an array into a format string that includes '%
1'-type specifiers
8877 String.prototype.format = function(substrings)
8879 var subRegExp = /(?:%(\d+))/mg;
8883 var match = subRegExp.exec(this);
8884 if(match && match[
1]) {
8885 if(match.index
> currPos)
8886 r.push(this.substring(currPos,match.index));
8887 r.push(substrings[parseInt(match[
1])]);
8888 currPos = subRegExp.lastIndex;
8891 if(currPos < this.length)
8892 r.push(this.substring(currPos,this.length));
8896 // Escape any special RegExp characters with that character preceded by a backslash
8897 String.prototype.escapeRegExp = function()
8899 var s =
"\\^$*+?()=!|,{}[].";
8901 for(var t=
0; t
<s.length; t++)
8902 c = c.replace(new RegExp(
"\\" + s.substr(t,
1),
"g"),
"\\" + s.substr(t,
1));
8906 // Convert
"\" to
"\s", newlines to
"\n" (and remove carriage returns)
8907 String.prototype.escapeLineBreaks = function()
8909 return this.replace(/\\/mg,
"\\s").replace(/\n/mg,
"\\n").replace(/\r/mg,
"");
8912 // Convert
"\n" to newlines,
"\b" to
" ",
"\s" to
"\" (and remove carriage returns)
8913 String.prototype.unescapeLineBreaks = function()
8915 return this.replace(/\\n/mg,
"\n").replace(/\\b/mg,
" ").replace(/\\s/mg,
"\\").replace(/\r/mg,
"");
8918 // Convert & to
"&", < to
"<",
> to
">" and
" to """
8919 String.prototype.htmlEncode = function()
8921 return this.replace(/&/mg,"&").replace(/</mg,"<").replace(/>/mg,">").replace(/\"/mg,
""");
8924 // Convert
"&" to &,
"<" to <,
">" to
> and
""" to
"
8925 String.prototype.htmlDecode = function()
8927 return this.replace(/</mg,"<
").replace(/>/mg,">").replace(/"/mg,"\
"").replace(/
&/mg,
"&");
8930 // Convert a string to it's JSON representation by encoding control characters, double quotes and backslash. See json.org
8931 String.prototype.toJSONString = function()
8942 var replaceFn = function(a,b) {
8947 return '\\u00' + Math.floor(c /
16).toString(
16) + (c %
16).toString(
16);
8949 if(/[
"\\\x00-\x1f]/.test(this))
8950 return '"' + this.replace(/([\x00-\x1f\\
"])/g,replaceFn) + '"';
8951 return '
"' + this + '"';
8954 // Parse a space-separated string of name:value parameters
8955 // The result is an array of objects:
8956 // result[
0] = object with a member for each parameter name, value of that member being an array of values
8957 // result[
1..n] = one object for each parameter, with 'name' and 'value' members
8958 String.prototype.parseParams = function(defaultName,defaultValue,allowEval,noNames,cascadeDefaults)
8960 var parseToken = function(match,p) {
8962 if(match[p]) // Double quoted
8964 else if(match[p+
1]) // Single quoted
8966 else if(match[p+
2]) // Double-square-bracket quoted
8968 else if(match[p+
3]) // Double-brace quoted
8974 throw
"Unable to evaluate {{" + match[p+
3] +
"}}: " + exceptionText(ex);
8976 else if(match[p+
4]) // Unquoted
8978 else if(match[p+
5]) // empty quote
8983 var dblQuote =
"(?:\"((?:(?:\\\\\
")|[^\"])+)\
")";
8984 var sngQuote =
"(?:'((?:(?:\\\\\')|[^'])+)')";
8985 var dblSquare =
"(?:\\[\\[((?:\\s|\\S)*?)\\]\\])";
8986 var dblBrace =
"(?:\\{\\{((?:\\s|\\S)*?)\\}\\})";
8987 var unQuoted = noNames ?
"([^\"'\\s]\\S*)
" : "([^\
"':\\s][^\\s:]*)";
8988 var emptyQuote =
"((?:\"\
")|(?:''))";
8989 var skipSpace =
"(?:\\s*)";
8990 var token =
"(?:" + dblQuote +
"|" + sngQuote +
"|" + dblSquare +
"|" + dblBrace +
"|" + unQuoted +
"|" + emptyQuote +
")";
8991 var re = noNames ? new RegExp(token,
"mg") : new RegExp(skipSpace + token + skipSpace +
"(?:(\\:)" + skipSpace + token +
")?",
"mg");
8994 var match = re.exec(this);
8996 var n = parseToken(match,
1);
8998 r.push({name:
"",value:n});
9000 var v = parseToken(match,
8);
9001 if(v == null && defaultName) {
9004 } else if(v == null && defaultValue) {
9007 r.push({name:n,value:v});
9008 if(cascadeDefaults) {
9015 // Summarise parameters into first element
9016 for(var t=
1; t
<r.length; t++) {
9018 r[
0][r[t].name].push(r[t].value);
9020 r[
0][r[t].name] = [r[t].value];
9025 // Process a string list of macro parameters into an array. Parameters can be quoted with
"", '',
9026 // [[]], {{ }} or left unquoted (and therefore space-separated). Double-braces {{}} results in
9027 // an *evaluated* parameter: e.g. {{config.options.txtUserName}} results in the current user's name.
9028 String.prototype.readMacroParams = function()
9030 var p = this.parseParams(
"list",null,true,true);
9032 for(var t=
1; t
<p.length; t++)
9037 // Process a string list of unique tiddler names into an array. Tiddler names that have spaces in them must be [[bracketed]]
9038 String.prototype.readBracketedList = function(unique)
9040 var p = this.parseParams(
"list",null,false,true);
9042 for(var t=
1; t
<p.length; t++) {
9044 n.pushUnique(p[t].value,unique);
9049 // Returns array with start and end index of chunk between given start and end marker, or undefined.
9050 String.prototype.getChunkRange = function(start,end)
9052 var s = this.indexOf(start);
9055 var e = this.indexOf(end,s);
9061 // Replace a chunk of a string given start and end markers
9062 String.prototype.replaceChunk = function(start,end,sub)
9064 var r = this.getChunkRange(start,end);
9065 return r ? this.substring(
0,r[
0]) + sub + this.substring(r[
1]) : this;
9068 // Returns a chunk of a string between start and end markers, or undefined
9069 String.prototype.getChunk = function(start,end)
9071 var r = this.getChunkRange(start,end);
9073 return this.substring(r[
0],r[
1]);
9077 // Static method to bracket a string with double square brackets if it contains a space
9078 String.encodeTiddlyLink = function(title)
9080 return title.indexOf(
" ") == -
1 ? title :
"[[" + title +
"]]";
9083 // Static method to encodeTiddlyLink for every item in an array and join them with spaces
9084 String.encodeTiddlyLinkList = function(list)
9088 for(var t=
0; t
<list.length; t++)
9089 results.push(String.encodeTiddlyLink(list[t]));
9090 return results.join(
" ");
9096 // Convert a string as a sequence of name:
"value" pairs into a hashmap
9097 String.prototype.decodeHashMap = function()
9099 var fields = this.parseParams(
"anon",
"",false);
9101 for(var t=
1; t
<fields.length; t++)
9102 r[fields[t].name] = fields[t].value;
9106 // Static method to encode a hashmap into a name:
"value"... string
9107 String.encodeHashMap = function(hashmap)
9110 for(var t in hashmap)
9111 r.push(t + ':
"' + hashmap[t] + '"');
9115 // Static method to left-pad a string with
0s to a certain width
9116 String.zeroPad = function(n,d)
9118 var s = n.toString();
9120 s =
"000000000000000000000000000".substr(
0,d-s.length) + s;
9124 String.prototype.startsWith = function(prefix)
9126 return !prefix || this.substring(
0,prefix.length) == prefix;
9129 // Returns the first value of the given named parameter.
9130 function getParam(params,name,defaultValue)
9133 return defaultValue;
9134 var p = params[
0][name];
9135 return p ? p[
0] : defaultValue;
9138 // Returns the first value of the given boolean named parameter.
9139 function getFlag(params,name,defaultValue)
9141 return !!getParam(params,name,defaultValue);
9144 // Substitute date components into a string
9145 Date.prototype.formatString = function(template)
9147 var t = template.replace(/
0hh12/g,String.zeroPad(this.getHours12(),
2));
9148 t = t.replace(/hh12/g,this.getHours12());
9149 t = t.replace(/
0hh/g,String.zeroPad(this.getHours(),
2));
9150 t = t.replace(/hh/g,this.getHours());
9151 t = t.replace(/mmm/g,config.messages.dates.shortMonths[this.getMonth()]);
9152 t = t.replace(/
0mm/g,String.zeroPad(this.getMinutes(),
2));
9153 t = t.replace(/mm/g,this.getMinutes());
9154 t = t.replace(/
0ss/g,String.zeroPad(this.getSeconds(),
2));
9155 t = t.replace(/ss/g,this.getSeconds());
9156 t = t.replace(/[ap]m/g,this.getAmPm().toLowerCase());
9157 t = t.replace(/[AP]M/g,this.getAmPm().toUpperCase());
9158 t = t.replace(/wYYYY/g,this.getYearForWeekNo());
9159 t = t.replace(/wYY/g,String.zeroPad(this.getYearForWeekNo()-
2000,
2));
9160 t = t.replace(/YYYY/g,this.getFullYear());
9161 t = t.replace(/YY/g,String.zeroPad(this.getFullYear()-
2000,
2));
9162 t = t.replace(/MMM/g,config.messages.dates.months[this.getMonth()]);
9163 t = t.replace(/
0MM/g,String.zeroPad(this.getMonth()+
1,
2));
9164 t = t.replace(/MM/g,this.getMonth()+
1);
9165 t = t.replace(/
0WW/g,String.zeroPad(this.getWeek(),
2));
9166 t = t.replace(/WW/g,this.getWeek());
9167 t = t.replace(/DDD/g,config.messages.dates.days[this.getDay()]);
9168 t = t.replace(/ddd/g,config.messages.dates.shortDays[this.getDay()]);
9169 t = t.replace(/
0DD/g,String.zeroPad(this.getDate(),
2));
9170 t = t.replace(/DDth/g,this.getDate()+this.daySuffix());
9171 t = t.replace(/DD/g,this.getDate());
9172 var tz = this.getTimezoneOffset();
9173 var atz = Math.abs(tz);
9174 t = t.replace(/TZD/g,(tz <
0 ? '+' : '-') + String.zeroPad(Math.floor(atz /
60),
2) + ':' + String.zeroPad(atz %
60,
2));
9175 t = t.replace(/\\/g,
"");
9179 Date.prototype.getWeek = function()
9181 var dt = new Date(this.getTime());
9182 var d = dt.getDay();
9183 if(d==
0) d=
7;// JavaScript Sun=
0, ISO Sun=
7
9184 dt.setTime(dt.getTime()+(
4-d)*
86400000);// shift day to Thurs of same week to calculate weekNo
9185 var n = Math.floor((dt.getTime()-new Date(dt.getFullYear(),
0,
1)+
3600000)/
86400000);
9186 return Math.floor(n/
7)+
1;
9189 Date.prototype.getYearForWeekNo = function()
9191 var dt = new Date(this.getTime());
9192 var d = dt.getDay();
9193 if(d==
0) d=
7;// JavaScript Sun=
0, ISO Sun=
7
9194 dt.setTime(dt.getTime()+(
4-d)*
86400000);// shift day to Thurs of same week
9195 return dt.getFullYear();
9198 Date.prototype.getHours12 = function()
9200 var h = this.getHours();
9201 return h
> 12 ? h-
12 : ( h
> 0 ? h :
12 );
9204 Date.prototype.getAmPm = function()
9206 return this.getHours()
>=
12 ? config.messages.dates.pm : config.messages.dates.am;
9209 Date.prototype.daySuffix = function()
9211 return config.messages.dates.daySuffixes[this.getDate()-
1];
9214 // Convert a date to local YYYYMMDDHHMM string format
9215 Date.prototype.convertToLocalYYYYMMDDHHMM = function()
9217 return this.getFullYear() + String.zeroPad(this.getMonth()+
1,
2) + String.zeroPad(this.getDate(),
2) + String.zeroPad(this.getHours(),
2) + String.zeroPad(this.getMinutes(),
2);
9220 // Convert a date to UTC YYYYMMDDHHMM string format
9221 Date.prototype.convertToYYYYMMDDHHMM = function()
9223 return this.getUTCFullYear() + String.zeroPad(this.getUTCMonth()+
1,
2) + String.zeroPad(this.getUTCDate(),
2) + String.zeroPad(this.getUTCHours(),
2) + String.zeroPad(this.getUTCMinutes(),
2);
9226 // Convert a date to UTC YYYYMMDD.HHMMSSMMM string format
9227 Date.prototype.convertToYYYYMMDDHHMMSSMMM = function()
9229 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);
9232 // Static method to create a date from a UTC YYYYMMDDHHMM format string
9233 Date.convertFromYYYYMMDDHHMM = function(d)
9235 var hh = d.substr(
8,
2) ||
"00";
9236 var mm = d.substr(
10,
2) ||
"00";
9237 return new Date(Date.UTC(parseInt(d.substr(
0,
4),
10),
9238 parseInt(d.substr(
4,
2),
10)-
1,
9239 parseInt(d.substr(
6,
2),
10),
9241 parseInt(mm,
10),
0,
0));
9245 //-- Crypto functions and associated conversion routines
9248 // Crypto 'namespace'
9249 function Crypto() {}
9251 // Convert a string to an array of big-endian
32-bit words
9252 Crypto.strToBe32s = function(str)
9255 var len=Math.floor(str.length/
4);
9257 for(i=
0, j=
0; i
<len; i++, j+=
4) {
9258 be[i]=((str.charCodeAt(j)&
0xff) <<
24)|((str.charCodeAt(j+
1)&
0xff) <<
16)|((str.charCodeAt(j+
2)&
0xff) <<
8)|(str.charCodeAt(j+
3)&
0xff);
9260 while(j
<str.length) {
9261 be[j
>>2] |= (str.charCodeAt(j)&
0xff)<<(
24-(j*
8)%
32);
9267 // Convert an array of big-endian
32-bit words to a string
9268 Crypto.be32sToStr = function(be)
9271 for(var i=
0;i
<be.length*
32;i+=
8) {
9272 str += String.fromCharCode((be[i
>>5]
>>>(
24-i%
32)) &
0xff);
9277 // Convert an array of big-endian
32-bit words to a hex string
9278 Crypto.be32sToHex = function(be)
9280 var hex='
0123456789ABCDEF';
9282 for(var i=
0;i
<be.length*
4;i++) {
9283 str += hex.charAt((be[i
>>2]
>>((
3-i%
4)*
8+
4))&
0xF) + hex.charAt((be[i
>>2]
>>((
3-i%
4)*
8))&
0xF);
9288 // Return, in hex, the SHA-
1 hash of a string
9289 Crypto.hexSha1Str = function(str)
9291 return Crypto.be32sToHex(Crypto.sha1Str(str));
9294 // Return the SHA-
1 hash of a string
9295 Crypto.sha1Str = function(str)
9297 return Crypto.sha1(Crypto.strToBe32s(str),str.length);
9300 // Calculate the SHA-
1 hash of an array of blen bytes of big-endian
32-bit words
9301 Crypto.sha1 = function(x,blen)
9303 // Add
32-bit integers, wrapping at
32 bits
9306 var lsw=(a&
0xFFFF)+(b&
0xFFFF);
9307 var msw=(a
>>16)+(b
>>16)+(lsw
>>16);
9308 return (msw<
<16)|(lsw&
0xFFFF);
9310 function AA(a,b,c,d,e)
9313 var lsw=(a&
0xFFFF)+(b&
0xFFFF)+(c&
0xFFFF)+(d&
0xFFFF)+(e&
0xFFFF);
9314 var msw=(a
>>16)+(b
>>16)+(c
>>16)+(d
>>16)+(e
>>16)+(lsw
>>16);
9315 return (msw<
<16)|(lsw&
0xFFFF);
9319 var n=w[j-
3]^w[j-
8]^w[j-
14]^w[j-
16];
9320 return (n
>>>31)|(n<
<1);
9324 x[len
>>5] |=
0x80 << (
24-len%
32);
9325 x[((len+
64>>9)<
<4)+
15]=len;
9326 var w=new Array(
80);
9339 for(var i=
0;i
<x.length;i+=
16) {
9349 t=AA(e,a,d^(b&(c^d)),w[j],k1);
9350 e=d; d=c; c=(b
>>>2)|(b<
<30); b=a; a=t; j++;
9354 t=AA(e,a,d^(b&(c^d)),w[j],k1);
9355 e=d; d=c; c=(b
>>>2)|(b<
<30); b=a; a=t; j++;
9359 t=AA(e,a,b^c^d,w[j],k2);
9360 e=d; d=c; c=(b
>>>2)|(b<
<30); b=a; a=t; j++;
9364 t=AA(e,a,(b&c)|(d&(b|c)),w[j],k3);
9365 e=d; d=c; c=(b
>>>2)|(b<
<30); b=a; a=t; j++;
9369 t=AA(e,a,b^c^d,w[j],k4);
9370 e=d; d=c; c=(b
>>>2)|(b<
<30); b=a; a=t; j++;
9378 return [h0,h1,h2,h3,h4];
9382 //-- RGB colour object
9385 // Construct an RGB colour object from a '#rrggbb', '#rgb' or 'rgb(n,n,n)' string or from separate r,g,b values
9391 if(typeof r ==
"string") {
9392 if(r.substr(
0,
1) ==
"#") {
9394 this.r = parseInt(r.substr(
1,
2),
16)/
255;
9395 this.g = parseInt(r.substr(
3,
2),
16)/
255;
9396 this.b = parseInt(r.substr(
5,
2),
16)/
255;
9398 this.r = parseInt(r.substr(
1,
1),
16)/
15;
9399 this.g = parseInt(r.substr(
2,
1),
16)/
15;
9400 this.b = parseInt(r.substr(
3,
1),
16)/
15;
9403 var rgbPattern = /rgb\s*\(\s*(\d{
1,
3})\s*,\s*(\d{
1,
3})\s*,\s*(\d{
1,
3})\s*\)/;
9404 var c = r.match(rgbPattern);
9406 this.r = parseInt(c[
1],
10)/
255;
9407 this.g = parseInt(c[
2],
10)/
255;
9408 this.b = parseInt(c[
3],
10)/
255;
9419 // Mixes this colour with another in a specified proportion
9420 // c = other colour to mix
9421 // f =
0.
.1 where
0 is this colour and
1 is the new colour
9422 // Returns an RGB object
9423 RGB.prototype.mix = function(c,f)
9425 return new RGB(this.r + (c.r-this.r) * f,this.g + (c.g-this.g) * f,this.b + (c.b-this.b) * f);
9428 // Return an rgb colour as a #rrggbb format hex string
9429 RGB.prototype.toString = function()
9431 return
"#" + (
"0" + Math.floor(this.r.clamp(
0,
1) *
255).toString(
16)).right(
2) +
9432 (
"0" + Math.floor(this.g.clamp(
0,
1) *
255).toString(
16)).right(
2) +
9433 (
"0" + Math.floor(this.b.clamp(
0,
1) *
255).toString(
16)).right(
2);
9437 //-- DOM utilities - many derived from www.quirksmode.org
9440 function drawGradient(place,horiz,locolors,hicolors)
9443 hicolors = locolors;
9444 for(var t=
0; t<=
100; t+=
2) {
9445 var bar = document.createElement(
"div");
9446 place.appendChild(bar);
9447 bar.style.position =
"absolute";
9448 bar.style.left = horiz ? t +
"%" :
0;
9449 bar.style.top = horiz ?
0 : t +
"%";
9450 bar.style.width = horiz ? (
101-t) +
"%" :
"100%";
9451 bar.style.height = horiz ?
"100%" : (
101-t) +
"%";
9452 bar.style.zIndex = -
1;
9453 var p = t/
100*(locolors.length-
1);
9454 bar.style.backgroundColor = hicolors[Math.floor(p)].mix(locolors[Math.ceil(p)],p-Math.floor(p)).toString();
9458 function createTiddlyText(parent,text)
9460 return parent.appendChild(document.createTextNode(text));
9463 function createTiddlyCheckbox(parent,caption,checked,onChange)
9465 var cb = document.createElement(
"input");
9466 cb.setAttribute(
"type",
"checkbox");
9467 cb.onclick = onChange;
9468 parent.appendChild(cb);
9469 cb.checked = checked;
9470 cb.className =
"chkOptionInput";
9472 wikify(caption,parent);
9476 function createTiddlyElement(parent,element,id,className,text,attribs)
9478 var e = document.createElement(element);
9479 if(className != null)
9480 e.className = className;
9482 e.setAttribute(
"id",id);
9484 e.appendChild(document.createTextNode(text));
9486 for(var n in attribs) {
9487 e.setAttribute(n,attribs[n]);
9491 parent.appendChild(e);
9495 function addEvent(obj,type,fn)
9497 if(obj.attachEvent) {
9498 obj['e'+type+fn] = fn;
9499 obj[type+fn] = function(){obj['e'+type+fn](window.event);};
9500 obj.attachEvent('on'+type,obj[type+fn]);
9502 obj.addEventListener(type,fn,false);
9506 function removeEvent(obj,type,fn)
9508 if(obj.detachEvent) {
9509 obj.detachEvent('on'+type,obj[type+fn]);
9510 obj[type+fn] = null;
9512 obj.removeEventListener(type,fn,false);
9516 function addClass(e,className)
9518 var currClass = e.className.split(
" ");
9519 if(currClass.indexOf(className) == -
1)
9520 e.className +=
" " + className;
9523 function removeClass(e,className)
9525 var currClass = e.className.split(
" ");
9526 var i = currClass.indexOf(className);
9528 currClass.splice(i,
1);
9529 i = currClass.indexOf(className);
9531 e.className = currClass.join(
" ");
9534 function hasClass(e,className)
9536 if(e.className && e.className.split(
" ").indexOf(className) != -
1) {
9542 // Find the closest relative with a given property value (property defaults to tagName, relative defaults to parentNode)
9543 function findRelated(e,value,name,relative)
9545 name = name ||
"tagName";
9546 relative = relative ||
"parentNode";
9547 if(name ==
"className") {
9548 while(e && !hasClass(e,value)) {
9552 while(e && e[name] != value) {
9559 // Resolve the target object of an event
9560 function resolveTarget(e)
9565 else if(e.srcElement)
9567 if(obj.nodeType ==
3) // defeat Safari bug
9568 obj = obj.parentNode;
9572 // Prevent an event from bubbling
9573 function stopEvent(e)
9575 var ev = e || window.event;
9576 ev.cancelBubble = true;
9577 if(ev.stopPropagation) ev.stopPropagation();
9581 // Return the content of an element as plain text with no formatting
9582 function getPlainText(e)
9587 else if(e.textContent)
9588 text = e.textContent;
9592 // Get the scroll position for window.scrollTo necessary to scroll a given element into view
9593 function ensureVisible(e)
9595 var posTop = findPosY(e);
9596 var posBot = posTop + e.offsetHeight;
9597 var winTop = findScrollY();
9598 var winHeight = findWindowHeight();
9599 var winBot = winTop + winHeight;
9600 if(posTop < winTop) {
9602 } else if(posBot
> winBot) {
9603 if(e.offsetHeight < winHeight)
9604 return posTop - (winHeight - e.offsetHeight);
9612 // Get the current width of the display window
9613 function findWindowWidth()
9615 return window.innerWidth || document.documentElement.clientWidth;
9618 // Get the current height of the display window
9619 function findWindowHeight()
9621 return window.innerHeight || document.documentElement.clientHeight;
9624 // Get the current horizontal page scroll position
9625 function findScrollX()
9627 return window.scrollX || document.documentElement.scrollLeft;
9630 // Get the current vertical page scroll position
9631 function findScrollY()
9633 return window.scrollY || document.documentElement.scrollTop;
9636 function findPosX(obj)
9639 while(obj.offsetParent) {
9640 curleft += obj.offsetLeft;
9641 obj = obj.offsetParent;
9646 function findPosY(obj)
9649 while(obj.offsetParent) {
9650 curtop += obj.offsetTop;
9651 obj = obj.offsetParent;
9656 // Blur a particular element
9657 function blurElement(e)
9659 if(e && e.focus && e.blur) {
9665 // Create a non-breaking space
9666 function insertSpacer(place)
9668 var e = document.createTextNode(String.fromCharCode(
160));
9670 place.appendChild(e);
9674 // Remove all children of a node
9675 function removeChildren(e)
9677 while(e && e.hasChildNodes())
9678 removeNode(e.firstChild);
9681 // Remove a node and all it's children
9682 function removeNode(e)
9685 e.parentNode.removeChild(e);
9688 // Remove any event handlers or non-primitve custom attributes
9689 function scrubNode(e)
9691 if(!config.browser.isIE)
9693 var att = e.attributes;
9695 for(var t=
0; t
<att.length; t++) {
9696 var n = att[t].name;
9697 if(n !== 'style' && (typeof e[n] === 'function' || (typeof e[n] === 'object' && e[n] != null))) {
9705 var c = e.firstChild;
9712 // Add a stylesheet, replacing any previous custom stylesheet
9713 function setStylesheet(s,id,doc)
9716 id =
"customStyleSheet";
9719 var n = doc.getElementById(id);
9720 if(doc.createStyleSheet) {
9721 // Test for IE's non-standard createStyleSheet method
9723 n.parentNode.removeChild(n);
9724 // This failed without the
9725 doc.getElementsByTagName(
"head")[
0].insertAdjacentHTML(
"beforeEnd",
" <style id='" + id +
"'>" + s +
"</style>");
9728 n.replaceChild(doc.createTextNode(s),n.firstChild);
9730 n = doc.createElement(
"style");
9731 n.type =
"text/css";
9733 n.appendChild(doc.createTextNode(s));
9734 doc.getElementsByTagName(
"head")[
0].appendChild(n);
9739 function removeStyleSheet(id)
9741 var e = document.getElementById(id);
9743 e.parentNode.removeChild(e);
9746 // Force the browser to do a document reflow when needed to workaround browser bugs
9747 function forceReflow()
9749 if(config.browser.isGecko) {
9750 setStylesheet(
"body {top:0px;margin-top:0px;}",
"forceReflow");
9751 setTimeout(function() {setStylesheet(
"",
"forceReflow");},
1);
9755 // Replace the current selection of a textarea or text input and scroll it into view
9756 function replaceSelection(e,text)
9758 if(e.setSelectionRange) {
9759 var oldpos = e.selectionStart;
9760 var isRange = e.selectionEnd
> e.selectionStart;
9761 e.value = e.value.substr(
0,e.selectionStart) + text + e.value.substr(e.selectionEnd);
9762 e.setSelectionRange(isRange ? oldpos : oldpos + text.length,oldpos + text.length);
9763 var linecount = e.value.split('\n').length;
9764 var thisline = e.value.substr(
0,e.selectionStart).split('\n').length-
1;
9765 e.scrollTop = Math.floor((thisline - e.rows /
2) * e.scrollHeight / linecount);
9766 } else if(document.selection) {
9767 var range = document.selection.createRange();
9768 if(range.parentElement() == e) {
9769 var isCollapsed = range.text ==
"";
9772 range.moveStart('character', -text.length);
9779 // Returns the text of the given (text) node, possibly merging subsequent text nodes
9780 function getNodeText(e)
9783 while(e && e.nodeName ==
"#text") {
9790 // Returns true if the element e has a given ancestor element
9791 function isDescendant(e,ancestor)
9802 //-- LoaderBase and SaverBase
9805 function LoaderBase() {}
9807 LoaderBase.prototype.loadTiddler = function(store,node,tiddlers)
9809 var title = this.getTitle(store,node);
9810 if(safeMode && store.isShadowTiddler(title))
9813 var tiddler = store.createTiddler(title);
9814 this.internalizeTiddler(store,tiddler,title,node);
9815 tiddlers.push(tiddler);
9819 LoaderBase.prototype.loadTiddlers = function(store,nodes)
9822 for(var t =
0; t < nodes.length; t++) {
9824 this.loadTiddler(store,nodes[t],tiddlers);
9826 showException(ex,config.messages.tiddlerLoadError.format([this.getTitle(store,nodes[t])]));
9832 function SaverBase() {}
9834 SaverBase.prototype.externalize = function(store)
9837 var tiddlers = store.getTiddlers(
"title");
9838 for(var t =
0; t < tiddlers.length; t++) {
9839 if(!tiddlers[t].doNotSave())
9840 results.push(this.externalizeTiddler(store, tiddlers[t]));
9842 return results.join(
"\n");
9846 //-- TW21Loader (inherits from LoaderBase)
9849 function TW21Loader() {}
9851 TW21Loader.prototype = new LoaderBase();
9853 TW21Loader.prototype.getTitle = function(store,node)
9856 if(node.getAttribute) {
9857 title = node.getAttribute(
"title");
9859 title = node.getAttribute(
"tiddler");
9861 if(!title && node.id) {
9862 var lenPrefix = store.idPrefix.length;
9863 if(node.id.substr(
0,lenPrefix) == store.idPrefix)
9864 title = node.id.substr(lenPrefix);
9869 TW21Loader.prototype.internalizeTiddler = function(store,tiddler,title,node)
9871 var e = node.firstChild;
9873 if(node.getAttribute(
"tiddler")) {
9874 text = getNodeText(e).unescapeLineBreaks();
9876 while(e.nodeName!=
"PRE" && e.nodeName!=
"pre") {
9879 text = e.innerHTML.replace(/\r/mg,
"").htmlDecode();
9881 var modifier = node.getAttribute(
"modifier");
9882 var c = node.getAttribute(
"created");
9883 var m = node.getAttribute(
"modified");
9884 var created = c ? Date.convertFromYYYYMMDDHHMM(c) : version.date;
9885 var modified = m ? Date.convertFromYYYYMMDDHHMM(m) : created;
9886 var tags = node.getAttribute(
"tags");
9888 var attrs = node.attributes;
9889 for(var i = attrs.length-
1; i
>=
0; i--) {
9890 var name = attrs[i].name;
9891 if(attrs[i].specified && !TiddlyWiki.isStandardField(name)) {
9892 fields[name] = attrs[i].value.unescapeLineBreaks();
9895 tiddler.assign(title,text,modifier,modified,tags,created,fields);
9900 //-- TW21Saver (inherits from SaverBase)
9903 function TW21Saver() {}
9905 TW21Saver.prototype = new SaverBase();
9907 TW21Saver.prototype.externalizeTiddler = function(store,tiddler)
9910 var extendedAttributes =
"";
9911 var usePre = config.options.chkUsePreForStorage;
9912 store.forEachField(tiddler,
9913 function(tiddler,fieldName,value) {
9914 // don't store stuff from the temp namespace
9915 if(typeof value !=
"string")
9917 if(!fieldName.match(/^temp\./))
9918 extendedAttributes += ' %
0=
"%1"'.format([fieldName,value.escapeLineBreaks().htmlEncode()]);
9920 var created = tiddler.created;
9921 var modified = tiddler.modified;
9922 var attributes = tiddler.modifier ? '
modifier=
"' + tiddler.modifier.htmlEncode() + '"' :
"";
9923 attributes += (usePre && created == version.date) ?
"" :'
created=
"' + created.convertToYYYYMMDDHHMM() + '"';
9924 attributes += (usePre && modified == created) ?
"" : '
modified=
"' + modified.convertToYYYYMMDDHHMM() +'"';
9925 var tags = tiddler.getTags();
9927 attributes += '
tags=
"' + tags.htmlEncode() + '"';
9928 return ('
<div %
0=
"%1"%
2%
3>%
4</'+'div
>').format([
9929 usePre ?
"title" :
"tiddler",
9930 tiddler.title.htmlEncode(),
9933 usePre ?
"\n<pre>" + tiddler.text.htmlEncode() +
"</pre>\n" : tiddler.text.escapeLineBreaks().htmlEncode()
9936 throw exceptionText(ex,config.messages.tiddlerSaveError.format([tiddler.title]));
9942 <script type=
"text/javascript">
9945 document
.write("<applet style='position:absolute;left:-1px' name='TiddlySaver' code='TiddlySaver.class' archive='TiddlySaver.jar' width='1' height='1'></applet>");
9948 <!--POST-SCRIPT-START-->
9950 <!--POST-SCRIPT-END-->