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=
"200810150858" changecount=
"255">
826 *Detect double nat with upnp
827 *Handle error loading a map from a peer that drops out
828 *camera rotation problem on Linux
830 *if this is a new plexus version, offer to toast the cache
831 *Pastry reconnect business (multiple root peers?)
832 *set CPPFLAGS -DTC in src/Makefile's call to src/enet/configure
833 *Make md3load handle missing files (deactivate md3skin, md3pitch, md3anim, md3link, mdlspec, mdlscale, mdltrans, etc.) -- maybe make it load a default model (like Zippy the Pinhead)
834 *make updateMappingDiag() also display results for dumpents
835 *Diag panes for sauer and pastry cmds
836 *plexus profile editor in sauer (prepopulate wizard)
837 *combine swing windows
838 *only show diag tools until the first successful connect. Then make it an option.
840 *Monsters (phantom players)
842 *responsibility allocation
843 **maybe have a doc for each world that allocates responsibilities among the peers of a world
844 **when responsibilities or peers join or leave, responsibilities shuffle around
845 **we can store responsibility allocation in PAST
846 **might need a world-ping
847 *Create, destroy, and move objects (GUIDs)
850 **Save hook defined in map.groovy
851 **Save menu choice (which calls map's save hook)
853 **clean up obsolete maps
856 **use crosshairs for aiming when they are visible
857 **register sauer cmds for hit effects based on shooter and/or target
858 **show bio when you shoot someone with an examination gun
862 **Directory entry ownership by digital signatures (peers and groups) -- only an owner can change it
865 **specify box with attachment point, vector, and height
866 **specify driving position with attachment point
867 **driver can switch between player view and vehicle view
868 **physics interacts with box
869 **players can move around inside, fire grenades, etc.
870 *groovy libraries in the cloud
872 *DONE Update costume list as thumbs arrive (don't wait for all of them before updating)
873 *DONE constumes getting reset to null (mr fixit) -- seems connected to costume uploading
874 *DONE when costume becomes null, sometimes it doesn't change it
875 *DONE fix case for md2 import
876 *DONE check response values to verify mappings succeed, remove upnp mappings during shutdown, remove cleanup call (make button?)
877 *DONE limbo name not showing up
879 *DONE Map name sometimes shows up as
"none
" when you are on a map
880 *DONE Blank costume no longer switches to Mr Fixit
881 *DONE players can taunt each other
882 *DONE Filter out Thumbs.db and
0 length files
883 *DONE drag selection with left button
884 *DONE cleanup connecting to existing sauer
885 *DONE remove date from directory properties so that dirs are not mistakenly unique
886 *DONE cloud
"power light
"
887 *DONE aiming/edit selection problem
888 *DONE Hidden worlds (not advertised, but part of the cloud)
889 *DONE Finish tutorial menus
890 *DONE [replaced by map.groovy] fix bug in saveGroovyData()
891 *DONE [replaced by map.groovy
& mapProps] persist JSON object as groovy file in map dir for metadata map development
892 *DONE allow turning off recoil for shots
893 *DONE allow user to restart plexus from sauer
894 *DONE cache player updates for your map so follow can teleport immediately
895 **DONE restore world when you restart sauer
896 *DONE pick random port in range
897 *DONE disable Launch Sauer until connected
898 *DONE test costume uploading
899 *DONE broadcast disconnect with shutting down
900 *DONE check java version on startup
901 *DONE remote guild names not showing
902 *DONE incoming cloud change notification to user
903 *DONE show feedback for unsuccessful port back-connection during ip discovery
904 *DONE Missing player problem -- when loading map, players clear out of sauer but not out of plexus id/name tables
905 *DONE Groovy version of build script
906 *DONE JSON format for cloud properties
907 *DONE (fluke?) on find map, couldn't find map with tume id instead of map id
908 *DONE upload/download progress notification
909 *DONE notification of droppage
911 *DONE Clean button in prep gui
912 *DONE options for cleanup on start/completely fresh start
914 *DONE left msg uses nodename instead of player name
915 *DONE switches remote player costume before d/l completed
916 *DONE download into temp dirs and only rename when completed
917 *DONE map member count is always
0
918 *DONE don't subscribe to map until successfully downloaded
920 *DONE Rework existing editing menu, much doesn't apply
921 *DONE decouple camera speed from players speed
922 *DONE Make players start off in Limbo
924 *DONE make LMB detach camera to orbit around the toon
925 *DONE make sauer load models from plexus folder
926 *DONE Global whispers
927 *DONE Map name on load screen
928 *DONE Make push handle regular maps (make manifest that places it in a subdir)
930 *DONE Replace map should broadcast
"switchmap
" pastry cmd
931 *DONE Message dialog: showmessage title text
932 *DONE make cursors load from plexus/dist
933 *DONE separate backup directory
934 *DONE Use UPnP to discover external IP address
935 *DONE delete player on peer disconnect
936 *DONE clean up players on world connection
937 *DONE Show connection count in world menu
938 *DONE Investigate occasional sauer/groovy lag on linux
939 *DONE Headless peer on Plubble
940 *DONE include UPnP to open external port in firewall
941 *DONE add tc_respawn command to put players back at spawn points
942 *DONE don't show original menu on startup
943 *DONE Hide Gun in First Person mode
944 *DONE Disable weapon swapping, disable shooting
945 *DONE Make Limbo map come up by default
946 *DONE Rename wowmode to tcmode
947 *DONE make cursor float above HUD
948 *DONE load limbo in sauer on startup
949 *DONE Show only filename on map thumbnails when loading
950 *DONE HUD add peer count
951 *DONE launch sauer in sep thread while connecting to pastry
952 *DONE add button to reload sauer
953 *DONE move commands from groovy init into a .cfg file
955 **DONE Check out out of PAST disk space errors
956 **DONE Global, shared directory for cloud
959 **DONE Presence topic for total online count
960 **DONE available world listing/selection from gui
963 **DONE Map connection count
972 **materials for sensors (use var to set material id) (callback set on material id)
973 **inventory mgmt (+ gui)
977 **dead monsters drop loot
978 **wizard guns (create monster, move, talk, paralyze, destroy)
979 **saved map chunks
</pre>
981 <div title=
"SiteSubtitle" modifier=
"Zot" created=
"200806171609" modified=
"200806191121" changecount=
"11">
982 <pre>The Killer App of the Future! //[[Reloaded]]//
</pre>
984 <div title=
"SiteTitle" modifier=
"Zot" created=
"200806171608" modified=
"200806191146" changecount=
"9">
985 <pre>[img[P2PMud|docs/images/p2pmud.gif]]
</pre>
987 <div title=
"StartingOut" modifier=
"BillBurdick" created=
"200807142041" modified=
"200809272113" changecount=
"12">
988 <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]]
989 *This repository is an Eclipse project, so you'll probably want Eclipse and the CDT feature for C/C++
990 Once you have the stuff, you need to:
992 *Copy/link sauer_client and scripts/autoexec.cfg into the top level of the sauerbraten directory (in the same directory as server.bat).
993 *Start the groovy command server in Eclipse (using the Cmd Server launcher)
994 *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)
995 !
<html
><span style=
"background: red
">HAVING PROBLEMS?
</span
></html
>
996 HowToReallyCleanAnEclipseProject
997 EmergencyWorspaceReconstruction
</pre>
999 <div title=
"StyleSheet" modifier=
"BillBurdick" created=
"200809280501" modified=
"200809280525" changecount=
"6">
1001 .cthulhu { text-decoration: none;
1003 font-family:
"arial
";
1005 font-weight: medium;
1006 background-color: #
282619;
1008 padding-bottom:
3px;
1010 .cmd.cthulhu { text-decoration: none;
1012 font-family:
"arial
";
1014 font-weight: medium;
1016 a.cthulhu:link { text-decoration: none;
1018 font-family:
"arial
";
1022 a.cthulhu:visited { text-decoration: none;
1024 font-family:
"arial
";
1030 <div title=
"ToDo" modifier=
"WRB" created=
"200806171610" modified=
"200807192045" changecount=
"5">
1037 Update docs to refer to new platform (Groovy/Sauerbraten) instead of old (Javascript/Mozilla)
1041 Drag and drop for browser images?
1044 master player -- invisible/intangible and does not transmit coords to the server
1046 puppet -- move according to instructions (default is frozen)
1047 normal -- moves with limitations (movement blockers)
1049 beacons: players can move as long as they remain within the radius of at least one authorized beacon
1050 This allows cell-style hand off
1051 walls: players can pass through authorized walls
1055 moveto id x y z -- sets id's movement target to location
1057 contexthook hook -- when active, execute hook on context change for player1
1058 usecontext on/off -- set whether player1's contexthook is active
1060 authorize id blocker
1061 unauthorize id blocker
1062 puppet id -- makes id move toward its movement target and if it's a player unyoke the controls
1063 unpuppet id -- makes id ignore its movement target and if it's a player, yoke the controls
1068 move -- shoot destination, then target(s)
1069 authorize -- shoot blocker, then target(s)
1070 unauthorize -- shoot blocker, then target(s)
1079 mastercommand cmd
</pre>
1081 <div title=
"Toys" modifier=
"BillBurdick" created=
"200808151458" changecount=
"1">
1082 <pre>launch gun -- sets velocity on opponents
1085 *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
1087 might be doable with only triggers and watcher code
1089 roman candles/wand of fireballs
1090 physics for wall splat
1092 monsters control assigned by DHT
1093 map sharing includes map.groovy file in addition to map.ogz and map.cfg
1095 *Each world gets a branch off the initial starting directory (a dir with a readme)
1096 *Git tree objects are stored in PAST, cached in a local Git repo, and extracted into a local directory
</pre>
1098 <div title=
"UseCases" modifier=
"BillBurdick" created=
"200807300630" modified=
"200807300650" changecount=
"3">
1100 ##player rolls over an item
1101 ##item adds to inventory
1102 ##player presses
"i
" key
1103 ##inventory menu pops up
1104 ##player chooses a different item from menu
1106 ##player chooses drop
1107 ##item leaves inventory and appears on map
1109 ##player approaches door
1110 ##door says
"locked
"
1111 ##player finds key and reapproaches door
1114 ##player enters shop
1115 ##shop keeper says
"welcome to the shop
"
1116 ##player switches to shopping gun
1117 ##items are visible on the counter
1118 ##player shoots item
1119 ##menu pops up and offers buy choice
1120 ##player selects
"buy
" and item gets added to inventory and gold is deducted
1121 ##player shoots shop keeper
1122 ##shop keeper menu pops up and offers to buy items
</pre>
1124 <div title=
"Zot" modifier=
"Zot" created=
"200806171649" changecount=
"1">
1125 <pre>Zot is one of the founding members of [[TEAM CTHULHU|http://teamcthulhu.com]]
</pre>
1128 <!--POST-STOREAREA-->
1129 <!--POST-BODY-START-->
1130 <!--POST-BODY-END-->
1131 <script id=
"jsArea" type=
"text/javascript">
1136 // * This code is designed to be readable but for compactness it only includes brief comments. You can see fuller comments
1137 // in the project Subversion repository at http://svn.tiddlywiki.org/Trunk/core/
1139 // * You should never need to modify this source code directly. TiddlyWiki is carefully designed to allow deep customisation
1140 // without changing the core code. Please consult the development group at http://groups.google.com/group/TiddlyWikiDev
1144 //-- Configuration repository
1147 // Miscellaneous options
1149 numRssItems:
20, // Number of items in the RSS feed
1150 animDuration:
400, // Duration of UI animations in milliseconds
1151 cascadeFast:
20, // Speed for cascade animations (higher == slower)
1152 cascadeSlow:
60, // Speed for EasterEgg cascade animations
1153 cascadeDepth:
5, // Depth of cascade animation
1154 locale:
"en" // W3C language tag
1157 // Hashmap of alternative parsers for the wikifier
1158 config.parsers = {};
1161 config.adaptors = {};
1162 config.defaultAdaptor = null;
1168 config.annotations = {};
1170 // Custom fields to be automatically added to new tiddlers
1171 config.defaultCustomFields = {};
1180 // Options that can be set in the options panel and/or cookies
1182 chkRegExpSearch: false,
1183 chkCaseSensitiveSearch: false,
1184 chkIncrementalSearch: true,
1186 chkSaveBackups: true,
1188 chkGenerateAnRssFeed: false,
1189 chkSaveEmptyTemplate: false,
1190 chkOpenInNewWindow: true,
1191 chkToggleLinks: false,
1192 chkHttpReadOnly: true,
1193 chkForceMinorUpdate: false,
1194 chkConfirmDelete: true,
1195 chkInsertTabs: false,
1196 chkUsePreForStorage: true, // Whether to use
<pre> format for storage
1197 chkDisplayInstrumentation: false,
1198 txtBackupFolder:
"",
1199 txtEditorFocus:
"text",
1200 txtMainTab:
"tabTimeline",
1201 txtMoreTab:
"moreTabAll",
1202 txtMaxEditRows:
"30",
1203 txtFileSystemCharSet:
"UTF-8",
1206 config.optionsDesc = {};
1208 // Default tiddler templates
1209 var DEFAULT_VIEW_TEMPLATE =
1;
1210 var DEFAULT_EDIT_TEMPLATE =
2;
1211 config.tiddlerTemplates = {
1216 // More messages (rather a legacy layout that should not really be like this)
1227 config.backstageTasks = [
"save",
"sync",
"importTask",
"tweak",
"upgrade",
"plugins"];
1229 // Macros; each has a 'handler' member that is inserted later
1233 search: {sizeTextbox:
15},
1259 view: {defaultView:
"text"},
1267 source:
"http://www.tiddlywiki.com/upgrade/",
1268 backupExtension:
"pre.core.upgrade"
1274 // Commands supported by the toolbar macro
1279 saveTiddler: {hideReadOnly: true},
1281 deleteTiddler: {hideReadOnly: true},
1283 references: {type:
"popup"},
1284 jump: {type:
"popup"},
1285 syncing: {type:
"popup"},
1286 fields: {type:
"popup"}
1289 // Browser detection... In a very few places, there's nothing else for it but to know what browser we're using.
1290 config.userAgent = navigator.userAgent.toLowerCase();
1292 isIE: config.userAgent.indexOf(
"msie") != -
1 && config.userAgent.indexOf(
"opera") == -
1,
1293 isGecko: config.userAgent.indexOf(
"gecko") != -
1,
1294 ieVersion: /MSIE (\d.\d)/i.exec(config.userAgent), // config.browser.ieVersion[
1], if it exists, will be the IE version string, eg
"6.0"
1295 isSafari: config.userAgent.indexOf(
"applewebkit") != -
1,
1296 isBadSafari: !((new RegExp(
"[\u0150\u0170]",
"g")).test(
"\u0150")),
1297 firefoxDate: /gecko\/(\d{
8})/i.exec(config.userAgent), // config.browser.firefoxDate[
1], if it exists, will be Firefox release date as
"YYYYMMDD"
1298 isOpera: config.userAgent.indexOf(
"opera") != -
1,
1299 isLinux: config.userAgent.indexOf(
"linux") != -
1,
1300 isUnix: config.userAgent.indexOf(
"x11") != -
1,
1301 isMac: config.userAgent.indexOf(
"mac") != -
1,
1302 isWindows: config.userAgent.indexOf(
"win") != -
1
1305 // Basic regular expressions
1306 config.textPrimitives = {
1307 upperLetter:
"[A-Z\u00c0-\u00de\u0150\u0170]",
1308 lowerLetter:
"[a-z0-9_\\-\u00df-\u00ff\u0151\u0171]",
1309 anyLetter:
"[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]",
1310 anyLetterStrict:
"[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]"
1312 if(config.browser.isBadSafari) {
1313 config.textPrimitives = {
1314 upperLetter:
"[A-Z\u00c0-\u00de]",
1315 lowerLetter:
"[a-z0-9_\\-\u00df-\u00ff]",
1316 anyLetter:
"[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff]",
1317 anyLetterStrict:
"[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff]"
1320 config.textPrimitives.sliceSeparator =
"::";
1321 config.textPrimitives.sectionSeparator =
"##";
1322 config.textPrimitives.urlPattern =
"(?:file|http|https|mailto|ftp|irc|news|data):[^\\s'\"]+(?:/|\\b)
";
1323 config.textPrimitives.unWikiLink = "~
";
1324 config.textPrimitives.wikiLink = "(?:(?:
" + config.textPrimitives.upperLetter + "+
" +
1325 config.textPrimitives.lowerLetter + "+
" +
1326 config.textPrimitives.upperLetter +
1327 config.textPrimitives.anyLetter + "*)|(?:
" +
1328 config.textPrimitives.upperLetter + "{
2,}
" +
1329 config.textPrimitives.lowerLetter + "+))
";
1331 config.textPrimitives.cssLookahead = "(?:(
" + config.textPrimitives.anyLetter + "+)\\(([^\\)\\|\\n]+)(?:\\):))|(?:(
" + config.textPrimitives.anyLetter + "+):([^;\\|\\n]+);)
";
1332 config.textPrimitives.cssLookaheadRegExp = new RegExp(config.textPrimitives.cssLookahead,"mg
");
1334 config.textPrimitives.brackettedLink = "\\[\\[([^\\]]+)\\]\\]
";
1335 config.textPrimitives.titledBrackettedLink = "\\[\\[([^\\[\\]\\|]+)\\|([^\\[\\]\\|]+)\\]\\]
";
1336 config.textPrimitives.tiddlerForcedLinkRegExp = new RegExp("(?:
" + config.textPrimitives.titledBrackettedLink + ")|(?:
" +
1337 config.textPrimitives.brackettedLink + ")|(?:
" +
1338 config.textPrimitives.urlPattern + ")
","mg
");
1339 config.textPrimitives.tiddlerAnyLinkRegExp = new RegExp("(
"+ config.textPrimitives.wikiLink + ")|(?:
" +
1340 config.textPrimitives.titledBrackettedLink + ")|(?:
" +
1341 config.textPrimitives.brackettedLink + ")|(?:
" +
1342 config.textPrimitives.urlPattern + ")
","mg
");
1346 function() {return config.browser.isIE;},
1347 function() {return true;}
1351 downTriangle: ["\u25BC
","\u25BE
"],
1352 downArrow: ["\u2193
","\u2193
"],
1353 bentArrowLeft: ["\u2190
","\u21A9
"],
1354 bentArrowRight: ["\u2192
","\u21AA
"]
1359 //-- Shadow tiddlers
1362 config.shadowTiddlers = {
1368 TabTimeline: '<<timeline>>',
1369 TabAll: '<<list all>>',
1370 TabTags: '<<allTags excludeLists>>',
1371 TabMoreMissing: '<<list missing>>',
1372 TabMoreOrphans: '<<list orphans>>',
1373 TabMoreShadowed: '<<list shadowed>>',
1374 AdvancedOptions: '<<options>>',
1375 PluginManager: '<<plugins>>',
1376 ToolbarCommands: '|~ViewToolbar|closeTiddler closeOthers +editTiddler > fields syncing permalink references jump|\n|~EditToolbar|+saveTiddler -cancelTiddler deleteTiddler|'
1380 //-- Translateable strings
1383 // Strings in "double quotes
" should be translated; strings in 'single quotes' should be left alone
1385 merge(config.options,{
1386 txtUserName: "YourName
"});
1388 merge(config.tasks,{
1389 save: {text: "save
", tooltip: "Save your changes to this TiddlyWiki
", action: saveChanges},
1390 sync: {text: "sync
", tooltip: "Synchronise changes with other TiddlyWiki files and servers
", content: '<<sync>>'},
1391 importTask: {text: "import
", tooltip: "Import tiddlers and plugins from other TiddlyWiki files and servers
", content: '<<importTiddlers>>'},
1392 tweak: {text: "tweak
", tooltip: "Tweak the appearance and behaviour of TiddlyWiki
", content: '<<options>>'},
1393 upgrade: {text: "upgrade
", tooltip: "Upgrade TiddlyWiki core code
", content: '<<upgrade>>'},
1394 plugins: {text: "plugins
", tooltip: "Manage installed plugins
", content: '<<plugins>>'}
1397 // Options that can be set in the options panel and/or cookies
1398 merge(config.optionsDesc,{
1399 txtUserName: "Username for signing your edits
",
1400 chkRegExpSearch: "Enable regular expressions for searches
",
1401 chkCaseSensitiveSearch: "Case-sensitive searching
",
1402 chkIncrementalSearch: "Incremental key-by-key searching
",
1403 chkAnimate: "Enable animations
",
1404 chkSaveBackups: "Keep backup file when saving changes
",
1405 chkAutoSave: "Automatically save changes
",
1406 chkGenerateAnRssFeed: "Generate an RSS feed when saving changes
",
1407 chkSaveEmptyTemplate: "Generate an empty template when saving changes
",
1408 chkOpenInNewWindow: "Open external links in a new window
",
1409 chkToggleLinks: "Clicking on links to open tiddlers causes them to close
",
1410 chkHttpReadOnly: "Hide editing features when viewed over HTTP
",
1411 chkForceMinorUpdate: "Don't update modifier username and date when editing tiddlers
",
1412 chkConfirmDelete: "Require confirmation before deleting tiddlers
",
1413 chkInsertTabs: "Use the tab key to insert tab characters instead of moving between fields
",
1414 txtBackupFolder: "Name of folder to use for backups
",
1415 txtMaxEditRows: "Maximum number of rows in edit boxes
",
1416 txtFileSystemCharSet: "Default character set for saving changes (Firefox/Mozilla only)
"});
1418 merge(config.messages,{
1419 customConfigError: "Problems were encountered loading plugins. See PluginManager for details
",
1420 pluginError: "Error: %
0",
1421 pluginDisabled: "Not executed because disabled via 'systemConfigDisable' tag
",
1422 pluginForced: "Executed because forced via 'systemConfigForce' tag
",
1423 pluginVersionError: "Not executed because this plugin needs a newer version of TiddlyWiki
",
1424 nothingSelected: "Nothing is selected. You must select one or more items first
",
1425 savedSnapshotError: "It appears that this TiddlyWiki has been incorrectly saved. Please see http://www.tiddlywiki.com/#DownloadSoftware for details
",
1426 subtitleUnknown: "(unknown)
",
1427 undefinedTiddlerToolTip: "The tiddler '%
0' doesn't yet exist
",
1428 shadowedTiddlerToolTip: "The tiddler '%
0' doesn't yet exist, but has a pre-defined shadow value
",
1429 tiddlerLinkTooltip: "%
0 - %
1, %
2",
1430 externalLinkTooltip: "External link to %
0",
1431 noTags: "There are no tagged tiddlers
",
1432 notFileUrlError: "You need to save this TiddlyWiki to a file before you can save changes
",
1433 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
",
1434 invalidFileError: "The original file '%
0' does not appear to be a valid TiddlyWiki
",
1435 backupSaved: "Backup saved
",
1436 backupFailed: "Failed to save backup file
",
1437 rssSaved: "RSS feed saved
",
1438 rssFailed: "Failed to save RSS feed file
",
1439 emptySaved: "Empty template saved
",
1440 emptyFailed: "Failed to save empty template file
",
1441 mainSaved: "Main TiddlyWiki file saved
",
1442 mainFailed: "Failed to save main TiddlyWiki file. Your changes have not been saved
",
1443 macroError: "Error in macro <<\%
0>>",
1444 macroErrorDetails: "Error while executing macro <<\%
0>>:\n%
1",
1445 missingMacro: "No such macro
",
1446 overwriteWarning: "A tiddler named '%
0' already exists. Choose OK to overwrite it
",
1447 unsavedChangesWarning: "WARNING! There are unsaved changes in TiddlyWiki\n\nChoose OK to save\nChoose CANCEL to discard
",
1448 confirmExit: "--------------------------------\n\nThere are unsaved changes in TiddlyWiki. If you continue you will lose those changes\n\n--------------------------------
",
1449 saveInstructions: "SaveChanges
",
1450 unsupportedTWFormat: "Unsupported TiddlyWiki format '%
0'
",
1451 tiddlerSaveError: "Error when saving tiddler '%
0'
",
1452 tiddlerLoadError: "Error when loading tiddler '%
0'
",
1453 wrongSaveFormat: "Cannot save with storage format '%
0'. Using standard format for save.
",
1454 invalidFieldName: "Invalid field name %
0",
1455 fieldCannotBeChanged: "Field '%
0' cannot be changed
",
1456 loadingMissingTiddler: "Attempting to retrieve the tiddler '%
0' from the '%
1' server at:\n\n'%
2' in the workspace '%
3'
",
1457 upgradeDone: "The upgrade to version %
0 is now complete\n\nClick 'OK' to reload the newly upgraded TiddlyWiki
"});
1459 merge(config.messages.messageClose,{
1461 tooltip: "close this message area
"});
1463 config.messages.backstage = {
1464 open: {text: "backstage
", tooltip: "Open the backstage area to perform authoring and editing tasks
"},
1465 close: {text: "close
", tooltip: "Close the backstage area
"},
1466 prompt: "backstage:
",
1468 edit: {text: "edit
", tooltip: "Edit the tiddler '%
0'
"}
1472 config.messages.listView = {
1473 tiddlerTooltip: "Click for the full text of this tiddler
",
1474 previewUnavailable: "(preview not available)
"
1477 config.messages.dates.months = ["January
", "February
", "March
", "April
", "May
", "June
", "July
", "August
", "September
", "October
", "November
","December
"];
1478 config.messages.dates.days = ["Sunday
", "Monday
", "Tuesday
", "Wednesday
", "Thursday
", "Friday
", "Saturday
"];
1479 config.messages.dates.shortMonths = ["Jan
", "Feb
", "Mar
", "Apr
", "May
", "Jun
", "Jul
", "Aug
", "Sep
", "Oct
", "Nov
", "Dec
"];
1480 config.messages.dates.shortDays = ["Sun
", "Mon
", "Tue
", "Wed
", "Thu
", "Fri
", "Sat
"];
1481 // suffixes for dates, eg "1st
","2nd
","3rd
"..."30th
","31st
"
1482 config.messages.dates.daySuffixes = ["st
","nd
","rd
","th
","th
","th
","th
","th
","th
","th
",
1483 "th
","th
","th
","th
","th
","th
","th
","th
","th
","th
",
1484 "st
","nd
","rd
","th
","th
","th
","th
","th
","th
","th
",
1486 config.messages.dates.am = "am
";
1487 config.messages.dates.pm = "pm
";
1489 merge(config.messages.tiddlerPopup,{
1492 merge(config.views.wikified.tag,{
1493 labelNoTags: "no tags
",
1494 labelTags: "tags:
",
1495 openTag: "Open tag '%
0'
",
1496 tooltip: "Show tiddlers tagged with '%
0'
",
1497 openAllText: "Open all
",
1498 openAllTooltip: "Open all of these tiddlers
",
1499 popupNone: "No other tiddlers tagged with '%
0'
"});
1501 merge(config.views.wikified,{
1502 defaultText: "The tiddler '%
0' doesn't yet exist. Double-click to create it
",
1503 defaultModifier: "(missing)
",
1504 shadowModifier: "(built-in shadow tiddler)
",
1505 dateFormat: "DD MMM YYYY
",
1506 createdPrompt: "created
"});
1508 merge(config.views.editor,{
1509 tagPrompt: "Type tags separated with spaces, [[use double square brackets]] if necessary, or add existing
",
1510 defaultText: "Type the text for '%
0'
"});
1512 merge(config.views.editor.tagChooser,{
1514 tooltip: "Choose existing tags to add to this tiddler
",
1515 popupNone: "There are no tags defined
",
1516 tagTooltip: "Add the tag '%
0'
"});
1518 merge(config.messages,{
1521 {unit: 1024*1024*1024, template: "%
0\u00a0GB
"},
1522 {unit: 1024*1024, template: "%
0\u00a0MB
"},
1523 {unit: 1024, template: "%
0\u00a0KB
"},
1524 {unit: 1, template: "%
0\u00a0B
"}
1527 merge(config.macros.search,{
1529 prompt: "Search this TiddlyWiki
",
1531 successMsg: "%
0 tiddlers found matching %
1",
1532 failureMsg: "No tiddlers found matching %
0"});
1534 merge(config.macros.tagging,{
1536 labelNotTag: "not tagging
",
1537 tooltip: "List of tiddlers tagged with '%
0'
"});
1539 merge(config.macros.timeline,{
1540 dateFormat: "DD MMM YYYY
"});
1542 merge(config.macros.allTags,{
1543 tooltip: "Show tiddlers tagged with '%
0'
",
1544 noTags: "There are no tagged tiddlers
"});
1546 config.macros.list.all.prompt = "All tiddlers in alphabetical order
";
1547 config.macros.list.missing.prompt = "Tiddlers that have links to them but are not defined
";
1548 config.macros.list.orphans.prompt = "Tiddlers that are not linked to from any other tiddlers
";
1549 config.macros.list.shadowed.prompt = "Tiddlers shadowed with default contents
";
1550 config.macros.list.touched.prompt = "Tiddlers that have been modified locally
";
1552 merge(config.macros.closeAll,{
1554 prompt: "Close all displayed tiddlers (except any that are being edited)
"});
1556 merge(config.macros.permaview,{
1558 prompt: "Link to an URL that retrieves all the currently displayed tiddlers
"});
1560 merge(config.macros.saveChanges,{
1561 label: "save changes
",
1562 prompt: "Save all tiddlers to create a new TiddlyWiki
",
1565 merge(config.macros.newTiddler,{
1566 label: "new tiddler
",
1567 prompt: "Create a new tiddler
",
1568 title: "New Tiddler
",
1571 merge(config.macros.newJournal,{
1572 label: "new journal
",
1573 prompt: "Create a new tiddler from the current date and time
",
1576 merge(config.macros.options,{
1577 wizardTitle: "Tweak advanced options
",
1578 step1Title: "These options are saved in cookies in your browser
",
1579 step1Html: "<input type='hidden' name='markList'
></input><br><input type='checkbox' checked='false' name='chkUnknown'
>Show unknown options
</input>",
1580 unknownDescription: "//(unknown)//
",
1583 {name: 'Option', field: 'option', title: "Option
", type: 'String'},
1584 {name: 'Description', field: 'description', title: "Description
", type: 'WikiText'},
1585 {name: 'Name', field: 'name', title: "Name
", type: 'String'}
1588 {className: 'lowlight', field: 'lowlight'}
1592 merge(config.macros.plugins,{
1593 wizardTitle: "Manage plugins
",
1594 step1Title: "Currently loaded plugins
",
1595 step1Html: "<input type='hidden' name='markList'
></input>", // DO NOT TRANSLATE
1596 skippedText: "(This plugin has not been executed because it was added since startup)
",
1597 noPluginText: "There are no plugins installed
",
1598 confirmDeleteText: "Are you sure you want to delete these plugins:\n\n%
0",
1599 removeLabel: "remove systemConfig tag
",
1600 removePrompt: "Remove systemConfig tag
",
1601 deleteLabel: "delete
",
1602 deletePrompt: "Delete these tiddlers forever
",
1605 {name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
1606 {name: 'Tiddler', field: 'tiddler', title: "Tiddler
", type: 'Tiddler'},
1607 {name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size
", type: 'Size'},
1608 {name: 'Forced', field: 'forced', title: "Forced
", tag: 'systemConfigForce', type: 'TagCheckbox'},
1609 {name: 'Disabled', field: 'disabled', title: "Disabled
", tag: 'systemConfigDisable', type: 'TagCheckbox'},
1610 {name: 'Executed', field: 'executed', title: "Loaded
", type: 'Boolean', trueText: "Yes
", falseText: "No
"},
1611 {name: 'Startup Time', field: 'startupTime', title: "Startup Time
", type: 'String'},
1612 {name: 'Error', field: 'error', title: "Status
", type: 'Boolean', trueText: "Error
", falseText: "OK
"},
1613 {name: 'Log', field: 'log', title: "Log
", type: 'StringList'}
1616 {className: 'error', field: 'error'},
1617 {className: 'warning', field: 'warning'}
1621 merge(config.macros.toolbar,{
1623 morePrompt: "Reveal further commands
"
1626 merge(config.macros.refreshDisplay,{
1628 prompt: "Redraw the entire TiddlyWiki display
"
1631 merge(config.macros.importTiddlers,{
1632 readOnlyWarning: "You cannot import into a read-only TiddlyWiki file. Try opening it from a file:// URL
",
1633 wizardTitle: "Import tiddlers from another file or server
",
1634 step1Title: "Step
1: Locate the server or TiddlyWiki file
",
1635 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>",
1637 openPrompt: "Open the connection to this file or server
",
1638 openError: "There were problems fetching the tiddlywiki file
",
1639 statusOpenHost: "Opening the host
",
1640 statusGetWorkspaceList: "Getting the list of available workspaces
",
1641 step2Title: "Step
2: Choose the workspace
",
1642 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>",
1643 cancelLabel: "cancel
",
1644 cancelPrompt: "Cancel this import
",
1645 statusOpenWorkspace: "Opening the workspace
",
1646 statusGetTiddlerList: "Getting the list of available tiddlers
",
1647 errorGettingTiddlerList: "Error getting list of tiddlers, click Cancel to try again
",
1648 step3Title: "Step
3: Choose the tiddlers to import
",
1649 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'
>",
1650 importLabel: "import
",
1651 importPrompt: "Import these tiddlers
",
1652 confirmOverwriteText: "Are you sure you want to overwrite these tiddlers:\n\n%
0",
1653 step4Title: "Step
4: Importing %
0 tiddler(s)
",
1654 step4Html: "<input type='hidden' name='markReport'
></input>", // DO NOT TRANSLATE
1656 donePrompt: "Close this wizard
",
1657 statusDoingImport: "Importing tiddlers
",
1658 statusDoneImport: "All tiddlers imported
",
1659 systemServerNamePattern: "%
2 on %
1",
1660 systemServerNamePatternNoWorkspace: "%
1",
1661 confirmOverwriteSaveTiddler: "The tiddler '%
0' already exists. Click 'OK' to overwrite it with the details of this server, or 'Cancel' to leave it unchanged
",
1662 serverSaveTemplate: "|''Type:''|%
0|\n|''URL:''|%
1|\n|''Workspace:''|%
2|\n\nThis tiddler was automatically created to record the details of this server
",
1663 serverSaveModifier: "(System)
",
1666 {name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
1667 {name: 'Tiddler', field: 'tiddler', title: "Tiddler
", type: 'Tiddler'},
1668 {name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size
", type: 'Size'},
1669 {name: 'Tags', field: 'tags', title: "Tags
", type: 'Tags'}
1675 merge(config.macros.upgrade,{
1676 wizardTitle: "Upgrade TiddlyWiki core code
",
1677 step1Title: "Update or repair this TiddlyWiki to the latest release
",
1678 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>",
1679 errorCantUpgrade: "Unable to upgrade this TiddlyWiki. You can only perform upgrades on TiddlyWiki files stored locally
",
1680 errorNotSaved: "You must save changes before you can perform an upgrade
",
1681 step2Title: "Confirm the upgrade details
",
1682 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
",
1683 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
",
1684 step2Html_upgrade: "You are about to upgrade to TiddlyWiki version %
0 from %
1",
1685 upgradeLabel: "upgrade
",
1686 upgradePrompt: "Prepare for the upgrade process
",
1687 statusPreparingBackup: "Preparing backup
",
1688 statusSavingBackup: "Saving backup file
",
1689 errorSavingBackup: "There was a problem saving the backup file
",
1690 statusLoadingCore: "Loading core code
",
1691 errorLoadingCore: "Error loading the core code
",
1692 errorCoreFormat: "Error with the new core code
",
1693 statusSavingCore: "Saving the new core code
",
1694 statusReloadingCore: "Reloading the new core code
",
1695 startLabel: "start
",
1696 startPrompt: "Start the upgrade process
",
1697 cancelLabel: "cancel
",
1698 cancelPrompt: "Cancel the upgrade process
",
1699 step3Title: "Upgrade cancelled
",
1700 step3Html: "You have cancelled the upgrade process
"
1703 merge(config.macros.sync,{
1706 {name: 'Selected', field: 'selected', rowName: 'title', type: 'Selector'},
1707 {name: 'Tiddler', field: 'tiddler', title: "Tiddler
", type: 'Tiddler'},
1708 {name: 'Server Type', field: 'serverType', title: "Server type
", type: 'String'},
1709 {name: 'Server Host', field: 'serverHost', title: "Server host
", type: 'String'},
1710 {name: 'Server Workspace', field: 'serverWorkspace', title: "Server workspace
", type: 'String'},
1711 {name: 'Status', field: 'status', title: "Synchronisation status
", type: 'String'},
1712 {name: 'Server URL', field: 'serverUrl', title: "Server URL
", text: "View
", type: 'Link'}
1717 {caption: "Sync these tiddlers
", name: 'sync'}
1719 wizardTitle: "Synchronize with external servers and files
",
1720 step1Title: "Choose the tiddlers you want to synchronize
",
1721 step1Html: "<input type='hidden' name='markList'
></input>", // DO NOT TRANSLATE
1723 syncPrompt: "Sync these tiddlers
",
1724 hasChanged: "Changed while unplugged
",
1725 hasNotChanged: "Unchanged while unplugged
",
1727 none: {text: "...
", display:null, className:'notChanged'},
1728 changedServer: {text: "Changed on server
", display:null, className:'changedServer'},
1729 changedLocally: {text: "Changed while unplugged
", display:null, className:'changedLocally'},
1730 changedBoth: {text: "Changed while unplugged and on server
", display:null, className:'changedBoth'},
1731 notFound: {text: "Not found on server
", display:null, className:'notFound'},
1732 putToServer: {text: "Saved update on server
", display:null, className:'putToServer'},
1733 gotFromServer: {text: "Retrieved update from server
", display:null, className:'gotFromServer'}
1737 merge(config.macros.annotations,{
1740 merge(config.commands.closeTiddler,{
1742 tooltip: "Close this tiddler
"});
1744 merge(config.commands.closeOthers,{
1745 text: "close others
",
1746 tooltip: "Close all other tiddlers
"});
1748 merge(config.commands.editTiddler,{
1750 tooltip: "Edit this tiddler
",
1751 readOnlyText: "view
",
1752 readOnlyTooltip: "View the source of this tiddler
"});
1754 merge(config.commands.saveTiddler,{
1756 tooltip: "Save changes to this tiddler
"});
1758 merge(config.commands.cancelTiddler,{
1760 tooltip: "Undo changes to this tiddler
",
1761 warning: "Are you sure you want to abandon your changes to '%
0'?
",
1762 readOnlyText: "done
",
1763 readOnlyTooltip: "View this tiddler normally
"});
1765 merge(config.commands.deleteTiddler,{
1767 tooltip: "Delete this tiddler
",
1768 warning: "Are you sure you want to delete '%
0'?
"});
1770 merge(config.commands.permalink,{
1772 tooltip: "Permalink for this tiddler
"});
1774 merge(config.commands.references,{
1776 tooltip: "Show tiddlers that link to this one
",
1777 popupNone: "No references
"});
1779 merge(config.commands.jump,{
1781 tooltip: "Jump to another open tiddler
"});
1783 merge(config.commands.syncing,{
1785 tooltip: "Control synchronisation of this tiddler with a server or external file
",
1786 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
1787 notCurrentlySyncing: "Not currently syncing
",
1788 captionUnSync: "Stop synchronising this tiddler
",
1789 chooseServer: "Synchronise this tiddler with another server:
",
1790 currServerMarker: "\u25cf
",
1791 notCurrServerMarker: " "});
1793 merge(config.commands.fields,{
1795 tooltip: "Show the extended fields of this tiddler
",
1796 emptyText: "There are no extended fields for this tiddler
",
1799 {name: 'Field', field: 'field', title: "Field
", type: 'String'},
1800 {name: 'Value', field: 'value', title: "Value
", type: 'String'}
1807 merge(config.shadowTiddlers,{
1808 DefaultTiddlers: "[[GettingStarted]]
",
1809 MainMenu: "[[GettingStarted]]
",
1810 SiteTitle: "My TiddlyWiki
",
1811 SiteSubtitle: "a reusable non-linear personal web notebook
",
1812 SiteUrl: "http://www.tiddlywiki.com/
",
1813 SideBarOptions: '<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal "DD MMM YYYY
" "journal
">><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel "options \u00bb
" "Change TiddlyWiki advanced options
">>',
1814 SideBarTabs: '<<tabs txtMainTab "Timeline
" "Timeline
" TabTimeline "All
" "All tiddlers
" TabAll "Tags
" "All tags
" TabTags "More
" "More lists
" TabMore>>',
1815 TabMore: '<<tabs txtMoreTab "Missing
" "Missing tiddlers
" TabMoreMissing "Orphans
" "Orphaned tiddlers
" TabMoreOrphans "Shadowed
" "Shadowed tiddlers
" TabMoreShadowed>>'
1818 merge(config.annotations,{
1819 AdvancedOptions: "This shadow tiddler provides access to several advanced options
",
1820 ColorPalette: "These values in this shadow tiddler determine the colour scheme of the ~TiddlyWiki user interface
",
1821 DefaultTiddlers: "The tiddlers listed in this shadow tiddler will be automatically displayed when ~TiddlyWiki starts up
",
1822 EditTemplate: "The HTML template in this shadow tiddler determines how tiddlers look while they are being edited
",
1823 GettingStarted: "This shadow tiddler provides basic usage instructions
",
1824 ImportTiddlers: "This shadow tiddler provides access to importing tiddlers
",
1825 MainMenu: "This shadow tiddler is used as the contents of the main menu in the left-hand column of the screen
",
1826 MarkupPreHead: "This tiddler is inserted at the top of the
<head> section of the TiddlyWiki HTML file
",
1827 MarkupPostHead: "This tiddler is inserted at the bottom of the
<head> section of the TiddlyWiki HTML file
",
1828 MarkupPreBody: "This tiddler is inserted at the top of the
<body> section of the TiddlyWiki HTML file
",
1829 MarkupPostBody: "This tiddler is inserted at the end of the
<body> section of the TiddlyWiki HTML file immediately after the script block
",
1830 OptionsPanel: "This shadow tiddler is used as the contents of the options panel slider in the right-hand sidebar
",
1831 PageTemplate: "The HTML template in this shadow tiddler determines the overall ~TiddlyWiki layout
",
1832 PluginManager: "This shadow tiddler provides access to the plugin manager
",
1833 SideBarOptions: "This shadow tiddler is used as the contents of the option panel in the right-hand sidebar
",
1834 SideBarTabs: "This shadow tiddler is used as the contents of the tabs panel in the right-hand sidebar
",
1835 SiteSubtitle: "This shadow tiddler is used as the second part of the page title
",
1836 SiteTitle: "This shadow tiddler is used as the first part of the page title
",
1837 SiteUrl: "This shadow tiddler should be set to the full target URL for publication
",
1838 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
",
1839 StyleSheet: "This tiddler can contain custom CSS definitions
",
1840 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
",
1841 StyleSheetLocale: "This shadow tiddler contains CSS definitions related to the translation locale
",
1842 StyleSheetPrint: "This shadow tiddler contains CSS definitions for printing
",
1843 TabAll: "This shadow tiddler contains the contents of the 'All' tab in the right-hand sidebar
",
1844 TabMore: "This shadow tiddler contains the contents of the 'More' tab in the right-hand sidebar
",
1845 TabMoreMissing: "This shadow tiddler contains the contents of the 'Missing' tab in the right-hand sidebar
",
1846 TabMoreOrphans: "This shadow tiddler contains the contents of the 'Orphans' tab in the right-hand sidebar
",
1847 TabMoreShadowed: "This shadow tiddler contains the contents of the 'Shadowed' tab in the right-hand sidebar
",
1848 TabTags: "This shadow tiddler contains the contents of the 'Tags' tab in the right-hand sidebar
",
1849 TabTimeline: "This shadow tiddler contains the contents of the 'Timeline' tab in the right-hand sidebar
",
1850 ToolbarCommands: "This shadow tiddler determines which commands are shown in tiddler toolbars
",
1851 ViewTemplate: "The HTML template in this shadow tiddler determines how tiddlers look
"
1858 var params = null; // Command line parameters
1859 var store = null; // TiddlyWiki storage
1860 var story = null; // Main story
1861 var formatter = null; // Default formatters for the wikifier
1862 var anim = typeof Animator == "function
" ? new Animator() : null; // Animation engine
1863 var readOnly = false; // Whether we're in readonly mode
1864 var highlightHack = null; // Embarrassing hack department...
1865 var hadConfirmExit = false; // Don't warn more than once
1866 var safeMode = false; // Disable all plugins and cookies
1867 var showBackstage; // Whether to include the backstage area
1868 var installedPlugins = []; // Information filled in when plugins are executed
1869 var startingUp = false; // Whether we're in the process of starting up
1870 var pluginInfo,tiddler; // Used to pass information to plugins in loadPlugins()
1872 // Whether to use the JavaSaver applet
1873 var useJavaSaver = (config.browser.isSafari || config.browser.isOpera) && (document.location.toString().substr(0,4) != "http
");
1878 var t10,t9,t8,t7,t6,t5,t4,t3,t2,t1,t0 = new Date();
1880 window.onbeforeunload = function(e) {if(window.confirmExit) return confirmExit();};
1881 params = getParameters();
1883 params = params.parseParams("open
",null,false);
1884 store = new TiddlyWiki();
1885 invokeParamifier(params,"oninit
");
1886 story = new Story("tiddlerDisplay
","tiddler
");
1887 addEvent(document,"click
",Popup.onDocumentClick);
1889 loadOptionsCookie();
1890 for(var s=0; s<config.notifyTiddlers.length; s++)
1891 store.addNotification(config.notifyTiddlers[s].name,config.notifyTiddlers[s].notify);
1893 loadShadowTiddlers();
1895 store.loadFromDiv("storeArea
","store
",true);
1897 invokeParamifier(params,"onload
");
1899 readOnly = (window.location.protocol == "file:
") ? false : config.options.chkHttpReadOnly;
1900 var pluginProblem = loadPlugins();
1902 formatter = new Formatter(config.formatters);
1903 invokeParamifier(params,"onconfig
");
1904 story.switchTheme(config.options.txtTheme);
1905 showBackstage = !readOnly;
1913 story.displayTiddler(null,"PluginManager
");
1914 displayMessage(config.messages.customConfigError);
1916 for(var m in config.macros) {
1917 if(config.macros[m].init)
1918 config.macros[m].init();
1924 if(config.options.chkDisplayInstrumentation) {
1925 displayMessage("LoadShadows
" + (t2-t1) + " ms
");
1926 displayMessage("LoadFromDiv
" + (t3-t2) + " ms
");
1927 displayMessage("LoadPlugins
" + (t5-t4) + " ms
");
1928 displayMessage("Notify
" + (t7-t6) + " ms
");
1929 displayMessage("Restart
" + (t8-t7) + " ms
");
1930 displayMessage("Macro init
" + (t9-t8) + " ms
");
1931 displayMessage("Total:
" + (t10-t0) + " ms
");
1939 invokeParamifier(params,"onstart
");
1940 if(story.isEmpty()) {
1941 story.displayDefaultTiddlers();
1943 window.scrollTo(0,0);
1948 var s = document.getElementById("saveTest
");
1949 if(s.hasChildNodes())
1950 alert(config.messages.savedSnapshotError);
1951 s.appendChild(document.createTextNode("savetest
"));
1954 function loadShadowTiddlers()
1956 var shadows = new TiddlyWiki();
1957 shadows.loadFromDiv("shadowArea
","shadows
",true);
1958 shadows.forEachTiddler(function(title,tiddler){config.shadowTiddlers[title] = tiddler.text;});
1962 function loadPlugins()
1966 var tiddlers = store.getTaggedTiddlers("systemConfig
");
1970 var nPlugins = tiddlers.length;
1971 installedPlugins = [];
1972 for(var i=0; i<nPlugins; i++) {
1973 var p = getPluginInfo(tiddlers[i]);
1974 installedPlugins[i] = p;
1982 var visit = function(p) {
1986 var reqs = p.Requires;
1988 reqs = reqs.readBracketedList();
1989 for(var i=0; i<reqs.length; i++)
1990 visit(map[reqs[i]]);
1994 for(i=0; i<nPlugins; i++)
1995 visit(installedPlugins[i]);
1996 for(i=0; i<toLoad.length; i++) {
1999 tiddler = p.tiddler;
2000 if(isPluginExecutable(p)) {
2001 if(isPluginEnabled(p)) {
2003 var startTime = new Date();
2006 window.eval(tiddler.text);
2009 p.log.push(config.messages.pluginError.format([exceptionText(ex)]));
2012 pluginInfo.startupTime = String((new Date()) - startTime) + "ms
";
2020 return nLoaded != nPlugins;
2023 function getPluginInfo(tiddler)
2025 var p = store.getTiddlerSlices(tiddler.title,["Name
","Description
","Version
","Requires
","CoreVersion
","Date
","Source
","Author
","License
","Browsers
"]);
2026 p.tiddler = tiddler;
2027 p.title = tiddler.title;
2032 // Check that a particular plugin is valid for execution
2033 function isPluginExecutable(plugin)
2035 if(plugin.tiddler.isTagged("systemConfigForce
")) {
2036 plugin.log.push(config.messages.pluginForced);
2039 if(plugin["CoreVersion
"]) {
2040 var coreVersion = plugin["CoreVersion
"].split(".
");
2041 var w = parseInt(coreVersion[0],10) - version.major;
2042 if(w == 0 && coreVersion[1])
2043 w = parseInt(coreVersion[1],10) - version.minor;
2044 if(w == 0 && coreVersion[2])
2045 w = parseInt(coreVersion[2],10) - version.revision;
2047 plugin.log.push(config.messages.pluginVersionError);
2054 function isPluginEnabled(plugin)
2056 if(plugin.tiddler.isTagged("systemConfigDisable
")) {
2057 plugin.log.push(config.messages.pluginDisabled);
2063 function invokeMacro(place,macro,params,wikifier,tiddler)
2066 var m = config.macros[macro];
2068 m.handler(place,macro,params.readMacroParams(),wikifier,params,tiddler);
2070 createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,config.messages.missingMacro]));
2072 createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,ex.toString()]));
2080 function getParameters()
2083 if(window.location.hash) {
2084 p = decodeURIComponent(window.location.hash.substr(1));
2085 if(config.browser.firefoxDate != null && config.browser.firefoxDate[1] < "20051111")
2086 p = convertUTF8ToUnicode(p);
2091 function invokeParamifier(params,handler)
2093 if(!params || params.length == undefined || params.length <= 1)
2095 for(var t=1; t<params.length; t++) {
2096 var p = config.paramifiers[params[t].name];
2097 if(p && p[handler] instanceof Function)
2098 p[handler](params[t].value);
2102 config.paramifiers = {};
2104 config.paramifiers.start = {
2105 oninit: function(v) {
2106 safeMode = v.toLowerCase() == "safe
";
2110 config.paramifiers.open = {
2111 onstart: function(v) {
2112 if(!readOnly || store.tiddlerExists(v) || store.isShadowTiddler(v))
2113 story.displayTiddler("bottom
",v,null,false,null);
2117 config.paramifiers.story = {
2118 onstart: function(v) {
2119 var list = store.getTiddlerText(v,"").parseParams("open
",null,false);
2120 invokeParamifier(list,"onstart
");
2124 config.paramifiers.search = {
2125 onstart: function(v) {
2126 story.search(v,false,false);
2130 config.paramifiers.searchRegExp = {
2131 onstart: function(v) {
2132 story.prototype.search(v,false,true);
2136 config.paramifiers.tag = {
2137 onstart: function(v) {
2138 story.displayTiddlers(null,store.filterTiddlers("[tag[
"+v+"]]
"),null,false,null);
2142 config.paramifiers.newTiddler = {
2143 onstart: function(v) {
2145 story.displayTiddler(null,v,DEFAULT_EDIT_TEMPLATE);
2146 story.focusTiddler(v,"text
");
2151 config.paramifiers.newJournal = {
2152 onstart: function(v) {
2154 var now = new Date();
2155 var title = now.formatString(v.trim());
2156 story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
2157 story.focusTiddler(title,"text
");
2162 config.paramifiers.readOnly = {
2163 onconfig: function(v) {
2164 var p = v.toLowerCase();
2165 readOnly = p == "yes
" ? true : (p == "no
" ? false : readOnly);
2169 config.paramifiers.theme = {
2170 onconfig: function(v) {
2171 story.switchTheme(v);
2175 config.paramifiers.upgrade = {
2176 onstart: function(v) {
2181 config.paramifiers.recent= {
2182 onstart: function(v) {
2184 var tiddlers=store.getTiddlers("modified
","excludeLists
").reverse();
2185 for(var i=0; i<v && i<tiddlers.length; i++)
2186 titles.push(tiddlers[i].title);
2187 story.displayTiddlers(null,titles);
2191 config.paramifiers.filter = {
2192 onstart: function(v) {
2193 story.displayTiddlers(null,store.filterTiddlers(v),null,false);
2198 //-- Formatter helpers
2201 function Formatter(formatters)
2203 this.formatters = [];
2205 for(var n=0; n<formatters.length; n++) {
2206 pattern.push("(
" + formatters[n].match + ")
");
2207 this.formatters.push(formatters[n]);
2209 this.formatterRegExp = new RegExp(pattern.join("|
"),"mg
");
2212 config.formatterHelpers = {
2214 createElementAndWikify: function(w)
2216 w.subWikifyTerm(createTiddlyElement(w.output,this.element),this.termRegExp);
2219 inlineCssHelper: function(w)
2222 config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
2223 var lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
2224 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
2226 if(lookaheadMatch[1]) {
2227 s = lookaheadMatch[1].unDash();
2228 v = lookaheadMatch[2];
2230 s = lookaheadMatch[3].unDash();
2231 v = lookaheadMatch[4];
2234 s = "backgroundColor
";
2235 styles.push({style: s, value: v});
2236 w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
2237 config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
2238 lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
2243 applyCssHelper: function(e,styles)
2245 for(var t=0; t< styles.length; t++) {
2247 e.style[styles[t].style] = styles[t].value;
2253 enclosedTextHelper: function(w)
2255 this.lookaheadRegExp.lastIndex = w.matchStart;
2256 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2257 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
2258 var text = lookaheadMatch[1];
2259 if(config.browser.isIE)
2260 text = text.replace(/\n/g,"\r
");
2261 createTiddlyElement(w.output,this.element,null,null,text);
2262 w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
2266 isExternalLink: function(link)
2268 if(store.tiddlerExists(link) || store.isShadowTiddler(link)) {
2271 var urlRegExp = new RegExp(config.textPrimitives.urlPattern,"mg
");
2272 if(urlRegExp.exec(link)) {
2275 if(link.indexOf(".
")!=-1 || link.indexOf("\\
")!=-1 || link.indexOf("/
")!=-1 || link.indexOf("#
")!=-1) {
2284 //-- Standard formatters
2287 config.formatters = [
2290 match: "^\\|(?:[^\\n]*)\\|(?:[fhck]?)$
",
2291 lookaheadRegExp: /^\|([^\n]*)\|([fhck]?)$/mg,
2292 rowTermRegExp: /(\|(?:[fhck]?)$\n?)/mg,
2293 cellRegExp: /(?:\|([^\n\|]*)\|)|(\|[fhck]?$\n?)/mg,
2294 cellTermRegExp: /((?:\x20*)\|)/mg,
2295 rowTypes: {"c
":"caption
", "h
":"thead
", "":"tbody
", "f
":"tfoot
"},
2296 handler: function(w)
2298 var table = createTiddlyElement(w.output,"table
",null,"twtable
");
2299 var prevColumns = [];
2300 var currRowType = null;
2303 w.nextMatch = w.matchStart;
2304 this.lookaheadRegExp.lastIndex = w.nextMatch;
2305 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2306 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
2307 var nextRowType = lookaheadMatch[2];
2308 if(nextRowType == "k
") {
2309 table.className = lookaheadMatch[1];
2310 w.nextMatch += lookaheadMatch[0].length+1;
2312 if(nextRowType != currRowType) {
2313 rowContainer = createTiddlyElement(table,this.rowTypes[nextRowType]);
2314 currRowType = nextRowType;
2316 if(currRowType == "c
") {
2319 if(rowContainer != table.firstChild)
2320 table.insertBefore(rowContainer,table.firstChild);
2321 rowContainer.setAttribute("align
",rowCount == 0?"top
":"bottom
");
2322 w.subWikifyTerm(rowContainer,this.rowTermRegExp);
2324 var theRow = createTiddlyElement(rowContainer,"tr
",null,(rowCount&1)?"oddRow
":"evenRow
");
2325 theRow.onmouseover = function() {addClass(this,"hoverRow
");};
2326 theRow.onmouseout = function() {removeClass(this,"hoverRow
");};
2327 this.rowHandler(w,theRow,prevColumns);
2331 this.lookaheadRegExp.lastIndex = w.nextMatch;
2332 lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2335 rowHandler: function(w,e,prevColumns)
2338 var colSpanCount = 1;
2339 var prevCell = null;
2340 this.cellRegExp.lastIndex = w.nextMatch;
2341 var cellMatch = this.cellRegExp.exec(w.source);
2342 while(cellMatch && cellMatch.index == w.nextMatch) {
2343 if(cellMatch[1] == "~
") {
2345 var last = prevColumns[col];
2347 last.rowSpanCount++;
2348 last.element.setAttribute("rowspan
",last.rowSpanCount);
2349 last.element.setAttribute("rowSpan
",last.rowSpanCount); // Needed for IE
2350 last.element.valign = "center
";
2352 w.nextMatch = this.cellRegExp.lastIndex-1;
2353 } else if(cellMatch[1] == ">") {
2356 w.nextMatch = this.cellRegExp.lastIndex-1;
2357 } else if(cellMatch[2]) {
2359 if(prevCell && colSpanCount > 1) {
2360 prevCell.setAttribute("colspan
",colSpanCount);
2361 prevCell.setAttribute("colSpan
",colSpanCount); // Needed for IE
2363 w.nextMatch = this.cellRegExp.lastIndex;
2368 var styles = config.formatterHelpers.inlineCssHelper(w);
2369 var spaceLeft = false;
2370 var chr = w.source.substr(w.nextMatch,1);
2374 chr = w.source.substr(w.nextMatch,1);
2378 cell = createTiddlyElement(e,"th
");
2381 cell = createTiddlyElement(e,"td
");
2384 prevColumns[col] = {rowSpanCount:1,element:cell};
2385 if(colSpanCount > 1) {
2386 cell.setAttribute("colspan
",colSpanCount);
2387 cell.setAttribute("colSpan
",colSpanCount); // Needed for IE
2390 config.formatterHelpers.applyCssHelper(cell,styles);
2391 w.subWikifyTerm(cell,this.cellTermRegExp);
2392 if(w.matchText.substr(w.matchText.length-2,1) == " ") // spaceRight
2393 cell.align = spaceLeft ? "center
" : "left
";
2395 cell.align = "right
";
2399 this.cellRegExp.lastIndex = w.nextMatch;
2400 cellMatch = this.cellRegExp.exec(w.source);
2408 termRegExp: /(\n)/mg,
2409 handler: function(w)
2411 w.subWikifyTerm(createTiddlyElement(w.output,"h
" + w.matchLength),this.termRegExp);
2417 match: "^(?:[\\*#;:]+)
",
2418 lookaheadRegExp: /^(?:(?:(\*)|(#)|(;)|(:))+)/mg,
2419 termRegExp: /(\n)/mg,
2420 handler: function(w)
2422 var stack = [w.output];
2423 var currLevel = 0, currType = null;
2424 var listLevel, listType, itemType, baseType;
2425 w.nextMatch = w.matchStart;
2426 this.lookaheadRegExp.lastIndex = w.nextMatch;
2427 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2428 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
2429 if(lookaheadMatch[1]) {
2432 } else if(lookaheadMatch[2]) {
2435 } else if(lookaheadMatch[3]) {
2438 } else if(lookaheadMatch[4]) {
2443 baseType = listType;
2444 listLevel = lookaheadMatch[0].length;
2445 w.nextMatch += lookaheadMatch[0].length;
2447 if(listLevel > currLevel) {
2448 for(t=currLevel; t<listLevel; t++) {
2449 var target = (currLevel == 0) ? stack[stack.length-1] : stack[stack.length-1].lastChild;
2450 stack.push(createTiddlyElement(target,listType));
2452 } else if(listType!=baseType && listLevel==1) {
2453 w.nextMatch -= lookaheadMatch[0].length;
2455 } else if(listLevel < currLevel) {
2456 for(t=currLevel; t>listLevel; t--)
2458 } else if(listLevel == currLevel && listType != currType) {
2460 stack.push(createTiddlyElement(stack[stack.length-1].lastChild,listType));
2462 currLevel = listLevel;
2463 currType = listType;
2464 var e = createTiddlyElement(stack[stack.length-1],itemType);
2465 w.subWikifyTerm(e,this.termRegExp);
2466 this.lookaheadRegExp.lastIndex = w.nextMatch;
2467 lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2473 name: "quoteByBlock
",
2475 termRegExp: /(^<<<(\n|$))/mg,
2476 element: "blockquote
",
2477 handler: config.formatterHelpers.createElementAndWikify
2481 name: "quoteByLine
",
2483 lookaheadRegExp: /^>+/mg,
2484 termRegExp: /(\n)/mg,
2485 element: "blockquote
",
2486 handler: function(w)
2488 var stack = [w.output];
2490 var newLevel = w.matchLength;
2493 if(newLevel > currLevel) {
2494 for(t=currLevel; t<newLevel; t++)
2495 stack.push(createTiddlyElement(stack[stack.length-1],this.element));
2496 } else if(newLevel < currLevel) {
2497 for(t=currLevel; t>newLevel; t--)
2500 currLevel = newLevel;
2501 w.subWikifyTerm(stack[stack.length-1],this.termRegExp);
2502 createTiddlyElement(stack[stack.length-1],"br
");
2503 this.lookaheadRegExp.lastIndex = w.nextMatch;
2504 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2505 var matched = lookaheadMatch && lookaheadMatch.index == w.nextMatch;
2507 newLevel = lookaheadMatch[0].length;
2508 w.nextMatch += lookaheadMatch[0].length;
2516 match: "^----+$\\n?
",
2517 handler: function(w)
2519 createTiddlyElement(w.output,"hr
");
2524 name: "monospacedByLine
",
2525 match: "^(?:/\\*\\{\\{\\{\\*/|\\{\\{\\{|//\\{\\{\\{|
<!--\\{\\{\\{-->)\\n
",
2527 handler: function(w)
2529 switch(w.matchText) {
2530 case "/*{{{*/\n
": // CSS
2531 this.lookaheadRegExp = /\/\*\{\{\{\*\/\n*((?:^[^\n]*\n)+?)(\n*^\/\*\}\}\}\*\/$\n?)/mg;
2533 case "{{{\n
": // monospaced block
2534 this.lookaheadRegExp = /^\{\{\{\n((?:^[^\n]*\n)+?)(^\}\}\}$\n?)/mg;
2536 case "//{{{\n
": // plugin
2537 this.lookaheadRegExp = /^\/\/\{\{\{\n\n*((?:^[^\n]*\n)+?)(\n*^\/\/\}\}\}$\n?)/mg;
2539 case "<!--{{{-->\n
": //template
2540 this.lookaheadRegExp = /<!--\{\{\{-->\n*((?:^[^\n]*\n)+?)(\n*^<!--\}\}\}-->$\n?)/mg;
2545 config.formatterHelpers.enclosedTextHelper.call(this,w);
2550 name: "wikifyComment
",
2551 match: "^(?:/\\*\\*\\*|
<!---)\\n",
2552 handler: function(w)
2554 var termRegExp = (w.matchText == "/***\n") ? (/(^\*\*\*\/\n)/mg) : (/(^--->\n)/mg);
2555 w.subWikifyTerm(w.output,termRegExp);
2562 lookaheadRegExp: /<<([^
>\s]+)(?:\s*)((?:[^
>]|(?:
>(?!
>)))*)
>>/mg,
2563 handler: function(w)
2565 this.lookaheadRegExp.lastIndex = w.matchStart;
2566 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2567 if(lookaheadMatch && lookaheadMatch.index == w.matchStart && lookaheadMatch[
1]) {
2568 w.nextMatch = this.lookaheadRegExp.lastIndex;
2569 invokeMacro(w.output,lookaheadMatch[
1],lookaheadMatch[
2],w,w.tiddler);
2577 lookaheadRegExp: /\[\[(.*?)(?:\|(~)?(.*?))?\]\]/mg,
2578 handler: function(w)
2580 this.lookaheadRegExp.lastIndex = w.matchStart;
2581 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2582 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
2584 var text = lookaheadMatch[
1];
2585 if(lookaheadMatch[
3]) {
2586 // Pretty bracketted link
2587 var link = lookaheadMatch[
3];
2588 e = (!lookaheadMatch[
2] && config.formatterHelpers.isExternalLink(link)) ?
2589 createExternalLink(w.output,link) : createTiddlyLink(w.output,decodeURIComponent(link),false,null,w.isStatic,w.tiddler);
2591 // Simple bracketted link
2592 e = createTiddlyLink(w.output,decodeURIComponent(text),false,null,w.isStatic,w.tiddler);
2594 createTiddlyText(e,text);
2595 w.nextMatch = this.lookaheadRegExp.lastIndex;
2602 match: config.textPrimitives.unWikiLink+
"?"+config.textPrimitives.wikiLink,
2603 handler: function(w)
2605 if(w.matchText.substr(
0,
1) == config.textPrimitives.unWikiLink) {
2606 w.outputText(w.output,w.matchStart+
1,w.nextMatch);
2609 if(w.matchStart
> 0) {
2610 var preRegExp = new RegExp(config.textPrimitives.anyLetterStrict,
"mg");
2611 preRegExp.lastIndex = w.matchStart-
1;
2612 var preMatch = preRegExp.exec(w.source);
2613 if(preMatch.index == w.matchStart-
1) {
2614 w.outputText(w.output,w.matchStart,w.nextMatch);
2618 if(w.autoLinkWikiWords || store.isShadowTiddler(w.matchText)) {
2619 var link = createTiddlyLink(w.output,w.matchText,false,null,w.isStatic,w.tiddler);
2620 w.outputText(link,w.matchStart,w.nextMatch);
2622 w.outputText(w.output,w.matchStart,w.nextMatch);
2629 match: config.textPrimitives.urlPattern,
2630 handler: function(w)
2632 w.outputText(createExternalLink(w.output,w.matchText),w.matchStart,w.nextMatch);
2638 match:
"\\[[<>]?[Ii][Mm][Gg]\\[",
2639 lookaheadRegExp: /\[([<]?)(
>?)[Ii][Mm][Gg]\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg,
2640 handler: function(w)
2642 this.lookaheadRegExp.lastIndex = w.matchStart;
2643 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2644 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
2646 if(lookaheadMatch[
5]) {
2647 var link = lookaheadMatch[
5];
2648 e = config.formatterHelpers.isExternalLink(link) ? createExternalLink(w.output,link) : createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
2649 addClass(e,
"imageLink");
2651 var img = createTiddlyElement(e,
"img");
2652 if(lookaheadMatch[
1])
2654 else if(lookaheadMatch[
2])
2655 img.align =
"right";
2656 if(lookaheadMatch[
3]) {
2657 img.title = lookaheadMatch[
3];
2658 img.setAttribute(
"alt",lookaheadMatch[
3]);
2660 img.src = lookaheadMatch[
4];
2661 w.nextMatch = this.lookaheadRegExp.lastIndex;
2668 match:
"<[Hh][Tt][Mm][Ll]>",
2669 lookaheadRegExp: /<[Hh][Tt][Mm][Ll]
>((?:.|\n)*?)<\/[Hh][Tt][Mm][Ll]
>/mg,
2670 handler: function(w)
2672 this.lookaheadRegExp.lastIndex = w.matchStart;
2673 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2674 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
2675 createTiddlyElement(w.output,
"span").innerHTML = lookaheadMatch[
1];
2676 w.nextMatch = this.lookaheadRegExp.lastIndex;
2682 name:
"commentByBlock",
2684 lookaheadRegExp: /\/%((?:.|\n)*?)%\//mg,
2685 handler: function(w)
2687 this.lookaheadRegExp.lastIndex = w.matchStart;
2688 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2689 if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
2690 w.nextMatch = this.lookaheadRegExp.lastIndex;
2695 name:
"characterFormat",
2696 match:
"''|//|__|\\^\\^|~~|--(?!\\s|$)|\\{\\{\\{",
2697 handler: function(w)
2699 switch(w.matchText) {
2701 w.subWikifyTerm(w.output.appendChild(document.createElement(
"strong")),/('')/mg);
2704 w.subWikifyTerm(createTiddlyElement(w.output,
"em"),/(\/\/)/mg);
2707 w.subWikifyTerm(createTiddlyElement(w.output,
"u"),/(__)/mg);
2710 w.subWikifyTerm(createTiddlyElement(w.output,
"sup"),/(\^\^)/mg);
2713 w.subWikifyTerm(createTiddlyElement(w.output,
"sub"),/(~~)/mg);
2716 w.subWikifyTerm(createTiddlyElement(w.output,
"strike"),/(--)/mg);
2719 var lookaheadRegExp = /\{\{\{((?:.|\n)*?)\}\}\}/mg;
2720 lookaheadRegExp.lastIndex = w.matchStart;
2721 var lookaheadMatch = lookaheadRegExp.exec(w.source);
2722 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
2723 createTiddlyElement(w.output,
"code",null,null,lookaheadMatch[
1]);
2724 w.nextMatch = lookaheadRegExp.lastIndex;
2732 name:
"customFormat",
2734 handler: function(w)
2736 switch(w.matchText) {
2738 var e = createTiddlyElement(w.output,
"span");
2739 var styles = config.formatterHelpers.inlineCssHelper(w);
2740 if(styles.length ==
0)
2741 e.className =
"marked";
2743 config.formatterHelpers.applyCssHelper(e,styles);
2744 w.subWikifyTerm(e,/(@@)/mg);
2747 var lookaheadRegExp = /\{\{[\s]*([\w]+[\s\w]*)[\s]*\{(\n?)/mg;
2748 lookaheadRegExp.lastIndex = w.matchStart;
2749 var lookaheadMatch = lookaheadRegExp.exec(w.source);
2750 if(lookaheadMatch) {
2751 w.nextMatch = lookaheadRegExp.lastIndex;
2752 e = createTiddlyElement(w.output,lookaheadMatch[
2] ==
"\n" ?
"div" :
"span",null,lookaheadMatch[
1]);
2753 w.subWikifyTerm(e,/(\}\}\})/mg);
2763 handler: function(w)
2765 createTiddlyElement(w.output,
"span").innerHTML =
"—";
2771 match:
"\\n|<br ?/?>",
2772 handler: function(w)
2774 createTiddlyElement(w.output,
"br");
2780 match:
"\\\"{
3}|
<nowiki>",
2781 lookaheadRegExp: /(?:\"{
3}|
<nowiki>)((?:.|\n)*?)(?:\
"{3}|<\/nowiki>)/mg,
2782 handler: function(w)
2784 this.lookaheadRegExp.lastIndex = w.matchStart;
2785 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
2786 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
2787 createTiddlyElement(w.output,"span
",null,null,lookaheadMatch[1]);
2788 w.nextMatch = this.lookaheadRegExp.lastIndex;
2794 name: "htmlEntitiesEncoding
",
2795 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};)
",
2796 handler: function(w)
2798 createTiddlyElement(w.output,"span
").innerHTML = w.matchText;
2808 function getParser(tiddler,format)
2812 format = tiddler.fields["wikiformat
"];
2815 for(i in config.parsers) {
2816 if(format == config.parsers[i].format)
2817 return config.parsers[i];
2820 for(i in config.parsers) {
2821 if(tiddler.isTagged(config.parsers[i].formatTag))
2822 return config.parsers[i];
2829 function wikify(source,output,highlightRegExp,tiddler)
2832 var wikifier = new Wikifier(source,getParser(tiddler),highlightRegExp,tiddler);
2833 var t0 = new Date();
2834 wikifier.subWikify(output);
2835 if(tiddler && config.options.chkDisplayInstrumentation)
2836 displayMessage("wikify:
" +tiddler.title+ " in
" + (new Date()-t0) + " ms
");
2840 function wikifyStatic(source,highlightRegExp,tiddler,format)
2842 var e = createTiddlyElement(document.body,"pre
");
2843 e.style.display = "none
";
2845 if(source && source != "") {
2847 tiddler = new Tiddler("temp
");
2848 var wikifier = new Wikifier(source,getParser(tiddler,format),highlightRegExp,tiddler);
2849 wikifier.isStatic = true;
2850 wikifier.subWikify(e);
2857 function wikifyPlain(title,theStore,limit)
2861 if(theStore.tiddlerExists(title) || theStore.isShadowTiddler(title)) {
2862 return wikifyPlainText(theStore.getTiddlerText(title),limit,tiddler);
2868 function wikifyPlainText(text,limit,tiddler)
2871 text = text.substr(0,limit);
2872 var wikifier = new Wikifier(text,formatter,null,tiddler);
2873 return wikifier.wikifyPlain();
2876 function highlightify(source,output,highlightRegExp,tiddler)
2879 var wikifier = new Wikifier(source,formatter,highlightRegExp,tiddler);
2880 wikifier.outputText(output,0,source.length);
2884 function Wikifier(source,formatter,highlightRegExp,tiddler)
2886 this.source = source;
2888 this.formatter = formatter;
2890 this.autoLinkWikiWords = tiddler && tiddler.autoLinkWikiWords() == false ? false : true;
2891 this.highlightRegExp = highlightRegExp;
2892 this.highlightMatch = null;
2893 this.isStatic = false;
2894 if(highlightRegExp) {
2895 highlightRegExp.lastIndex = 0;
2896 this.highlightMatch = highlightRegExp.exec(source);
2898 this.tiddler = tiddler;
2901 Wikifier.prototype.wikifyPlain = function()
2903 var e = createTiddlyElement(document.body,"div
");
2904 e.style.display = "none
";
2906 var text = getPlainText(e);
2911 Wikifier.prototype.subWikify = function(output,terminator)
2915 this.subWikifyTerm(output,new RegExp("(
" + terminator + ")
","mg
"));
2917 this.subWikifyUnterm(output);
2923 Wikifier.prototype.subWikifyUnterm = function(output)
2925 var oldOutput = this.output;
2926 this.output = output;
2927 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
2928 var formatterMatch = this.formatter.formatterRegExp.exec(this.source);
2929 while(formatterMatch) {
2930 // Output any text before the match
2931 if(formatterMatch.index > this.nextMatch)
2932 this.outputText(this.output,this.nextMatch,formatterMatch.index);
2933 // Set the match parameters for the handler
2934 this.matchStart = formatterMatch.index;
2935 this.matchLength = formatterMatch[0].length;
2936 this.matchText = formatterMatch[0];
2937 this.nextMatch = this.formatter.formatterRegExp.lastIndex;
2938 for(var t=1; t<formatterMatch.length; t++) {
2939 if(formatterMatch[t]) {
2940 this.formatter.formatters[t-1].handler(this);
2941 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
2945 formatterMatch = this.formatter.formatterRegExp.exec(this.source);
2947 if(this.nextMatch < this.source.length) {
2948 this.outputText(this.output,this.nextMatch,this.source.length);
2949 this.nextMatch = this.source.length;
2951 this.output = oldOutput;
2954 Wikifier.prototype.subWikifyTerm = function(output,terminatorRegExp)
2956 var oldOutput = this.output;
2957 this.output = output;
2958 terminatorRegExp.lastIndex = this.nextMatch;
2959 var terminatorMatch = terminatorRegExp.exec(this.source);
2960 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
2961 var formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
2962 while(terminatorMatch || formatterMatch) {
2963 if(terminatorMatch && (!formatterMatch || terminatorMatch.index <= formatterMatch.index)) {
2964 if(terminatorMatch.index > this.nextMatch)
2965 this.outputText(this.output,this.nextMatch,terminatorMatch.index);
2966 this.matchText = terminatorMatch[1];
2967 this.matchLength = terminatorMatch[1].length;
2968 this.matchStart = terminatorMatch.index;
2969 this.nextMatch = this.matchStart + this.matchLength;
2970 this.output = oldOutput;
2973 if(formatterMatch.index > this.nextMatch)
2974 this.outputText(this.output,this.nextMatch,formatterMatch.index);
2975 this.matchStart = formatterMatch.index;
2976 this.matchLength = formatterMatch[0].length;
2977 this.matchText = formatterMatch[0];
2978 this.nextMatch = this.formatter.formatterRegExp.lastIndex;
2979 for(var t=1; t<formatterMatch.length; t++) {
2980 if(formatterMatch[t]) {
2981 this.formatter.formatters[t-1].handler(this);
2982 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
2986 terminatorRegExp.lastIndex = this.nextMatch;
2987 terminatorMatch = terminatorRegExp.exec(this.source);
2988 formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
2990 if(this.nextMatch < this.source.length) {
2991 this.outputText(this.output,this.nextMatch,this.source.length);
2992 this.nextMatch = this.source.length;
2994 this.output = oldOutput;
2997 Wikifier.prototype.outputText = function(place,startPos,endPos)
2999 while(this.highlightMatch && (this.highlightRegExp.lastIndex > startPos) && (this.highlightMatch.index < endPos) && (startPos < endPos)) {
3000 if(this.highlightMatch.index > startPos) {
3001 createTiddlyText(place,this.source.substring(startPos,this.highlightMatch.index));
3002 startPos = this.highlightMatch.index;
3004 var highlightEnd = Math.min(this.highlightRegExp.lastIndex,endPos);
3005 var theHighlight = createTiddlyElement(place,"span
",null,"highlight
",this.source.substring(startPos,highlightEnd));
3006 startPos = highlightEnd;
3007 if(startPos >= this.highlightRegExp.lastIndex)
3008 this.highlightMatch = this.highlightRegExp.exec(this.source);
3010 if(startPos < endPos) {
3011 createTiddlyText(place,this.source.substring(startPos,endPos));
3016 //-- Macro definitions
3019 config.macros.today.handler = function(place,macroName,params)
3021 var now = new Date();
3022 var text = params[0] ? now.formatString(params[0].trim()) : now.toLocaleString();
3023 createTiddlyElement(place,"span
",null,null,text);
3026 config.macros.version.handler = function(place)
3028 createTiddlyElement(place,"span
",null,null,formatVersion());
3031 config.macros.list.handler = function(place,macroName,params)
3033 var type = params[0] || "all
";
3034 var list = document.createElement("ul
");
3035 place.appendChild(list);
3036 if(this[type].prompt)
3037 createTiddlyElement(list,"li
",null,"listTitle
",this[type].prompt);
3039 if(this[type].handler)
3040 results = this[type].handler(params);
3041 for(var t = 0; t < results.length; t++) {
3042 var li = document.createElement("li
");
3043 list.appendChild(li);
3044 createTiddlyLink(li,typeof results[t] == "string
" ? results[t] : results[t].title,true);
3048 config.macros.list.all.handler = function(params)
3050 return store.reverseLookup("tags
","excludeLists
",false,"title
");
3053 config.macros.list.missing.handler = function(params)
3055 return store.getMissingLinks();
3058 config.macros.list.orphans.handler = function(params)
3060 return store.getOrphans();
3063 config.macros.list.shadowed.handler = function(params)
3065 return store.getShadowed();
3068 config.macros.list.touched.handler = function(params)
3070 return store.getTouched();
3073 config.macros.list.filter.handler = function(params)
3075 var filter = params[1];
3078 var tiddlers = store.filterTiddlers(filter);
3079 for(var t=0; t<tiddlers.length; t++)
3080 results.push(tiddlers[t].title);
3085 config.macros.allTags.handler = function(place,macroName,params)
3087 var tags = store.getTags(params[0]);
3088 var ul = createTiddlyElement(place,"ul
");
3089 if(tags.length == 0)
3090 createTiddlyElement(ul,"li
",null,"listTitle
",this.noTags);
3091 for(var t=0; t<tags.length; t++) {
3092 var title = tags[t][0];
3093 var info = getTiddlyLinkInfo(title);
3094 var li = createTiddlyElement(ul,"li
");
3095 var btn = createTiddlyButton(li,title + " (
" + tags[t][1] + ")
",this.tooltip.format([title]),onClickTag,info.classes);
3096 btn.setAttribute("tag
",title);
3097 btn.setAttribute("refresh
","link
");
3098 btn.setAttribute("tiddlyLink
",title);
3102 config.macros.timeline.handler = function(place,macroName,params)
3104 var field = params[0] || "modified
";
3105 var tiddlers = store.reverseLookup("tags
","excludeLists
",false,field);
3107 var last = params[1] ? tiddlers.length-Math.min(tiddlers.length,parseInt(params[1])) : 0;
3108 var dateFormat = params[2] || this.dateFormat;
3109 for(var t=tiddlers.length-1; t>=last; t--) {
3110 var tiddler = tiddlers[t];
3111 var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
3112 if(theDay != lastDay) {
3113 var ul = document.createElement("ul
");
3114 place.appendChild(ul);
3115 createTiddlyElement(ul,"li
",null,"listTitle
",tiddler[field].formatString(dateFormat));
3118 createTiddlyElement(ul,"li
",null,"listLink
").appendChild(createTiddlyLink(place,tiddler.title,true));
3122 config.macros.tiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3124 params = paramString.parseParams("name
",null,true,false,true);
3125 var names = params[0]["name
"];
3126 var tiddlerName = names[0];
3127 var className = names[1] || null;
3128 var args = params[0]["with
"];
3129 var wrapper = createTiddlyElement(place,"span
",null,className);
3131 wrapper.setAttribute("refresh
","content
");
3132 wrapper.setAttribute("tiddler
",tiddlerName);
3134 var text = store.getTiddlerText(tiddlerName);
3136 var stack = config.macros.tiddler.tiddlerStack;
3137 if(stack.indexOf(tiddlerName) !== -1)
3139 stack.push(tiddlerName);
3141 var n = args ? Math.min(args.length,9) : 0;
3142 for(var i=0; i<n; i++) {
3143 var placeholderRE = new RegExp("\\$
" + (i + 1),"mg
");
3144 text = text.replace(placeholderRE,args[i]);
3146 config.macros.tiddler.renderText(wrapper,text,tiddlerName,params);
3153 config.macros.tiddler.renderText = function(place,text,tiddlerName,params)
3155 wikify(text,place,null,store.getTiddler(tiddlerName));
3158 config.macros.tiddler.tiddlerStack = [];
3160 config.macros.tag.handler = function(place,macroName,params)
3162 createTagButton(place,params[0],null,params[1],params[2]);
3165 config.macros.tags.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3167 params = paramString.parseParams("anon
",null,true,false,false);
3168 var ul = createTiddlyElement(place,"ul
");
3169 var title = getParam(params,"anon
","");
3170 if(title && store.tiddlerExists(title))
3171 tiddler = store.getTiddler(title);
3172 var sep = getParam(params,"sep
"," ");
3173 var lingo = config.views.wikified.tag;
3174 var prompt = tiddler.tags.length == 0 ? lingo.labelNoTags : lingo.labelTags;
3175 createTiddlyElement(ul,"li
",null,"listTitle
",prompt.format([tiddler.title]));
3176 for(var t=0; t<tiddler.tags.length; t++) {
3177 createTagButton(createTiddlyElement(ul,"li
"),tiddler.tags[t],tiddler.title);
3178 if(t<tiddler.tags.length-1)
3179 createTiddlyText(ul,sep);
3183 config.macros.tagging.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3185 params = paramString.parseParams("anon
",null,true,false,false);
3186 var ul = createTiddlyElement(place,"ul
");
3187 var title = getParam(params,"anon
","");
3188 if(title == "" && tiddler instanceof Tiddler)
3189 title = tiddler.title;
3190 var sep = getParam(params,"sep
"," ");
3191 ul.setAttribute("title
",this.tooltip.format([title]));
3192 var tagged = store.getTaggedTiddlers(title);
3193 var prompt = tagged.length == 0 ? this.labelNotTag : this.label;
3194 createTiddlyElement(ul,"li
",null,"listTitle
",prompt.format([title,tagged.length]));
3195 for(var t=0; t<tagged.length; t++) {
3196 createTiddlyLink(createTiddlyElement(ul,"li
"),tagged[t].title,true);
3197 if(t<tagged.length-1)
3198 createTiddlyText(ul,sep);
3202 config.macros.closeAll.handler = function(place)
3204 createTiddlyButton(place,this.label,this.prompt,this.onClick);
3207 config.macros.closeAll.onClick = function(e)
3209 story.closeAllTiddlers();
3213 config.macros.permaview.handler = function(place)
3215 createTiddlyButton(place,this.label,this.prompt,this.onClick);
3218 config.macros.permaview.onClick = function(e)
3224 config.macros.saveChanges.handler = function(place,macroName,params)
3227 createTiddlyButton(place,params[0] || this.label,params[1] || this.prompt,this.onClick,null,null,this.accessKey);
3230 config.macros.saveChanges.onClick = function(e)
3236 config.macros.slider.onClickSlider = function(ev)
3238 var e = ev || window.event;
3239 var n = this.nextSibling;
3240 var cookie = n.getAttribute("cookie
");
3241 var isOpen = n.style.display != "none
";
3242 if(config.options.chkAnimate && anim && typeof Slider == "function
")
3243 anim.startAnimating(new Slider(n,!isOpen,null,"none
"));
3245 n.style.display = isOpen ? "none
" : "block
";
3246 config.options[cookie] = !isOpen;
3247 saveOptionCookie(cookie);
3251 config.macros.slider.createSlider = function(place,cookie,title,tooltip)
3253 var c = cookie || "";
3254 var btn = createTiddlyButton(place,title,tooltip,this.onClickSlider);
3255 var panel = createTiddlyElement(null,"div
",null,"sliderPanel
");
3256 panel.setAttribute("cookie
",c);
3257 panel.style.display = config.options[c] ? "block
" : "none
";
3258 place.appendChild(panel);
3262 config.macros.slider.handler = function(place,macroName,params)
3264 var panel = this.createSlider(place,params[0],params[2],params[3]);
3265 var text = store.getTiddlerText(params[1]);
3266 panel.setAttribute("refresh
","content
");
3267 panel.setAttribute("tiddler
",params[1]);
3269 wikify(text,panel,null,store.getTiddler(params[1]));
3272 // <<gradient [[tiddler name]] vert|horiz rgb rgb rgb rgb... >>
3273 config.macros.gradient.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3275 var panel = wikifier ? createTiddlyElement(place,"div
",null,"gradient
") : place;
3276 panel.style.position = "relative
";
3277 panel.style.overflow = "hidden
";
3278 panel.style.zIndex = "0";
3280 var styles = config.formatterHelpers.inlineCssHelper(wikifier);
3281 config.formatterHelpers.applyCssHelper(panel,styles);
3283 params = paramString.parseParams("color
");
3284 var locolors = [], hicolors = [];
3285 for(var t=2; t<params.length; t++) {
3286 var c = new RGB(params[t].value);
3287 if(params[t].name == "snap
") {
3288 hicolors[hicolors.length-1] = c;
3294 drawGradient(panel,params[1].value != "vert
",locolors,hicolors);
3296 wikifier.subWikify(panel,">>");
3298 panel.style.height = "100%
";
3299 panel.style.width = "100%
";
3303 config.macros.message.handler = function(place,macroName,params)
3306 var names = params[0].split(".
");
3307 var lookupMessage = function(root,nameIndex) {
3308 if(names[nameIndex] in root) {
3309 if(nameIndex < names.length-1)
3310 return (lookupMessage(root[names[nameIndex]],nameIndex+1));
3312 return root[names[nameIndex]];
3316 var m = lookupMessage(config,0);
3318 m = lookupMessage(window,0);
3319 createTiddlyText(place,m.toString().format(params.splice(1)));
3324 config.macros.view.views = {
3325 text: function(value,place,params,wikifier,paramString,tiddler) {
3326 highlightify(value,place,highlightHack,tiddler);
3328 link: function(value,place,params,wikifier,paramString,tiddler) {
3329 createTiddlyLink(place,value,true);
3331 wikified: function(value,place,params,wikifier,paramString,tiddler) {
3333 value=params[2].unescapeLineBreaks().format([value]);
3334 wikify(value,place,highlightHack,tiddler);
3336 date: function(value,place,params,wikifier,paramString,tiddler) {
3337 value = Date.convertFromYYYYMMDDHHMM(value);
3338 createTiddlyText(place,value.formatString(params[2] ? params[2] : config.views.wikified.dateFormat));
3342 config.macros.view.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3344 if((tiddler instanceof Tiddler) && params[0]) {
3345 var value = store.getValue(tiddler,params[0]);
3347 var type = params[1] || config.macros.view.defaultView;
3348 var handler = config.macros.view.views[type];
3350 handler(value,place,params,wikifier,paramString,tiddler);
3355 config.macros.edit.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3357 var field = params[0];
3358 var rows = params[1] || 0;
3359 var defVal = params[2] || '';
3360 if((tiddler instanceof Tiddler) && field) {
3361 story.setDirty(tiddler.title,true);
3363 if(field != "text
" && !rows) {
3364 e = createTiddlyElement(null,"input
");
3365 if(tiddler.isReadOnly())
3366 e.setAttribute("readOnly
","readOnly
");
3367 e.setAttribute("edit
",field);
3368 e.setAttribute("type
","text
");
3369 e.value = store.getValue(tiddler,field) || defVal;
3370 e.setAttribute("size
","40");
3371 e.setAttribute("autocomplete
","off
");
3372 place.appendChild(e);
3374 var wrapper1 = createTiddlyElement(null,"fieldset
",null,"fieldsetFix
");
3375 var wrapper2 = createTiddlyElement(wrapper1,"div
");
3376 e = createTiddlyElement(wrapper2,"textarea
");
3377 if(tiddler.isReadOnly())
3378 e.setAttribute("readOnly
","readOnly
");
3379 e.value = v = store.getValue(tiddler,field) || defVal;
3381 var lines = v.match(/\n/mg);
3382 var maxLines = Math.max(parseInt(config.options.txtMaxEditRows),5);
3383 if(lines != null && lines.length > rows)
3384 rows = lines.length + 5;
3385 rows = Math.min(rows,maxLines);
3386 e.setAttribute("rows
",rows);
3387 e.setAttribute("edit
",field);
3388 place.appendChild(wrapper1);
3394 config.macros.tagChooser.onClick = function(ev)
3396 var e = ev || window.event;
3397 var lingo = config.views.editor.tagChooser;
3398 var popup = Popup.create(this);
3399 var tags = store.getTags("excludeLists
");
3400 if(tags.length == 0)
3401 createTiddlyText(createTiddlyElement(popup,"li
"),lingo.popupNone);
3402 for(var t=0; t<tags.length; t++) {
3403 var tag = createTiddlyButton(createTiddlyElement(popup,"li
"),tags[t][0],lingo.tagTooltip.format([tags[t][0]]),config.macros.tagChooser.onTagClick);
3404 tag.setAttribute("tag
",tags[t][0]);
3405 tag.setAttribute("tiddler
",this.getAttribute("tiddler
"));
3408 e.cancelBubble = true;
3409 if(e.stopPropagation) e.stopPropagation();
3413 config.macros.tagChooser.onTagClick = function(ev)
3415 var e = ev || window.event;
3416 if(e.metaKey || e.ctrlKey) stopEvent(e); //# keep popup open on CTRL-click
3417 var tag = this.getAttribute("tag
");
3418 var title = this.getAttribute("tiddler
");
3420 story.setTiddlerTag(title,tag,0);
3424 config.macros.tagChooser.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3426 if(tiddler instanceof Tiddler) {
3427 var lingo = config.views.editor.tagChooser;
3428 var btn = createTiddlyButton(place,lingo.text,lingo.tooltip,this.onClick);
3429 btn.setAttribute("tiddler
",tiddler.title);
3433 config.macros.refreshDisplay.handler = function(place)
3435 createTiddlyButton(place,this.label,this.prompt,this.onClick);
3438 config.macros.refreshDisplay.onClick = function(e)
3444 config.macros.annotations.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3446 var title = tiddler ? tiddler.title : null;
3447 var a = title ? config.annotations[title] : null;
3448 if(!tiddler || !title || !a)
3450 var text = a.format([title]);
3451 wikify(text,createTiddlyElement(place,"div
",null,"annotation
"),null,tiddler);
3455 //-- NewTiddler and NewJournal macros
3458 config.macros.newTiddler.createNewTiddlerButton = function(place,title,params,label,prompt,accessKey,newFocus,isJournal)
3461 for(var t=1; t<params.length; t++) {
3462 if((params[t].name == "anon
" && t != 1) || (params[t].name == "tag
"))
3463 tags.push(params[t].value);
3465 label = getParam(params,"label
",label);
3466 prompt = getParam(params,"prompt
",prompt);
3467 accessKey = getParam(params,"accessKey
",accessKey);
3468 newFocus = getParam(params,"focus
",newFocus);
3469 var customFields = getParam(params,"fields
","");
3470 if(!customFields && !store.isShadowTiddler(title))
3471 customFields = String.encodeHashMap(config.defaultCustomFields);
3472 var btn = createTiddlyButton(place,label,prompt,this.onClickNewTiddler,null,null,accessKey);
3473 btn.setAttribute("newTitle
",title);
3474 btn.setAttribute("isJournal
",isJournal ? "true
" : "false
");
3476 btn.setAttribute("params
",tags.join("|
"));
3477 btn.setAttribute("newFocus
",newFocus);
3478 btn.setAttribute("newTemplate
",getParam(params,"template
",DEFAULT_EDIT_TEMPLATE));
3479 if(customFields !== "")
3480 btn.setAttribute("customFields
",customFields);
3481 var text = getParam(params,"text
");
3482 if(text !== undefined)
3483 btn.setAttribute("newText
",text);
3487 config.macros.newTiddler.onClickNewTiddler = function()
3489 var title = this.getAttribute("newTitle
");
3490 if(this.getAttribute("isJournal
") == "true
") {
3491 title = new Date().formatString(title.trim());
3493 var params = this.getAttribute("params
");
3494 var tags = params ? params.split("|
") : [];
3495 var focus = this.getAttribute("newFocus
");
3496 var template = this.getAttribute("newTemplate
");
3497 var customFields = this.getAttribute("customFields
");
3498 if(!customFields && !store.isShadowTiddler(title))
3499 customFields = String.encodeHashMap(config.defaultCustomFields);
3500 story.displayTiddler(null,title,template,false,null,null);
3501 var tiddlerElem = story.getTiddler(title);
3503 story.addCustomFields(tiddlerElem,customFields);
3504 var text = this.getAttribute("newText
");
3505 if(typeof text == "string
")
3506 story.getTiddlerField(title,"text
").value = text.format([title]);
3507 for(var t=0;t<tags.length;t++)
3508 story.setTiddlerTag(title,tags[t],+1);
3509 story.focusTiddler(title,focus);
3513 config.macros.newTiddler.handler = function(place,macroName,params,wikifier,paramString)
3516 params = paramString.parseParams("anon
",null,true,false,false);
3517 var title = params[1] && params[1].name == "anon
" ? params[1].value : this.title;
3518 title = getParam(params,"title
",title);
3519 this.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"title
",false);
3523 config.macros.newJournal.handler = function(place,macroName,params,wikifier,paramString)
3526 params = paramString.parseParams("anon
",null,true,false,false);
3527 var title = params[1] && params[1].name == "anon
" ? params[1].value : config.macros.timeline.dateFormat;
3528 title = getParam(params,"title
",title);
3529 config.macros.newTiddler.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"text
",true);
3537 config.macros.search.handler = function(place,macroName,params)
3539 var searchTimeout = null;
3540 var btn = createTiddlyButton(place,this.label,this.prompt,this.onClick,"searchButton
");
3541 var txt = createTiddlyElement(place,"input
",null,"txtOptionInput searchField
");
3543 txt.value = params[0];
3544 txt.onkeyup = this.onKeyPress;
3545 txt.onfocus = this.onFocus;
3546 txt.setAttribute("size
",this.sizeTextbox);
3547 txt.setAttribute("accessKey
",this.accessKey);
3548 txt.setAttribute("autocomplete
","off
");
3549 txt.setAttribute("lastSearchText
","");
3550 if(config.browser.isSafari) {
3551 txt.setAttribute("type
","search
");
3552 txt.setAttribute("results
","5");
3554 txt.setAttribute("type
","text
");
3558 // Global because there's only ever one outstanding incremental search timer
3559 config.macros.search.timeout = null;
3561 config.macros.search.doSearch = function(txt)
3563 if(txt.value.length > 0) {
3564 story.search(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch);
3565 txt.setAttribute("lastSearchText
",txt.value);
3569 config.macros.search.onClick = function(e)
3571 config.macros.search.doSearch(this.nextSibling);
3575 config.macros.search.onKeyPress = function(ev)
3577 var e = ev || window.event;
3579 case 13: // Ctrl-Enter
3580 case 10: // Ctrl-Enter on IE PC
3581 config.macros.search.doSearch(this);
3588 if(config.options.chkIncrementalSearch) {
3589 if(this.value.length > 2) {
3590 if(this.value != this.getAttribute("lastSearchText
")) {
3591 if(config.macros.search.timeout)
3592 clearTimeout(config.macros.search.timeout);
3594 config.macros.search.timeout = setTimeout(function() {config.macros.search.doSearch(txt);},500);
3597 if(config.macros.search.timeout)
3598 clearTimeout(config.macros.search.timeout);
3603 config.macros.search.onFocus = function(e)
3612 config.macros.tabs.handler = function(place,macroName,params)
3614 var cookie = params[0];
3615 var numTabs = (params.length-1)/3;
3616 var wrapper = createTiddlyElement(null,"div
",null,"tabsetWrapper
" + cookie);
3617 var tabset = createTiddlyElement(wrapper,"div
",null,"tabset
");
3618 tabset.setAttribute("cookie
",cookie);
3619 var validTab = false;
3620 for(var t=0; t<numTabs; t++) {
3621 var label = params[t*3+1];
3622 var prompt = params[t*3+2];
3623 var content = params[t*3+3];
3624 var tab = createTiddlyButton(tabset,label,prompt,this.onClickTab,"tab tabUnselected
");
3625 tab.setAttribute("tab
",label);
3626 tab.setAttribute("content
",content);
3628 if(config.options[cookie] == label)
3632 config.options[cookie] = params[1];
3633 place.appendChild(wrapper);
3634 this.switchTab(tabset,config.options[cookie]);
3637 config.macros.tabs.onClickTab = function(e)
3639 config.macros.tabs.switchTab(this.parentNode,this.getAttribute("tab
"));
3643 config.macros.tabs.switchTab = function(tabset,tab)
3645 var cookie = tabset.getAttribute("cookie
");
3647 var nodes = tabset.childNodes;
3648 for(var t=0; t<nodes.length; t++) {
3649 if(nodes[t].getAttribute && nodes[t].getAttribute("tab
") == tab) {
3651 theTab.className = "tab tabSelected
";
3653 nodes[t].className = "tab tabUnselected
";
3657 if(tabset.nextSibling && tabset.nextSibling.className == "tabContents
")
3658 removeNode(tabset.nextSibling);
3659 var tabContent = createTiddlyElement(null,"div
",null,"tabContents
");
3660 tabset.parentNode.insertBefore(tabContent,tabset.nextSibling);
3661 var contentTitle = theTab.getAttribute("content
");
3662 wikify(store.getTiddlerText(contentTitle),tabContent,null,store.getTiddler(contentTitle));
3664 config.options[cookie] = tab;
3665 saveOptionCookie(cookie);
3671 //-- Tiddler toolbar
3674 // Create a toolbar command button
3675 config.macros.toolbar.createCommand = function(place,commandName,tiddler,className)
3677 if(typeof commandName != "string
") {
3679 for(var t in config.commands) {
3680 if(config.commands[t] == commandName)
3685 if((tiddler instanceof Tiddler) && (typeof commandName == "string
")) {
3686 var command = config.commands[commandName];
3687 if(command.isEnabled ? command.isEnabled(tiddler) : this.isCommandEnabled(command,tiddler)) {
3688 var text = command.getText ? command.getText(tiddler) : this.getCommandText(command,tiddler);
3689 var tooltip = command.getTooltip ? command.getTooltip(tiddler) : this.getCommandTooltip(command,tiddler);
3691 switch(command.type) {
3693 cmd = this.onClickPopup;
3697 cmd = this.onClickCommand;
3700 var btn = createTiddlyButton(null,text,tooltip,cmd);
3701 btn.setAttribute("commandName
",commandName);
3702 btn.setAttribute("tiddler
",tiddler.title);
3704 addClass(btn,className);
3705 place.appendChild(btn);
3710 config.macros.toolbar.isCommandEnabled = function(command,tiddler)
3712 var title = tiddler.title;
3713 var ro = tiddler.isReadOnly();
3714 var shadow = store.isShadowTiddler(title) && !store.tiddlerExists(title);
3715 return (!ro || (ro && !command.hideReadOnly)) && !(shadow && command.hideShadow);
3718 config.macros.toolbar.getCommandText = function(command,tiddler)
3720 return tiddler.isReadOnly() && command.readOnlyText || command.text;
3723 config.macros.toolbar.getCommandTooltip = function(command,tiddler)
3725 return tiddler.isReadOnly() && command.readOnlyTooltip || command.tooltip;
3728 config.macros.toolbar.onClickCommand = function(ev)
3730 var e = ev || window.event;
3731 e.cancelBubble = true;
3732 if(e.stopPropagation) e.stopPropagation();
3733 var command = config.commands[this.getAttribute("commandName
")];
3734 return command.handler(e,this,this.getAttribute("tiddler
"));
3737 config.macros.toolbar.onClickPopup = function(ev)
3739 var e = ev || window.event;
3740 e.cancelBubble = true;
3741 if(e.stopPropagation) e.stopPropagation();
3742 var popup = Popup.create(this);
3743 var command = config.commands[this.getAttribute("commandName
")];
3744 var title = this.getAttribute("tiddler
");
3745 var tiddler = store.fetchTiddler(title);
3746 popup.setAttribute("tiddler
",title);
3747 command.handlePopup(popup,title);
3752 // Invoke the first command encountered from a given place that is tagged with a specified class
3753 config.macros.toolbar.invokeCommand = function(place,className,event)
3755 var children = place.getElementsByTagName("a
");
3756 for(var t=0; t<children.length; t++) {
3757 var c = children[t];
3758 if(hasClass(c,className) && c.getAttribute && c.getAttribute("commandName
")) {
3759 if(c.onclick instanceof Function)
3760 c.onclick.call(c,event);
3766 config.macros.toolbar.onClickMore = function(ev)
3768 var e = this.nextSibling;
3769 e.style.display = "inline
";
3774 config.macros.toolbar.handler = function(place,macroName,params,wikifier,paramString,tiddler)
3776 for(var t=0; t<params.length; t++) {
3780 var btn = createTiddlyButton(place,this.moreLabel,this.morePrompt,config.macros.toolbar.onClickMore);
3781 addClass(btn,"moreCommand
");
3782 var e = createTiddlyElement(place,"span
",null,"moreCommand
");
3783 e.style.display = "none
";
3788 switch(c.substr(0,1)) {
3790 className = "defaultCommand
";
3794 className = "cancelCommand
";
3798 if(c in config.commands)
3799 this.createCommand(place,c,tiddler,className);
3806 //-- Menu and toolbar commands
3809 config.commands.closeTiddler.handler = function(event,src,title)
3811 if(story.isDirty(title) && !readOnly) {
3812 if(!confirm(config.commands.cancelTiddler.warning.format([title])))
3815 story.setDirty(title,false);
3816 story.closeTiddler(title,true);
3820 config.commands.closeOthers.handler = function(event,src,title)
3822 story.closeAllTiddlers(title);
3826 config.commands.editTiddler.handler = function(event,src,title)
3829 var tiddlerElem = story.getTiddler(title);
3830 var fields = tiddlerElem.getAttribute("tiddlyFields
");
3831 story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE,false,null,fields);
3832 story.focusTiddler(title,config.options.txtEditorFocus||"text
");
3836 config.commands.saveTiddler.handler = function(event,src,title)
3838 var newTitle = story.saveTiddler(title,event.shiftKey);
3840 story.displayTiddler(null,newTitle);
3844 config.commands.cancelTiddler.handler = function(event,src,title)
3846 if(story.hasChanges(title) && !readOnly) {
3847 if(!confirm(this.warning.format([title])))
3850 story.setDirty(title,false);
3851 story.displayTiddler(null,title);
3855 config.commands.deleteTiddler.handler = function(event,src,title)
3857 var deleteIt = true;
3858 if(config.options.chkConfirmDelete)
3859 deleteIt = confirm(this.warning.format([title]));
3861 store.removeTiddler(title);
3862 story.closeTiddler(title,true);
3868 config.commands.permalink.handler = function(event,src,title)
3870 var t = encodeURIComponent(String.encodeTiddlyLink(title));
3871 if(window.location.hash != t)
3872 window.location.hash = t;
3876 config.commands.references.handlePopup = function(popup,title)
3878 var references = store.getReferringTiddlers(title);
3880 for(var r=0; r<references.length; r++) {
3881 if(references[r].title != title && !references[r].isTagged("excludeLists
")) {
3882 createTiddlyLink(createTiddlyElement(popup,"li
"),references[r].title,true);
3887 createTiddlyText(createTiddlyElement(popup,"li
",null,"disabled
"),this.popupNone);
3890 config.commands.jump.handlePopup = function(popup,title)
3892 story.forEachTiddler(function(title,element) {
3893 createTiddlyLink(createTiddlyElement(popup,"li
"),title,true,null,false,null,true);
3897 config.commands.syncing.handlePopup = function(popup,title)
3899 var tiddler = store.fetchTiddler(title);
3902 var serverType = tiddler.getServerType();
3903 var serverHost = tiddler.fields['server.host'];
3904 var serverWorkspace = tiddler.fields['server.workspace'];
3905 if(!serverWorkspace)
3906 serverWorkspace = "";
3908 var e = createTiddlyElement(popup,"li
",null,"popupMessage
");
3909 e.innerHTML = config.commands.syncing.currentlySyncing.format([serverType,serverHost,serverWorkspace]);
3911 createTiddlyElement(popup,"li
",null,"popupMessage
",config.commands.syncing.notCurrentlySyncing);
3914 createTiddlyElement(createTiddlyElement(popup,"li
",null,"listBreak
"),"div
");
3915 var btn = createTiddlyButton(createTiddlyElement(popup,"li
"),this.captionUnSync,null,config.commands.syncing.onChooseServer);
3916 btn.setAttribute("tiddler
",title);
3917 btn.setAttribute("server.type
","");
3919 createTiddlyElement(createTiddlyElement(popup,"li
",null,"listBreak
"),"div
");
3920 createTiddlyElement(popup,"li
",null,"popupMessage
",config.commands.syncing.chooseServer);
3921 var feeds = store.getTaggedTiddlers("systemServer
","title
");
3922 for(var t=0; t<feeds.length; t++) {
3924 var feedServerType = store.getTiddlerSlice(f.title,"Type
");
3926 feedServerType = "file
";
3927 var feedServerHost = store.getTiddlerSlice(f.title,"URL
");
3929 feedServerHost = "";
3930 var feedServerWorkspace = store.getTiddlerSlice(f.title,"Workspace
");
3931 if(!feedServerWorkspace)
3932 feedServerWorkspace = "";
3933 var caption = f.title;
3934 if(serverType == feedServerType && serverHost == feedServerHost && serverWorkspace == feedServerWorkspace) {
3935 caption = config.commands.syncing.currServerMarker + caption;
3937 caption = config.commands.syncing.notCurrServerMarker + caption;
3939 btn = createTiddlyButton(createTiddlyElement(popup,"li
"),caption,null,config.commands.syncing.onChooseServer);
3940 btn.setAttribute("tiddler
",title);
3941 btn.setAttribute("server.type
",feedServerType);
3942 btn.setAttribute("server.host
",feedServerHost);
3943 btn.setAttribute("server.workspace
",feedServerWorkspace);
3947 config.commands.syncing.onChooseServer = function(e)
3949 var tiddler = this.getAttribute("tiddler
");
3950 var serverType = this.getAttribute("server.type
");
3952 store.addTiddlerFields(tiddler,{
3953 "server.type
": serverType,
3954 "server.host
": this.getAttribute("server.host
"),
3955 "server.workspace
": this.getAttribute("server.workspace
")
3958 store.setValue(tiddler,"server
",null);
3963 config.commands.fields.handlePopup = function(popup,title)
3965 var tiddler = store.fetchTiddler(title);
3969 store.forEachField(tiddler,function(tiddler,fieldName,value) {fields[fieldName] = value;},true);
3971 for(var t in fields) {
3972 items.push({field: t,value: fields[t]});
3974 items.sort(function(a,b) {return a.field < b.field ? -1 : (a.field == b.field ? 0 : +1);});
3975 if(items.length > 0)
3976 ListView.create(popup,items,this.listViewTemplate);
3978 createTiddlyElement(popup,"div
",null,null,this.emptyText);
3982 //-- Tiddler() object
3985 function Tiddler(title)
3989 this.modifier = null;
3990 this.created = new Date();
3991 this.modified = this.created;
3993 this.linksUpdated = false;
3999 Tiddler.prototype.getLinks = function()
4001 if(this.linksUpdated==false)
4006 // Returns the fields that are inherited in string field:"value
" field2:"value2
" format
4007 Tiddler.prototype.getInheritedFields = function()
4010 for(var i in this.fields) {
4011 if(i=="server.host
" || i=="server.workspace
" || i=="wikiformat
"|| i=="server.type
") {
4012 f[i] = this.fields[i];
4015 return String.encodeHashMap(f);
4018 // Increment the changeCount of a tiddler
4019 Tiddler.prototype.incChangeCount = function()
4021 var c = this.fields['changecount'];
4022 c = c ? parseInt(c,10) : 0;
4023 this.fields['changecount'] = String(c+1);
4026 // Clear the changeCount of a tiddler
4027 Tiddler.prototype.clearChangeCount = function()
4029 if(this.fields['changecount']) {
4030 delete this.fields['changecount'];
4034 Tiddler.prototype.doNotSave = function()
4036 return this.fields['doNotSave'];
4039 // Returns true if the tiddler has been updated since the tiddler was created or downloaded
4040 Tiddler.prototype.isTouched = function()
4042 var changeCount = this.fields['changecount'];
4043 if(changeCount === undefined)
4045 return changeCount > 0;
4048 // Return the tiddler as an RSS item
4049 Tiddler.prototype.toRssItem = function(uri)
4052 s.push("<title" + ">" + this.title.htmlEncode() + "</title" + ">");
4053 s.push("<description>" + wikifyStatic(this.text,null,this).htmlEncode() + "</description>");
4054 for(var t=0; t<this.tags.length; t++)
4055 s.push("<category>" + this.tags[t] + "</category>");
4056 s.push("<link>" + uri + "#
" + encodeURIComponent(String.encodeTiddlyLink(this.title)) + "</link>");
4057 s.push("<pubDate>" + this.modified.toGMTString() + "</pubDate>");
4058 return s.join("\n
");
4061 // Format the text for storage in an RSS item
4062 Tiddler.prototype.saveToRss = function(uri)
4064 return "<item>\n
" + this.toRssItem(uri) + "\n
</item>";
4067 // Change the text and other attributes of a tiddler
4068 Tiddler.prototype.set = function(title,text,modifier,modified,tags,created,fields)
4070 this.assign(title,text,modifier,modified,tags,created,fields);
4075 // Change the text and other attributes of a tiddler without triggered a tiddler.changed() call
4076 Tiddler.prototype.assign = function(title,text,modifier,modified,tags,created,fields)
4078 if(title != undefined)
4080 if(text != undefined)
4082 if(modifier != undefined)
4083 this.modifier = modifier;
4084 if(modified != undefined)
4085 this.modified = modified;
4086 if(created != undefined)
4087 this.created = created;
4088 if(fields != undefined)
4089 this.fields = fields;
4090 if(tags != undefined)
4091 this.tags = (typeof tags == "string
") ? tags.readBracketedList() : tags;
4092 else if(this.tags == undefined)
4097 // Get the tags for a tiddler as a string (space delimited, using [[brackets]] for tags containing spaces)
4098 Tiddler.prototype.getTags = function()
4100 return String.encodeTiddlyLinkList(this.tags);
4103 // Test if a tiddler carries a tag
4104 Tiddler.prototype.isTagged = function(tag)
4106 return this.tags.indexOf(tag) != -1;
4109 // Static method to convert "\n
" to newlines, "\s
" to "\
"
4110 Tiddler.unescapeLineBreaks = function(text)
4112 return text ? text.unescapeLineBreaks() : "";
4115 // Convert newlines to "\n
", "\
" to "\s
"
4116 Tiddler.prototype.escapeLineBreaks = function()
4118 return this.text.escapeLineBreaks();
4121 // Updates the secondary information (like links[] array) after a change to a tiddler
4122 Tiddler.prototype.changed = function()
4125 var t = this.autoLinkWikiWords() ? 0 : 1;
4126 var tiddlerLinkRegExp = t==0 ? config.textPrimitives.tiddlerAnyLinkRegExp : config.textPrimitives.tiddlerForcedLinkRegExp;
4127 tiddlerLinkRegExp.lastIndex = 0;
4128 var formatMatch = tiddlerLinkRegExp.exec(this.text);
4129 while(formatMatch) {
4130 var lastIndex = tiddlerLinkRegExp.lastIndex;
4131 if(t==0 && formatMatch[1] && formatMatch[1] != this.title) {
4133 if(formatMatch.index > 0) {
4134 var preRegExp = new RegExp(config.textPrimitives.unWikiLink+"|
"+config.textPrimitives.anyLetter,"mg
");
4135 preRegExp.lastIndex = formatMatch.index-1;
4136 var preMatch = preRegExp.exec(this.text);
4137 if(preMatch.index != formatMatch.index-1)
4138 this.links.pushUnique(formatMatch[1]);
4140 this.links.pushUnique(formatMatch[1]);
4143 else if(formatMatch[2-t] && !config.formatterHelpers.isExternalLink(formatMatch[3-t])) // titledBrackettedLink
4144 this.links.pushUnique(formatMatch[3-t]);
4145 else if(formatMatch[4-t] && formatMatch[4-t] != this.title) // brackettedLink
4146 this.links.pushUnique(formatMatch[4-t]);
4147 tiddlerLinkRegExp.lastIndex = lastIndex;
4148 formatMatch = tiddlerLinkRegExp.exec(this.text);
4150 this.linksUpdated = true;
4153 Tiddler.prototype.getSubtitle = function()
4155 var modifier = this.modifier;
4157 modifier = config.messages.subtitleUnknown;
4158 var modified = this.modified;
4160 modified = modified.toLocaleString();
4162 modified = config.messages.subtitleUnknown;
4163 return config.messages.tiddlerLinkTooltip.format([this.title,modifier,modified]);
4166 Tiddler.prototype.isReadOnly = function()
4171 Tiddler.prototype.autoLinkWikiWords = function()
4173 return !(this.isTagged("systemConfig
") || this.isTagged("excludeMissing
"));
4176 Tiddler.prototype.generateFingerprint = function()
4178 return "0x
" + Crypto.hexSha1Str(this.text);
4181 Tiddler.prototype.getServerType = function()
4183 var serverType = null;
4184 if(this.fields['server.type'])
4185 serverType = this.fields['server.type'];
4187 serverType = this.fields['wikiformat'];
4188 if(serverType && !config.adaptors[serverType])
4193 Tiddler.prototype.getAdaptor = function()
4195 var serverType = this.getServerType();
4196 return serverType ? new config.adaptors[serverType]() : null;
4200 //-- TiddlyWiki() object contains Tiddler()s
4203 function TiddlyWiki()
4205 var tiddlers = {}; // Hashmap by name of tiddlers
4206 this.tiddlersUpdated = false;
4207 this.namedNotifications = []; // Array of {name:,notify:} of notification functions
4208 this.notificationLevel = 0;
4209 this.slices = {}; // map tiddlerName->(map sliceName->sliceValue). Lazy.
4210 this.clear = function() {
4212 this.setDirty(false);
4214 this.fetchTiddler = function(title) {
4215 var t = tiddlers[title];
4216 return t instanceof Tiddler ? t : null;
4218 this.deleteTiddler = function(title) {
4219 delete this.slices[title];
4220 delete tiddlers[title];
4222 this.addTiddler = function(tiddler) {
4223 delete this.slices[tiddler.title];
4224 tiddlers[tiddler.title] = tiddler;
4226 this.forEachTiddler = function(callback) {
4227 for(var t in tiddlers) {
4228 var tiddler = tiddlers[t];
4229 if(tiddler instanceof Tiddler)
4230 callback.call(this,t,tiddler);
4235 TiddlyWiki.prototype.setDirty = function(dirty)
4240 TiddlyWiki.prototype.isDirty = function()
4245 TiddlyWiki.prototype.tiddlerExists = function(title)
4247 var t = this.fetchTiddler(title);
4248 return t != undefined;
4251 TiddlyWiki.prototype.isShadowTiddler = function(title)
4253 return typeof config.shadowTiddlers[title] == "string
";
4256 TiddlyWiki.prototype.createTiddler = function(title)
4258 var tiddler = this.fetchTiddler(title);
4260 tiddler = new Tiddler(title);
4261 this.addTiddler(tiddler);
4262 this.setDirty(true);
4267 TiddlyWiki.prototype.getTiddler = function(title)
4269 var t = this.fetchTiddler(title);
4276 TiddlyWiki.prototype.getTiddlerText = function(title,defaultText)
4280 var pos = title.indexOf(config.textPrimitives.sectionSeparator);
4283 section = title.substr(pos + config.textPrimitives.sectionSeparator.length);
4284 title = title.substr(0,pos);
4286 pos = title.indexOf(config.textPrimitives.sliceSeparator);
4288 var slice = this.getTiddlerSlice(title.substr(0,pos),title.substr(pos + config.textPrimitives.sliceSeparator.length));
4292 var tiddler = this.fetchTiddler(title);
4295 return tiddler.text;
4296 var re = new RegExp("(^!{
1,
6}
" + section.escapeRegExp() + "[ \t]*\n)
","mg
");
4298 var match = re.exec(tiddler.text);
4300 var t = tiddler.text.substr(match.index+match[1].length);
4303 match = re2.exec(t); //# search for the next heading
4305 t = t.substr(0,match.index-1);//# don't include final \n
4310 if(this.isShadowTiddler(title))
4311 return config.shadowTiddlers[title];
4312 if(defaultText != undefined)
4317 TiddlyWiki.prototype.getRecursiveTiddlerText = function(title,defaultText,depth)
4319 var bracketRegExp = new RegExp("(?:\\[\\[([^\\]]+)\\]\\])
","mg
");
4320 var text = this.getTiddlerText(title,null);
4326 var match = bracketRegExp.exec(text);
4328 textOut.push(text.substr(lastPos,match.index-lastPos));
4331 textOut.push(match[1]);
4333 textOut.push(this.getRecursiveTiddlerText(match[1],"[[
" + match[1] + "]]
",depth-1));
4335 lastPos = match.index + match[0].length;
4337 textOut.push(text.substr(lastPos));
4340 return textOut.join("");
4343 TiddlyWiki.prototype.slicesRE = /(?:^([\'\/]{0,2})~?([\.\w]+)\:\1\s*([^\n]+)\s*$)|(?:^\|([\'\/]{0,2})~?([\.\w]+)\:?\4\|\s*([^\|\n]+)\s*\|$)/gm;
4346 TiddlyWiki.prototype.calcAllSlices = function(title)
4349 var text = this.getTiddlerText(title,"");
4350 this.slicesRE.lastIndex = 0;
4351 var m = this.slicesRE.exec(text);
4354 slices[m[2]] = m[3];
4356 slices[m[5]] = m[6];
4357 m = this.slicesRE.exec(text);
4362 // Returns the slice of text of the given name
4363 TiddlyWiki.prototype.getTiddlerSlice = function(title,sliceName)
4365 var slices = this.slices[title];
4367 slices = this.calcAllSlices(title);
4368 this.slices[title] = slices;
4370 return slices[sliceName];
4373 // Build an hashmap of the specified named slices of a tiddler
4374 TiddlyWiki.prototype.getTiddlerSlices = function(title,sliceNames)
4377 for(var t=0; t<sliceNames.length; t++) {
4378 var slice = this.getTiddlerSlice(title,sliceNames[t]);
4380 r[sliceNames[t]] = slice;
4385 TiddlyWiki.prototype.suspendNotifications = function()
4387 this.notificationLevel--;
4390 TiddlyWiki.prototype.resumeNotifications = function()
4392 this.notificationLevel++;
4395 // Invoke the notification handlers for a particular tiddler
4396 TiddlyWiki.prototype.notify = function(title,doBlanket)
4398 if(!this.notificationLevel) {
4399 for(var t=0; t<this.namedNotifications.length; t++) {
4400 var n = this.namedNotifications[t];
4401 if((n.name == null && doBlanket) || (n.name == title))
4407 // Invoke the notification handlers for all tiddlers
4408 TiddlyWiki.prototype.notifyAll = function()
4410 if(!this.notificationLevel) {
4411 for(var t=0; t<this.namedNotifications.length; t++) {
4412 var n = this.namedNotifications[t];
4419 // Add a notification handler to a tiddler
4420 TiddlyWiki.prototype.addNotification = function(title,fn)
4422 for(var i=0; i<this.namedNotifications.length; i++) {
4423 if((this.namedNotifications[i].name == title) && (this.namedNotifications[i].notify == fn))
4426 this.namedNotifications.push({name: title, notify: fn});
4430 TiddlyWiki.prototype.removeTiddler = function(title)
4432 var tiddler = this.fetchTiddler(title);
4434 this.deleteTiddler(title);
4435 this.notify(title,true);
4436 this.setDirty(true);
4440 // Reset the sync status of a freshly synced tiddler
4441 TiddlyWiki.prototype.resetTiddler = function(title)
4443 var tiddler = this.fetchTiddler(title);
4445 tiddler.clearChangeCount();
4446 this.notify(title,true);
4447 this.setDirty(true);
4451 TiddlyWiki.prototype.setTiddlerTag = function(title,status,tag)
4453 var tiddler = this.fetchTiddler(title);
4455 var t = tiddler.tags.indexOf(tag);
4457 tiddler.tags.splice(t,1);
4459 tiddler.tags.push(tag);
4461 tiddler.incChangeCount(title);
4462 this.notify(title,true);
4463 this.setDirty(true);
4467 TiddlyWiki.prototype.addTiddlerFields = function(title,fields)
4469 var tiddler = this.fetchTiddler(title);
4472 merge(tiddler.fields,fields);
4474 tiddler.incChangeCount(title);
4475 this.notify(title,true);
4476 this.setDirty(true);
4479 TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created)
4481 var tiddler = this.fetchTiddler(title);
4483 created = created || tiddler.created; // Preserve created date
4484 this.deleteTiddler(title);
4486 created = created || modified;
4487 tiddler = new Tiddler();
4489 tiddler.set(newTitle,newBody,modifier,modified,tags,created,fields);
4490 this.addTiddler(tiddler);
4491 if(clearChangeCount)
4492 tiddler.clearChangeCount();
4494 tiddler.incChangeCount();
4495 if(title != newTitle)
4496 this.notify(title,true);
4497 this.notify(newTitle,true);
4498 this.setDirty(true);
4502 TiddlyWiki.prototype.incChangeCount = function(title)
4504 var tiddler = this.fetchTiddler(title);
4506 tiddler.incChangeCount();
4509 TiddlyWiki.prototype.getLoader = function()
4512 this.loader = new TW21Loader();
4516 TiddlyWiki.prototype.getSaver = function()
4519 this.saver = new TW21Saver();
4523 // Return all tiddlers formatted as an HTML string
4524 TiddlyWiki.prototype.allTiddlersAsHtml = function()
4526 return this.getSaver().externalize(store);
4529 // Load contents of a TiddlyWiki from an HTML DIV
4530 TiddlyWiki.prototype.loadFromDiv = function(src,idPrefix,noUpdate)
4532 this.idPrefix = idPrefix;
4533 var storeElem = (typeof src == "string
") ? document.getElementById(src) : src;
4536 var tiddlers = this.getLoader().loadTiddlers(this,storeElem.childNodes);
4537 this.setDirty(false);
4539 for(var i = 0;i<tiddlers.length; i++)
4540 tiddlers[i].changed();
4544 // Load contents of a TiddlyWiki from a string
4545 // Returns null if there's an error
4546 TiddlyWiki.prototype.importTiddlyWiki = function(text)
4548 var posDiv = locateStoreArea(text);
4551 var content = "<
" + "html
><
" + "body
>" + text.substring(posDiv[0],posDiv[1] + endSaveArea.length) + "<
" + "/body
><
" + "/html
>";
4552 // Create the iframe
4553 var iframe = document.createElement("iframe
");
4554 iframe.style.display = "none
";
4555 document.body.appendChild(iframe);
4556 var doc = iframe.document;
4557 if(iframe.contentDocument)
4558 doc = iframe.contentDocument; // For NS6
4559 else if(iframe.contentWindow)
4560 doc = iframe.contentWindow.document; // For IE5.5 and IE6
4561 // Put the content in the iframe
4563 doc.writeln(content);
4565 // Load the content into a TiddlyWiki() object
4566 var storeArea = doc.getElementById("storeArea
");
4567 this.loadFromDiv(storeArea,"store
");
4568 // Get rid of the iframe
4569 iframe.parentNode.removeChild(iframe);
4573 TiddlyWiki.prototype.updateTiddlers = function()
4575 this.tiddlersUpdated = true;
4576 this.forEachTiddler(function(title,tiddler) {
4581 // Return an array of tiddlers matching a search regular expression
4582 TiddlyWiki.prototype.search = function(searchRegExp,sortField,excludeTag,match)
4584 var candidates = this.reverseLookup("tags
",excludeTag,!!match);
4586 for(var t=0; t<candidates.length; t++) {
4587 if((candidates[t].title.search(searchRegExp) != -1) || (candidates[t].text.search(searchRegExp) != -1))
4588 results.push(candidates[t]);
4591 sortField = "title
";
4592 results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
4596 // Returns a list of all tags in use
4597 // excludeTag - if present, excludes tags that are themselves tagged with excludeTag
4598 // Returns an array of arrays where [tag][0] is the name of the tag and [tag][1] is the number of occurances
4599 TiddlyWiki.prototype.getTags = function(excludeTag)
4602 this.forEachTiddler(function(title,tiddler) {
4603 for(var g=0; g<tiddler.tags.length; g++) {
4604 var tag = tiddler.tags[g];
4606 for(var c=0; c<results.length; c++) {
4607 if(results[c][0] == tag) {
4612 if(n && excludeTag) {
4613 var t = this.fetchTiddler(tag);
4614 if(t && t.isTagged(excludeTag))
4618 results.push([tag,1]);
4621 results.sort(function(a,b) {return a[0].toLowerCase() < b[0].toLowerCase() ? -1 : (a[0].toLowerCase() == b[0].toLowerCase() ? 0 : +1);});
4625 // Return an array of the tiddlers that are tagged with a given tag
4626 TiddlyWiki.prototype.getTaggedTiddlers = function(tag,sortField)
4628 return this.reverseLookup("tags
",tag,true,sortField);
4631 // Return an array of the tiddlers that link to a given tiddler
4632 TiddlyWiki.prototype.getReferringTiddlers = function(title,unusedParameter,sortField)
4634 if(!this.tiddlersUpdated)
4635 this.updateTiddlers();
4636 return this.reverseLookup("links
",title,true,sortField);
4639 // Return an array of the tiddlers that do or do not have a specified entry in the specified storage array (ie, "links
" or "tags
")
4640 // lookupMatch == true to match tiddlers, false to exclude tiddlers
4641 TiddlyWiki.prototype.reverseLookup = function(lookupField,lookupValue,lookupMatch,sortField)
4644 this.forEachTiddler(function(title,tiddler) {
4645 var f = !lookupMatch;
4646 for(var lookup=0; lookup<tiddler[lookupField].length; lookup++) {
4647 if(tiddler[lookupField][lookup] == lookupValue)
4651 results.push(tiddler);
4654 sortField = "title
";
4655 results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
4659 // Return the tiddlers as a sorted array
4660 TiddlyWiki.prototype.getTiddlers = function(field,excludeTag)
4663 this.forEachTiddler(function(title,tiddler) {
4664 if(excludeTag == undefined || !tiddler.isTagged(excludeTag))
4665 results.push(tiddler);
4668 results.sort(function(a,b) {return a[field] < b[field] ? -1 : (a[field] == b[field] ? 0 : +1);});
4672 // Return array of names of tiddlers that are referred to but not defined
4673 TiddlyWiki.prototype.getMissingLinks = function(sortField)
4675 if(!this.tiddlersUpdated)
4676 this.updateTiddlers();
4678 this.forEachTiddler(function (title,tiddler) {
4679 if(tiddler.isTagged("excludeMissing
") || tiddler.isTagged("systemConfig
"))
4681 for(var n=0; n<tiddler.links.length;n++) {
4682 var link = tiddler.links[n];
4683 if(this.fetchTiddler(link) == null && !this.isShadowTiddler(link))
4684 results.pushUnique(link);
4691 // Return an array of names of tiddlers that are defined but not referred to
4692 TiddlyWiki.prototype.getOrphans = function()
4695 this.forEachTiddler(function (title,tiddler) {
4696 if(this.getReferringTiddlers(title).length == 0 && !tiddler.isTagged("excludeLists
"))
4697 results.push(title);
4703 // Return an array of names of all the shadow tiddlers
4704 TiddlyWiki.prototype.getShadowed = function()
4707 for(var t in config.shadowTiddlers) {
4708 if(typeof config.shadowTiddlers[t] == "string
")
4715 // Return an array of tiddlers that have been touched since they were downloaded or created
4716 TiddlyWiki.prototype.getTouched = function()
4719 this.forEachTiddler(function(title,tiddler) {
4720 if(tiddler.isTouched())
4721 results.push(tiddler);
4727 // Resolves a Tiddler reference or tiddler title into a Tiddler object, or null if it doesn't exist
4728 TiddlyWiki.prototype.resolveTiddler = function(tiddler)
4730 var t = (typeof tiddler == 'string') ? this.getTiddler(tiddler) : tiddler;
4731 return t instanceof Tiddler ? t : null;
4734 // Filter a list of tiddlers
4735 TiddlyWiki.prototype.filterTiddlers = function(filter)
4740 var re = /([^\s\[\]]+)|(?:\[([ \w]+)\[([^\]]+)\]\])|(?:\[\[([^\]]+)\]\])/mg;
4741 var match = re.exec(filter);
4743 if(match[1] || match[4]) {
4744 var title = match[1] || match[4];
4745 tiddler = this.fetchTiddler(title);
4747 results.pushUnique(tiddler);
4748 } else if(this.isShadowTiddler(title)) {
4749 tiddler = new Tiddler();
4750 tiddler.set(title,this.getTiddlerText(title));
4751 results.pushUnique(tiddler);
4753 } else if(match[2]) {
4756 var matched = this.getTaggedTiddlers(match[3]);
4757 for(var m = 0; m < matched.length; m++)
4758 results.pushUnique(matched[m]);
4761 results = this.sortTiddlers(results,match[3]);
4765 match = re.exec(filter);
4771 // Sort a list of tiddlers
4772 TiddlyWiki.prototype.sortTiddlers = function(tiddlers,field)
4775 switch(field.substr(0,1)) {
4778 // Note: this fall-through is intentional
4781 field = field.substr(1);
4784 if(TiddlyWiki.standardFieldAccess[field])
4785 tiddlers.sort(function(a,b) {return a[field] < b[field] ? -asc : (a[field] == b[field] ? 0 : asc);});
4787 tiddlers.sort(function(a,b) {return a.fields[field] < b.fields[field] ? -asc : (a.fields[field] == b.fields[field] ? 0 : +asc);});
4791 // Returns true if path is a valid field name (path),
4792 // i.e. a sequence of identifiers, separated by '.'
4793 TiddlyWiki.isValidFieldName = function(name)
4795 var match = /[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*/.exec(name);
4796 return match && (match[0] == name);
4799 // Throws an exception when name is not a valid field name.
4800 TiddlyWiki.checkFieldName = function(name)
4802 if(!TiddlyWiki.isValidFieldName(name))
4803 throw config.messages.invalidFieldName.format([name]);
4806 function StringFieldAccess(n,readOnly)
4808 this.set = readOnly ?
4809 function(t,v) {if(v != t[n]) throw config.messages.fieldCannotBeChanged.format([n]);} :
4810 function(t,v) {if(v != t[n]) {t[n] = v; return true;}};
4811 this.get = function(t) {return t[n];};
4814 function DateFieldAccess(n)
4816 this.set = function(t,v) {
4817 var d = v instanceof Date ? v : Date.convertFromYYYYMMDDHHMM(v);
4819 t[n] = d; return true;
4822 this.get = function(t) {return t[n].convertToYYYYMMDDHHMM();};
4825 function LinksFieldAccess(n)
4827 this.set = function(t,v) {
4828 var s = (typeof v == "string
") ? v.readBracketedList() : v;
4829 if(s.toString() != t[n].toString()) {
4830 t[n] = s; return true;
4833 this.get = function(t) {return String.encodeTiddlyLinkList(t[n]);};
4836 TiddlyWiki.standardFieldAccess = {
4837 // The set functions return true when setting the data has changed the value.
4838 "title
": new StringFieldAccess("title
",true),
4839 // Handle the "tiddler
" field name as the title
4840 "tiddler
": new StringFieldAccess("title
",true),
4841 "text
": new StringFieldAccess("text
"),
4842 "modifier
": new StringFieldAccess("modifier
"),
4843 "modified
": new DateFieldAccess("modified
"),
4844 "created
": new DateFieldAccess("created
"),
4845 "tags
": new LinksFieldAccess("tags
")
4848 TiddlyWiki.isStandardField = function(name)
4850 return TiddlyWiki.standardFieldAccess[name] != undefined;
4853 // Sets the value of the given field of the tiddler to the value.
4854 // Setting an ExtendedField's value to null or undefined removes the field.
4855 // Setting a namespace to undefined removes all fields of that namespace.
4856 // The fieldName is case-insensitive.
4857 // All values will be converted to a string value.
4858 TiddlyWiki.prototype.setValue = function(tiddler,fieldName,value)
4860 TiddlyWiki.checkFieldName(fieldName);
4861 var t = this.resolveTiddler(tiddler);
4864 fieldName = fieldName.toLowerCase();
4865 var isRemove = (value === undefined) || (value === null);
4866 var accessor = TiddlyWiki.standardFieldAccess[fieldName];
4869 // don't remove StandardFields
4871 var h = TiddlyWiki.standardFieldAccess[fieldName];
4875 var oldValue = t.fields[fieldName];
4877 if(oldValue !== undefined) {
4878 // deletes a single field
4879 delete t.fields[fieldName];
4881 // no concrete value is defined for the fieldName
4882 // so we guess this is a namespace path.
4883 // delete all fields in a namespace
4884 var re = new RegExp('^'+fieldName+'\\.');
4886 for(var n in t.fields) {
4896 // the "normal
" set case. value is defined (not null/undefined)
4897 // For convenience provide a nicer conversion Date->String
4898 value = value instanceof Date ? value.convertToYYYYMMDDHHMMSSMMM() : String(value);
4899 if(oldValue == value)
4901 t.fields[fieldName] = value;
4904 // When we are here the tiddler/store really was changed.
4905 this.notify(t.title,true);
4906 if(!fieldName.match(/^temp\./))
4907 this.setDirty(true);
4910 // Returns the value of the given field of the tiddler.
4911 // The fieldName is case-insensitive.
4912 // Will only return String values (or undefined).
4913 TiddlyWiki.prototype.getValue = function(tiddler,fieldName)
4915 var t = this.resolveTiddler(tiddler);
4918 fieldName = fieldName.toLowerCase();
4919 var accessor = TiddlyWiki.standardFieldAccess[fieldName];
4921 return accessor.get(t);
4923 return t.fields[fieldName];
4926 // Calls the callback function for every field in the tiddler.
4927 // When callback function returns a non-false value the iteration stops
4928 // and that value is returned.
4929 // The order of the fields is not defined.
4930 // @param callback a function(tiddler,fieldName,value).
4931 TiddlyWiki.prototype.forEachField = function(tiddler,callback,onlyExtendedFields)
4933 var t = this.resolveTiddler(tiddler);
4937 for(n in t.fields) {
4938 result = callback(t,n,t.fields[n]);
4942 if(onlyExtendedFields)
4944 for(n in TiddlyWiki.standardFieldAccess) {
4946 // even though the "title
" field can also be referenced through the name "tiddler
"
4947 // we only visit this field once.
4949 result = callback(t,n,TiddlyWiki.standardFieldAccess[n].get(t));
4957 //-- Story functions
4960 function Story(containerId,idPrefix)
4962 this.container = containerId;
4963 this.idPrefix = idPrefix;
4964 this.highlightRegExp = null;
4965 this.tiddlerId = function(title) {
4966 var id = this.idPrefix + title;
4967 return id==this.container ? this.idPrefix + "_
" + title : id;
4969 this.containerId = function() {
4970 return this.container;
4974 Story.prototype.getTiddler = function(title)
4976 return document.getElementById(this.tiddlerId(title));
4979 Story.prototype.getContainer = function()
4981 return document.getElementById(this.containerId());
4984 Story.prototype.forEachTiddler = function(fn)
4986 var place = this.getContainer();
4989 var e = place.firstChild;
4991 var n = e.nextSibling;
4992 var title = e.getAttribute("tiddler
");
4993 fn.call(this,title,e);
4998 Story.prototype.displayDefaultTiddlers = function()
5000 this.displayTiddlers(null,store.filterTiddlers(store.getTiddlerText("DefaultTiddlers
")));
5003 Story.prototype.displayTiddlers = function(srcElement,titles,template,animate,unused,customFields,toggle)
5005 for(var t = titles.length-1;t>=0;t--)
5006 this.displayTiddler(srcElement,titles[t],template,animate,unused,customFields);
5009 Story.prototype.displayTiddler = function(srcElement,tiddler,template,animate,unused,customFields,toggle,animationSrc)
5011 var title = (tiddler instanceof Tiddler) ? tiddler.title : tiddler;
5012 var tiddlerElem = this.getTiddler(title);
5015 this.closeTiddler(title,true);
5017 this.refreshTiddler(title,template,false,customFields);
5019 var place = this.getContainer();
5020 var before = this.positionTiddler(srcElement);
5021 tiddlerElem = this.createTiddler(place,before,title,template,customFields);
5023 if(animationSrc && typeof animationSrc !== "string
") {
5024 srcElement = animationSrc;
5026 if(srcElement && typeof srcElement !== "string
") {
5027 if(config.options.chkAnimate && (animate == undefined || animate == true) && anim && typeof Zoomer == "function
" && typeof Scroller == "function
")
5028 anim.startAnimating(new Zoomer(title,srcElement,tiddlerElem),new Scroller(tiddlerElem));
5030 window.scrollTo(0,ensureVisible(tiddlerElem));
5034 Story.prototype.positionTiddler = function(srcElement)
5036 var place = this.getContainer();
5038 if(typeof srcElement == "string
") {
5039 switch(srcElement) {
5041 before = place.firstChild;
5048 var after = this.findContainingTiddler(srcElement);
5050 before = place.firstChild;
5051 } else if(after.nextSibling) {
5052 before = after.nextSibling;
5053 if(before.nodeType != 1)
5060 Story.prototype.createTiddler = function(place,before,title,template,customFields)
5062 var tiddlerElem = createTiddlyElement(null,"div
",this.tiddlerId(title),"tiddler
");
5063 tiddlerElem.setAttribute("refresh
","tiddler
");
5065 tiddlerElem.setAttribute("tiddlyFields
",customFields);
5066 place.insertBefore(tiddlerElem,before);
5067 var defaultText = null;
5068 if(!store.tiddlerExists(title) && !store.isShadowTiddler(title))
5069 defaultText = this.loadMissingTiddler(title,customFields,tiddlerElem);
5070 this.refreshTiddler(title,template,false,customFields,defaultText);
5074 Story.prototype.loadMissingTiddler = function(title,fields,tiddlerElem)
5076 var tiddler = new Tiddler(title);
5077 tiddler.fields = typeof fields == "string
" ? fields.decodeHashMap() : (fields || {});
5078 var serverType = tiddler.getServerType();
5079 var host = tiddler.fields['server.host'];
5080 var workspace = tiddler.fields['server.workspace'];
5081 if(!serverType || !host)
5083 var sm = new SyncMachine(serverType,{
5085 return this.openHost(host,"openWorkspace
");
5087 openWorkspace: function() {
5088 return this.openWorkspace(workspace,"getTiddler
");
5090 getTiddler: function() {
5091 return this.getTiddler(title,"onGetTiddler
");
5093 onGetTiddler: function(context) {
5094 var tiddler = context.tiddler;
5095 if(tiddler && tiddler.text) {
5096 var downloaded = new Date();
5097 if(!tiddler.created)
5098 tiddler.created = downloaded;
5099 if(!tiddler.modified)
5100 tiddler.modified = tiddler.created;
5101 store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields,true,tiddler.created);
5107 error: function(message) {
5108 displayMessage("Error loading missing tiddler from %
0: %
1".format([host,message]));
5112 return config.messages.loadingMissingTiddler.format([title,serverType,host,workspace]);
5115 Story.prototype.chooseTemplateForTiddler = function(title,template)
5118 template = DEFAULT_VIEW_TEMPLATE;
5119 if(template == DEFAULT_VIEW_TEMPLATE || template == DEFAULT_EDIT_TEMPLATE)
5120 template = config.tiddlerTemplates[template];
5124 Story.prototype.getTemplateForTiddler = function(title,template,tiddler)
5126 return store.getRecursiveTiddlerText(template,null,10);
5129 Story.prototype.refreshTiddler = function(title,template,force,customFields,defaultText)
5131 var tiddlerElem = this.getTiddler(title);
5133 if(tiddlerElem.getAttribute("dirty
") == "true
" && !force)
5135 template = this.chooseTemplateForTiddler(title,template);
5136 var currTemplate = tiddlerElem.getAttribute("template
");
5137 if((template != currTemplate) || force) {
5138 var tiddler = store.getTiddler(title);
5140 tiddler = new Tiddler();
5141 if(store.isShadowTiddler(title)) {
5142 tiddler.set(title,store.getTiddlerText(title),config.views.wikified.shadowModifier,version.date,[],version.date);
5144 var text = template=="EditTemplate
" ?
5145 config.views.editor.defaultText.format([title]) :
5146 config.views.wikified.defaultText.format([title]);
5147 text = defaultText || text;
5148 var fields = customFields ? customFields.decodeHashMap() : null;
5149 tiddler.set(title,text,config.views.wikified.defaultModifier,version.date,[],version.date,fields);
5152 tiddlerElem.setAttribute("tags
",tiddler.tags.join(" "));
5153 tiddlerElem.setAttribute("tiddler
",title);
5154 tiddlerElem.setAttribute("template
",template);
5155 tiddlerElem.onmouseover = this.onTiddlerMouseOver;
5156 tiddlerElem.onmouseout = this.onTiddlerMouseOut;
5157 tiddlerElem.ondblclick = this.onTiddlerDblClick;
5158 tiddlerElem[window.event?"onkeydown
":"onkeypress
"] = this.onTiddlerKeyPress;
5159 tiddlerElem.innerHTML = this.getTemplateForTiddler(title,template,tiddler);
5160 applyHtmlMacros(tiddlerElem,tiddler);
5161 if(store.getTaggedTiddlers(title).length > 0)
5162 addClass(tiddlerElem,"isTag
");
5164 removeClass(tiddlerElem,"isTag
");
5165 if(store.tiddlerExists(title)) {
5166 removeClass(tiddlerElem,"shadow
");
5167 removeClass(tiddlerElem,"missing
");
5169 addClass(tiddlerElem,store.isShadowTiddler(title) ? "shadow
" : "missing
");
5172 this.addCustomFields(tiddlerElem,customFields);
5179 Story.prototype.addCustomFields = function(place,customFields)
5181 var fields = customFields.decodeHashMap();
5182 var w = document.createElement("div
");
5183 w.style.display = "none
";
5184 place.appendChild(w);
5185 for(var t in fields) {
5186 var e = document.createElement("input
");
5187 e.setAttribute("type
","text
");
5188 e.setAttribute("value
",fields[t]);
5190 e.setAttribute("edit
",t);
5194 Story.prototype.refreshAllTiddlers = function(force)
5196 var e = this.getContainer().firstChild;
5198 var template = e.getAttribute("template
");
5199 if(template && e.getAttribute("dirty
") != "true
") {
5200 this.refreshTiddler(e.getAttribute("tiddler
"),force ? null : template,true);
5206 Story.prototype.onTiddlerMouseOver = function(e)
5208 if(window.addClass instanceof Function)
5209 addClass(this,"selected
");
5212 Story.prototype.onTiddlerMouseOut = function(e)
5214 if(window.removeClass instanceof Function)
5215 removeClass(this,"selected
");
5218 Story.prototype.onTiddlerDblClick = function(ev)
5220 var e = ev || window.event;
5221 var target = resolveTarget(e);
5222 if(target && target.nodeName.toLowerCase() != "input
" && target.nodeName.toLowerCase() != "textarea
") {
5223 if(document.selection && document.selection.empty)
5224 document.selection.empty();
5225 config.macros.toolbar.invokeCommand(this,"defaultCommand
",e);
5226 e.cancelBubble = true;
5227 if(e.stopPropagation) e.stopPropagation();
5233 Story.prototype.onTiddlerKeyPress = function(ev)
5235 var e = ev || window.event;
5237 var consume = false;
5238 var title = this.getAttribute("tiddler
");
5239 var target = resolveTarget(e);
5242 if(config.options.chkInsertTabs && target.tagName.toLowerCase() == "textarea
") {
5243 replaceSelection(target,String.fromCharCode(9));
5246 if(config.isOpera) {
5247 target.onblur = function() {
5253 case 13: // Ctrl-Enter
5254 case 10: // Ctrl-Enter on IE PC
5255 case 77: // Ctrl-Enter is "M
" on some platforms
5258 config.macros.toolbar.invokeCommand(this,"defaultCommand
",e);
5264 config.macros.toolbar.invokeCommand(this,"cancelCommand
",e);
5268 e.cancelBubble = consume;
5270 if(e.stopPropagation) e.stopPropagation(); // Stop Propagation
5271 e.returnValue = true; // Cancel The Event in IE
5272 if(e.preventDefault ) e.preventDefault(); // Cancel The Event in Moz
5277 Story.prototype.getTiddlerField = function(title,field)
5279 var tiddlerElem = this.getTiddler(title);
5282 var children = tiddlerElem.getElementsByTagName("*
");
5283 for(var t=0; t<children.length; t++) {
5284 var c = children[t];
5285 if(c.tagName.toLowerCase() == "input
" || c.tagName.toLowerCase() == "textarea
") {
5288 if(c.getAttribute("edit
") == field)
5296 Story.prototype.focusTiddler = function(title,field)
5298 var e = this.getTiddlerField(title,field);
5305 Story.prototype.blurTiddler = function(title)
5307 var tiddlerElem = this.getTiddler(title);
5308 if(tiddlerElem && tiddlerElem.focus && tiddlerElem.blur) {
5309 tiddlerElem.focus();
5314 Story.prototype.setTiddlerField = function(title,tag,mode,field)
5316 var c = this.getTiddlerField(title,field);
5317 var tags = c.value.readBracketedList();
5318 tags.setItem(tag,mode);
5319 c.value = String.encodeTiddlyLinkList(tags);
5322 Story.prototype.setTiddlerTag = function(title,tag,mode)
5324 this.setTiddlerField(title,tag,mode,"tags
");
5327 Story.prototype.closeTiddler = function(title,animate,unused)
5329 var tiddlerElem = this.getTiddler(title);
5332 this.scrubTiddler(tiddlerElem);
5333 if(config.options.chkAnimate && animate && anim && typeof Slider == "function
")
5334 anim.startAnimating(new Slider(tiddlerElem,false,null,"all
"));
5336 removeNode(tiddlerElem);
5342 Story.prototype.scrubTiddler = function(tiddlerElem)
5344 tiddlerElem.id = null;
5347 Story.prototype.setDirty = function(title,dirty)
5349 var tiddlerElem = this.getTiddler(title);
5351 tiddlerElem.setAttribute("dirty
",dirty ? "true
" : "false
");
5354 Story.prototype.isDirty = function(title)
5356 var tiddlerElem = this.getTiddler(title);
5358 return tiddlerElem.getAttribute("dirty
") == "true
";
5362 Story.prototype.areAnyDirty = function()
5365 this.forEachTiddler(function(title,element) {
5366 if(this.isDirty(title))
5372 Story.prototype.closeAllTiddlers = function(exclude)
5375 this.forEachTiddler(function(title,element) {
5376 if((title != exclude) && element.getAttribute("dirty
") != "true
")
5377 this.closeTiddler(title);
5379 window.scrollTo(0,ensureVisible(this.container));
5382 Story.prototype.isEmpty = function()
5384 var place = this.getContainer();
5385 return place && place.firstChild == null;
5388 Story.prototype.search = function(text,useCaseSensitive,useRegExp)
5390 this.closeAllTiddlers();
5391 highlightHack = new RegExp(useRegExp ? text : text.escapeRegExp(),useCaseSensitive ? "mg
" : "img
");
5392 var matches = store.search(highlightHack,"title
","excludeSearch
");
5393 this.displayTiddlers(null,matches);
5394 highlightHack = null;
5395 var q = useRegExp ? "/
" : "'
";
5396 if(matches.length > 0)
5397 displayMessage(config.macros.search.successMsg.format([matches.length.toString(),q + text + q]));
5399 displayMessage(config.macros.search.failureMsg.format([q + text + q]));
5402 Story.prototype.findContainingTiddler = function(e)
5404 while(e && !hasClass(e,"tiddler
"))
5409 Story.prototype.gatherSaveFields = function(e,fields)
5411 if(e && e.getAttribute) {
5412 var f = e.getAttribute("edit
");
5414 fields[f] = e.value.replace(/\r/mg,"");
5415 if(e.hasChildNodes()) {
5416 var c = e.childNodes;
5417 for(var t=0; t<c.length; t++)
5418 this.gatherSaveFields(c[t],fields);
5423 Story.prototype.hasChanges = function(title)
5425 var e = this.getTiddler(title);
5428 this.gatherSaveFields(e,fields);
5429 var tiddler = store.fetchTiddler(title);
5432 for(var n in fields) {
5433 if(store.getValue(title,n) != fields[n])
5440 Story.prototype.saveTiddler = function(title,minorUpdate)
5442 var tiddlerElem = this.getTiddler(title);
5445 this.gatherSaveFields(tiddlerElem,fields);
5446 var newTitle = fields.title || title;
5447 if(!store.tiddlerExists(newTitle))
5448 newTitle = newTitle.trim();
5449 if(store.tiddlerExists(newTitle) && newTitle != title) {
5450 if(!confirm(config.messages.overwriteWarning.format([newTitle.toString()])))
5453 if(newTitle != title)
5454 this.closeTiddler(newTitle,false);
5455 tiddlerElem.id = this.tiddlerId(newTitle);
5456 tiddlerElem.setAttribute("tiddler
",newTitle);
5457 tiddlerElem.setAttribute("template
",DEFAULT_VIEW_TEMPLATE);
5458 tiddlerElem.setAttribute("dirty
","false
");
5459 if(config.options.chkForceMinorUpdate)
5460 minorUpdate = !minorUpdate;
5461 if(!store.tiddlerExists(newTitle))
5462 minorUpdate = false;
5463 var newDate = new Date();
5464 var extendedFields = store.tiddlerExists(newTitle) ? store.fetchTiddler(newTitle).fields : (newTitle!=title && store.tiddlerExists(title) ? store.fetchTiddler(title).fields : config.defaultCustomFields);
5465 for(var n in fields) {
5466 if(!TiddlyWiki.isStandardField(n))
5467 extendedFields[n] = fields[n];
5469 var tiddler = store.saveTiddler(title,newTitle,fields.text,minorUpdate ? undefined : config.options.txtUserName,minorUpdate ? undefined : newDate,fields.tags,extendedFields);
5470 autoSaveChanges(null,[tiddler]);
5476 Story.prototype.permaView = function()
5479 this.forEachTiddler(function(title,element) {
5480 links.push(String.encodeTiddlyLink(title));
5482 var t = encodeURIComponent(links.join(" "));
5485 if(window.location.hash != t)
5486 window.location.hash = t;
5489 Story.prototype.switchTheme = function(theme)
5494 var isAvailable = function(title) {
5495 var s = title ? title.indexOf(config.textPrimitives.sectionSeparator) : -1;
5497 title = title.substr(0,s);
5498 return store.tiddlerExists(title) || store.isShadowTiddler(title);
5501 var getSlice = function(theme,slice) {
5504 r = store.getTiddlerSlice(theme,slice+"ReadOnly
") || store.getTiddlerSlice(theme,"Web
"+slice);
5505 r = r || store.getTiddlerSlice(theme,slice);
5506 if(r && r.indexOf(config.textPrimitives.sectionSeparator)==0)
5508 return isAvailable(r) ? r : slice;
5511 var replaceNotification = function(i,name,theme,slice) {
5512 var newName = getSlice(theme,slice);
5513 if(name!=newName && store.namedNotifications[i].name==name) {
5514 store.namedNotifications[i].name = newName;
5520 var pt = config.refresherData.pageTemplate;
5521 var vi = DEFAULT_VIEW_TEMPLATE;
5522 var vt = config.tiddlerTemplates[vi];
5523 var ei = DEFAULT_EDIT_TEMPLATE;
5524 var et = config.tiddlerTemplates[ei];
5526 for(var i=0; i<config.notifyTiddlers.length; i++) {
5527 var name = config.notifyTiddlers[i].name;
5529 case "PageTemplate
":
5530 config.refresherData.pageTemplate = replaceNotification(i,config.refresherData.pageTemplate,theme,name);
5533 removeStyleSheet(config.refresherData.styleSheet);
5534 config.refresherData.styleSheet = replaceNotification(i,config.refresherData.styleSheet,theme,name);
5536 case "ColorPalette
":
5537 config.refresherData.colorPalette = replaceNotification(i,config.refresherData.colorPalette,theme,name);
5543 config.tiddlerTemplates[vi] = getSlice(theme,"ViewTemplate
");
5544 config.tiddlerTemplates[ei] = getSlice(theme,"EditTemplate
");
5546 if(config.refresherData.pageTemplate!=pt || config.tiddlerTemplates[vi]!=vt || config.tiddlerTemplates[ei]!=et) {
5548 this.refreshAllTiddlers(true);
5550 setStylesheet(store.getRecursiveTiddlerText(config.refresherData.styleSheet,"",10),config.refreshers.styleSheet);
5552 config.options.txtTheme = theme;
5553 saveOptionCookie("txtTheme
");
5576 var cmb = config.messages.backstage;
5577 this.area = document.getElementById("backstageArea
");
5578 this.toolbar = document.getElementById("backstageToolbar
");
5579 this.button = document.getElementById("backstageButton
");
5580 this.button.style.display = "block
";
5581 var t = cmb.open.text + " " + glyph("bentArrowLeft
");
5582 this.showButton = createTiddlyButton(this.button,t,cmb.open.tooltip,
5583 function(e) {backstage.show(); return false;},null,"backstageShow
");
5584 t = glyph("bentArrowRight
") + " " + cmb.close.text;
5585 this.hideButton = createTiddlyButton(this.button,t,cmb.close.tooltip,
5586 function(e) {backstage.hide(); return false;},null,"backstageHide
");
5587 this.cloak = document.getElementById("backstageCloak
");
5588 this.panel = document.getElementById("backstagePanel
");
5589 this.panelFooter = createTiddlyElement(this.panel,"div
",null,"backstagePanelFooter
");
5590 this.panelBody = createTiddlyElement(this.panel,"div
",null,"backstagePanelBody
");
5591 this.cloak.onmousedown = function(e) {backstage.switchTab(null);};
5592 createTiddlyText(this.toolbar,cmb.prompt);
5593 for(t=0; t<config.backstageTasks.length; t++) {
5594 var taskName = config.backstageTasks[t];
5595 var task = config.tasks[taskName];
5596 var handler = task.action ? this.onClickCommand : this.onClickTab;
5597 var text = task.text + (task.action ? "" : glyph("downTriangle
"));
5598 var btn = createTiddlyButton(this.toolbar,text,task.tooltip,handler,"backstageTab
");
5599 btn.setAttribute("task
",taskName);
5600 addClass(btn,task.action ? "backstageAction
" : "backstageTask
");
5602 this.content = document.getElementById("contentWrapper
");
5603 if(config.options.chkBackstage)
5609 isVisible: function() {
5610 return this.area ? this.area.style.display == "block
" : false;
5614 this.area.style.display = "block
";
5615 if(anim && config.options.chkAnimate) {
5616 backstage.toolbar.style.left = findWindowWidth() + "px
";
5617 var p = [{style: "left
", start: findWindowWidth(), end: 0, template: "%
0px
"}];
5618 anim.startAnimating(new Morpher(backstage.toolbar,config.animDuration,p));
5620 backstage.area.style.left = "0px
";
5622 this.showButton.style.display = "none
";
5623 this.hideButton.style.display = "block
";
5624 config.options.chkBackstage = true;
5625 saveOptionCookie("chkBackstage
");
5626 addClass(this.content,"backstageVisible
");
5630 if(this.currTabElem) {
5631 this.switchTab(null);
5633 backstage.toolbar.style.left = "0px
";
5634 if(anim && config.options.chkAnimate) {
5635 var p = [{style: "left
", start: 0, end: findWindowWidth(), template: "%
0px
"}];
5636 var c = function(element,properties) {backstage.area.style.display = "none
";};
5637 anim.startAnimating(new Morpher(backstage.toolbar,config.animDuration,p,c));
5639 this.area.style.display = "none
";
5641 this.showButton.style.display = "block
";
5642 this.hideButton.style.display = "none
";
5643 config.options.chkBackstage = false;
5644 saveOptionCookie("chkBackstage
");
5645 removeClass(this.content,"backstageVisible
");
5649 onClickCommand: function(e) {
5650 var task = config.tasks[this.getAttribute("task
")];
5651 displayMessage(task);
5653 backstage.switchTab(null);
5659 onClickTab: function(e) {
5660 backstage.switchTab(this.getAttribute("task
"));
5664 // Switch to a given tab, or none if null is passed
5665 switchTab: function(tabName) {
5667 var e = this.toolbar.firstChild;
5670 if(e.getAttribute && e.getAttribute("task
") == tabName)
5674 if(tabName == backstage.currTabName)
5676 if(backstage.currTabElem) {
5677 removeClass(this.currTabElem,"backstageSelTab
");
5679 if(tabElem && tabName) {
5680 backstage.preparePanel();
5681 addClass(tabElem,"backstageSelTab
");
5682 var task = config.tasks[tabName];
5683 wikify(task.content,backstage.panelBody,null,null);
5684 backstage.showPanel();
5685 } else if(backstage.currTabElem) {
5686 backstage.hidePanel();
5688 backstage.currTabName = tabName;
5689 backstage.currTabElem = tabElem;
5692 isPanelVisible: function() {
5693 return backstage.panel ? backstage.panel.style.display == "block
" : false;
5696 preparePanel: function() {
5697 backstage.cloak.style.height = findWindowHeight() + "px
";
5698 backstage.cloak.style.display = "block
";
5699 removeChildren(backstage.panelBody);
5700 return backstage.panelBody;
5703 showPanel: function() {
5704 backstage.panel.style.display = "block
";
5705 if(anim && config.options.chkAnimate) {
5706 backstage.panel.style.top = (-backstage.panel.offsetHeight) + "px
";
5707 var p = [{style: "top
", start: -backstage.panel.offsetHeight, end: 0, template: "%
0px
"}];
5708 anim.startAnimating(new Morpher(backstage.panel,config.animDuration,p),new Scroller(backstage.panel,false));
5710 backstage.panel.style.top = "0px
";
5712 return backstage.panelBody;
5715 hidePanel: function() {
5716 if(backstage.currTabElem)
5717 removeClass(backstage.currTabElem,"backstageSelTab
");
5718 backstage.currTabElem = null;
5719 backstage.currTabName = null;
5720 if(anim && config.options.chkAnimate) {
5722 {style: "top
", start: 0, end: -(backstage.panel.offsetHeight), template: "%
0px
"},
5723 {style: "display
", atEnd: "none
"}
5725 var c = function(element,properties) {backstage.cloak.style.display = "none
";};
5726 anim.startAnimating(new Morpher(backstage.panel,config.animDuration,p,c));
5728 backstage.panel.style.display = "none
";
5729 backstage.cloak.style.display = "none
";
5734 config.macros.backstage = {};
5736 config.macros.backstage.handler = function(place,macroName,params)
5738 var backstageTask = config.tasks[params[0]];
5740 createTiddlyButton(place,backstageTask.text,backstageTask.tooltip,function(e) {backstage.switchTab(params[0]); return false;});
5744 //-- ImportTiddlers macro
5747 config.macros.importTiddlers.handler = function(place,macroName,params,wikifier,paramString,tiddler)
5750 createTiddlyElement(place,"div
",null,"marked
",this.readOnlyWarning);
5753 var w = new Wizard();
5754 w.createWizard(place,this.wizardTitle);
5758 config.macros.importTiddlers.onCancel = function(e)
5760 var wizard = new Wizard(this);
5761 var place = wizard.clear();
5762 config.macros.importTiddlers.restart(wizard);
5766 config.macros.importTiddlers.onClose = function(e)
5768 backstage.hidePanel();
5772 config.macros.importTiddlers.restart = function(wizard)
5774 wizard.addStep(this.step1Title,this.step1Html);
5775 var s = wizard.getElement("selTypes
");
5776 for(var t in config.adaptors) {
5777 var e = createTiddlyElement(s,"option
",null,null,config.adaptors[t].serverLabel ? config.adaptors[t].serverLabel : t);
5780 if(config.defaultAdaptor)
5781 s.value = config.defaultAdaptor;
5782 s = wizard.getElement("selFeeds
");
5783 var feeds = this.getFeeds();
5785 e = createTiddlyElement(s,"option
",null,null,t);
5788 wizard.setValue("feeds
",feeds);
5789 s.onchange = config.macros.importTiddlers.onFeedChange;
5790 var fileInput = wizard.getElement("txtBrowse
");
5791 fileInput.onchange = config.macros.importTiddlers.onBrowseChange;
5792 fileInput.onkeyup = config.macros.importTiddlers.onBrowseChange;
5793 wizard.setButtons([{caption: this.openLabel, tooltip: this.openPrompt, onClick: config.macros.importTiddlers.onOpen}]);
5794 wizard.formElem.action = "javascript:;
";
5795 wizard.formElem.onsubmit = function() {
5796 if(this.txtPath.value.length)
5797 this.lastChild.firstChild.onclick();
5801 config.macros.importTiddlers.getFeeds = function()
5804 var tagged = store.getTaggedTiddlers("systemServer
","title
");
5805 for(var t=0; t<tagged.length; t++) {
5806 var title = tagged[t].title;
5807 var serverType = store.getTiddlerSlice(title,"Type
");
5809 serverType = "file
";
5810 feeds[title] = {title: title,
5811 url: store.getTiddlerSlice(title,"URL
"),
5812 workspace: store.getTiddlerSlice(title,"Workspace
"),
5813 workspaceList: store.getTiddlerSlice(title,"WorkspaceList
"),
5814 tiddlerFilter: store.getTiddlerSlice(title,"TiddlerFilter
"),
5815 serverType: serverType,
5816 description: store.getTiddlerSlice(title,"Description
")};
5821 config.macros.importTiddlers.onFeedChange = function(e)
5823 var wizard = new Wizard(this);
5824 var selTypes = wizard.getElement("selTypes
");
5825 var fileInput = wizard.getElement("txtPath
");
5826 var feeds = wizard.getValue("feeds
");
5827 var f = feeds[this.value];
5829 selTypes.value = f.serverType;
5830 fileInput.value = f.url;
5831 wizard.setValue("feedName
",f.serverType);
5832 wizard.setValue("feedHost
",f.url);
5833 wizard.setValue("feedWorkspace
",f.workspace);
5834 wizard.setValue("feedWorkspaceList
",f.workspaceList);
5835 wizard.setValue("feedTiddlerFilter
",f.tiddlerFilter);
5840 config.macros.importTiddlers.onBrowseChange = function(e)
5842 var wizard = new Wizard(this);
5843 var fileInput = wizard.getElement("txtPath
");
5844 fileInput.value = config.macros.importTiddlers.getURLFromLocalPath(this.value);
5845 var serverType = wizard.getElement("selTypes
");
5846 serverType.value = "file
";
5850 config.macros.importTiddlers.getURLFromLocalPath = function(v)
5854 v = v.replace(/\\/g,"/
"); // use "/
" for cross-platform consistency
5856 var t = v.split(":
");
5857 var p = t[1]||t[0]; // remove drive letter (if any)
5858 if (t[1] && (t[0]=="http
"||t[0]=="https
"||t[0]=="file
")) {
5860 } else if(p.substr(0,1)=="/
") {
5861 u = document.location.protocol + "//
" + document.location.hostname + (t[1] ? "/
" : "") + v;
5863 var c = document.location.href.replace(/\\/g,"/
");
5864 var pos = c.lastIndexOf("/
");
5866 c = c.substr(0,pos); // remove filename
5872 config.macros.importTiddlers.onOpen = function(e)
5874 var wizard = new Wizard(this);
5875 var fileInput = wizard.getElement("txtPath
");
5876 var url = fileInput.value;
5877 var serverType = wizard.getElement("selTypes
").value || config.defaultAdaptor;
5878 var adaptor = new config.adaptors[serverType]();
5879 wizard.setValue("adaptor
",adaptor);
5880 wizard.setValue("serverType
",serverType);
5881 wizard.setValue("host
",url);
5882 var ret = adaptor.openHost(url,null,wizard,config.macros.importTiddlers.onOpenHost);
5884 displayMessage(ret);
5885 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusOpenHost);
5889 config.macros.importTiddlers.onOpenHost = function(context,wizard)
5891 var adaptor = wizard.getValue("adaptor
");
5892 if(context.status !== true)
5893 displayMessage("Error in importTiddlers.onOpenHost:
" + context.statusText);
5894 var ret = adaptor.getWorkspaceList(context,wizard,config.macros.importTiddlers.onGetWorkspaceList);
5896 displayMessage(ret);
5897 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusGetWorkspaceList);
5900 config.macros.importTiddlers.onGetWorkspaceList = function(context,wizard)
5902 if(context.status !== true)
5903 displayMessage("Error in importTiddlers.onGetWorkspaceList:
" + context.statusText);
5904 wizard.setValue("context
",context);
5905 var workspace = wizard.getValue("feedWorkspace
");
5906 if(!workspace && context.workspaces.length==1)
5907 workspace = context.workspaces[0].title;
5909 var ret = context.adaptor.openWorkspace(workspace,context,wizard,config.macros.importTiddlers.onOpenWorkspace);
5911 displayMessage(ret);
5912 wizard.setValue("workspace
",workspace);
5913 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusOpenWorkspace);
5916 wizard.addStep(config.macros.importTiddlers.step2Title,config.macros.importTiddlers.step2Html);
5917 var s = wizard.getElement("selWorkspace
");
5918 s.onchange = config.macros.importTiddlers.onWorkspaceChange;
5919 for(var t=0; t<context.workspaces.length; t++) {
5920 var e = createTiddlyElement(s,"option
",null,null,context.workspaces[t].title);
5921 e.value = context.workspaces[t].title;
5923 var workspaceList = wizard.getValue("feedWorkspaceList
");
5925 var list = workspaceList.parseParams("workspace
",null,false,true);
5926 for(var n=1; n<list.length; n++) {
5927 if(context.workspaces.findByField("title
",list[n].value) == null) {
5928 e = createTiddlyElement(s,"option
",null,null,list[n].value);
5929 e.value = list[n].value;
5934 t = wizard.getElement("txtWorkspace
");
5935 t.value = workspace;
5937 wizard.setButtons([{caption: config.macros.importTiddlers.openLabel, tooltip: config.macros.importTiddlers.openPrompt, onClick: config.macros.importTiddlers.onChooseWorkspace}]);
5940 config.macros.importTiddlers.onWorkspaceChange = function(e)
5942 var wizard = new Wizard(this);
5943 var t = wizard.getElement("txtWorkspace
");
5944 t.value = this.value;
5945 this.selectedIndex = 0;
5949 config.macros.importTiddlers.onChooseWorkspace = function(e)
5951 var wizard = new Wizard(this);
5952 var adaptor = wizard.getValue("adaptor
");
5953 var workspace = wizard.getElement("txtWorkspace
").value;
5954 wizard.setValue("workspace
",workspace);
5955 var context = wizard.getValue("context
");
5956 var ret = adaptor.openWorkspace(workspace,context,wizard,config.macros.importTiddlers.onOpenWorkspace);
5958 displayMessage(ret);
5959 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusOpenWorkspace);
5963 config.macros.importTiddlers.onOpenWorkspace = function(context,wizard)
5965 if(context.status !== true)
5966 displayMessage("Error in importTiddlers.onOpenWorkspace:
" + context.statusText);
5967 var adaptor = wizard.getValue("adaptor
");
5968 var ret = adaptor.getTiddlerList(context,wizard,config.macros.importTiddlers.onGetTiddlerList,wizard.getValue("feedTiddlerFilter
"));
5970 displayMessage(ret);
5971 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusGetTiddlerList);
5974 config.macros.importTiddlers.onGetTiddlerList = function(context,wizard)
5976 if(context.status !== true) {
5977 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.errorGettingTiddlerList);
5980 // Extract data for the listview
5981 var listedTiddlers = [];
5982 if(context.tiddlers) {
5983 for(var n=0; n<context.tiddlers.length; n++) {
5984 var tiddler = context.tiddlers[n];
5985 listedTiddlers.push({
5986 title: tiddler.title,
5987 modified: tiddler.modified,
5988 modifier: tiddler.modifier,
5989 text: tiddler.text ? wikifyPlainText(tiddler.text,100) : "",
5991 size: tiddler.text ? tiddler.text.length : 0,
5996 listedTiddlers.sort(function(a,b) {return a.title < b.title ? -1 : (a.title == b.title ? 0 : +1);});
5997 // Display the listview
5998 wizard.addStep(config.macros.importTiddlers.step3Title,config.macros.importTiddlers.step3Html);
5999 var markList = wizard.getElement("markList
");
6000 var listWrapper = document.createElement("div
");
6001 markList.parentNode.insertBefore(listWrapper,markList);
6002 var listView = ListView.create(listWrapper,listedTiddlers,config.macros.importTiddlers.listViewTemplate);
6003 wizard.setValue("listView
",listView);
6004 var txtSaveTiddler = wizard.getElement("txtSaveTiddler
");
6005 txtSaveTiddler.value = config.macros.importTiddlers.generateSystemServerName(wizard);
6007 {caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel},
6008 {caption: config.macros.importTiddlers.importLabel, tooltip: config.macros.importTiddlers.importPrompt, onClick: config.macros.importTiddlers.doImport}
6012 config.macros.importTiddlers.generateSystemServerName = function(wizard)
6014 var serverType = wizard.getValue("serverType
");
6015 var host = wizard.getValue("host
");
6016 var workspace = wizard.getValue("workspace
");
6017 var pattern = config.macros.importTiddlers[workspace ? "systemServerNamePattern
" : "systemServerNamePatternNoWorkspace
"];
6018 return pattern.format([serverType,host,workspace]);
6021 config.macros.importTiddlers.saveServerTiddler = function(wizard)
6023 var txtSaveTiddler = wizard.getElement("txtSaveTiddler
").value;
6024 if(store.tiddlerExists(txtSaveTiddler)) {
6025 if(!confirm(config.macros.importTiddlers.confirmOverwriteSaveTiddler.format([txtSaveTiddler])))
6027 store.suspendNotifications();
6028 store.removeTiddler(txtSaveTiddler);
6029 store.resumeNotifications();
6031 var serverType = wizard.getValue("serverType
");
6032 var host = wizard.getValue("host
");
6033 var workspace = wizard.getValue("workspace
");
6034 var text = config.macros.importTiddlers.serverSaveTemplate.format([serverType,host,workspace]);
6035 store.saveTiddler(txtSaveTiddler,txtSaveTiddler,text,config.macros.importTiddlers.serverSaveModifier,new Date(),["systemServer
"]);
6038 config.macros.importTiddlers.doImport = function(e)
6040 var wizard = new Wizard(this);
6041 if(wizard.getElement("chkSave
").checked)
6042 config.macros.importTiddlers.saveServerTiddler(wizard);
6043 var chkSync = wizard.getElement("chkSync
").checked;
6044 wizard.setValue("sync
",chkSync);
6045 var listView = wizard.getValue("listView
");
6046 var rowNames = ListView.getSelectedRows(listView);
6047 var adaptor = wizard.getValue("adaptor
");
6050 for(t=0; t<rowNames.length; t++) {
6051 if(store.tiddlerExists(rowNames[t]))
6052 overwrite.push(rowNames[t]);
6054 if(overwrite.length > 0) {
6055 if(!confirm(config.macros.importTiddlers.confirmOverwriteText.format([overwrite.join(",
")])))
6058 wizard.addStep(config.macros.importTiddlers.step4Title.format([rowNames.length]),config.macros.importTiddlers.step4Html);
6059 for(t=0; t<rowNames.length; t++) {
6060 var link = document.createElement("div
");
6061 createTiddlyLink(link,rowNames[t],true);
6062 var place = wizard.getElement("markReport
");
6063 place.parentNode.insertBefore(link,place);
6065 wizard.setValue("remainingImports
",rowNames.length);
6067 {caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}
6068 ],config.macros.importTiddlers.statusDoingImport);
6069 for(t=0; t<rowNames.length; t++) {
6071 context.allowSynchronous = true;
6072 var inbound = adaptor.getTiddler(rowNames[t],context,wizard,config.macros.importTiddlers.onGetTiddler);
6077 config.macros.importTiddlers.onGetTiddler = function(context,wizard)
6080 displayMessage("Error in importTiddlers.onGetTiddler:
" + context.statusText);
6081 var tiddler = context.tiddler;
6082 store.suspendNotifications();
6083 store.saveTiddler(tiddler.title, tiddler.title, tiddler.text, tiddler.modifier, tiddler.modified, tiddler.tags, tiddler.fields, true, tiddler.created);
6084 if(!wizard.getValue("sync
")) {
6085 store.setValue(tiddler.title,'server',null);
6087 store.resumeNotifications();
6088 if(!context.isSynchronous)
6089 store.notify(tiddler.title,true);
6090 var remainingImports = wizard.getValue("remainingImports
")-1;
6091 wizard.setValue("remainingImports
",remainingImports);
6092 if(remainingImports == 0) {
6093 if(context.isSynchronous) {
6098 {caption: config.macros.importTiddlers.doneLabel, tooltip: config.macros.importTiddlers.donePrompt, onClick: config.macros.importTiddlers.onClose}
6099 ],config.macros.importTiddlers.statusDoneImport);
6108 config.macros.upgrade.handler = function(place)
6110 var w = new Wizard();
6111 w.createWizard(place,this.wizardTitle);
6112 w.addStep(this.step1Title,this.step1Html.format([this.source,this.source]));
6113 w.setButtons([{caption: this.upgradeLabel, tooltip: this.upgradePrompt, onClick: this.onClickUpgrade}]);
6116 config.macros.upgrade.onClickUpgrade = function(e)
6118 var me = config.macros.upgrade;
6119 var w = new Wizard(this);
6120 if(window.location.protocol != "file:
") {
6121 alert(me.errorCantUpgrade);
6124 if(story.areAnyDirty() || store.isDirty()) {
6125 alert(me.errorNotSaved);
6128 var localPath = getLocalPath(document.location.toString());
6129 var backupPath = getBackupPath(localPath,me.backupExtension);
6130 w.setValue("backupPath
",backupPath);
6131 w.setButtons([],me.statusPreparingBackup);
6132 var original = loadOriginal(localPath);
6133 w.setButtons([],me.statusSavingBackup);
6134 var backup = config.browser.isIE ? ieCopyFile(backupPath,localPath) : saveFile(backupPath,original);
6135 if(backup != true) {
6136 w.setButtons([],me.errorSavingBackup);
6137 alert(me.errorSavingBackup);
6140 w.setButtons([],me.statusLoadingCore);
6141 var load = loadRemoteFile(me.source,me.onLoadCore,w);
6142 if(typeof load == "string
") {
6143 w.setButtons([],me.errorLoadingCore);
6144 alert(me.errorLoadingCore);
6150 config.macros.upgrade.onLoadCore = function(status,params,responseText,url,xhr)
6152 var me = config.macros.upgrade;
6156 errMsg = me.errorLoadingCore;
6157 var newVer = me.extractVersion(responseText);
6159 errMsg = me.errorCoreFormat;
6161 w.setButtons([],errMsg);
6165 var onStartUpgrade = function(e) {
6166 w.setButtons([],me.statusSavingCore);
6167 var localPath = getLocalPath(document.location.toString());
6168 saveFile(localPath,responseText);
6169 w.setButtons([],me.statusReloadingCore);
6170 var backupPath = w.getValue("backupPath
");
6171 var newLoc = document.location.toString() + '?time=' + new Date().convertToYYYYMMDDHHMM() + '#upgrade:[[' + encodeURI(backupPath) + ']]';
6172 window.setTimeout(function () {window.location = newLoc;},10);
6174 var step2 = [me.step2Html_downgrade,me.step2Html_restore,me.step2Html_upgrade][compareVersions(version,newVer) + 1];
6175 w.addStep(me.step2Title,step2.format([formatVersion(newVer),formatVersion(version)]));
6176 w.setButtons([{caption: me.startLabel, tooltip: me.startPrompt, onClick: onStartUpgrade},{caption: me.cancelLabel, tooltip: me.cancelPrompt, onClick: me.onCancel}]);
6179 config.macros.upgrade.onCancel = function(e)
6181 var me = config.macros.upgrade;
6182 var w = new Wizard(this);
6183 w.addStep(me.step3Title,me.step3Html);
6188 config.macros.upgrade.extractVersion = function(upgradeFile)
6190 var re = /^var version = \{title: "([^
"]+)", major: (\d+), minor: (\d+), revision: (\d+)(, beta: (\d+)){
0,
1}, date: new Date\(
"([^"]+)
"\)/mg;
6191 var m = re.exec(upgradeFile);
6192 return m ? {title: m[1], major: m[2], minor: m[3], revision: m[4], beta: m[6], date: new Date(m[7])} : null;
6195 function upgradeFrom(path)
6197 var importStore = new TiddlyWiki();
6198 var tw = loadFile(path);
6199 if(window.netscape !== undefined)
6200 tw = convertUTF8ToUnicode(tw);
6201 importStore.importTiddlyWiki(tw);
6202 importStore.forEachTiddler(function(title,tiddler) {
6203 if(!store.getTiddler(title)) {
6204 store.addTiddler(tiddler);
6208 saveChanges(); //# To create appropriate Markup* sections
6209 alert(config.messages.upgradeDone.format([formatVersion()]));
6210 window.location = window.location.toString().substr(0,window.location.toString().lastIndexOf('?'));
6217 // Synchronisation handlers
6218 config.syncers = {};
6221 var currSync = null;
6224 config.macros.sync.handler = function(place,macroName,params,wikifier,paramString,tiddler)
6226 if(!wikifier.isStatic)
6227 this.startSync(place);
6230 config.macros.sync.cancelSync = function()
6235 config.macros.sync.startSync = function(place)
6238 config.macros.sync.cancelSync();
6240 currSync.syncList = this.getSyncableTiddlers();
6241 currSync.syncTasks = this.createSyncTasks(currSync.syncList);
6242 this.preProcessSyncableTiddlers(currSync.syncList);
6243 var wizard = new Wizard();
6244 currSync.wizard = wizard;
6245 wizard.createWizard(place,this.wizardTitle);
6246 wizard.addStep(this.step1Title,this.step1Html);
6247 var markList = wizard.getElement("markList
");
6248 var listWrapper = document.createElement("div
");
6249 markList.parentNode.insertBefore(listWrapper,markList);
6250 currSync.listView = ListView.create(listWrapper,currSync.syncList,this.listViewTemplate);
6251 this.processSyncableTiddlers(currSync.syncList);
6252 wizard.setButtons([{caption: this.syncLabel, tooltip: this.syncPrompt, onClick: this.doSync}]);
6255 config.macros.sync.getSyncableTiddlers = function()
6258 store.forEachTiddler(function(title,tiddler) {
6260 syncItem.serverType = tiddler.getServerType();
6261 syncItem.serverHost = tiddler.fields['server.host'];
6262 if(syncItem.serverType && syncItem.serverHost) {
6263 syncItem.serverWorkspace = tiddler.fields['server.workspace'];
6264 syncItem.tiddler = tiddler;
6265 syncItem.title = tiddler.title;
6266 syncItem.isTouched = tiddler.isTouched();
6267 syncItem.selected = syncItem.isTouched;
6268 syncItem.syncStatus = config.macros.sync.syncStatusList[syncItem.isTouched ? "changedLocally
" : "none
"];
6269 syncItem.status = syncItem.syncStatus.text;
6270 list.push(syncItem);
6273 list.sort(function(a,b) {return a.title < b.title ? -1 : (a.title == b.title ? 0 : +1);});
6277 config.macros.sync.preProcessSyncableTiddlers = function(syncList)
6279 for(var i=0; i<syncList.length; i++) {
6280 var si = syncList[i];
6281 si.serverUrl = si.syncTask.syncMachine.generateTiddlerInfo(si.tiddler).uri;
6285 config.macros.sync.processSyncableTiddlers = function(syncList)
6287 for(var i=0; i<syncList.length; i++) {
6288 var si = syncList[i];
6289 si.rowElement.style.display = si.syncStatus.display;
6290 if(si.syncStatus.className)
6291 si.rowElement.className = si.syncStatus.className;
6295 config.macros.sync.createSyncTasks = function(syncList)
6298 for(var i=0; i<syncList.length; i++) {
6299 var si = syncList[i];
6301 for(var j=0; j<syncTasks.length; j++) {
6302 var cst = syncTasks[j];
6303 if(si.serverType == cst.serverType && si.serverHost == cst.serverHost && si.serverWorkspace == cst.serverWorkspace)
6308 r.syncItems.push(si);
6310 si.syncTask = this.createSyncTask(si);
6311 syncTasks.push(si.syncTask);
6317 config.macros.sync.createSyncTask = function(syncItem)
6320 st.serverType = syncItem.serverType;
6321 st.serverHost = syncItem.serverHost;
6322 st.serverWorkspace = syncItem.serverWorkspace;
6323 st.syncItems = [syncItem];
6324 st.syncMachine = new SyncMachine(st.serverType,{
6326 return this.openHost(st.serverHost,"openWorkspace
");
6328 openWorkspace: function() {
6329 return this.openWorkspace(st.serverWorkspace,"getTiddlerList
");
6331 getTiddlerList: function() {
6332 return this.getTiddlerList("onGetTiddlerList
");
6334 onGetTiddlerList: function(context) {
6335 var tiddlers = context.tiddlers;
6336 for(var i=0; i<st.syncItems.length; i++) {
6337 var si = st.syncItems[i];
6338 var f = tiddlers.findByField("title
",si.title);
6340 if(tiddlers[f].fields['server.page.revision'] > si.tiddler.fields['server.page.revision']) {
6341 si.syncStatus = config.macros.sync.syncStatusList[si.isTouched ? 'changedBoth' : 'changedServer'];
6344 si.syncStatus = config.macros.sync.syncStatusList.notFound;
6346 config.macros.sync.updateSyncStatus(si);
6349 getTiddler: function(title) {
6350 return this.getTiddler(title,"onGetTiddler
");
6352 onGetTiddler: function(context) {
6353 var tiddler = context.tiddler;
6354 var syncItem = st.syncItems.findByField("title
",tiddler.title);
6355 if(syncItem !== null) {
6356 syncItem = st.syncItems[syncItem];
6357 store.saveTiddler(tiddler.title, tiddler.title, tiddler.text, tiddler.modifier, tiddler.modified, tiddler.tags, tiddler.fields, true, tiddler.created);
6358 syncItem.syncStatus = config.macros.sync.syncStatusList.gotFromServer;
6359 config.macros.sync.updateSyncStatus(syncItem);
6362 putTiddler: function(tiddler) {
6363 return this.putTiddler(tiddler,"onPutTiddler
");
6365 onPutTiddler: function(context) {
6366 var title = context.title;
6367 var syncItem = st.syncItems.findByField("title
",title);
6368 if(syncItem !== null) {
6369 syncItem = st.syncItems[syncItem];
6370 store.resetTiddler(title);
6371 if(context.status) {
6372 syncItem.syncStatus = config.macros.sync.syncStatusList.putToServer;
6373 config.macros.sync.updateSyncStatus(syncItem);
6378 st.syncMachine.go();
6382 config.macros.sync.updateSyncStatus = function(syncItem)
6384 var e = syncItem.colElements["status
"];
6386 createTiddlyText(e,syncItem.syncStatus.text);
6387 syncItem.rowElement.style.display = syncItem.syncStatus.display;
6388 if(syncItem.syncStatus.className)
6389 syncItem.rowElement.className = syncItem.syncStatus.className;
6392 config.macros.sync.doSync = function(e)
6394 var rowNames = ListView.getSelectedRows(currSync.listView);
6395 var sl = config.macros.sync.syncStatusList;
6396 for(var i=0; i<currSync.syncList.length; i++) {
6397 var si = currSync.syncList[i];
6398 if(rowNames.indexOf(si.title) != -1) {
6400 switch(si.syncStatus) {
6401 case sl.changedServer:
6402 r = si.syncTask.syncMachine.go("getTiddler
",si.title);
6405 case sl.changedLocally:
6406 case sl.changedBoth:
6407 r = si.syncTask.syncMachine.go("putTiddler
",si.tiddler);
6413 displayMessage("Error in doSync:
" + r);
6419 function SyncMachine(serverType,steps)
6421 this.serverType = serverType;
6422 this.adaptor = new config.adaptors[serverType]();
6426 SyncMachine.prototype.go = function(step,context)
6428 var r = context ? context.status : null;
6429 if(typeof r == "string
") {
6430 this.invokeError(r);
6433 var h = this.steps[step ? step : "start
"];
6436 r = h.call(this,context);
6437 if(typeof r == "string
")
6438 this.invokeError(r);
6442 SyncMachine.prototype.invokeError = function(message)
6444 if(this.steps.error)
6445 this.steps.error(message);
6448 SyncMachine.prototype.openHost = function(host,nextStep)
6451 return me.adaptor.openHost(host,null,null,function(context) {me.go(nextStep,context);});
6454 SyncMachine.prototype.getWorkspaceList = function(nextStep)
6457 return me.adaptor.getWorkspaceList(null,null,function(context) {me.go(nextStep,context);});
6460 SyncMachine.prototype.openWorkspace = function(workspace,nextStep)
6463 return me.adaptor.openWorkspace(workspace,null,null,function(context) {me.go(nextStep,context);});
6466 SyncMachine.prototype.getTiddlerList = function(nextStep)
6469 return me.adaptor.getTiddlerList(null,null,function(context) {me.go(nextStep,context);});
6472 SyncMachine.prototype.generateTiddlerInfo = function(tiddler)
6474 return this.adaptor.generateTiddlerInfo(tiddler);
6477 SyncMachine.prototype.getTiddler = function(title,nextStep)
6480 return me.adaptor.getTiddler(title,null,null,function(context) {me.go(nextStep,context);});
6483 SyncMachine.prototype.putTiddler = function(tiddler,nextStep)
6486 return me.adaptor.putTiddler(tiddler,null,null,function(context) {me.go(nextStep,context);});
6490 //-- Manager UI for groups of tiddlers
6493 config.macros.plugins.handler = function(place,macroName,params,wikifier,paramString)
6495 var wizard = new Wizard();
6496 wizard.createWizard(place,this.wizardTitle);
6497 wizard.addStep(this.step1Title,this.step1Html);
6498 var markList = wizard.getElement("markList
");
6499 var listWrapper = document.createElement("div
");
6500 markList.parentNode.insertBefore(listWrapper,markList);
6501 listWrapper.setAttribute("refresh
","macro
");
6502 listWrapper.setAttribute("macroName
","plugins
");
6503 listWrapper.setAttribute("params
",paramString);
6504 this.refresh(listWrapper,paramString);
6507 config.macros.plugins.refresh = function(listWrapper,params)
6509 var wizard = new Wizard(listWrapper);
6510 var selectedRows = [];
6511 ListView.forEachSelector(listWrapper,function(e,rowName) {
6513 selectedRows.push(e.getAttribute("rowName
"));
6515 removeChildren(listWrapper);
6516 params = params.parseParams("anon
");
6517 var plugins = installedPlugins.slice(0);
6519 var configTiddlers = store.getTaggedTiddlers("systemConfig
");
6520 for(t=0; t<configTiddlers.length; t++) {
6521 tiddler = configTiddlers[t];
6522 if(plugins.findByField("title
",tiddler.title) == null) {
6523 p = getPluginInfo(tiddler);
6525 p.log.splice(0,0,this.skippedText);
6529 for(t=0; t<plugins.length; t++) {
6531 p.size = p.tiddler.text ? p.tiddler.text.length : 0;
6532 p.forced = p.tiddler.isTagged("systemConfigForce
");
6533 p.disabled = p.tiddler.isTagged("systemConfigDisable
");
6534 p.Selected = selectedRows.indexOf(plugins[t].title) != -1;
6536 if(plugins.length == 0) {
6537 createTiddlyElement(listWrapper,"em
",null,null,this.noPluginText);
6538 wizard.setButtons([]);
6540 var listView = ListView.create(listWrapper,plugins,this.listViewTemplate,this.onSelectCommand);
6541 wizard.setValue("listView
",listView);
6543 {caption: config.macros.plugins.removeLabel, tooltip: config.macros.plugins.removePrompt, onClick: config.macros.plugins.doRemoveTag},
6544 {caption: config.macros.plugins.deleteLabel, tooltip: config.macros.plugins.deletePrompt, onClick: config.macros.plugins.doDelete}
6549 config.macros.plugins.doRemoveTag = function(e)
6551 var wizard = new Wizard(this);
6552 var listView = wizard.getValue("listView
");
6553 var rowNames = ListView.getSelectedRows(listView);
6554 if(rowNames.length == 0) {
6555 alert(config.messages.nothingSelected);
6557 for(var t=0; t<rowNames.length; t++)
6558 store.setTiddlerTag(rowNames[t],false,"systemConfig
");
6562 config.macros.plugins.doDelete = function(e)
6564 var wizard = new Wizard(this);
6565 var listView = wizard.getValue("listView
");
6566 var rowNames = ListView.getSelectedRows(listView);
6567 if(rowNames.length == 0) {
6568 alert(config.messages.nothingSelected);
6570 if(confirm(config.macros.plugins.confirmDeleteText.format([rowNames.join(",
")]))) {
6571 for(var t=0; t<rowNames.length; t++) {
6572 store.removeTiddler(rowNames[t]);
6573 story.closeTiddler(rowNames[t],true);
6583 function getMessageDiv()
6585 var msgArea = document.getElementById("messageArea
");
6588 if(!msgArea.hasChildNodes())
6589 createTiddlyButton(createTiddlyElement(msgArea,"div
",null,"messageToolbar
"),
6590 config.messages.messageClose.text,
6591 config.messages.messageClose.tooltip,
6593 msgArea.style.display = "block
";
6594 return createTiddlyElement(msgArea,"div
");
6597 function displayMessage(text,linkText)
6599 var e = getMessageDiv();
6605 var link = createTiddlyElement(e,"a
",null,null,text);
6606 link.href = linkText;
6607 link.target = "_blank
";
6609 e.appendChild(document.createTextNode(text));
6613 function clearMessage()
6615 var msgArea = document.getElementById("messageArea
");
6617 removeChildren(msgArea);
6618 msgArea.style.display = "none
";
6624 //-- Refresh mechanism
6627 config.notifyTiddlers = [
6628 {name: "StyleSheetLayout
", notify: refreshStyles},
6629 {name: "StyleSheetColors
", notify: refreshStyles},
6630 {name: "StyleSheet
", notify: refreshStyles},
6631 {name: "StyleSheetPrint
", notify: refreshStyles},
6632 {name: "PageTemplate
", notify: refreshPageTemplate},
6633 {name: "SiteTitle
", notify: refreshPageTitle},
6634 {name: "SiteSubtitle
", notify: refreshPageTitle},
6635 {name: "ColorPalette
", notify: refreshColorPalette},
6636 {name: null, notify: refreshDisplay}
6639 config.refreshers = {
6640 link: function(e,changeList)
6642 var title = e.getAttribute("tiddlyLink
");
6643 refreshTiddlyLink(e,title);
6647 tiddler: function(e,changeList)
6649 var title = e.getAttribute("tiddler
");
6650 var template = e.getAttribute("template
");
6651 if(changeList && changeList.indexOf(title) != -1 && !story.isDirty(title))
6652 story.refreshTiddler(title,template,true);
6654 refreshElements(e,changeList);
6658 content: function(e,changeList)
6660 var title = e.getAttribute("tiddler
");
6661 var force = e.getAttribute("force
");
6662 if(force != null || changeList == null || changeList.indexOf(title) != -1) {
6664 wikify(store.getTiddlerText(title,""),e,null,store.fetchTiddler(title));
6670 macro: function(e,changeList)
6672 var macro = e.getAttribute("macroName
");
6673 var params = e.getAttribute("params
");
6675 macro = config.macros[macro];
6676 if(macro && macro.refresh)
6677 macro.refresh(e,params);
6682 config.refresherData = {
6683 styleSheet: "StyleSheet
",
6684 defaultStyleSheet: "StyleSheet
",
6685 pageTemplate: "PageTemplate
",
6686 defaultPageTemplate: "PageTemplate
",
6687 colorPalette: "ColorPalette
",
6688 defaultColorPalette: "ColorPalette
"
6691 function refreshElements(root,changeList)
6693 var nodes = root.childNodes;
6694 for(var c=0; c<nodes.length; c++) {
6695 var e = nodes[c], type = null;
6696 if(e.getAttribute && (e.tagName ? e.tagName != "IFRAME
" : true))
6697 type = e.getAttribute("refresh
");
6698 var refresher = config.refreshers[type];
6699 var refreshed = false;
6700 if(refresher != undefined)
6701 refreshed = refresher(e,changeList);
6702 if(e.hasChildNodes() && !refreshed)
6703 refreshElements(e,changeList);
6707 function applyHtmlMacros(root,tiddler)
6709 var e = root.firstChild;
6711 var nextChild = e.nextSibling;
6712 if(e.getAttribute) {
6713 var macro = e.getAttribute("macro
");
6715 e.removeAttribute("macro
");
6717 var p = macro.indexOf(" ");
6719 params = macro.substr(p+1);
6720 macro = macro.substr(0,p);
6722 invokeMacro(e,macro,params,null,tiddler);
6725 if(e.hasChildNodes())
6726 applyHtmlMacros(e,tiddler);
6731 function refreshPageTemplate(title)
6733 var stash = createTiddlyElement(document.body,"div
");
6734 stash.style.display = "none
";
6735 var display = story.getContainer();
6738 nodes = display.childNodes;
6739 for(t=nodes.length-1; t>=0; t--)
6740 stash.appendChild(nodes[t]);
6742 var wrapper = document.getElementById("contentWrapper
");
6744 var isAvailable = function(title) {
6745 var s = title ? title.indexOf(config.textPrimitives.sectionSeparator) : -1;
6747 title = title.substr(0,s);
6748 return store.tiddlerExists(title) || store.isShadowTiddler(title);
6750 if(!title || !isAvailable(title))
6751 title = config.refresherData.pageTemplate;
6752 if(!isAvailable(title))
6753 title = config.refresherData.defaultPageTemplate; //# this one is always avaialable
6754 wrapper.innerHTML = store.getRecursiveTiddlerText(title,null,10);
6755 applyHtmlMacros(wrapper);
6756 refreshElements(wrapper);
6757 display = story.getContainer();
6758 removeChildren(display);
6760 display = createTiddlyElement(wrapper,"div
",story.containerId());
6761 nodes = stash.childNodes;
6762 for(t=nodes.length-1; t>=0; t--)
6763 display.appendChild(nodes[t]);
6767 function refreshDisplay(hint)
6769 if(typeof hint == "string
")
6771 var e = document.getElementById("contentWrapper
");
6772 refreshElements(e,hint);
6773 if(backstage.isPanelVisible()) {
6774 e = document.getElementById("backstage
");
6775 refreshElements(e,hint);
6779 function refreshPageTitle()
6781 document.title = getPageTitle();
6784 function getPageTitle()
6786 var st = wikifyPlain("SiteTitle
");
6787 var ss = wikifyPlain("SiteSubtitle
");
6788 return st + ((st == "" || ss == "") ? "" : " -
") + ss;
6791 function refreshStyles(title,doc)
6793 setStylesheet(title == null ? "" : store.getRecursiveTiddlerText(title,"",10),title,doc || document);
6796 function refreshColorPalette(title)
6802 function refreshAll()
6804 refreshPageTemplate();
6806 refreshStyles("StyleSheetLayout
");
6807 refreshStyles("StyleSheetColors
");
6808 refreshStyles(config.refresherData.styleSheet);
6809 refreshStyles("StyleSheetPrint
");
6816 config.optionHandlers = {
6818 get: function(name) {return encodeCookie(config.options[name].toString());},
6819 set: function(name,value) {config.options[name] = decodeCookie(value);}
6822 get: function(name) {return config.options[name] ? "true
" : "false
";},
6823 set: function(name,value) {config.options[name] = value == "true
";}
6827 function loadOptionsCookie()
6831 var cookies = document.cookie.split(";
");
6832 for(var c=0; c<cookies.length; c++) {
6833 var p = cookies[c].indexOf("=
");
6835 var name = cookies[c].substr(0,p).trim();
6836 var value = cookies[c].substr(p+1).trim();
6837 var optType = name.substr(0,3);
6838 if(config.optionHandlers[optType] && config.optionHandlers[optType].set)
6839 config.optionHandlers[optType].set(name,value);
6844 function saveOptionCookie(name)
6849 var optType = name.substr(0,3);
6850 if(config.optionHandlers[optType] && config.optionHandlers[optType].get)
6851 c += config.optionHandlers[optType].get(name);
6852 c += "; expires=Fri,
1 Jan
2038 12:
00:
00 UTC; path=/
";
6853 document.cookie = c;
6856 function encodeCookie(s)
6858 return escape(convertUnicodeToHtmlEntities(s));
6861 function decodeCookie(s)
6864 var re = /&#[0-9]{1,5};/g;
6865 return s.replace(re,function($0) {return String.fromCharCode(eval($0.replace(/[&#;]/g,"")));});
6869 config.macros.option.genericCreate = function(place,type,opt,className,desc)
6871 var typeInfo = config.macros.option.types[type];
6872 var c = document.createElement(typeInfo.elementType);
6873 if(typeInfo.typeValue)
6874 c.setAttribute("type
",typeInfo.typeValue);
6875 c[typeInfo.eventName] = typeInfo.onChange;
6876 c.setAttribute("option
",opt);
6877 c.className = className || typeInfo.className;
6878 if(config.optionsDesc[opt])
6879 c.setAttribute("title
",config.optionsDesc[opt]);
6880 place.appendChild(c);
6882 createTiddlyText(place,config.optionsDesc[opt] || opt);
6883 c[typeInfo.valueField] = config.options[opt];
6887 config.macros.option.genericOnChange = function(e)
6889 var opt = this.getAttribute("option
");
6891 var optType = opt.substr(0,3);
6892 var handler = config.macros.option.types[optType];
6893 if(handler.elementType && handler.valueField)
6894 config.macros.option.propagateOption(opt,handler.valueField,this[handler.valueField],handler.elementType,this);
6899 config.macros.option.types = {
6901 elementType: "input
",
6902 valueField: "value
",
6903 eventName: "onchange
",
6904 className: "txtOptionInput
",
6905 create: config.macros.option.genericCreate,
6906 onChange: config.macros.option.genericOnChange
6909 elementType: "input
",
6910 valueField: "checked
",
6911 eventName: "onclick
",
6912 className: "chkOptionInput
",
6913 typeValue: "checkbox
",
6914 create: config.macros.option.genericCreate,
6915 onChange: config.macros.option.genericOnChange
6919 config.macros.option.propagateOption = function(opt,valueField,value,elementType,elem)
6921 config.options[opt] = value;
6922 saveOptionCookie(opt);
6923 var nodes = document.getElementsByTagName(elementType);
6924 for(var t=0; t<nodes.length; t++) {
6925 var optNode = nodes[t].getAttribute("option
");
6926 if(opt == optNode && nodes[t]!=elem)
6927 nodes[t][valueField] = value;
6931 config.macros.option.handler = function(place,macroName,params,wikifier,paramString)
6933 params = paramString.parseParams("anon
",null,true,false,false);
6934 var opt = (params[1] && params[1].name == "anon
") ? params[1].value : getParam(params,"name
",null);
6935 var className = (params[2] && params[2].name == "anon
") ? params[2].value : getParam(params,"class
",null);
6936 var desc = getParam(params,"desc
","no
");
6937 var type = opt.substr(0,3);
6938 var h = config.macros.option.types[type];
6940 h.create(place,type,opt,className,desc);
6943 config.macros.options.handler = function(place,macroName,params,wikifier,paramString)
6945 params = paramString.parseParams("anon
",null,true,false,false);
6946 var showUnknown = getParam(params,"showUnknown
","no
");
6947 var wizard = new Wizard();
6948 wizard.createWizard(place,this.wizardTitle);
6949 wizard.addStep(this.step1Title,this.step1Html);
6950 var markList = wizard.getElement("markList
");
6951 var chkUnknown = wizard.getElement("chkUnknown
");
6952 chkUnknown.checked = showUnknown == "yes
";
6953 chkUnknown.onchange = this.onChangeUnknown;
6954 var listWrapper = document.createElement("div
");
6955 markList.parentNode.insertBefore(listWrapper,markList);
6956 wizard.setValue("listWrapper
",listWrapper);
6957 this.refreshOptions(listWrapper,showUnknown == "yes
");
6960 config.macros.options.refreshOptions = function(listWrapper,showUnknown)
6963 for(var n in config.options) {
6967 opt.lowlight = !config.optionsDesc[n];
6968 opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
6969 if(!opt.lowlight || showUnknown)
6972 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);});
6973 var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
6974 for(n=0; n<opts.length; n++) {
6975 var type = opts[n].name.substr(0,3);
6976 var h = config.macros.option.types[type];
6978 h.create(opts[n].colElements['option'],type,opts[n].name,null,"no
");
6983 config.macros.options.onChangeUnknown = function(e)
6985 var wizard = new Wizard(this);
6986 var listWrapper = wizard.getValue("listWrapper
");
6987 removeChildren(listWrapper);
6988 config.macros.options.refreshOptions(listWrapper,this.checked);
6996 var saveUsingSafari = false;
6998 var startSaveArea = '<div id="' + 'storeArea
">'; // Split up into two so that indexOf() of this source doesn't find it
6999 var endSaveArea = '</d' + 'iv>';
7001 // If there are unsaved changes, force the user to confirm before exitting
7002 function confirmExit()
7004 hadConfirmExit = true;
7005 if((store && store.isDirty && store.isDirty()) || (story && story.areAnyDirty && story.areAnyDirty()))
7006 return config.messages.confirmExit;
7009 // Give the user a chance to save changes before exitting
7010 function checkUnsavedChanges()
7012 if(store && store.isDirty && store.isDirty() && window.hadConfirmExit === false) {
7013 if(confirm(config.messages.unsavedChangesWarning))
7018 function updateLanguageAttribute(s)
7021 var mRE = /(<html(?:.*?)?)(?: xml:lang\="([a-z]+)
")?(?: lang\="([a-z]+)
")?>/;
7022 var m = mRE.exec(s);
7026 t += ' xml:lang="' + config.locale + '
"';
7028 t += ' lang="' + config.locale + '
"';
7030 s = s.substr(0,m.index) + t + s.substr(m.index+m[0].length);
7036 function updateMarkupBlock(s,blockName,tiddlerName)
7038 return s.replaceChunk(
7039 "<!--%0-START-->".format([blockName]),
7040 "<!--%0-END-->".format([blockName]),
7041 "\n
" + convertUnicodeToFileFormat(store.getRecursiveTiddlerText(tiddlerName,"")) + "\n
");
7044 function updateOriginal(original,posDiv,localPath)
7047 posDiv = locateStoreArea(original);
7049 alert(config.messages.invalidFileError.format([localPath]));
7052 var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n
" +
7053 convertUnicodeToFileFormat(store.allTiddlersAsHtml()) + "\n
" +
7054 original.substr(posDiv[1]);
7055 var newSiteTitle = convertUnicodeToFileFormat(getPageTitle()).htmlEncode();
7056 revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
7057 revised = updateLanguageAttribute(revised);
7058 revised = updateMarkupBlock(revised,"PRE-HEAD
","MarkupPreHead
");
7059 revised = updateMarkupBlock(revised,"POST-HEAD
","MarkupPostHead
");
7060 revised = updateMarkupBlock(revised,"PRE-BODY
","MarkupPreBody
");
7061 revised = updateMarkupBlock(revised,"POST-SCRIPT
","MarkupPostBody
");
7065 function locateStoreArea(original)
7067 // Locate the storeArea div's
7068 var posOpeningDiv = original.indexOf(startSaveArea);
7069 var limitClosingDiv = original.indexOf("<
"+"!--POST-STOREAREA--
"+">");
7070 if(limitClosingDiv == -1)
7071 limitClosingDiv = original.indexOf("<
"+"!--POST-BODY-START--
"+">");
7072 var posClosingDiv = original.lastIndexOf(endSaveArea,limitClosingDiv == -1 ? original.length : limitClosingDiv);
7073 return (posOpeningDiv != -1 && posClosingDiv != -1) ? [posOpeningDiv,posClosingDiv] : null;
7076 function autoSaveChanges(onlyIfDirty,tiddlers)
7078 if(config.options.chkAutoSave)
7079 saveChanges(onlyIfDirty,tiddlers);
7082 function loadOriginal(localPath)
7084 return loadFile(localPath);
7087 // Save this tiddlywiki with the pending changes
7088 function saveChanges(onlyIfDirty,tiddlers)
7090 if(onlyIfDirty && !store.isDirty())
7093 var t0 = new Date();
7094 var originalPath = document.location.toString();
7095 if(originalPath.substr(0,5) != "file:
") {
7096 alert(config.messages.notFileUrlError);
7097 if(store.tiddlerExists(config.messages.saveInstructions))
7098 story.displayTiddler(null,config.messages.saveInstructions);
7101 var localPath = getLocalPath(originalPath);
7102 var original = loadOriginal(localPath);
7103 if(original == null) {
7104 alert(config.messages.cantSaveError);
7105 if(store.tiddlerExists(config.messages.saveInstructions))
7106 story.displayTiddler(null,config.messages.saveInstructions);
7109 var posDiv = locateStoreArea(original);
7111 alert(config.messages.invalidFileError.format([localPath]));
7114 saveMain(localPath,original,posDiv);
7115 if(config.options.chkSaveBackups)
7116 saveBackup(localPath,original);
7117 if(config.options.chkSaveEmptyTemplate)
7118 saveEmpty(localPath,original,posDiv);
7119 if(config.options.chkGenerateAnRssFeed && saveRss instanceof Function)
7121 if(config.options.chkDisplayInstrumentation)
7122 displayMessage("saveChanges
" + (new Date()-t0) + " ms
");
7125 function saveMain(localPath,original,posDiv)
7129 var revised = updateOriginal(original,posDiv,localPath);
7130 save = saveFile(localPath,revised);
7135 displayMessage(config.messages.mainSaved,"file://
" + localPath);
7136 store.setDirty(false);
7138 alert(config.messages.mainFailed);
7142 function saveBackup(localPath,original)
7144 var backupPath = getBackupPath(localPath);
7145 var backup = copyFile(backupPath,localPath);
7147 backup = saveFile(backupPath,original);
7149 displayMessage(config.messages.backupSaved,"file://
" + backupPath);
7151 alert(config.messages.backupFailed);
7154 function saveEmpty(localPath,original,posDiv)
7157 if((p = localPath.lastIndexOf("/
")) != -1)
7158 emptyPath = localPath.substr(0,p) + "/
";
7159 else if((p = localPath.lastIndexOf("\\
")) != -1)
7160 emptyPath = localPath.substr(0,p) + "\\
";
7162 emptyPath = localPath + ".
";
7163 emptyPath += "empty.html
";
7164 var empty = original.substr(0,posDiv[0] + startSaveArea.length) + original.substr(posDiv[1]);
7165 var emptySave = saveFile(emptyPath,empty);
7167 displayMessage(config.messages.emptySaved,"file://
" + emptyPath);
7169 alert(config.messages.emptyFailed);
7172 function getLocalPath(origPath)
7174 var originalPath = convertUriToUTF8(origPath,config.options.txtFileSystemCharSet);
7175 // Remove any location or query part of the URL
7176 var argPos = originalPath.indexOf("?
");
7178 originalPath = originalPath.substr(0,argPos);
7179 var hashPos = originalPath.indexOf("#
");
7181 originalPath = originalPath.substr(0,hashPos);
7182 // Convert file://localhost/ to file:///
7183 if(originalPath.indexOf("file://localhost/
") == 0)
7184 originalPath = "file://
" + originalPath.substr(16);
7185 // Convert to a native file format
7187 if(originalPath.charAt(9) == ":
") // pc local file
7188 localPath = unescape(originalPath.substr(8)).replace(new RegExp("/
","g
"),"\\
");
7189 else if(originalPath.indexOf("file://///
") == 0) // FireFox pc network file
7190 localPath = "\\\\
" + unescape(originalPath.substr(10)).replace(new RegExp("/
","g
"),"\\
");
7191 else if(originalPath.indexOf("file:///
") == 0) // mac/unix local file
7192 localPath = unescape(originalPath.substr(7));
7193 else if(originalPath.indexOf("file:/
") == 0) // mac/unix local file
7194 localPath = unescape(originalPath.substr(5));
7195 else // pc network file
7196 localPath = "\\\\
" + unescape(originalPath.substr(7)).replace(new RegExp("/
","g
"),"\\
");
7200 function getBackupPath(localPath,title,extension)
7203 var dirPathPos = localPath.lastIndexOf("\\
");
7204 if(dirPathPos == -1) {
7205 dirPathPos = localPath.lastIndexOf("/
");
7208 var backupFolder = config.options.txtBackupFolder;
7209 if(!backupFolder || backupFolder == "")
7211 var backupPath = localPath.substr(0,dirPathPos) + slash + backupFolder + localPath.substr(dirPathPos);
7212 backupPath = backupPath.substr(0,backupPath.lastIndexOf(".
")) + ".
";
7214 backupPath += title.replace(/[\\\/\*\?\":<
> ]/g,
"_") +
".";
7215 backupPath += (new Date()).convertToYYYYMMDDHHMMSSMMM() +
"." + (extension ||
"html");
7223 function saveRss(localPath)
7225 var rssPath = localPath.substr(
0,localPath.lastIndexOf(
".")) +
".xml";
7226 if(saveFile(rssPath,convertUnicodeToFileFormat(generateRss())))
7227 displayMessage(config.messages.rssSaved,
"file://" + rssPath);
7229 alert(config.messages.rssFailed);
7232 function generateRss()
7236 var u = store.getTiddlerText(
"SiteUrl");
7237 // Assemble the header
7238 s.push(
"<" +
"?xml version=\"1.0\
"?" +
">");
7239 s.push(
"<rss version=\"2.0\
">");
7240 s.push(
"<channel>");
7241 s.push(
"<title" +
">" + wikifyPlain(
"SiteTitle").htmlEncode() +
"</title" +
">");
7243 s.push(
"<link>" + u.htmlEncode() +
"</link>");
7244 s.push(
"<description>" + wikifyPlain(
"SiteSubtitle").htmlEncode() +
"</description>");
7245 s.push(
"<language>" + config.locale +
"</language>");
7246 s.push(
"<copyright>Copyright " + d.getFullYear() +
" " + config.options.txtUserName.htmlEncode() +
"</copyright>");
7247 s.push(
"<pubDate>" + d.toGMTString() +
"</pubDate>");
7248 s.push(
"<lastBuildDate>" + d.toGMTString() +
"</lastBuildDate>");
7249 s.push(
"<docs>http://blogs.law.harvard.edu/tech/rss</docs>");
7250 s.push(
"<generator>TiddlyWiki " + formatVersion() +
"</generator>");
7252 var tiddlers = store.getTiddlers(
"modified",
"excludeLists");
7253 var n = config.numRssItems
> tiddlers.length ?
0 : tiddlers.length-config.numRssItems;
7254 for(var t=tiddlers.length-
1; t
>=n; t--) {
7255 s.push(
"<item>\n" + tiddlers[t].toRssItem(u) +
"\n</item>");
7258 s.push(
"</channel>");
7261 return s.join(
"\n");
7265 //-- Filesystem code
7268 function convertUTF8ToUnicode(u)
7270 return config.browser.isOpera || !window.netscape ? manualConvertUTF8ToUnicode(u) : mozConvertUTF8ToUnicode(u);
7273 function manualConvertUTF8ToUnicode(utf)
7280 while(src < utf.length) {
7281 b1 = utf.charCodeAt(src++);
7284 } else if(b1 <
0xE0) {
7285 b2 = utf.charCodeAt(src++);
7286 c = String.fromCharCode(((b1 &
0x1F) <<
6) | (b2 &
0x3F));
7287 uni = uni.substring(
0,dst++).concat(c,utf.substr(src));
7289 b2 = utf.charCodeAt(src++);
7290 b3 = utf.charCodeAt(src++);
7291 c = String.fromCharCode(((b1 &
0xF) <<
12) | ((b2 &
0x3F) <<
6) | (b3 &
0x3F));
7292 uni = uni.substring(
0,dst++).concat(c,utf.substr(src));
7298 function mozConvertUTF8ToUnicode(u)
7301 netscape.security.PrivilegeManager.enablePrivilege(
"UniversalXPConnect");
7302 var converter = Components.classes[
"@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
7303 converter.charset =
"UTF-8";
7305 return manualConvertUTF8ToUnicode(u);
7307 var s = converter.ConvertToUnicode(u);
7308 var fin = converter.Finish();
7309 return fin.length
> 0 ? s+fin : s;
7312 function convertUnicodeToFileFormat(s)
7314 return config.browser.isOpera || !window.netscape ? convertUnicodeToHtmlEntities(s) : mozConvertUnicodeToUTF8(s);
7317 function convertUnicodeToHtmlEntities(s)
7319 var re = /[^\u0000-\u007F]/g;
7320 return s.replace(re,function($
0) {return
"&#" + $
0.charCodeAt(
0).toString() +
";";});
7323 function convertUnicodeToUTF8(s)
7325 // return convertUnicodeToFileFormat to allow plugin migration
7326 return convertUnicodeToFileFormat(s);
7329 function manualConvertUnicodeToUTF8(s)
7331 return unescape(encodeURIComponent(s));
7334 function mozConvertUnicodeToUTF8(s)
7337 netscape.security.PrivilegeManager.enablePrivilege(
"UniversalXPConnect");
7338 var converter = Components.classes[
"@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
7339 converter.charset =
"UTF-8";
7341 return manualConvertUnicodeToUTF8(s);
7343 var u = converter.ConvertFromUnicode(s);
7344 var fin = converter.Finish();
7345 return fin.length
> 0 ? u + fin : u;
7348 function convertUriToUTF8(uri,charSet)
7350 if(window.netscape == undefined || charSet == undefined || charSet ==
"")
7353 netscape.security.PrivilegeManager.enablePrivilege(
"UniversalXPConnect");
7354 var converter = Components.classes[
"@mozilla.org/intl/utf8converterservice;1"].getService(Components.interfaces.nsIUTF8ConverterService);
7358 return converter.convertURISpecToUTF8(uri,charSet);
7361 function copyFile(dest,source)
7363 return config.browser.isIE ? ieCopyFile(dest,source) : false;
7366 function saveFile(fileUrl,content)
7368 var r = mozillaSaveFile(fileUrl,content);
7370 r = ieSaveFile(fileUrl,content);
7372 r = javaSaveFile(fileUrl,content);
7376 function loadFile(fileUrl)
7378 var r = mozillaLoadFile(fileUrl);
7379 if((r == null) || (r == false))
7380 r = ieLoadFile(fileUrl);
7381 if((r == null) || (r == false))
7382 r = javaLoadFile(fileUrl);
7386 function ieCreatePath(path)
7389 var fso = new ActiveXObject(
"Scripting.FileSystemObject");
7394 var pos = path.lastIndexOf(
"\\");
7396 path = path.substring(
0,pos+
1);
7399 var parent = fso.GetParentFolderName(path);
7400 while(parent && !fso.FolderExists(parent)) {
7402 parent = fso.GetParentFolderName(parent);
7405 for(i=scan.length-
1;i
>=
0;i--) {
7406 if(!fso.FolderExists(scan[i])) {
7407 fso.CreateFolder(scan[i]);
7413 // Returns null if it can't do it, false if there's an error, true if it saved OK
7414 function ieSaveFile(filePath,content)
7416 ieCreatePath(filePath);
7418 var fso = new ActiveXObject(
"Scripting.FileSystemObject");
7422 var file = fso.OpenTextFile(filePath,
2,-
1,
0);
7423 file.Write(content);
7428 // Returns null if it can't do it, false if there's an error, or a string of the content if successful
7429 function ieLoadFile(filePath)
7432 var fso = new ActiveXObject(
"Scripting.FileSystemObject");
7433 var file = fso.OpenTextFile(filePath,
1);
7434 var content = file.ReadAll();
7442 function ieCopyFile(dest,source)
7446 var fso = new ActiveXObject(
"Scripting.FileSystemObject");
7447 fso.GetFile(source).Copy(dest);
7454 // Returns null if it can't do it, false if there's an error, true if it saved OK
7455 function mozillaSaveFile(filePath,content)
7457 if(window.Components) {
7459 netscape.security.PrivilegeManager.enablePrivilege(
"UniversalXPConnect");
7460 var file = Components.classes[
"@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
7461 file.initWithPath(filePath);
7463 file.create(
0,
0664);
7464 var out = Components.classes[
"@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
7465 out.init(file,
0x20|
0x02,
00004,null);
7466 out.write(content,content.length);
7477 // Returns null if it can't do it, false if there's an error, or a string of the content if successful
7478 function mozillaLoadFile(filePath)
7480 if(window.Components) {
7482 netscape.security.PrivilegeManager.enablePrivilege(
"UniversalXPConnect");
7483 var file = Components.classes[
"@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
7484 file.initWithPath(filePath);
7487 var inputStream = Components.classes[
"@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
7488 inputStream.init(file,
0x01,
00004,null);
7489 var sInputStream = Components.classes[
"@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
7490 sInputStream.init(inputStream);
7491 var contents = sInputStream.read(sInputStream.available());
7492 sInputStream.close();
7493 inputStream.close();
7502 function javaUrlToFilename(url)
7504 var f =
"//localhost";
7505 if(url.indexOf(f) ==
0)
7506 return url.substring(f.length);
7507 var i = url.indexOf(
":");
7508 return i
> 0 ? url.substring(i-
1) : url;
7511 function javaSaveFile(filePath,content)
7514 if(document.applets[
"TiddlySaver"])
7515 return document.applets[
"TiddlySaver"].saveFile(javaUrlToFilename(filePath),
"UTF-8",content);
7519 var s = new java.io.PrintStream(new java.io.FileOutputStream(javaUrlToFilename(filePath)));
7528 function javaLoadFile(filePath)
7531 if(document.applets[
"TiddlySaver"])
7532 return String(document.applets[
"TiddlySaver"].loadFile(javaUrlToFilename(filePath),
"UTF-8"));
7537 var r = new java.io.BufferedReader(new java.io.FileReader(javaUrlToFilename(filePath)));
7539 while((line = r.readLine()) != null)
7540 content.push(new String(line));
7545 return content.join(
"\n");
7549 //-- Server adaptor base class
7552 function AdaptorBase()
7559 AdaptorBase.prototype.close = function()
7564 AdaptorBase.prototype.fullHostName = function(host)
7569 if(!host.match(/:\/\//))
7570 host = 'http://' + host;
7571 if(host.substr(host.length-
1) == '/')
7572 host = host.substr(
0,host.length-
1)
7576 AdaptorBase.minHostName = function(host)
7578 return host ? host.replace(/^http:\/\//,'').replace(/\/$/,'') : '';
7581 AdaptorBase.prototype.setContext = function(context,userParams,callback)
7583 if(!context) context = {};
7584 context.userParams = userParams;
7585 if(callback) context.callback = callback;
7586 context.adaptor = this;
7588 context.host = this.host;
7589 context.host = this.fullHostName(context.host);
7590 if(!context.workspace)
7591 context.workspace = this.workspace;
7595 // Open the specified host
7596 AdaptorBase.prototype.openHost = function(host,context,userParams,callback)
7599 context = this.setContext(context,userParams,callback);
7600 context.status = true;
7602 window.setTimeout(function() {context.callback(context,userParams);},
10);
7606 // Open the specified workspace
7607 AdaptorBase.prototype.openWorkspace = function(workspace,context,userParams,callback)
7609 this.workspace = workspace;
7610 context = this.setContext(context,userParams,callback);
7611 context.status = true;
7613 window.setTimeout(function() {callback(context,userParams);},
10);
7618 //-- Server adaptor for talking to static TiddlyWiki files
7621 function FileAdaptor()
7625 FileAdaptor.prototype = new AdaptorBase();
7627 FileAdaptor.serverType = 'file';
7628 FileAdaptor.serverLabel = 'TiddlyWiki';
7630 FileAdaptor.loadTiddlyWikiCallback = function(status,context,responseText,url,xhr)
7632 context.status = status;
7634 context.statusText =
"Error reading file";
7636 context.adaptor.store = new TiddlyWiki();
7637 if(!context.adaptor.store.importTiddlyWiki(responseText)) {
7638 context.statusText = config.messages.invalidFileError.format([url]);
7639 context.status = false;
7642 context.complete(context,context.userParams);
7645 // Get the list of workspaces on a given server
7646 FileAdaptor.prototype.getWorkspaceList = function(context,userParams,callback)
7648 context = this.setContext(context,userParams,callback);
7649 context.workspaces = [{title:
"(default)"}];
7650 context.status = true;
7652 window.setTimeout(function() {callback(context,userParams);},
10);
7656 // Gets the list of tiddlers within a given workspace
7657 FileAdaptor.prototype.getTiddlerList = function(context,userParams,callback,filter)
7659 context = this.setContext(context,userParams,callback);
7661 context.filter = filter;
7662 context.complete = FileAdaptor.getTiddlerListComplete;
7664 var ret = context.complete(context,context.userParams);
7666 ret = loadRemoteFile(context.host,FileAdaptor.loadTiddlyWikiCallback,context);
7667 if(typeof ret !=
"string")
7673 FileAdaptor.getTiddlerListComplete = function(context,userParams)
7675 if(context.status) {
7676 if(context.filter) {
7677 context.tiddlers = context.adaptor.store.filterTiddlers(context.filter);
7679 context.tiddlers = [];
7680 context.adaptor.store.forEachTiddler(function(title,tiddler) {context.tiddlers.push(tiddler);});
7682 for(var i=
0; i
<context.tiddlers.length; i++) {
7683 context.tiddlers[i].fields['server.type'] = FileAdaptor.serverType;
7684 context.tiddlers[i].fields['server.host'] = AdaptorBase.minHostName(context.host);
7685 context.tiddlers[i].fields['server.page.revision'] = context.tiddlers[i].modified.convertToYYYYMMDDHHMM();
7687 context.status = true;
7689 if(context.callback) {
7690 window.setTimeout(function() {context.callback(context,userParams);},
10);
7695 FileAdaptor.prototype.generateTiddlerInfo = function(tiddler)
7698 info.uri = tiddler.fields['server.host'] +
"#" + tiddler.title;
7702 // Retrieve a tiddler from a given workspace on a given server
7703 FileAdaptor.prototype.getTiddler = function(title,context,userParams,callback)
7705 context = this.setContext(context,userParams,callback);
7706 context.title = title;
7707 context.complete = FileAdaptor.getTiddlerComplete;
7708 return context.adaptor.store ?
7709 context.complete(context,context.userParams) :
7710 loadRemoteFile(context.host,FileAdaptor.loadTiddlyWikiCallback,context);
7713 FileAdaptor.getTiddlerComplete = function(context,userParams)
7715 var t = context.adaptor.store.fetchTiddler(context.title);
7716 t.fields['server.type'] = FileAdaptor.serverType;
7717 t.fields['server.host'] = AdaptorBase.minHostName(context.host);
7718 t.fields['server.page.revision'] = t.modified.convertToYYYYMMDDHHMM();
7719 context.tiddler = t;
7720 context.status = true;
7721 if(context.allowSynchronous) {
7722 context.isSynchronous = true;
7723 context.callback(context,userParams);
7725 window.setTimeout(function() {context.callback(context,userParams);},
10);
7730 FileAdaptor.prototype.close = function()
7736 config.adaptors[FileAdaptor.serverType] = FileAdaptor;
7738 config.defaultAdaptor = FileAdaptor.serverType;
7741 //-- Remote HTTP requests
7744 function loadRemoteFile(url,callback,params)
7746 return httpReq(
"GET",url,callback,params);
7749 function httpReq(type,url,callback,params,headers,data,contentType,username,password,allowCache)
7753 x = new XMLHttpRequest(); //# Modern
7756 x = new ActiveXObject(
"Msxml2.XMLHTTP"); //# IE
6
7761 return
"Can't create XMLHttpRequest object";
7762 x.onreadystatechange = function() {
7764 var status = x.status;
7768 if(x.readyState ==
4 && callback && (status !== undefined)) {
7769 if([
0,
200,
201,
204,
207].contains(status))
7770 callback(true,params,x.responseText,url,x);
7772 callback(false,params,null,url,x);
7773 x.onreadystatechange = function(){};
7777 if(window.Components && window.netscape && window.netscape.security && document.location.protocol.indexOf(
"http") == -
1)
7778 window.netscape.security.PrivilegeManager.enablePrivilege(
"UniversalBrowserRead");
7781 url = url + (url.indexOf(
"?") <
0 ?
"?" :
"&") +
"nocache=" + Math.random();
7782 x.open(type,url,true,username,password);
7784 x.setRequestHeader(
"Content-Type", contentType ||
"application/x-www-form-urlencoded");
7785 if(x.overrideMimeType)
7786 x.setRequestHeader(
"Connection",
"close");
7788 for(var n in headers)
7789 x.setRequestHeader(n,headers[n]);
7791 x.setRequestHeader(
"X-Requested-With",
"TiddlyWiki " + formatVersion());
7794 return exceptionText(ex);
7799 // included for compatibility
7800 function getXMLHttpRequest()
7803 var x = new XMLHttpRequest(); // Modern
7806 x = new ActiveXObject(
"Msxml2.XMLHTTP"); // IE
6
7814 // included for compatibility
7815 function doHttp(type,url,data,contentType,username,password,callback,params,headers,allowCache)
7817 return httpReq(type,url,callback,params,headers,data,contentType,username,password,allowCache);
7821 //-- TiddlyWiki-specific utility functions
7824 function formatVersion(v)
7827 return v.major +
"." + v.minor +
"." + v.revision + (v.beta ?
" (beta " + v.beta +
")" :
"");
7830 function compareVersions(v1,v2)
7832 var a = [
"major",
"minor",
"revision"];
7833 for(var i =
0; i
<a.length; i++) {
7834 var x1 = v1[a[i]] ||
0;
7835 var x2 = v2[a[i]] ||
0;
7841 x1 = v1.beta ||
9999;
7842 x2 = v2.beta ||
9999;
7845 return x1
> x2 ? -
1 :
0;
7848 function createTiddlyButton(parent,text,tooltip,action,className,id,accessKey,attribs)
7850 var btn = document.createElement(
"a");
7852 btn.onclick = action;
7853 btn.setAttribute(
"href",
"javascript:;");
7856 btn.setAttribute(
"title",tooltip);
7858 btn.appendChild(document.createTextNode(text));
7859 btn.className = className ||
"button";
7863 for(var i in attribs) {
7864 btn.setAttribute(i,attribs[i]);
7868 parent.appendChild(btn);
7870 btn.setAttribute(
"accessKey",accessKey);
7874 function createTiddlyLink(place,title,includeText,className,isStatic,linkedFromTiddler,noToggle)
7876 var text = includeText ? title : null;
7877 var i = getTiddlyLinkInfo(title,className);
7878 var btn = isStatic ? createExternalLink(place,store.getTiddlerText(
"SiteUrl",null) +
"#" + title) : createTiddlyButton(place,text,i.subTitle,onClickTiddlerLink,i.classes);
7880 btn.className += ' ' + className;
7881 btn.setAttribute(
"refresh",
"link");
7882 btn.setAttribute(
"tiddlyLink",title);
7884 btn.setAttribute(
"noToggle",
"true");
7885 if(linkedFromTiddler) {
7886 var fields = linkedFromTiddler.getInheritedFields();
7888 btn.setAttribute(
"tiddlyFields",fields);
7893 function refreshTiddlyLink(e,title)
7895 var i = getTiddlyLinkInfo(title,e.className);
7896 e.className = i.classes;
7897 e.title = i.subTitle;
7900 function getTiddlyLinkInfo(title,currClasses)
7902 var classes = currClasses ? currClasses.split(
" ") : [];
7903 classes.pushUnique(
"tiddlyLink");
7904 var tiddler = store.fetchTiddler(title);
7907 subTitle = tiddler.getSubtitle();
7908 classes.pushUnique(
"tiddlyLinkExisting");
7909 classes.remove(
"tiddlyLinkNonExisting");
7910 classes.remove(
"shadow");
7912 classes.remove(
"tiddlyLinkExisting");
7913 classes.pushUnique(
"tiddlyLinkNonExisting");
7914 if(store.isShadowTiddler(title)) {
7915 subTitle = config.messages.shadowedTiddlerToolTip.format([title]);
7916 classes.pushUnique(
"shadow");
7918 subTitle = config.messages.undefinedTiddlerToolTip.format([title]);
7919 classes.remove(
"shadow");
7922 if(typeof config.annotations[title]==
"string")
7923 subTitle = config.annotations[title];
7924 return {classes: classes.join(
" "),subTitle: subTitle};
7927 function createExternalLink(place,url)
7929 var link = document.createElement(
"a");
7930 link.className =
"externalLink";
7932 link.title = config.messages.externalLinkTooltip.format([url]);
7933 if(config.options.chkOpenInNewWindow)
7934 link.target =
"_blank";
7935 place.appendChild(link);
7939 // Event handler for clicking on a tiddly link
7940 function onClickTiddlerLink(ev)
7942 var e = ev || window.event;
7943 var target = resolveTarget(e);
7947 var noToggle = null;
7949 title = link.getAttribute(
"tiddlyLink");
7950 fields = link.getAttribute(
"tiddlyFields");
7951 noToggle = link.getAttribute(
"noToggle");
7952 link = link.parentNode;
7953 } while(title == null && link != null);
7954 if(!store.isShadowTiddler(title)) {
7955 var f = fields ? fields.decodeHashMap() : {};
7956 fields = String.encodeHashMap(merge(f,config.defaultCustomFields,true));
7959 var toggling = e.metaKey || e.ctrlKey;
7960 if(config.options.chkToggleLinks)
7961 toggling = !toggling;
7964 if(store.getTiddler(title))
7966 story.displayTiddler(target,title,null,true,null,fields,toggling);
7972 // Create a button for a tag with a popup listing all the tiddlers that it tags
7973 function createTagButton(place,tag,excludeTiddler,title,tooltip)
7975 var btn = createTiddlyButton(place,title||tag,(tooltip||config.views.wikified.tag.tooltip).format([tag]),onClickTag);
7976 btn.setAttribute(
"tag",tag);
7978 btn.setAttribute(
"tiddler",excludeTiddler);
7982 // Event handler for clicking on a tiddler tag
7983 function onClickTag(ev)
7985 var e = ev || window.event;
7986 var popup = Popup.create(this);
7987 var tag = this.getAttribute(
"tag");
7988 var title = this.getAttribute(
"tiddler");
7990 var tagged = store.getTaggedTiddlers(tag);
7993 for(r=
0;r
<tagged.length;r++) {
7994 if(tagged[r].title != title)
7995 titles.push(tagged[r].title);
7997 var lingo = config.views.wikified.tag;
7998 if(titles.length
> 0) {
7999 var openAll = createTiddlyButton(createTiddlyElement(popup,
"li"),lingo.openAllText.format([tag]),lingo.openAllTooltip,onClickTagOpenAll);
8000 openAll.setAttribute(
"tag",tag);
8001 createTiddlyElement(createTiddlyElement(popup,
"li",null,
"listBreak"),
"div");
8002 for(r=
0; r
<titles.length; r++) {
8003 createTiddlyLink(createTiddlyElement(popup,
"li"),titles[r],true);
8006 createTiddlyText(createTiddlyElement(popup,
"li",null,
"disabled"),lingo.popupNone.format([tag]));
8008 createTiddlyElement(createTiddlyElement(popup,
"li",null,
"listBreak"),
"div");
8009 var h = createTiddlyLink(createTiddlyElement(popup,
"li"),tag,false);
8010 createTiddlyText(h,lingo.openTag.format([tag]));
8013 e.cancelBubble = true;
8014 if(e.stopPropagation) e.stopPropagation();
8018 // Event handler for 'open all' on a tiddler popup
8019 function onClickTagOpenAll(ev)
8021 var tiddlers = store.getTaggedTiddlers(this.getAttribute(
"tag"));
8022 story.displayTiddlers(this,tiddlers);
8026 function onClickError(ev)
8028 var e = ev || window.event;
8029 var popup = Popup.create(this);
8030 var lines = this.getAttribute(
"errorText").split(
"\n");
8031 for(var t=
0; t
<lines.length; t++)
8032 createTiddlyElement(popup,
"li",null,null,lines[t]);
8034 e.cancelBubble = true;
8035 if(e.stopPropagation) e.stopPropagation();
8039 function createTiddlyDropDown(place,onchange,options,defaultValue)
8041 var sel = createTiddlyElement(place,
"select");
8042 sel.onchange = onchange;
8043 for(var t=
0; t
<options.length; t++) {
8044 var e = createTiddlyElement(sel,
"option",null,null,options[t].caption);
8045 e.value = options[t].name;
8046 if(options[t].name == defaultValue)
8052 function createTiddlyPopup(place,caption,tooltip,tiddler)
8055 createTiddlyLink(place,caption,true);
8056 var btn = createTiddlyButton(place,glyph(
"downArrow"),tooltip,onClickTiddlyPopup,
"tiddlerPopupButton");
8057 btn.tiddler = tiddler;
8059 createTiddlyText(place,caption);
8063 function onClickTiddlyPopup(ev)
8065 var e = ev || window.event;
8066 var tiddler = this.tiddler;
8068 var popup = Popup.create(this,
"div",
"popupTiddler");
8069 wikify(tiddler.text,popup,null,tiddler);
8072 if(e) e.cancelBubble = true;
8073 if(e && e.stopPropagation) e.stopPropagation();
8077 function createTiddlyError(place,title,text)
8079 var btn = createTiddlyButton(place,title,null,onClickError,
"errorButton");
8080 if(text) btn.setAttribute(
"errorText",text);
8083 function merge(dst,src,preserveExisting)
8086 if(!preserveExisting || dst[i] === undefined)
8092 // Returns a string containing the description of an exception, optionally prepended by a message
8093 function exceptionText(e,message)
8095 var s = e.description || e.toString();
8096 return message ?
"%0:\n%1".format([message,s]) : s;
8099 // Displays an alert of an exception description with optional message
8100 function showException(e,message)
8102 alert(exceptionText(e,message));
8105 function alertAndThrow(m)
8111 function glyph(name)
8113 var g = config.glyphs;
8114 var b = g.currBrowser;
8117 while(!g.browsers[b]() && b < g.browsers.length-
1)
8123 return g.codes[name][b];
8126 if(!window.console) {
8127 console = {log:function(message) {displayMessage(message);}};
8131 //- Animation engine
8136 this.running =
0; // Incremented at start of each animation, decremented afterwards. If zero, the interval timer is disabled
8137 this.timerID =
0; // ID of the timer used for animating
8138 this.animations = []; // List of animations in progress
8142 // Start animation engine
8143 Animator.prototype.startAnimating = function() //# Variable number of arguments
8145 for(var t=
0; t
<arguments.length; t++)
8146 this.animations.push(arguments[t]);
8147 if(this.running ==
0) {
8149 this.timerID = window.setInterval(function() {me.doAnimate(me);},
10);
8151 this.running += arguments.length;
8154 // Perform an animation engine tick, calling each of the known animation modules
8155 Animator.prototype.doAnimate = function(me)
8158 while(a < me.animations.length) {
8159 var animation = me.animations[a];
8160 if(animation.tick()) {
8163 me.animations.splice(a,
1);
8164 if(--me.running ==
0)
8165 window.clearInterval(me.timerID);
8170 Animator.slowInSlowOut = function(progress)
8172 return(
1-((Math.cos(progress * Math.PI)+
1)/
2));
8176 //-- Morpher animation
8179 // Animate a set of properties of an element
8180 function Morpher(element,duration,properties,callback)
8182 this.element = element;
8183 this.duration = duration;
8184 this.properties = properties;
8185 this.startTime = new Date();
8186 this.endTime = Number(this.startTime) + duration;
8187 this.callback = callback;
8192 Morpher.prototype.assignStyle = function(element,style,value)
8195 case
"-tw-vertScroll":
8196 window.scrollTo(findScrollX(),value);
8198 case
"-tw-horizScroll":
8199 window.scrollTo(value,findScrollY());
8202 element.style[style] = value;
8207 Morpher.prototype.stop = function()
8209 for(var t=
0; t
<this.properties.length; t++) {
8210 var p = this.properties[t];
8211 if(p.atEnd !== undefined) {
8212 this.assignStyle(this.element,p.style,p.atEnd);
8216 this.callback(this.element,this.properties);
8219 Morpher.prototype.tick = function()
8221 var currTime = Number(new Date());
8222 var progress = Animator.slowInSlowOut(Math.min(
1,(currTime-this.startTime)/this.duration));
8223 for(var t=
0; t
<this.properties.length; t++) {
8224 var p = this.properties[t];
8225 if(p.start !== undefined && p.end !== undefined) {
8226 var template = p.template ||
"%0";
8230 var v = p.start + (p.end-p.start) * progress;
8231 this.assignStyle(this.element,p.style,template.format([v]));
8238 if(currTime
>= this.endTime) {
8246 //-- Zoomer animation
8249 function Zoomer(text,startElement,targetElement,unused)
8251 var e = createTiddlyElement(document.body,
"div",null,
"zoomer");
8252 createTiddlyElement(e,
"div",null,null,text);
8253 var winWidth = findWindowWidth();
8254 var winHeight = findWindowHeight();
8256 {style: 'left', start: findPosX(startElement), end: findPosX(targetElement), template: '%
0px'},
8257 {style: 'top', start: findPosY(startElement), end: findPosY(targetElement), template: '%
0px'},
8258 {style: 'width', start: Math.min(startElement.scrollWidth,winWidth), end: Math.min(targetElement.scrollWidth,winWidth), template: '%
0px', atEnd: 'auto'},
8259 {style: 'height', start: Math.min(startElement.scrollHeight,winHeight), end: Math.min(targetElement.scrollHeight,winHeight), template: '%
0px', atEnd: 'auto'},
8260 {style: 'fontSize', start:
8, end:
24, template: '%
0pt'}
8262 var c = function(element,properties) {removeNode(element);};
8263 return new Morpher(e,config.animDuration,p,c);
8267 //-- Scroller animation
8270 function Scroller(targetElement)
8272 var p = [{style: '-tw-vertScroll', start: findScrollY(), end: ensureVisible(targetElement)}];
8273 return new Morpher(targetElement,config.animDuration,p);
8277 //-- Slider animation
8280 // deleteMode -
"none",
"all" [delete target element and it's children], [only]
"children" [but not the target element]
8281 function Slider(element,opening,unused,deleteMode)
8283 element.style.overflow = 'hidden';
8285 element.style.height = '
0px'; // Resolves a Firefox flashing bug
8286 element.style.display = 'block';
8287 var left = findPosX(element);
8288 var width = element.scrollWidth;
8289 var height = element.scrollHeight;
8290 var winWidth = findWindowWidth();
8294 p.push({style: 'height', start:
0, end: height, template: '%
0px', atEnd: 'auto'});
8295 p.push({style: 'opacity', start:
0, end:
1, template: '%
0'});
8296 p.push({style: 'filter', start:
0, end:
100, template: 'alpha(opacity:%
0)'});
8298 p.push({style: 'height', start: height, end:
0, template: '%
0px'});
8299 p.push({style: 'display', atEnd: 'none'});
8300 p.push({style: 'opacity', start:
1, end:
0, template: '%
0'});
8301 p.push({style: 'filter', start:
100, end:
0, template: 'alpha(opacity:%
0)'});
8302 switch(deleteMode) {
8304 c = function(element,properties) {removeNode(element);};
8307 c = function(element,properties) {removeChildren(element);};
8311 return new Morpher(element,config.animDuration,p,c);
8319 stack: [] // Array of objects with members root: and popup:
8322 Popup.create = function(root,elem,className)
8324 var stackPosition = this.find(root,
"popup");
8325 Popup.remove(stackPosition+
1);
8326 var popup = createTiddlyElement(document.body,elem ||
"ol",
"popup",className ||
"popup");
8327 popup.stackPosition = stackPosition;
8328 Popup.stack.push({root: root, popup: popup});
8332 Popup.onDocumentClick = function(ev)
8334 var e = ev || window.event;
8335 if(e.eventPhase == undefined)
8337 else if(e.eventPhase == Event.BUBBLING_PHASE || e.eventPhase == Event.AT_TARGET)
8342 Popup.show = function(valign,halign,offset)
8344 var curr = Popup.stack[Popup.stack.length-
1];
8345 this.place(curr.root,curr.popup,valign,halign,offset);
8346 addClass(curr.root,
"highlight");
8347 if(config.options.chkAnimate && anim && typeof Scroller ==
"function")
8348 anim.startAnimating(new Scroller(curr.popup));
8350 window.scrollTo(
0,ensureVisible(curr.popup));
8353 Popup.place = function(root,popup,valign,halign,offset)
8356 var offset = {x:
0,y:
0};
8357 if(popup.stackPosition
>=
0 && !valign && !halign) {
8358 offset.x = offset.x + root.offsetWidth;
8360 offset.x = (halign == 'right') ? offset.x + root.offsetWidth : offset.x;
8361 offset.y = (valign == 'top') ? offset.y : offset.y + root.offsetHeight;
8363 var rootLeft = findPosX(root);
8364 var rootTop = findPosY(root);
8365 var popupLeft = rootLeft + offset.x;
8366 var popupTop = rootTop + offset.y;
8367 var winWidth = findWindowWidth();
8368 if(popup.offsetWidth
> winWidth*
0.75)
8369 popup.style.width = winWidth*
0.75 +
"px";
8370 var popupWidth = popup.offsetWidth;
8371 var scrollWidth = winWidth - document.body.offsetWidth;
8372 if(popupLeft + popupWidth
> winWidth - scrollWidth -
1) {
8373 if(halign == 'right')
8374 popupLeft = popupLeft - root.offsetWidth - popupWidth;
8376 popupLeft = winWidth - popupWidth - scrollWidth -
1;
8378 popup.style.left = popupLeft +
"px";
8379 popup.style.top = popupTop +
"px";
8380 popup.style.display =
"block";
8383 Popup.find = function(e)
8386 for (var t=this.stack.length-
1; t
>=
0; t--) {
8387 if(isDescendant(e,this.stack[t].popup))
8393 Popup.remove = function(pos)
8395 if(!pos) var pos =
0;
8396 if(Popup.stack.length
> pos) {
8397 Popup.removeFrom(pos);
8401 Popup.removeFrom = function(from)
8403 for(var t=Popup.stack.length-
1; t
>=from; t--) {
8404 var p = Popup.stack[t];
8405 removeClass(p.root,
"highlight");
8406 removeNode(p.popup);
8408 Popup.stack = Popup.stack.slice(
0,from);
8415 function Wizard(elem)
8418 this.formElem = findRelated(elem,
"wizard",
"className");
8419 this.bodyElem = findRelated(this.formElem.firstChild,
"wizardBody",
"className",
"nextSibling");
8420 this.footElem = findRelated(this.formElem.firstChild,
"wizardFooter",
"className",
"nextSibling");
8422 this.formElem = null;
8423 this.bodyElem = null;
8424 this.footElem = null;
8428 Wizard.prototype.setValue = function(name,value)
8431 this.formElem[name] = value;
8434 Wizard.prototype.getValue = function(name)
8436 return this.formElem ? this.formElem[name] : null;
8439 Wizard.prototype.createWizard = function(place,title)
8441 this.formElem = createTiddlyElement(place,
"form",null,
"wizard");
8442 createTiddlyElement(this.formElem,
"h1",null,null,title);
8443 this.bodyElem = createTiddlyElement(this.formElem,
"div",null,
"wizardBody");
8444 this.footElem = createTiddlyElement(this.formElem,
"div",null,
"wizardFooter");
8447 Wizard.prototype.clear = function()
8449 removeChildren(this.bodyElem);
8452 Wizard.prototype.setButtons = function(buttonInfo,status)
8454 removeChildren(this.footElem);
8455 for(var t=
0; t
<buttonInfo.length; t++) {
8456 createTiddlyButton(this.footElem,buttonInfo[t].caption,buttonInfo[t].tooltip,buttonInfo[t].onClick);
8457 insertSpacer(this.footElem);
8459 if(typeof status ==
"string") {
8460 createTiddlyElement(this.footElem,
"span",null,
"status",status);
8464 Wizard.prototype.addStep = function(stepTitle,html)
8466 removeChildren(this.bodyElem);
8467 var w = createTiddlyElement(this.bodyElem,
"div");
8468 createTiddlyElement(w,
"h2",null,null,stepTitle);
8469 var step = createTiddlyElement(w,
"div",null,
"wizardStep");
8470 step.innerHTML = html;
8471 applyHtmlMacros(step,tiddler);
8474 Wizard.prototype.getElement = function(name)
8476 return this.formElem.elements[name];
8480 //-- ListView gadget
8485 // Create a listview
8486 ListView.create = function(place,listObject,listTemplate,callback,className)
8488 var table = createTiddlyElement(place,
"table",null,className ||
"listView twtable");
8489 var thead = createTiddlyElement(table,
"thead");
8490 var r = createTiddlyElement(thead,
"tr");
8491 for(var t=
0; t
<listTemplate.columns.length; t++) {
8492 var columnTemplate = listTemplate.columns[t];
8493 var c = createTiddlyElement(r,
"th");
8494 var colType = ListView.columnTypes[columnTemplate.type];
8495 if(colType && colType.createHeader) {
8496 colType.createHeader(c,columnTemplate,t);
8497 if(columnTemplate.className)
8498 addClass(c,columnTemplate.className);
8501 var tbody = createTiddlyElement(table,
"tbody");
8502 for(var rc=
0; rc
<listObject.length; rc++) {
8503 var rowObject = listObject[rc];
8504 r = createTiddlyElement(tbody,
"tr");
8505 for(c=
0; c
<listTemplate.rowClasses.length; c++) {
8506 if(rowObject[listTemplate.rowClasses[c].field])
8507 addClass(r,listTemplate.rowClasses[c].className);
8509 rowObject.rowElement = r;
8510 rowObject.colElements = {};
8511 for(var cc=
0; cc
<listTemplate.columns.length; cc++) {
8512 c = createTiddlyElement(r,
"td");
8513 columnTemplate = listTemplate.columns[cc];
8514 var field = columnTemplate.field;
8515 colType = ListView.columnTypes[columnTemplate.type];
8516 if(colType && colType.createItem) {
8517 colType.createItem(c,rowObject,field,columnTemplate,cc,rc);
8518 if(columnTemplate.className)
8519 addClass(c,columnTemplate.className);
8521 rowObject.colElements[field] = c;
8524 if(callback && listTemplate.actions)
8525 createTiddlyDropDown(place,ListView.getCommandHandler(callback),listTemplate.actions);
8526 if(callback && listTemplate.buttons) {
8527 for(t=
0; t
<listTemplate.buttons.length; t++) {
8528 var a = listTemplate.buttons[t];
8529 if(a && a.name !=
"")
8530 createTiddlyButton(place,a.caption,null,ListView.getCommandHandler(callback,a.name,a.allowEmptySelection));
8536 ListView.getCommandHandler = function(callback,name,allowEmptySelection)
8538 return function(e) {
8539 var view = findRelated(this,
"TABLE",null,
"previousSibling");
8541 ListView.forEachSelector(view,function(e,rowName) {
8543 tiddlers.push(rowName);
8545 if(tiddlers.length ==
0 && !allowEmptySelection) {
8546 alert(config.messages.nothingSelected);
8548 if(this.nodeName.toLowerCase() ==
"select") {
8549 callback(view,this.value,tiddlers);
8550 this.selectedIndex =
0;
8552 callback(view,name,tiddlers);
8558 // Invoke a callback for each selector checkbox in the listview
8559 ListView.forEachSelector = function(view,callback)
8561 var checkboxes = view.getElementsByTagName(
"input");
8563 for(var t=
0; t
<checkboxes.length; t++) {
8564 var cb = checkboxes[t];
8565 if(cb.getAttribute(
"type") ==
"checkbox") {
8566 var rn = cb.getAttribute(
"rowName");
8576 ListView.getSelectedRows = function(view)
8579 ListView.forEachSelector(view,function(e,rowName) {
8581 rowNames.push(rowName);
8586 ListView.columnTypes = {};
8588 ListView.columnTypes.String = {
8589 createHeader: function(place,columnTemplate,col)
8591 createTiddlyText(place,columnTemplate.title);
8593 createItem: function(place,listObject,field,columnTemplate,col,row)
8595 var v = listObject[field];
8597 createTiddlyText(place,v);
8601 ListView.columnTypes.WikiText = {
8602 createHeader: ListView.columnTypes.String.createHeader,
8603 createItem: function(place,listObject,field,columnTemplate,col,row)
8605 var v = listObject[field];
8607 wikify(v,place,null,null);
8611 ListView.columnTypes.Tiddler = {
8612 createHeader: ListView.columnTypes.String.createHeader,
8613 createItem: function(place,listObject,field,columnTemplate,col,row)
8615 var v = listObject[field];
8616 if(v != undefined && v.title)
8617 createTiddlyPopup(place,v.title,config.messages.listView.tiddlerTooltip,v);
8621 ListView.columnTypes.Size = {
8622 createHeader: ListView.columnTypes.String.createHeader,
8623 createItem: function(place,listObject,field,columnTemplate,col,row)
8625 var v = listObject[field];
8626 if(v != undefined) {
8628 while(t
<config.messages.sizeTemplates.length-
1 && v
<config.messages.sizeTemplates[t].unit)
8630 createTiddlyText(place,config.messages.sizeTemplates[t].template.format([Math.round(v/config.messages.sizeTemplates[t].unit)]));
8635 ListView.columnTypes.Link = {
8636 createHeader: ListView.columnTypes.String.createHeader,
8637 createItem: function(place,listObject,field,columnTemplate,col,row)
8639 var v = listObject[field];
8640 var c = columnTemplate.text;
8642 createTiddlyText(createExternalLink(place,v),c || v);
8646 ListView.columnTypes.Date = {
8647 createHeader: ListView.columnTypes.String.createHeader,
8648 createItem: function(place,listObject,field,columnTemplate,col,row)
8650 var v = listObject[field];
8652 createTiddlyText(place,v.formatString(columnTemplate.dateFormat));
8656 ListView.columnTypes.StringList = {
8657 createHeader: ListView.columnTypes.String.createHeader,
8658 createItem: function(place,listObject,field,columnTemplate,col,row)
8660 var v = listObject[field];
8661 if(v != undefined) {
8662 for(var t=
0; t
<v.length; t++) {
8663 createTiddlyText(place,v[t]);
8664 createTiddlyElement(place,
"br");
8670 ListView.columnTypes.Selector = {
8671 createHeader: function(place,columnTemplate,col)
8673 createTiddlyCheckbox(place,null,false,this.onHeaderChange);
8675 createItem: function(place,listObject,field,columnTemplate,col,row)
8677 var e = createTiddlyCheckbox(place,null,listObject[field],null);
8678 e.setAttribute(
"rowName",listObject[columnTemplate.rowName]);
8680 onHeaderChange: function(e)
8682 var state = this.checked;
8683 var view = findRelated(this,
"TABLE");
8686 ListView.forEachSelector(view,function(e,rowName) {
8692 ListView.columnTypes.Tags = {
8693 createHeader: ListView.columnTypes.String.createHeader,
8694 createItem: function(place,listObject,field,columnTemplate,col,row)
8696 var tags = listObject[field];
8697 createTiddlyText(place,String.encodeTiddlyLinkList(tags));
8701 ListView.columnTypes.Boolean = {
8702 createHeader: ListView.columnTypes.String.createHeader,
8703 createItem: function(place,listObject,field,columnTemplate,col,row)
8705 if(listObject[field] == true)
8706 createTiddlyText(place,columnTemplate.trueText);
8707 if(listObject[field] == false)
8708 createTiddlyText(place,columnTemplate.falseText);
8712 ListView.columnTypes.TagCheckbox = {
8713 createHeader: ListView.columnTypes.String.createHeader,
8714 createItem: function(place,listObject,field,columnTemplate,col,row)
8716 var e = createTiddlyCheckbox(place,null,listObject[field],this.onChange);
8717 e.setAttribute(
"tiddler",listObject.title);
8718 e.setAttribute(
"tag",columnTemplate.tag);
8720 onChange : function(e)
8722 var tag = this.getAttribute(
"tag");
8723 var tiddler = this.getAttribute(
"tiddler");
8724 store.setTiddlerTag(tiddler,this.checked,tag);
8728 ListView.columnTypes.TiddlerLink = {
8729 createHeader: ListView.columnTypes.String.createHeader,
8730 createItem: function(place,listObject,field,columnTemplate,col,row)
8732 var v = listObject[field];
8733 if(v != undefined) {
8734 var link = createTiddlyLink(place,listObject[columnTemplate.tiddlerLink],false,null);
8735 createTiddlyText(link,listObject[field]);
8741 //-- Augmented methods for the JavaScript Number(), Array(), String() and Date() objects
8744 // Clamp a number to a range
8745 Number.prototype.clamp = function(min,max)
8755 // Add indexOf function if browser does not support it
8756 if(!Array.indexOf) {
8757 Array.prototype.indexOf = function(item,from)
8761 for(var i=from; i
<this.length; i++) {
8762 if(this[i] === item)
8768 // Find an entry in a given field of the members of an array
8769 Array.prototype.findByField = function(field,value)
8771 for(var t=
0; t
<this.length; t++) {
8772 if(this[t][field] == value)
8778 // Return whether an entry exists in an array
8779 Array.prototype.contains = function(item)
8781 return this.indexOf(item) != -
1;
8784 // Adds, removes or toggles a particular value within an array
8785 // value - value to add
8786 // mode - +
1 to add value, -
1 to remove value,
0 to toggle it
8787 Array.prototype.setItem = function(value,mode)
8789 var p = this.indexOf(value);
8791 mode = (p == -
1) ? +
1 : -
1;
8795 } else if(mode == -
1) {
8801 // Return whether one of a list of values exists in an array
8802 Array.prototype.containsAny = function(items)
8804 for(var i=
0; i
<items.length; i++) {
8805 if(this.indexOf(items[i]) != -
1)
8811 // Return whether all of a list of values exists in an array
8812 Array.prototype.containsAll = function(items)
8814 for(var i =
0; i
<items.length; i++) {
8815 if(this.indexOf(items[i]) == -
1)
8821 // 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
8822 Array.prototype.pushUnique = function(item,unique)
8824 if(unique === false) {
8827 if(this.indexOf(item) == -
1)
8832 Array.prototype.remove = function(item)
8834 var p = this.indexOf(item);
8839 if(!Array.prototype.map) {
8840 Array.prototype.map = function(fn,thisObj)
8842 var scope = thisObj || window;
8844 for(var i=
0, j=this.length; i < j; ++i) {
8845 a.push(fn.call(scope,this[i],i,this));
8850 // Get characters from the right end of a string
8851 String.prototype.right = function(n)
8853 return n < this.length ? this.slice(this.length-n) : this;
8856 // Trim whitespace from both ends of a string
8857 String.prototype.trim = function()
8859 return this.replace(/^\s*|\s*$/g,
"");
8862 // Convert a string from a CSS style property name to a JavaScript style name (
"background-color" -
> "backgroundColor")
8863 String.prototype.unDash = function()
8865 var s = this.split(
"-");
8867 for(var t=
1; t
<s.length; t++)
8868 s[t] = s[t].substr(
0,
1).toUpperCase() + s[t].substr(
1);
8873 // Substitute substrings from an array into a format string that includes '%
1'-type specifiers
8874 String.prototype.format = function(substrings)
8876 var subRegExp = /(?:%(\d+))/mg;
8880 var match = subRegExp.exec(this);
8881 if(match && match[
1]) {
8882 if(match.index
> currPos)
8883 r.push(this.substring(currPos,match.index));
8884 r.push(substrings[parseInt(match[
1])]);
8885 currPos = subRegExp.lastIndex;
8888 if(currPos < this.length)
8889 r.push(this.substring(currPos,this.length));
8893 // Escape any special RegExp characters with that character preceded by a backslash
8894 String.prototype.escapeRegExp = function()
8896 var s =
"\\^$*+?()=!|,{}[].";
8898 for(var t=
0; t
<s.length; t++)
8899 c = c.replace(new RegExp(
"\\" + s.substr(t,
1),
"g"),
"\\" + s.substr(t,
1));
8903 // Convert
"\" to
"\s", newlines to
"\n" (and remove carriage returns)
8904 String.prototype.escapeLineBreaks = function()
8906 return this.replace(/\\/mg,
"\\s").replace(/\n/mg,
"\\n").replace(/\r/mg,
"");
8909 // Convert
"\n" to newlines,
"\b" to
" ",
"\s" to
"\" (and remove carriage returns)
8910 String.prototype.unescapeLineBreaks = function()
8912 return this.replace(/\\n/mg,
"\n").replace(/\\b/mg,
" ").replace(/\\s/mg,
"\\").replace(/\r/mg,
"");
8915 // Convert & to
"&", < to
"<",
> to
">" and
" to """
8916 String.prototype.htmlEncode = function()
8918 return this.replace(/&/mg,"&").replace(/</mg,"<").replace(/>/mg,">").replace(/\"/mg,
""");
8921 // Convert
"&" to &,
"<" to <,
">" to
> and
""" to
"
8922 String.prototype.htmlDecode = function()
8924 return this.replace(/</mg,"<
").replace(/>/mg,">").replace(/"/mg,"\
"").replace(/
&/mg,
"&");
8927 // Convert a string to it's JSON representation by encoding control characters, double quotes and backslash. See json.org
8928 String.prototype.toJSONString = function()
8939 var replaceFn = function(a,b) {
8944 return '\\u00' + Math.floor(c /
16).toString(
16) + (c %
16).toString(
16);
8946 if(/[
"\\\x00-\x1f]/.test(this))
8947 return '"' + this.replace(/([\x00-\x1f\\
"])/g,replaceFn) + '"';
8948 return '
"' + this + '"';
8951 // Parse a space-separated string of name:value parameters
8952 // The result is an array of objects:
8953 // result[
0] = object with a member for each parameter name, value of that member being an array of values
8954 // result[
1..n] = one object for each parameter, with 'name' and 'value' members
8955 String.prototype.parseParams = function(defaultName,defaultValue,allowEval,noNames,cascadeDefaults)
8957 var parseToken = function(match,p) {
8959 if(match[p]) // Double quoted
8961 else if(match[p+
1]) // Single quoted
8963 else if(match[p+
2]) // Double-square-bracket quoted
8965 else if(match[p+
3]) // Double-brace quoted
8971 throw
"Unable to evaluate {{" + match[p+
3] +
"}}: " + exceptionText(ex);
8973 else if(match[p+
4]) // Unquoted
8975 else if(match[p+
5]) // empty quote
8980 var dblQuote =
"(?:\"((?:(?:\\\\\
")|[^\"])+)\
")";
8981 var sngQuote =
"(?:'((?:(?:\\\\\')|[^'])+)')";
8982 var dblSquare =
"(?:\\[\\[((?:\\s|\\S)*?)\\]\\])";
8983 var dblBrace =
"(?:\\{\\{((?:\\s|\\S)*?)\\}\\})";
8984 var unQuoted = noNames ?
"([^\"'\\s]\\S*)
" : "([^\
"':\\s][^\\s:]*)";
8985 var emptyQuote =
"((?:\"\
")|(?:''))";
8986 var skipSpace =
"(?:\\s*)";
8987 var token =
"(?:" + dblQuote +
"|" + sngQuote +
"|" + dblSquare +
"|" + dblBrace +
"|" + unQuoted +
"|" + emptyQuote +
")";
8988 var re = noNames ? new RegExp(token,
"mg") : new RegExp(skipSpace + token + skipSpace +
"(?:(\\:)" + skipSpace + token +
")?",
"mg");
8991 var match = re.exec(this);
8993 var n = parseToken(match,
1);
8995 r.push({name:
"",value:n});
8997 var v = parseToken(match,
8);
8998 if(v == null && defaultName) {
9001 } else if(v == null && defaultValue) {
9004 r.push({name:n,value:v});
9005 if(cascadeDefaults) {
9012 // Summarise parameters into first element
9013 for(var t=
1; t
<r.length; t++) {
9015 r[
0][r[t].name].push(r[t].value);
9017 r[
0][r[t].name] = [r[t].value];
9022 // Process a string list of macro parameters into an array. Parameters can be quoted with
"", '',
9023 // [[]], {{ }} or left unquoted (and therefore space-separated). Double-braces {{}} results in
9024 // an *evaluated* parameter: e.g. {{config.options.txtUserName}} results in the current user's name.
9025 String.prototype.readMacroParams = function()
9027 var p = this.parseParams(
"list",null,true,true);
9029 for(var t=
1; t
<p.length; t++)
9034 // Process a string list of unique tiddler names into an array. Tiddler names that have spaces in them must be [[bracketed]]
9035 String.prototype.readBracketedList = function(unique)
9037 var p = this.parseParams(
"list",null,false,true);
9039 for(var t=
1; t
<p.length; t++) {
9041 n.pushUnique(p[t].value,unique);
9046 // Returns array with start and end index of chunk between given start and end marker, or undefined.
9047 String.prototype.getChunkRange = function(start,end)
9049 var s = this.indexOf(start);
9052 var e = this.indexOf(end,s);
9058 // Replace a chunk of a string given start and end markers
9059 String.prototype.replaceChunk = function(start,end,sub)
9061 var r = this.getChunkRange(start,end);
9062 return r ? this.substring(
0,r[
0]) + sub + this.substring(r[
1]) : this;
9065 // Returns a chunk of a string between start and end markers, or undefined
9066 String.prototype.getChunk = function(start,end)
9068 var r = this.getChunkRange(start,end);
9070 return this.substring(r[
0],r[
1]);
9074 // Static method to bracket a string with double square brackets if it contains a space
9075 String.encodeTiddlyLink = function(title)
9077 return title.indexOf(
" ") == -
1 ? title :
"[[" + title +
"]]";
9080 // Static method to encodeTiddlyLink for every item in an array and join them with spaces
9081 String.encodeTiddlyLinkList = function(list)
9085 for(var t=
0; t
<list.length; t++)
9086 results.push(String.encodeTiddlyLink(list[t]));
9087 return results.join(
" ");
9093 // Convert a string as a sequence of name:
"value" pairs into a hashmap
9094 String.prototype.decodeHashMap = function()
9096 var fields = this.parseParams(
"anon",
"",false);
9098 for(var t=
1; t
<fields.length; t++)
9099 r[fields[t].name] = fields[t].value;
9103 // Static method to encode a hashmap into a name:
"value"... string
9104 String.encodeHashMap = function(hashmap)
9107 for(var t in hashmap)
9108 r.push(t + ':
"' + hashmap[t] + '"');
9112 // Static method to left-pad a string with
0s to a certain width
9113 String.zeroPad = function(n,d)
9115 var s = n.toString();
9117 s =
"000000000000000000000000000".substr(
0,d-s.length) + s;
9121 String.prototype.startsWith = function(prefix)
9123 return !prefix || this.substring(
0,prefix.length) == prefix;
9126 // Returns the first value of the given named parameter.
9127 function getParam(params,name,defaultValue)
9130 return defaultValue;
9131 var p = params[
0][name];
9132 return p ? p[
0] : defaultValue;
9135 // Returns the first value of the given boolean named parameter.
9136 function getFlag(params,name,defaultValue)
9138 return !!getParam(params,name,defaultValue);
9141 // Substitute date components into a string
9142 Date.prototype.formatString = function(template)
9144 var t = template.replace(/
0hh12/g,String.zeroPad(this.getHours12(),
2));
9145 t = t.replace(/hh12/g,this.getHours12());
9146 t = t.replace(/
0hh/g,String.zeroPad(this.getHours(),
2));
9147 t = t.replace(/hh/g,this.getHours());
9148 t = t.replace(/mmm/g,config.messages.dates.shortMonths[this.getMonth()]);
9149 t = t.replace(/
0mm/g,String.zeroPad(this.getMinutes(),
2));
9150 t = t.replace(/mm/g,this.getMinutes());
9151 t = t.replace(/
0ss/g,String.zeroPad(this.getSeconds(),
2));
9152 t = t.replace(/ss/g,this.getSeconds());
9153 t = t.replace(/[ap]m/g,this.getAmPm().toLowerCase());
9154 t = t.replace(/[AP]M/g,this.getAmPm().toUpperCase());
9155 t = t.replace(/wYYYY/g,this.getYearForWeekNo());
9156 t = t.replace(/wYY/g,String.zeroPad(this.getYearForWeekNo()-
2000,
2));
9157 t = t.replace(/YYYY/g,this.getFullYear());
9158 t = t.replace(/YY/g,String.zeroPad(this.getFullYear()-
2000,
2));
9159 t = t.replace(/MMM/g,config.messages.dates.months[this.getMonth()]);
9160 t = t.replace(/
0MM/g,String.zeroPad(this.getMonth()+
1,
2));
9161 t = t.replace(/MM/g,this.getMonth()+
1);
9162 t = t.replace(/
0WW/g,String.zeroPad(this.getWeek(),
2));
9163 t = t.replace(/WW/g,this.getWeek());
9164 t = t.replace(/DDD/g,config.messages.dates.days[this.getDay()]);
9165 t = t.replace(/ddd/g,config.messages.dates.shortDays[this.getDay()]);
9166 t = t.replace(/
0DD/g,String.zeroPad(this.getDate(),
2));
9167 t = t.replace(/DDth/g,this.getDate()+this.daySuffix());
9168 t = t.replace(/DD/g,this.getDate());
9169 var tz = this.getTimezoneOffset();
9170 var atz = Math.abs(tz);
9171 t = t.replace(/TZD/g,(tz <
0 ? '+' : '-') + String.zeroPad(Math.floor(atz /
60),
2) + ':' + String.zeroPad(atz %
60,
2));
9172 t = t.replace(/\\/g,
"");
9176 Date.prototype.getWeek = function()
9178 var dt = new Date(this.getTime());
9179 var d = dt.getDay();
9180 if(d==
0) d=
7;// JavaScript Sun=
0, ISO Sun=
7
9181 dt.setTime(dt.getTime()+(
4-d)*
86400000);// shift day to Thurs of same week to calculate weekNo
9182 var n = Math.floor((dt.getTime()-new Date(dt.getFullYear(),
0,
1)+
3600000)/
86400000);
9183 return Math.floor(n/
7)+
1;
9186 Date.prototype.getYearForWeekNo = function()
9188 var dt = new Date(this.getTime());
9189 var d = dt.getDay();
9190 if(d==
0) d=
7;// JavaScript Sun=
0, ISO Sun=
7
9191 dt.setTime(dt.getTime()+(
4-d)*
86400000);// shift day to Thurs of same week
9192 return dt.getFullYear();
9195 Date.prototype.getHours12 = function()
9197 var h = this.getHours();
9198 return h
> 12 ? h-
12 : ( h
> 0 ? h :
12 );
9201 Date.prototype.getAmPm = function()
9203 return this.getHours()
>=
12 ? config.messages.dates.pm : config.messages.dates.am;
9206 Date.prototype.daySuffix = function()
9208 return config.messages.dates.daySuffixes[this.getDate()-
1];
9211 // Convert a date to local YYYYMMDDHHMM string format
9212 Date.prototype.convertToLocalYYYYMMDDHHMM = function()
9214 return this.getFullYear() + String.zeroPad(this.getMonth()+
1,
2) + String.zeroPad(this.getDate(),
2) + String.zeroPad(this.getHours(),
2) + String.zeroPad(this.getMinutes(),
2);
9217 // Convert a date to UTC YYYYMMDDHHMM string format
9218 Date.prototype.convertToYYYYMMDDHHMM = function()
9220 return this.getUTCFullYear() + String.zeroPad(this.getUTCMonth()+
1,
2) + String.zeroPad(this.getUTCDate(),
2) + String.zeroPad(this.getUTCHours(),
2) + String.zeroPad(this.getUTCMinutes(),
2);
9223 // Convert a date to UTC YYYYMMDD.HHMMSSMMM string format
9224 Date.prototype.convertToYYYYMMDDHHMMSSMMM = function()
9226 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);
9229 // Static method to create a date from a UTC YYYYMMDDHHMM format string
9230 Date.convertFromYYYYMMDDHHMM = function(d)
9232 var hh = d.substr(
8,
2) ||
"00";
9233 var mm = d.substr(
10,
2) ||
"00";
9234 return new Date(Date.UTC(parseInt(d.substr(
0,
4),
10),
9235 parseInt(d.substr(
4,
2),
10)-
1,
9236 parseInt(d.substr(
6,
2),
10),
9238 parseInt(mm,
10),
0,
0));
9242 //-- Crypto functions and associated conversion routines
9245 // Crypto 'namespace'
9246 function Crypto() {}
9248 // Convert a string to an array of big-endian
32-bit words
9249 Crypto.strToBe32s = function(str)
9252 var len=Math.floor(str.length/
4);
9254 for(i=
0, j=
0; i
<len; i++, j+=
4) {
9255 be[i]=((str.charCodeAt(j)&
0xff) <<
24)|((str.charCodeAt(j+
1)&
0xff) <<
16)|((str.charCodeAt(j+
2)&
0xff) <<
8)|(str.charCodeAt(j+
3)&
0xff);
9257 while(j
<str.length) {
9258 be[j
>>2] |= (str.charCodeAt(j)&
0xff)<<(
24-(j*
8)%
32);
9264 // Convert an array of big-endian
32-bit words to a string
9265 Crypto.be32sToStr = function(be)
9268 for(var i=
0;i
<be.length*
32;i+=
8) {
9269 str += String.fromCharCode((be[i
>>5]
>>>(
24-i%
32)) &
0xff);
9274 // Convert an array of big-endian
32-bit words to a hex string
9275 Crypto.be32sToHex = function(be)
9277 var hex='
0123456789ABCDEF';
9279 for(var i=
0;i
<be.length*
4;i++) {
9280 str += hex.charAt((be[i
>>2]
>>((
3-i%
4)*
8+
4))&
0xF) + hex.charAt((be[i
>>2]
>>((
3-i%
4)*
8))&
0xF);
9285 // Return, in hex, the SHA-
1 hash of a string
9286 Crypto.hexSha1Str = function(str)
9288 return Crypto.be32sToHex(Crypto.sha1Str(str));
9291 // Return the SHA-
1 hash of a string
9292 Crypto.sha1Str = function(str)
9294 return Crypto.sha1(Crypto.strToBe32s(str),str.length);
9297 // Calculate the SHA-
1 hash of an array of blen bytes of big-endian
32-bit words
9298 Crypto.sha1 = function(x,blen)
9300 // Add
32-bit integers, wrapping at
32 bits
9303 var lsw=(a&
0xFFFF)+(b&
0xFFFF);
9304 var msw=(a
>>16)+(b
>>16)+(lsw
>>16);
9305 return (msw<
<16)|(lsw&
0xFFFF);
9307 function AA(a,b,c,d,e)
9310 var lsw=(a&
0xFFFF)+(b&
0xFFFF)+(c&
0xFFFF)+(d&
0xFFFF)+(e&
0xFFFF);
9311 var msw=(a
>>16)+(b
>>16)+(c
>>16)+(d
>>16)+(e
>>16)+(lsw
>>16);
9312 return (msw<
<16)|(lsw&
0xFFFF);
9316 var n=w[j-
3]^w[j-
8]^w[j-
14]^w[j-
16];
9317 return (n
>>>31)|(n<
<1);
9321 x[len
>>5] |=
0x80 << (
24-len%
32);
9322 x[((len+
64>>9)<
<4)+
15]=len;
9323 var w=new Array(
80);
9336 for(var i=
0;i
<x.length;i+=
16) {
9346 t=AA(e,a,d^(b&(c^d)),w[j],k1);
9347 e=d; d=c; c=(b
>>>2)|(b<
<30); b=a; a=t; j++;
9351 t=AA(e,a,d^(b&(c^d)),w[j],k1);
9352 e=d; d=c; c=(b
>>>2)|(b<
<30); b=a; a=t; j++;
9356 t=AA(e,a,b^c^d,w[j],k2);
9357 e=d; d=c; c=(b
>>>2)|(b<
<30); b=a; a=t; j++;
9361 t=AA(e,a,(b&c)|(d&(b|c)),w[j],k3);
9362 e=d; d=c; c=(b
>>>2)|(b<
<30); b=a; a=t; j++;
9366 t=AA(e,a,b^c^d,w[j],k4);
9367 e=d; d=c; c=(b
>>>2)|(b<
<30); b=a; a=t; j++;
9375 return [h0,h1,h2,h3,h4];
9379 //-- RGB colour object
9382 // Construct an RGB colour object from a '#rrggbb', '#rgb' or 'rgb(n,n,n)' string or from separate r,g,b values
9388 if(typeof r ==
"string") {
9389 if(r.substr(
0,
1) ==
"#") {
9391 this.r = parseInt(r.substr(
1,
2),
16)/
255;
9392 this.g = parseInt(r.substr(
3,
2),
16)/
255;
9393 this.b = parseInt(r.substr(
5,
2),
16)/
255;
9395 this.r = parseInt(r.substr(
1,
1),
16)/
15;
9396 this.g = parseInt(r.substr(
2,
1),
16)/
15;
9397 this.b = parseInt(r.substr(
3,
1),
16)/
15;
9400 var rgbPattern = /rgb\s*\(\s*(\d{
1,
3})\s*,\s*(\d{
1,
3})\s*,\s*(\d{
1,
3})\s*\)/;
9401 var c = r.match(rgbPattern);
9403 this.r = parseInt(c[
1],
10)/
255;
9404 this.g = parseInt(c[
2],
10)/
255;
9405 this.b = parseInt(c[
3],
10)/
255;
9416 // Mixes this colour with another in a specified proportion
9417 // c = other colour to mix
9418 // f =
0.
.1 where
0 is this colour and
1 is the new colour
9419 // Returns an RGB object
9420 RGB.prototype.mix = function(c,f)
9422 return new RGB(this.r + (c.r-this.r) * f,this.g + (c.g-this.g) * f,this.b + (c.b-this.b) * f);
9425 // Return an rgb colour as a #rrggbb format hex string
9426 RGB.prototype.toString = function()
9428 return
"#" + (
"0" + Math.floor(this.r.clamp(
0,
1) *
255).toString(
16)).right(
2) +
9429 (
"0" + Math.floor(this.g.clamp(
0,
1) *
255).toString(
16)).right(
2) +
9430 (
"0" + Math.floor(this.b.clamp(
0,
1) *
255).toString(
16)).right(
2);
9434 //-- DOM utilities - many derived from www.quirksmode.org
9437 function drawGradient(place,horiz,locolors,hicolors)
9440 hicolors = locolors;
9441 for(var t=
0; t<=
100; t+=
2) {
9442 var bar = document.createElement(
"div");
9443 place.appendChild(bar);
9444 bar.style.position =
"absolute";
9445 bar.style.left = horiz ? t +
"%" :
0;
9446 bar.style.top = horiz ?
0 : t +
"%";
9447 bar.style.width = horiz ? (
101-t) +
"%" :
"100%";
9448 bar.style.height = horiz ?
"100%" : (
101-t) +
"%";
9449 bar.style.zIndex = -
1;
9450 var p = t/
100*(locolors.length-
1);
9451 bar.style.backgroundColor = hicolors[Math.floor(p)].mix(locolors[Math.ceil(p)],p-Math.floor(p)).toString();
9455 function createTiddlyText(parent,text)
9457 return parent.appendChild(document.createTextNode(text));
9460 function createTiddlyCheckbox(parent,caption,checked,onChange)
9462 var cb = document.createElement(
"input");
9463 cb.setAttribute(
"type",
"checkbox");
9464 cb.onclick = onChange;
9465 parent.appendChild(cb);
9466 cb.checked = checked;
9467 cb.className =
"chkOptionInput";
9469 wikify(caption,parent);
9473 function createTiddlyElement(parent,element,id,className,text,attribs)
9475 var e = document.createElement(element);
9476 if(className != null)
9477 e.className = className;
9479 e.setAttribute(
"id",id);
9481 e.appendChild(document.createTextNode(text));
9483 for(var n in attribs) {
9484 e.setAttribute(n,attribs[n]);
9488 parent.appendChild(e);
9492 function addEvent(obj,type,fn)
9494 if(obj.attachEvent) {
9495 obj['e'+type+fn] = fn;
9496 obj[type+fn] = function(){obj['e'+type+fn](window.event);};
9497 obj.attachEvent('on'+type,obj[type+fn]);
9499 obj.addEventListener(type,fn,false);
9503 function removeEvent(obj,type,fn)
9505 if(obj.detachEvent) {
9506 obj.detachEvent('on'+type,obj[type+fn]);
9507 obj[type+fn] = null;
9509 obj.removeEventListener(type,fn,false);
9513 function addClass(e,className)
9515 var currClass = e.className.split(
" ");
9516 if(currClass.indexOf(className) == -
1)
9517 e.className +=
" " + className;
9520 function removeClass(e,className)
9522 var currClass = e.className.split(
" ");
9523 var i = currClass.indexOf(className);
9525 currClass.splice(i,
1);
9526 i = currClass.indexOf(className);
9528 e.className = currClass.join(
" ");
9531 function hasClass(e,className)
9533 if(e.className && e.className.split(
" ").indexOf(className) != -
1) {
9539 // Find the closest relative with a given property value (property defaults to tagName, relative defaults to parentNode)
9540 function findRelated(e,value,name,relative)
9542 name = name ||
"tagName";
9543 relative = relative ||
"parentNode";
9544 if(name ==
"className") {
9545 while(e && !hasClass(e,value)) {
9549 while(e && e[name] != value) {
9556 // Resolve the target object of an event
9557 function resolveTarget(e)
9562 else if(e.srcElement)
9564 if(obj.nodeType ==
3) // defeat Safari bug
9565 obj = obj.parentNode;
9569 // Prevent an event from bubbling
9570 function stopEvent(e)
9572 var ev = e || window.event;
9573 ev.cancelBubble = true;
9574 if(ev.stopPropagation) ev.stopPropagation();
9578 // Return the content of an element as plain text with no formatting
9579 function getPlainText(e)
9584 else if(e.textContent)
9585 text = e.textContent;
9589 // Get the scroll position for window.scrollTo necessary to scroll a given element into view
9590 function ensureVisible(e)
9592 var posTop = findPosY(e);
9593 var posBot = posTop + e.offsetHeight;
9594 var winTop = findScrollY();
9595 var winHeight = findWindowHeight();
9596 var winBot = winTop + winHeight;
9597 if(posTop < winTop) {
9599 } else if(posBot
> winBot) {
9600 if(e.offsetHeight < winHeight)
9601 return posTop - (winHeight - e.offsetHeight);
9609 // Get the current width of the display window
9610 function findWindowWidth()
9612 return window.innerWidth || document.documentElement.clientWidth;
9615 // Get the current height of the display window
9616 function findWindowHeight()
9618 return window.innerHeight || document.documentElement.clientHeight;
9621 // Get the current horizontal page scroll position
9622 function findScrollX()
9624 return window.scrollX || document.documentElement.scrollLeft;
9627 // Get the current vertical page scroll position
9628 function findScrollY()
9630 return window.scrollY || document.documentElement.scrollTop;
9633 function findPosX(obj)
9636 while(obj.offsetParent) {
9637 curleft += obj.offsetLeft;
9638 obj = obj.offsetParent;
9643 function findPosY(obj)
9646 while(obj.offsetParent) {
9647 curtop += obj.offsetTop;
9648 obj = obj.offsetParent;
9653 // Blur a particular element
9654 function blurElement(e)
9656 if(e && e.focus && e.blur) {
9662 // Create a non-breaking space
9663 function insertSpacer(place)
9665 var e = document.createTextNode(String.fromCharCode(
160));
9667 place.appendChild(e);
9671 // Remove all children of a node
9672 function removeChildren(e)
9674 while(e && e.hasChildNodes())
9675 removeNode(e.firstChild);
9678 // Remove a node and all it's children
9679 function removeNode(e)
9682 e.parentNode.removeChild(e);
9685 // Remove any event handlers or non-primitve custom attributes
9686 function scrubNode(e)
9688 if(!config.browser.isIE)
9690 var att = e.attributes;
9692 for(var t=
0; t
<att.length; t++) {
9693 var n = att[t].name;
9694 if(n !== 'style' && (typeof e[n] === 'function' || (typeof e[n] === 'object' && e[n] != null))) {
9702 var c = e.firstChild;
9709 // Add a stylesheet, replacing any previous custom stylesheet
9710 function setStylesheet(s,id,doc)
9713 id =
"customStyleSheet";
9716 var n = doc.getElementById(id);
9717 if(doc.createStyleSheet) {
9718 // Test for IE's non-standard createStyleSheet method
9720 n.parentNode.removeChild(n);
9721 // This failed without the
9722 doc.getElementsByTagName(
"head")[
0].insertAdjacentHTML(
"beforeEnd",
" <style id='" + id +
"'>" + s +
"</style>");
9725 n.replaceChild(doc.createTextNode(s),n.firstChild);
9727 n = doc.createElement(
"style");
9728 n.type =
"text/css";
9730 n.appendChild(doc.createTextNode(s));
9731 doc.getElementsByTagName(
"head")[
0].appendChild(n);
9736 function removeStyleSheet(id)
9738 var e = document.getElementById(id);
9740 e.parentNode.removeChild(e);
9743 // Force the browser to do a document reflow when needed to workaround browser bugs
9744 function forceReflow()
9746 if(config.browser.isGecko) {
9747 setStylesheet(
"body {top:0px;margin-top:0px;}",
"forceReflow");
9748 setTimeout(function() {setStylesheet(
"",
"forceReflow");},
1);
9752 // Replace the current selection of a textarea or text input and scroll it into view
9753 function replaceSelection(e,text)
9755 if(e.setSelectionRange) {
9756 var oldpos = e.selectionStart;
9757 var isRange = e.selectionEnd
> e.selectionStart;
9758 e.value = e.value.substr(
0,e.selectionStart) + text + e.value.substr(e.selectionEnd);
9759 e.setSelectionRange(isRange ? oldpos : oldpos + text.length,oldpos + text.length);
9760 var linecount = e.value.split('\n').length;
9761 var thisline = e.value.substr(
0,e.selectionStart).split('\n').length-
1;
9762 e.scrollTop = Math.floor((thisline - e.rows /
2) * e.scrollHeight / linecount);
9763 } else if(document.selection) {
9764 var range = document.selection.createRange();
9765 if(range.parentElement() == e) {
9766 var isCollapsed = range.text ==
"";
9769 range.moveStart('character', -text.length);
9776 // Returns the text of the given (text) node, possibly merging subsequent text nodes
9777 function getNodeText(e)
9780 while(e && e.nodeName ==
"#text") {
9787 // Returns true if the element e has a given ancestor element
9788 function isDescendant(e,ancestor)
9799 //-- LoaderBase and SaverBase
9802 function LoaderBase() {}
9804 LoaderBase.prototype.loadTiddler = function(store,node,tiddlers)
9806 var title = this.getTitle(store,node);
9807 if(safeMode && store.isShadowTiddler(title))
9810 var tiddler = store.createTiddler(title);
9811 this.internalizeTiddler(store,tiddler,title,node);
9812 tiddlers.push(tiddler);
9816 LoaderBase.prototype.loadTiddlers = function(store,nodes)
9819 for(var t =
0; t < nodes.length; t++) {
9821 this.loadTiddler(store,nodes[t],tiddlers);
9823 showException(ex,config.messages.tiddlerLoadError.format([this.getTitle(store,nodes[t])]));
9829 function SaverBase() {}
9831 SaverBase.prototype.externalize = function(store)
9834 var tiddlers = store.getTiddlers(
"title");
9835 for(var t =
0; t < tiddlers.length; t++) {
9836 if(!tiddlers[t].doNotSave())
9837 results.push(this.externalizeTiddler(store, tiddlers[t]));
9839 return results.join(
"\n");
9843 //-- TW21Loader (inherits from LoaderBase)
9846 function TW21Loader() {}
9848 TW21Loader.prototype = new LoaderBase();
9850 TW21Loader.prototype.getTitle = function(store,node)
9853 if(node.getAttribute) {
9854 title = node.getAttribute(
"title");
9856 title = node.getAttribute(
"tiddler");
9858 if(!title && node.id) {
9859 var lenPrefix = store.idPrefix.length;
9860 if(node.id.substr(
0,lenPrefix) == store.idPrefix)
9861 title = node.id.substr(lenPrefix);
9866 TW21Loader.prototype.internalizeTiddler = function(store,tiddler,title,node)
9868 var e = node.firstChild;
9870 if(node.getAttribute(
"tiddler")) {
9871 text = getNodeText(e).unescapeLineBreaks();
9873 while(e.nodeName!=
"PRE" && e.nodeName!=
"pre") {
9876 text = e.innerHTML.replace(/\r/mg,
"").htmlDecode();
9878 var modifier = node.getAttribute(
"modifier");
9879 var c = node.getAttribute(
"created");
9880 var m = node.getAttribute(
"modified");
9881 var created = c ? Date.convertFromYYYYMMDDHHMM(c) : version.date;
9882 var modified = m ? Date.convertFromYYYYMMDDHHMM(m) : created;
9883 var tags = node.getAttribute(
"tags");
9885 var attrs = node.attributes;
9886 for(var i = attrs.length-
1; i
>=
0; i--) {
9887 var name = attrs[i].name;
9888 if(attrs[i].specified && !TiddlyWiki.isStandardField(name)) {
9889 fields[name] = attrs[i].value.unescapeLineBreaks();
9892 tiddler.assign(title,text,modifier,modified,tags,created,fields);
9897 //-- TW21Saver (inherits from SaverBase)
9900 function TW21Saver() {}
9902 TW21Saver.prototype = new SaverBase();
9904 TW21Saver.prototype.externalizeTiddler = function(store,tiddler)
9907 var extendedAttributes =
"";
9908 var usePre = config.options.chkUsePreForStorage;
9909 store.forEachField(tiddler,
9910 function(tiddler,fieldName,value) {
9911 // don't store stuff from the temp namespace
9912 if(typeof value !=
"string")
9914 if(!fieldName.match(/^temp\./))
9915 extendedAttributes += ' %
0=
"%1"'.format([fieldName,value.escapeLineBreaks().htmlEncode()]);
9917 var created = tiddler.created;
9918 var modified = tiddler.modified;
9919 var attributes = tiddler.modifier ? '
modifier=
"' + tiddler.modifier.htmlEncode() + '"' :
"";
9920 attributes += (usePre && created == version.date) ?
"" :'
created=
"' + created.convertToYYYYMMDDHHMM() + '"';
9921 attributes += (usePre && modified == created) ?
"" : '
modified=
"' + modified.convertToYYYYMMDDHHMM() +'"';
9922 var tags = tiddler.getTags();
9924 attributes += '
tags=
"' + tags.htmlEncode() + '"';
9925 return ('
<div %
0=
"%1"%
2%
3>%
4</'+'div
>').format([
9926 usePre ?
"title" :
"tiddler",
9927 tiddler.title.htmlEncode(),
9930 usePre ?
"\n<pre>" + tiddler.text.htmlEncode() +
"</pre>\n" : tiddler.text.escapeLineBreaks().htmlEncode()
9933 throw exceptionText(ex,config.messages.tiddlerSaveError.format([tiddler.title]));
9939 <script type=
"text/javascript">
9942 document
.write("<applet style='position:absolute;left:-1px' name='TiddlySaver' code='TiddlySaver.class' archive='TiddlySaver.jar' width='1' height='1'></applet>");
9945 <!--POST-SCRIPT-START-->
9947 <!--POST-SCRIPT-END-->