1 <!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2 <html xmlns=
"http://www.w3.org/1999/xhtml" xml:
lang=
"en" lang=
"en">
4 <script type=
"text/javascript">
6 var version
= {title
: "TiddlyWiki", major
: 2, minor
: 3, revision
: 0, date
: new Date("Dec 4, 2007"), extensions
: {}};
10 TiddlyWiki created by Jeremy Ruston, (jeremy [at] osmosoft [dot] com)
12 Copyright (c) UnaMesa Association 2004-2007
14 Redistribution and use in source and binary forms, with or without modification,
15 are permitted provided that the following conditions are met:
17 Redistributions of source code must retain the above copyright notice, this
18 list of conditions and the following disclaimer.
20 Redistributions in binary form must reproduce the above copyright notice, this
21 list of conditions and the following disclaimer in the documentation and/or other
22 materials provided with the distribution.
24 Neither the name of the UnaMesa Association nor the names of its contributors may be
25 used to endorse or promote products derived from this software without specific
26 prior written permission.
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
29 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
30 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
31 SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
32 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
33 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
34 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
36 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
39 <meta http-equiv=
"Content-Type" content=
"text/html;charset=utf-8" />
42 <link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'
/>
45 <title> Open Lease - open source rental property management software
</title>
46 <style 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 #messageArea {border:
1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
155 #messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}
157 .popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:
2px solid [[ColorPalette::TertiaryMid]];}
159 .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]];}
160 .popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:
1px;}
161 .popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
162 .popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
163 .popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
164 .popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
165 .popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
166 .listBreak div {border-bottom:
1px solid [[ColorPalette::TertiaryDark]];}
168 .tiddler .defaultCommand {font-weight:bold;}
170 .shadow .title {color:[[ColorPalette::TertiaryDark]];}
172 .title {color:[[ColorPalette::SecondaryDark]];}
173 .subtitle {color:[[ColorPalette::TertiaryDark]];}
175 .toolbar {color:[[ColorPalette::PrimaryMid]];}
176 .toolbar a {color:[[ColorPalette::TertiaryLight]];}
177 .selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
178 .selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}
180 .tagging, .tagged {border:
1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
181 .selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:
1px solid [[ColorPalette::TertiaryMid]];}
182 .tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
183 .tagging .button, .tagged .button {border:none;}
185 .footer {color:[[ColorPalette::TertiaryLight]];}
186 .selected .footer {color:[[ColorPalette::TertiaryMid]];}
188 .sparkline {background:[[ColorPalette::PrimaryPale]]; border:
0;}
189 .sparktick {background:[[ColorPalette::PrimaryDark]];}
191 .error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
192 .warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
193 .lowlight {background:[[ColorPalette::TertiaryLight]];}
195 .zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:
3px solid [[ColorPalette::TertiaryMid]];}
197 .imageLink, #displayArea .imageLink {background:transparent;}
199 .annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:
2px solid [[ColorPalette::SecondaryMid]];}
201 .viewer .listTitle {list-style-type:none; margin-left:-
2em;}
202 .viewer .button {border:
1px solid [[ColorPalette::SecondaryMid]];}
203 .viewer blockquote {border-left:
3px solid [[ColorPalette::TertiaryDark]];}
205 .viewer table, table.twtable {border:
2px solid [[ColorPalette::TertiaryDark]];}
206 .viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:
1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
207 .viewer td, .viewer tr, .twtable td, .twtable tr {border:
1px solid [[ColorPalette::TertiaryDark]];}
209 .viewer pre {border:
1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
210 .viewer code {color:[[ColorPalette::SecondaryDark]];}
211 .viewer hr {border:
0; border-top:dashed
1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}
213 .highlight, .marked {background:[[ColorPalette::SecondaryLight]];}
215 .editor input {border:
1px solid [[ColorPalette::PrimaryMid]];}
216 .editor textarea {border:
1px solid [[ColorPalette::PrimaryMid]]; width:
100%;}
217 .editorFooter {color:[[ColorPalette::TertiaryMid]];}
219 #backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
220 #backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
221 #backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
222 #backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
223 #backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
224 #backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
225 #backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
226 .backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
227 .backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
228 #backstageCloak {background:[[ColorPalette::Foreground]]; opacity:
0.6; filter:'alpha(opacity:
60)';}
231 <div title=
"StyleSheetLayout">
233 * html .tiddler {height:
1%;}
235 body {font-size:
.75em; font-family:arial,helvetica; margin:
0; padding:
0;}
237 h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
238 h1,h2,h3 {padding-bottom:
1px; margin-top:
1.2em;margin-bottom:
0.3em;}
239 h4,h5,h6 {margin-top:
1em;}
240 h1 {font-size:
1.35em;}
241 h2 {font-size:
1.25em;}
242 h3 {font-size:
1.1em;}
248 a {text-decoration:none;}
250 dt {font-weight:bold;}
252 ol {list-style-type:decimal;}
253 ol ol {list-style-type:lower-alpha;}
254 ol ol ol {list-style-type:lower-roman;}
255 ol ol ol ol {list-style-type:decimal;}
256 ol ol ol ol ol {list-style-type:lower-alpha;}
257 ol ol ol ol ol ol {list-style-type:lower-roman;}
258 ol ol ol ol ol ol ol {list-style-type:decimal;}
260 .txtOptionInput {width:
11em;}
262 #contentWrapper .chkOptionInput {border:
0;}
264 .externalLink {text-decoration:underline;}
266 .indent {margin-left:
3em;}
267 .outdent {margin-left:
3em; text-indent:-
3em;}
268 code.escaped {white-space:nowrap;}
270 .tiddlyLinkExisting {font-weight:bold;}
271 .tiddlyLinkNonExisting {font-style:italic;}
273 /* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
274 a.tiddlyLinkNonExisting.shadow {font-weight:bold;}
276 #mainMenu .tiddlyLinkExisting,
277 #mainMenu .tiddlyLinkNonExisting,
278 #sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
279 #sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}
281 .header {position:relative;}
282 .header a:hover {background:transparent;}
283 .headerShadow {position:relative; padding:
4.5em
0em
1em
1em; left:-
1px; top:-
1px;}
284 .headerForeground {position:absolute; padding:
4.5em
0em
1em
1em; left:
0px; top:
0px;}
286 .siteTitle {font-size:
3em;}
287 .siteSubtitle {font-size:
1.2em;}
289 #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;}
291 #sidebar {position:absolute; right:
3px; width:
16em; font-size:
.9em;}
292 #sidebarOptions {padding-top:
0.3em;}
293 #sidebarOptions a {margin:
0em
0.2em; padding:
0.2em
0.3em; display:block;}
294 #sidebarOptions input {margin:
0.4em
0.5em;}
295 #sidebarOptions .sliderPanel {margin-left:
1em; padding:
0.5em; font-size:
.85em;}
296 #sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:
0;}
297 #sidebarOptions .sliderPanel input {margin:
0 0 .3em
0;}
298 #sidebarTabs .tabContents {width:
15em; overflow:hidden;}
300 .wizard {padding:
0.1em
1em
0em
2em;}
301 .wizard h1 {font-size:
2em; font-weight:bold; background:none; padding:
0em
0em
0em
0em; margin:
0.4em
0em
0.2em
0em;}
302 .wizard h2 {font-size:
1.2em; font-weight:bold; background:none; padding:
0em
0em
0em
0em; margin:
0.4em
0em
0.2em
0em;}
303 .wizardStep {padding:
1em
1em
1em
1em;}
304 .wizard .button {margin:
0.5em
0em
0em
0em; font-size:
1.2em;}
305 .wizardFooter {padding:
0.8em
0.4em
0.8em
0em;}
306 .wizardFooter .status {padding:
0em
0.4em
0em
0.4em; margin-left:
1em;}
307 .wizard .button {padding:
0.1em
0.2em
0.1em
0.2em;}
309 #messageArea {position:fixed; top:
2em; right:
0em; margin:
0.5em; padding:
0.5em; z-index:
2000; _position:absolute;}
310 .messageToolbar {display:block; text-align:right; padding:
0.2em
0.2em
0.2em
0.2em;}
311 #messageArea a {text-decoration:underline;}
313 .tiddlerPopupButton {padding:
0.2em
0.2em
0.2em
0.2em;}
314 .popupTiddler {position: absolute; z-index:
300; padding:
1em
1em
1em
1em; margin:
0;}
316 .popup {position:absolute; z-index:
300; font-size:
.9em; padding:
0; list-style:none; margin:
0;}
317 .popup .popupMessage {padding:
0.4em;}
318 .popup hr {display:block; height:
1px; width:auto; padding:
0; margin:
0.2em
0em;}
319 .popup li.disabled {padding:
0.4em;}
320 .popup li a {display:block; padding:
0.4em; font-weight:normal; cursor:pointer;}
321 .listBreak {font-size:
1px; line-height:
1px;}
322 .listBreak div {margin:
2px
0;}
324 .tabset {padding:
1em
0em
0em
0.5em;}
325 .tab {margin:
0em
0em
0em
0.25em; padding:
2px;}
326 .tabContents {padding:
0.5em;}
327 .tabContents ul, .tabContents ol {margin:
0; padding:
0;}
328 .txtMainTab .tabContents li {list-style:none;}
329 .tabContents li.listLink { margin-left:
.75em;}
331 #contentWrapper {display:block;}
332 #splashScreen {display:none;}
334 #displayArea {margin:
1em
17em
0em
14em;}
336 .toolbar {text-align:right; font-size:
.9em;}
338 .tiddler {padding:
1em
1em
0em
1em;}
340 .missing .viewer,.missing .title {font-style:italic;}
342 .title {font-size:
1.6em; font-weight:bold;}
344 .missing .subtitle {display:none;}
345 .subtitle {font-size:
1.1em;}
347 .tiddler .button {padding:
0.2em
0.4em;}
349 .tagging {margin:
0.5em
0.5em
0.5em
0; float:left; display:none;}
350 .isTag .tagging {display:block;}
351 .tagged {margin:
0.5em; float:right;}
352 .tagging, .tagged {font-size:
0.9em; padding:
0.25em;}
353 .tagging ul, .tagged ul {list-style:none; margin:
0.25em; padding:
0;}
354 .tagClear {clear:both;}
356 .footer {font-size:
.9em;}
357 .footer li {display:inline;}
359 .annotation {padding:
0.5em; margin:
0.5em;}
361 * html .viewer pre {width:
99%; padding:
0 0 1em
0;}
362 .viewer {line-height:
1.4em; padding-top:
0.5em;}
363 .viewer .button {margin:
0em
0.25em; padding:
0em
0.25em;}
364 .viewer blockquote {line-height:
1.5em; padding-left:
0.8em;margin-left:
2.5em;}
365 .viewer ul, .viewer ol {margin-left:
0.5em; padding-left:
1.5em;}
367 .viewer table, table.twtable {border-collapse:collapse; margin:
0.8em
1.0em;}
368 .viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:
3px;}
369 table.listView {font-size:
0.85em; margin:
0.8em
1.0em;}
370 table.listView th, table.listView td, table.listView tr {padding:
0px
3px
0px
3px;}
372 .viewer pre {padding:
0.5em; margin-left:
0.5em; font-size:
1.2em; line-height:
1.4em; overflow:auto;}
373 .viewer code {font-size:
1.2em; line-height:
1.4em;}
375 .editor {font-size:
1.1em;}
376 .editor input, .editor textarea {display:block; width:
100%; font:inherit;}
377 .editorFooter {padding:
0.25em
0em; font-size:
.9em;}
378 .editorFooter .button {padding-top:
0px; padding-bottom:
0px;}
380 .fieldsetFix {border:
0; padding:
0; margin:
1px
0px
1px
0px;}
382 .sparkline {line-height:
1em;}
383 .sparktick {outline:
0;}
385 .zoomer {font-size:
1.1em; position:absolute; overflow:hidden;}
386 .zoomer div {padding:
1em;}
388 * html #backstage {width:
99%;}
389 * html #backstageArea {width:
99%;}
390 #backstageArea {display:none; position:relative; overflow: hidden; z-index:
150; padding:
0.3em
0.5em
0.3em
0.5em;}
391 #backstageToolbar {position:relative;}
392 #backstageArea a {font-weight:bold; margin-left:
0.5em; padding:
0.3em
0.5em
0.3em
0.5em;}
393 #backstageButton {display:none; position:absolute; z-index:
175; top:
0em; right:
0em;}
394 #backstageButton a {padding:
0.1em
0.4em
0.1em
0.4em; margin:
0.1em
0.1em
0.1em
0.1em;}
395 #backstage {position:relative; width:
100%; z-index:
50;}
396 #backstagePanel {display:none; z-index:
100; position:absolute; margin:
0em
3em
0em
3em; padding:
1em
1em
1em
1em;}
397 .backstagePanelFooter {padding-top:
0.2em; float:right;}
398 .backstagePanelFooter a {padding:
0.2em
0.4em
0.2em
0.4em;}
399 #backstageCloak {display:none; z-index:
20; position:absolute; width:
100%; height:
100px;}
401 .whenBackstage {display:none;}
402 .backstageVisible .whenBackstage {display:block;}
405 <div title=
"StyleSheetLocale">
407 StyleSheet for use when a translation requires any css style changes.
408 This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which need larger font sizes.
411 body {font-size:
0.8em;}
412 #sidebarOptions {font-size:
1.05em;}
413 #sidebarOptions a {font-style:normal;}
414 #sidebarOptions .sliderPanel {font-size:
0.95em;}
415 .subtitle {font-size:
0.8em;}
416 .viewer table.listView {font-size:
0.95em;}
419 <div title=
"StyleSheetPrint">
422 #mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none ! important;}
423 #displayArea {margin:
1em
1em
0em
1em;}
424 /* Fixes a feature in Firefox
1.5.0.2 where print preview displays the noscript content */
425 noscript {display:none;}
429 <div title=
"PageTemplate">
430 <pre><!--{{{--
>
431 <div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'
>
432 <div class='headerShadow'
>
433 <span class='siteTitle' refresh='content' tiddler='SiteTitle'
></span
>&nbsp;
434 <span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'
></span
>
436 <div class='headerForeground'
>
437 <span class='siteTitle' refresh='content' tiddler='SiteTitle'
></span
>&nbsp;
438 <span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'
></span
>
441 <div id='mainMenu' refresh='content' tiddler='MainMenu'
></div
>
442 <div id='sidebar'
>
443 <div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'
></div
>
444 <div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'
></div
>
446 <div id='displayArea'
>
447 <div id='messageArea'
></div
>
448 <div id='tiddlerDisplay'
></div
>
450 <!--}}}--
></pre>
452 <div title=
"ViewTemplate">
453 <pre><!--{{{--
>
454 <div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler
> fields syncing permalink references jump'
></div
>
455 <div class='title' macro='view title'
></div
>
456 <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
>
457 <div class='tagging' macro='tagging'
></div
>
458 <div class='tagged' macro='tags'
></div
>
459 <div class='viewer' macro='view text wikified'
></div
>
460 <div class='tagClear'
></div
>
461 <!--}}}--
></pre>
463 <div title=
"EditTemplate">
464 <pre><!--{{{--
>
465 <div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'
></div
>
466 <div class='title' macro='view title'
></div
>
467 <div class='editor' macro='edit title'
></div
>
468 <div macro='annotations'
></div
>
469 <div class='editor' macro='edit text'
></div
>
470 <div class='editor' macro='edit tags'
></div
><div class='editorFooter'
><span macro='message views.editor.tagPrompt'
></span
><span macro='tagChooser'
></span
></div
>
471 <!--}}}--
></pre>
473 <div title=
"GettingStarted">
474 <pre>To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
475 * SiteTitle
& SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
476 * MainMenu: The menu (usually on the left)
477 * DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
478 You'll also need to enter your username for signing your edits:
<<option txtUserName
>></pre>
480 <div title=
"OptionsPanel">
481 <pre>These InterfaceOptions for customising TiddlyWiki are saved in your browser
483 Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)
485 <<option txtUserName
>>
486 <<option chkSaveBackups
>> SaveBackups
487 <<option chkAutoSave
>> AutoSave
488 <<option chkRegExpSearch
>> RegExpSearch
489 <<option chkCaseSensitiveSearch
>> CaseSensitiveSearch
490 <<option chkAnimate
>> EnableAnimations
493 Also see AdvancedOptions
</pre>
495 <div title=
"ImportTiddlers">
496 <pre><<importTiddlers
>></pre>
499 <!--POST-SHADOWAREA-->
501 <div title=
"Boilerplate Lease Contracts" modifier=
"Jason McVetta" modified=
"200803112242" created=
"200803112239" changecount=
"2">
502 <pre>What kind of CreativeCommons license is appropriate?
</pre>
504 <div title=
"CryptoFunctionsPlugin" created=
"200710131134" tags=
"systemConfig excludeLists excludeSearch">
506 |''Name:''|CryptoFunctionsPlugin|
507 |''Description:''|Support for cryptographic functions|
510 if(!version.extensions.CryptoFunctionsPlugin) {
511 version.extensions.CryptoFunctionsPlugin = {installed:true};
514 //-- Crypto functions and associated conversion routines
517 // Crypto
"namespace
"
520 // Convert a string to an array of big-endian
32-bit words
521 Crypto.strToBe32s = function(str)
524 var len = Math.floor(str.length/
4);
526 for(i=
0, j=
0; i
<len; i++, j+=
4) {
527 be[i] = ((str.charCodeAt(j)
&0xff)
<< 24)|((str.charCodeAt(j+
1)
&0xff)
<< 16)|((str.charCodeAt(j+
2)
&0xff)
<< 8)|(str.charCodeAt(j+
3)
&0xff);
529 while (j
<str.length) {
530 be[j
>>2] |= (str.charCodeAt(j)
&0xff)
<<(
24-(j*
8)%
32);
536 // Convert an array of big-endian
32-bit words to a string
537 Crypto.be32sToStr = function(be)
539 var str =
"";
540 for(var i=
0;i
<be.length*
32;i+=
8)
541 str += String.fromCharCode((be[i
>>5]
>>>(
24-i%
32))
& 0xff);
545 // Convert an array of big-endian
32-bit words to a hex string
546 Crypto.be32sToHex = function(be)
548 var hex =
"0123456789ABCDEF
";
549 var str =
"";
550 for(var i=
0;i
<be.length*
4;i++)
551 str += hex.charAt((be[i
>>2]
>>((
3-i%
4)*
8+
4))
&0xF) + hex.charAt((be[i
>>2]
>>((
3-i%
4)*
8))
&0xF);
555 // Return, in hex, the SHA-
1 hash of a string
556 Crypto.hexSha1Str = function(str)
558 return Crypto.be32sToHex(Crypto.sha1Str(str));
561 // Return the SHA-
1 hash of a string
562 Crypto.sha1Str = function(str)
564 return Crypto.sha1(Crypto.strToBe32s(str),str.length);
567 // Calculate the SHA-
1 hash of an array of blen bytes of big-endian
32-bit words
568 Crypto.sha1 = function(x,blen)
570 // Add
32-bit integers, wrapping at
32 bits
571 add32 = function(a,b)
573 var lsw = (a
&0xFFFF)+(b
&0xFFFF);
574 var msw = (a
>>16)+(b
>>16)+(lsw
>>16);
575 return (msw
<<16)|(lsw
&0xFFFF);
577 // Add five
32-bit integers, wrapping at
32 bits
578 add32x5 = function(a,b,c,d,e)
580 var lsw = (a
&0xFFFF)+(b
&0xFFFF)+(c
&0xFFFF)+(d
&0xFFFF)+(e
&0xFFFF);
581 var msw = (a
>>16)+(b
>>16)+(c
>>16)+(d
>>16)+(e
>>16)+(lsw
>>16);
582 return (msw
<<16)|(lsw
&0xFFFF);
584 // Bitwise rotate left a
32-bit integer by
1 bit
587 return (n
>>>31)|(n
<<1);
591 // Append padding so length in bits is
448 mod
512
592 x[len
>>5] |=
0x80 << (
24-len%
32);
594 x[((len+
64>>9)
<<4)+
15] = len;
608 for(var i=
0;i
<x.length;i+=
16) {
615 for(j =
0;j
<16;j++) {
617 t = add32x5(e,(a
>>>27)|(a
<<5),d^(b
&(c^d)),w[j],k1);
618 e=d; d=c; c=(b
>>>2)|(b
<<30); b=a; a = t;
620 for(j=
16;j
<20;j++) {
621 w[j] = rol32(w[j-
3]^w[j-
8]^w[j-
14]^w[j-
16]);
622 t = add32x5(e,(a
>>>27)|(a
<<5),d^(b
&(c^d)),w[j],k1);
623 e=d; d=c; c=(b
>>>2)|(b
<<30); b=a; a = t;
625 for(j=
20;j
<40;j++) {
626 w[j] = rol32(w[j-
3]^w[j-
8]^w[j-
14]^w[j-
16]);
627 t = add32x5(e,(a
>>>27)|(a
<<5),b^c^d,w[j],k2);
628 e=d; d=c; c=(b
>>>2)|(b
<<30); b=a; a = t;
630 for(j=
40;j
<60;j++) {
631 w[j] = rol32(w[j-
3]^w[j-
8]^w[j-
14]^w[j-
16]);
632 t = add32x5(e,(a
>>>27)|(a
<<5),(b
&c)|(d
&(b|c)),w[j],k3);
633 e=d; d=c; c=(b
>>>2)|(b
<<30); b=a; a = t;
635 for(j=
60;j
<80;j++) {
636 w[j] = rol32(w[j-
3]^w[j-
8]^w[j-
14]^w[j-
16]);
637 t = add32x5(e,(a
>>>27)|(a
<<5),b^c^d,w[j],k4);
638 e=d; d=c; c=(b
>>>2)|(b
<<30); b=a; a = t;
647 return Array(h0,h1,h2,h3,h4);
654 <div title=
"DeprecatedFunctionsPlugin" created=
"200710140259" tags=
"systemConfig excludeLists excludeSearch">
656 |''Name:''|DeprecatedFunctionsPlugin|
657 |''Description:''|Support for deprecated functions removed from core|
660 if(!version.extensions.DeprecatedFunctionsPlugin) {
661 version.extensions.DeprecatedFunctionsPlugin = {installed:true};
667 // @Deprecated: Use createElementAndWikify and this.termRegExp instead
668 config.formatterHelpers.charFormatHelper = function(w)
670 w.subWikify(createTiddlyElement(w.output,this.element),this.terminator);
673 // @Deprecated: Use enclosedTextHelper and this.lookaheadRegExp instead
674 config.formatterHelpers.monospacedByLineHelper = function(w)
676 var lookaheadRegExp = new RegExp(this.lookahead,
"mg
");
677 lookaheadRegExp.lastIndex = w.matchStart;
678 var lookaheadMatch = lookaheadRegExp.exec(w.source);
679 if(lookaheadMatch
&& lookaheadMatch.index == w.matchStart) {
680 var text = lookaheadMatch[
1];
681 if(config.browser.isIE)
682 text = text.replace(/\n/g,
"\r
");
683 createTiddlyElement(w.output,
"pre
",null,null,text);
684 w.nextMatch = lookaheadRegExp.lastIndex;
688 // @Deprecated: Use
<br
> or
<br /
> instead of
<<br
>>
689 config.macros.br = {};
690 config.macros.br.handler = function(place)
692 createTiddlyElement(place,
"br
");
695 // Find an entry in an array. Returns the array index or null
696 // @Deprecated: Use indexOf instead
697 Array.prototype.find = function(item)
699 var i = this.indexOf(item);
700 return i == -
1 ? null : i;
703 // Load a tiddler from an HTML DIV. The caller should make sure to later call Tiddler.changed()
704 // @Deprecated: Use store.getLoader().internalizeTiddler instead
705 Tiddler.prototype.loadFromDiv = function(divRef,title)
707 return store.getLoader().internalizeTiddler(store,this,title,divRef);
710 // Format the text for storage in an HTML DIV
711 // @Deprecated Use store.getSaver().externalizeTiddler instead.
712 Tiddler.prototype.saveToDiv = function()
714 return store.getSaver().externalizeTiddler(store,this);
717 // @Deprecated: Use store.allTiddlersAsHtml() instead
718 function allTiddlersAsHtml()
720 return store.allTiddlersAsHtml();
723 // @Deprecated: Use refreshPageTemplate instead
724 function applyPageTemplate(title)
726 refreshPageTemplate(title);
729 // @Deprecated: Use story.displayTiddlers instead
730 function displayTiddlers(srcElement,titles,template,unused1,unused2,animate,unused3)
732 story.displayTiddlers(srcElement,titles,template,animate);
735 // @Deprecated: Use story.displayTiddler instead
736 function displayTiddler(srcElement,title,template,unused1,unused2,animate,unused3)
738 story.displayTiddler(srcElement,title,template,animate);
741 // @Deprecated: Use functions on right hand side directly instead
742 var createTiddlerPopup = Popup.create;
743 var scrollToTiddlerPopup = Popup.show;
744 var hideTiddlerPopup = Popup.remove;
746 // @Deprecated: Use right hand side directly instead
747 var regexpBackSlashEn = new RegExp(
"\\\\n
",
"mg
");
748 var regexpBackSlash = new RegExp(
"\\\\
",
"mg
");
749 var regexpBackSlashEss = new RegExp(
"\\\\s
",
"mg
");
750 var regexpNewLine = new RegExp(
"\n
",
"mg
");
751 var regexpCarriageReturn = new RegExp(
"\r
",
"mg
");
756 <div title=
"Jason McVetta" modifier=
"Jason McVetta" modified=
"200803112257" created=
"200803112255" changecount=
"7">
757 <pre>[[jason.mcvetta@gmail.com|mailto:jason.mcvetta@gmail.com]]
</pre>
759 <div title=
"LegacyStrikeThroughPlugin" modifier=
"MartinBudden" created=
"200607210000" tags=
"systemConfig">
761 |''Name:''|LegacyStrikeThroughPlugin|
762 |''Description:''|Support for legacy (pre
2.1) strike through formatting|
764 |''Date:''|Jul
21,
2006|
765 |''Source:''|http://www.tiddlywiki.com/#LegacyStrikeThroughPlugin|
766 |''Author:''|MartinBudden (mjbudden (at) gmail (dot) com)|
767 |''License:''|[[BSD open source license]]|
768 |''CoreVersion:''|
2.1.0|
772 // Ensure that the LegacyStrikeThrough Plugin is only installed once.
773 if(!version.extensions.LegacyStrikeThroughPlugin) {
774 version.extensions.LegacyStrikeThroughPlugin = {installed:true};
776 config.formatters.push(
778 name:
"legacyStrikeByChar
",
779 match:
"==
",
780 termRegExp: /(==)/mg,
781 element:
"strike
",
782 handler: config.formatterHelpers.createElementAndWikify
785 } //# end of
"install only once
"
788 <div title=
"MainMenu" modifier=
"Jason McVetta" created=
"200803112251" changecount=
"1">
789 <pre>[[New Lease Processing]]
790 [[Boilerplate Lease Contracts]]
791 [[Tenant Protection]]
792 [[Landlord Protection]]
795 <div title=
"New Lease Processing" modifier=
"Jason McVetta" modified=
"200803112253" created=
"200803112201" changecount=
"9">
796 <pre>* Boilerplate legal contracts that match the default functionality of the app
797 ** Need to be written on a per-state, or per-city, basis?
798 * Incomplete leases can be entered
799 ** need {{{lease.is_complete()}}} instance method
800 ** A lease cannot take effect until it {{{is_complete()}}}
801 ** System can tell user (property manager //and// tenant) what steps/documents must yet be completed
802 * A sublease is just like a lease, except it has a master tenant and a chain of liability
803 ** Sublease must have its own contract, which wholly supercedes that of the master lease -- clarity
805 *** 'cascading' contract from master lease?
</pre>
807 <div title=
"Self-Service" modifier=
"Jason McVetta" created=
"200803112212" changecount=
"1">
808 <pre>* Rental payment via credit card
809 ** Easy for tenants
& subtenants to pay individually
810 ** Tenants
& subtenants can be assigned explicit proportional responsibility for rent payment
</pre>
812 <div title=
"SiteSubtitle" modifier=
"Jason McVetta" modified=
"200803112200" created=
"200803112159" changecount=
"2">
813 <pre>open source rental property management software
</pre>
815 <div title=
"SiteTitle" modifier=
"Jason McVetta" modified=
"200803112200" created=
"200803112159" changecount=
"2">
816 <pre>Open Lease
</pre>
818 <div title=
"SparklinePlugin" created=
"200710140259" tags=
"systemConfig excludeLists excludeSearch">
820 |''Name:''|SparklinePlugin|
821 |''Description:''|Sparklines macro|
824 if(!version.extensions.SparklinePlugin) {
825 version.extensions.SparklinePlugin = {installed:true};
831 config.macros.sparkline = {};
832 config.macros.sparkline.handler = function(place,macroName,params)
838 for(var t=
0; t
<params.length; t++) {
839 v = parseInt(params[t]);
846 if(data.length
< 1)
848 var box = createTiddlyElement(place,
"span
",null,
"sparkline
",String.fromCharCode(
160));
849 box.title = data.join(
",
");
850 var w = box.offsetWidth;
851 var h = box.offsetHeight;
852 box.style.paddingRight = (data.length *
2 - w) +
"px
";
853 box.style.position =
"relative
";
854 for(var d=
0; d
<data.length; d++) {
855 var tick = document.createElement(
"img
");
857 tick.className =
"sparktick
";
858 tick.style.position =
"absolute
";
859 tick.src =
"data:image/gif,GIF89a%
01%
00%
01%
00%
91%FF%
00%FF%FF%FF%
00%
00%
00%C0%C0%C0%
00%
00%
00!%F9%
04%
01%
00%
00%
02%
00%
2C%
00%
00%
00%
00%
01%
00%
01%
00%
40%
02%
02T%
01%
00%
3B
";
860 tick.style.left = d*
2 +
"px
";
861 tick.style.width =
"2px
";
862 v = Math.floor(((data[d] - min)/(max-min)) * h);
863 tick.style.top = (h-v) +
"px
";
864 tick.style.height = v +
"px
";
865 box.appendChild(tick);
873 <div title=
"Tenant Protection" modifier=
"Jason McVetta" modified=
"200803112247" created=
"200803112238" changecount=
"5">
874 <pre>The proportional rent responsibility built-in to the system and its associated [[ready-made leases|Boilerplate Lease Contracts]] affords tenants added protection from their fellow tenants. Landlords will often continue to demand joint
& several liability in the contract language, but the proportion for which each party to the lease is responsible will be explicity fixed at signing. Should one of tenant among several default, this explicit contractual relationship may make it easier for the other tenants to recover damages in court.
</pre>
877 <!--POST-STOREAREA-->
878 <!--POST-BODY-START-->
880 <script type=
"text/javascript">
885 // * This code is designed to be readable but for compactness it only includes brief comments. You can see fuller comments
886 // in the project Subversion repository at http://svn.tiddlywiki.org/Trunk/core/
888 // * You should never need to modify this source code directly. TiddlyWiki is carefully designed to allow deep customisation
889 // without changing the core code. Please consult the development group at http://groups.google.com/group/TiddlyWikiDev
893 //-- Configuration repository
896 // Miscellaneous options
898 numRssItems
: 20, // Number of items in the RSS feed
899 animDuration
: 400, // Duration of UI animations in milliseconds
900 cascadeFast
: 20, // Speed for cascade animations (higher == slower)
901 cascadeSlow
: 60, // Speed for EasterEgg cascade animations
902 cascadeDepth
: 5 // Depth of cascade animation
906 config
.adaptors
= {};
912 config
.annotations
= {};
914 // Custom fields to be automatically added to new tiddlers
915 config
.defaultCustomFields
= {};
924 // Options that can be set in the options panel and/or cookies
926 chkRegExpSearch
: false,
927 chkCaseSensitiveSearch
: false,
929 chkSaveBackups
: true,
931 chkGenerateAnRssFeed
: false,
932 chkSaveEmptyTemplate
: false,
933 chkOpenInNewWindow
: true,
934 chkToggleLinks
: false,
935 chkHttpReadOnly
: true,
936 chkForceMinorUpdate
: false,
937 chkConfirmDelete
: true,
938 chkInsertTabs
: false,
939 chkUsePreForStorage
: true, // Whether to use <pre> format for storage
940 chkDisplayStartupTime
: false,
942 txtMainTab
: "tabTimeline",
943 txtMoreTab
: "moreTabAll",
944 txtMaxEditRows
: "30",
945 txtFileSystemCharSet
: "UTF-8",
948 config
.optionsDesc
= {};
950 // List of notification functions to be called when certain tiddlers are changed or deleted
951 config
.notifyTiddlers
= [
952 {name
: "StyleSheetLayout", notify
: refreshStyles
},
953 {name
: "StyleSheetColors", notify
: refreshStyles
},
954 {name
: "StyleSheet", notify
: refreshStyles
},
955 {name
: "StyleSheetPrint", notify
: refreshStyles
},
956 {name
: "PageTemplate", notify
: refreshPageTemplate
},
957 {name
: "SiteTitle", notify
: refreshPageTitle
},
958 {name
: "SiteSubtitle", notify
: refreshPageTitle
},
959 {name
: "ColorPalette", notify
: refreshColorPalette
},
960 {name
: null, notify
: refreshDisplay
}
963 // Default tiddler templates
964 var DEFAULT_VIEW_TEMPLATE
= 1;
965 var DEFAULT_EDIT_TEMPLATE
= 2;
966 config
.tiddlerTemplates
= {
971 // More messages (rather a legacy layout that shouldn't really be like this)
982 config
.backstageTasks
= ["save","sync","importTask","tweak","plugins"];
984 // Macros; each has a 'handler' member that is inserted later
988 search
: {sizeTextbox
: 15},
1025 // Commands supported by the toolbar macro
1030 saveTiddler
: {hideReadOnly
: true},
1032 deleteTiddler
: {hideReadOnly
: true},
1034 references
: {type
: "popup"},
1035 jump
: {type
: "popup"},
1036 syncing
: {type
: "popup"},
1037 fields
: {type
: "popup"}
1040 // Browser detection... In a very few places, there's nothing else for it but to know what browser we're using.
1041 config
.userAgent
= navigator
.userAgent
.toLowerCase();
1043 isIE
: config
.userAgent
.indexOf("msie") != -1 && config
.userAgent
.indexOf("opera") == -1,
1044 isGecko
: config
.userAgent
.indexOf("gecko") != -1,
1045 ieVersion
: /MSIE (\d.\d)/i.exec(config
.userAgent
), // config.browser.ieVersion[1], if it exists, will be the IE version string, eg "6.0"
1046 isSafari
: config
.userAgent
.indexOf("applewebkit") != -1,
1047 isBadSafari
: !((new RegExp("[\u0150\u0170]","g")).test("\u0150")),
1048 firefoxDate
: /gecko\/(\d{8})/i.exec(config
.userAgent
), // config.browser.firefoxDate[1], if it exists, will be Firefox release date as "YYYYMMDD"
1049 isOpera
: config
.userAgent
.indexOf("opera") != -1,
1050 isLinux
: config
.userAgent
.indexOf("linux") != -1,
1051 isUnix
: config
.userAgent
.indexOf("x11") != -1,
1052 isMac
: config
.userAgent
.indexOf("mac") != -1,
1053 isWindows
: config
.userAgent
.indexOf("win") != -1
1056 // Basic regular expressions
1057 config
.textPrimitives
= {
1058 upperLetter
: "[A-Z\u00c0-\u00de\u0150\u0170]",
1059 lowerLetter
: "[a-z0-9_\\-\u00df-\u00ff\u0151\u0171]",
1060 anyLetter
: "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]",
1061 anyLetterStrict
: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]"
1063 if(config
.browser
.isBadSafari
) {
1064 config
.textPrimitives
= {
1065 upperLetter
: "[A-Z\u00c0-\u00de]",
1066 lowerLetter
: "[a-z0-9_\\-\u00df-\u00ff]",
1067 anyLetter
: "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff]",
1068 anyLetterStrict
: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff]"
1071 config
.textPrimitives
.sliceSeparator
= "::";
1072 config
.textPrimitives
.sectionSeparator
= "##";
1073 config
.textPrimitives
.urlPattern
= "(?:file|http|https|mailto|ftp|irc|news|data):[^\\s'\"]+(?:/|\\b)";
1074 config
.textPrimitives
.unWikiLink
= "~";
1075 config
.textPrimitives
.wikiLink
= "(?:(?:" + config
.textPrimitives
.upperLetter
+ "+" +
1076 config
.textPrimitives
.lowerLetter
+ "+" +
1077 config
.textPrimitives
.upperLetter
+
1078 config
.textPrimitives
.anyLetter
+ "*)|(?:" +
1079 config
.textPrimitives
.upperLetter
+ "{2,}" +
1080 config
.textPrimitives
.lowerLetter
+ "+))";
1082 config
.textPrimitives
.cssLookahead
= "(?:(" + config
.textPrimitives
.anyLetter
+ "+)\\(([^\\)\\|\\n]+)(?:\\):))|(?:(" + config
.textPrimitives
.anyLetter
+ "+):([^;\\|\\n]+);)";
1083 config
.textPrimitives
.cssLookaheadRegExp
= new RegExp(config
.textPrimitives
.cssLookahead
,"mg");
1085 config
.textPrimitives
.brackettedLink
= "\\[\\[([^\\]]+)\\]\\]";
1086 config
.textPrimitives
.titledBrackettedLink
= "\\[\\[([^\\[\\]\\|]+)\\|([^\\[\\]\\|]+)\\]\\]";
1087 config
.textPrimitives
.tiddlerForcedLinkRegExp
= new RegExp("(?:" + config
.textPrimitives
.titledBrackettedLink
+ ")|(?:" +
1088 config
.textPrimitives
.brackettedLink
+ ")|(?:" +
1089 config
.textPrimitives
.urlPattern
+ ")","mg");
1090 config
.textPrimitives
.tiddlerAnyLinkRegExp
= new RegExp("("+ config
.textPrimitives
.wikiLink
+ ")|(?:" +
1091 config
.textPrimitives
.titledBrackettedLink
+ ")|(?:" +
1092 config
.textPrimitives
.brackettedLink
+ ")|(?:" +
1093 config
.textPrimitives
.urlPattern
+ ")","mg");
1097 function() {return config
.browser
.isIE
;},
1098 function() {return true;}
1102 downTriangle
: ["\u25BC","\u25BE"],
1103 downArrow
: ["\u2193","\u2193"],
1104 bentArrowLeft
: ["\u2190","\u21A9"],
1105 bentArrowRight
: ["\u2192","\u21AA"]
1110 //-- Shadow tiddlers
1113 config
.shadowTiddlers
= {
1119 TabTimeline
: '<<timeline>>',
1120 TabAll
: '<<list all>>',
1121 TabTags
: '<<allTags excludeLists>>',
1122 TabMoreMissing
: '<<list missing>>',
1123 TabMoreOrphans
: '<<list orphans>>',
1124 TabMoreShadowed
: '<<list shadowed>>',
1125 AdvancedOptions
: '<<options>>',
1126 PluginManager
: '<<plugins>>'
1130 //-- Translateable strings
1133 // Strings in "double quotes" should be translated; strings in 'single quotes' should be left alone
1135 merge(config
.options
,{
1136 txtUserName
: "YourName"});
1138 merge(config
.tasks
,{
1139 save
: {text
: "save", tooltip
: "Save your changes to this TiddlyWiki", action
: saveChanges
},
1140 sync
: {text
: "sync", tooltip
: "Synchronise changes with other TiddlyWiki files and servers", content
: '<<sync>>'},
1141 importTask
: {text
: "import", tooltip
: "Import tiddlers and plugins from other TiddlyWiki files and servers", content
: '<<importTiddlers>>'},
1142 tweak
: {text
: "tweak", tooltip
: "Tweak the appearance and behaviour of TiddlyWiki", content
: '<<options>>'},
1143 plugins
: {text
: "plugins", tooltip
: "Manage installed plugins", content
: '<<plugins>>'}
1146 // Options that can be set in the options panel and/or cookies
1147 merge(config
.optionsDesc
,{
1148 txtUserName
: "Username for signing your edits",
1149 chkRegExpSearch
: "Enable regular expressions for searches",
1150 chkCaseSensitiveSearch
: "Case-sensitive searching",
1151 chkAnimate
: "Enable animations",
1152 chkSaveBackups
: "Keep backup file when saving changes",
1153 chkAutoSave
: "Automatically save changes",
1154 chkGenerateAnRssFeed
: "Generate an RSS feed when saving changes",
1155 chkSaveEmptyTemplate
: "Generate an empty template when saving changes",
1156 chkOpenInNewWindow
: "Open external links in a new window",
1157 chkToggleLinks
: "Clicking on links to open tiddlers causes them to close",
1158 chkHttpReadOnly
: "Hide editing features when viewed over HTTP",
1159 chkForceMinorUpdate
: "Don't update modifier username and date when editing tiddlers",
1160 chkConfirmDelete
: "Require confirmation before deleting tiddlers",
1161 chkInsertTabs
: "Use the tab key to insert tab characters instead of moving between fields",
1162 txtBackupFolder
: "Name of folder to use for backups",
1163 txtMaxEditRows
: "Maximum number of rows in edit boxes",
1164 txtFileSystemCharSet
: "Default character set for saving changes (Firefox/Mozilla only)"});
1166 merge(config
.messages
,{
1167 customConfigError
: "Problems were encountered loading plugins. See PluginManager for details",
1168 pluginError
: "Error: %0",
1169 pluginDisabled
: "Not executed because disabled via 'systemConfigDisable' tag",
1170 pluginForced
: "Executed because forced via 'systemConfigForce' tag",
1171 pluginVersionError
: "Not executed because this plugin needs a newer version of TiddlyWiki",
1172 nothingSelected
: "Nothing is selected. You must select one or more items first",
1173 savedSnapshotError
: "It appears that this TiddlyWiki has been incorrectly saved. Please see http://www.tiddlywiki.com/#DownloadSoftware for details",
1174 subtitleUnknown
: "(unknown)",
1175 undefinedTiddlerToolTip
: "The tiddler '%0' doesn't yet exist",
1176 shadowedTiddlerToolTip
: "The tiddler '%0' doesn't yet exist, but has a pre-defined shadow value",
1177 tiddlerLinkTooltip
: "%0 - %1, %2",
1178 externalLinkTooltip
: "External link to %0",
1179 noTags
: "There are no tagged tiddlers",
1180 notFileUrlError
: "You need to save this TiddlyWiki to a file before you can save changes",
1181 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",
1182 invalidFileError
: "The original file '%0' does not appear to be a valid TiddlyWiki",
1183 backupSaved
: "Backup saved",
1184 backupFailed
: "Failed to save backup file",
1185 rssSaved
: "RSS feed saved",
1186 rssFailed
: "Failed to save RSS feed file",
1187 emptySaved
: "Empty template saved",
1188 emptyFailed
: "Failed to save empty template file",
1189 mainSaved
: "Main TiddlyWiki file saved",
1190 mainFailed
: "Failed to save main TiddlyWiki file. Your changes have not been saved",
1191 macroError
: "Error in macro <<\%0>>",
1192 macroErrorDetails
: "Error while executing macro <<\%0>>:\n%1",
1193 missingMacro
: "No such macro",
1194 overwriteWarning
: "A tiddler named '%0' already exists. Choose OK to overwrite it",
1195 unsavedChangesWarning
: "WARNING! There are unsaved changes in TiddlyWiki\n\nChoose OK to save\nChoose CANCEL to discard",
1196 confirmExit
: "--------------------------------\n\nThere are unsaved changes in TiddlyWiki. If you continue you will lose those changes\n\n--------------------------------",
1197 saveInstructions
: "SaveChanges",
1198 unsupportedTWFormat
: "Unsupported TiddlyWiki format '%0'",
1199 tiddlerSaveError
: "Error when saving tiddler '%0'",
1200 tiddlerLoadError
: "Error when loading tiddler '%0'",
1201 wrongSaveFormat
: "Cannot save with storage format '%0'. Using standard format for save.",
1202 invalidFieldName
: "Invalid field name %0",
1203 fieldCannotBeChanged
: "Field '%0' cannot be changed",
1204 loadingMissingTiddler
: "Attempting to retrieve the tiddler '%0' from the '%1' server at:\n\n'%2' in the workspace '%3'"});
1206 merge(config
.messages
.messageClose
,{
1208 tooltip
: "close this message area"});
1210 config
.messages
.backstage
= {
1211 open
: {text
: "backstage", tooltip
: "Open the backstage area to perform authoring and editing tasks"},
1212 close
: {text
: "close", tooltip
: "Close the backstage area"},
1213 prompt
: "backstage: ",
1215 edit
: {text
: "edit", tooltip
: "Edit the tiddler '%0'"}
1219 config
.messages
.listView
= {
1220 tiddlerTooltip
: "Click for the full text of this tiddler",
1221 previewUnavailable
: "(preview not available)"
1224 config
.messages
.dates
.months
= ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November","December"];
1225 config
.messages
.dates
.days
= ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
1226 config
.messages
.dates
.shortMonths
= ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
1227 config
.messages
.dates
.shortDays
= ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
1228 // suffixes for dates, eg "1st","2nd","3rd"..."30th","31st"
1229 config
.messages
.dates
.daySuffixes
= ["st","nd","rd","th","th","th","th","th","th","th",
1230 "th","th","th","th","th","th","th","th","th","th",
1231 "st","nd","rd","th","th","th","th","th","th","th",
1233 config
.messages
.dates
.am
= "am";
1234 config
.messages
.dates
.pm
= "pm";
1236 merge(config
.messages
.tiddlerPopup
,{
1239 merge(config
.views
.wikified
.tag
,{
1240 labelNoTags
: "no tags",
1241 labelTags
: "tags: ",
1242 openTag
: "Open tag '%0'",
1243 tooltip
: "Show tiddlers tagged with '%0'",
1244 openAllText
: "Open all",
1245 openAllTooltip
: "Open all of these tiddlers",
1246 popupNone
: "No other tiddlers tagged with '%0'"});
1248 merge(config
.views
.wikified
,{
1249 defaultText
: "The tiddler '%0' doesn't yet exist. Double-click to create it",
1250 defaultModifier
: "(missing)",
1251 shadowModifier
: "(built-in shadow tiddler)",
1252 dateFormat
: "DD MMM YYYY",
1253 createdPrompt
: "created"});
1255 merge(config
.views
.editor
,{
1256 tagPrompt
: "Type tags separated with spaces, [[use double square brackets]] if necessary, or add existing",
1257 defaultText
: "Type the text for '%0'"});
1259 merge(config
.views
.editor
.tagChooser
,{
1261 tooltip
: "Choose existing tags to add to this tiddler",
1262 popupNone
: "There are no tags defined",
1263 tagTooltip
: "Add the tag '%0'"});
1265 merge(config
.messages
,{
1268 {unit
: 1024*1024*1024, template
: "%0\u00a0GB"},
1269 {unit
: 1024*1024, template
: "%0\u00a0MB"},
1270 {unit
: 1024, template
: "%0\u00a0KB"},
1271 {unit
: 1, template
: "%0\u00a0B"}
1274 merge(config
.macros
.search
,{
1276 prompt
: "Search this TiddlyWiki",
1278 successMsg
: "%0 tiddlers found matching %1",
1279 failureMsg
: "No tiddlers found matching %0"});
1281 merge(config
.macros
.tagging
,{
1283 labelNotTag
: "not tagging",
1284 tooltip
: "List of tiddlers tagged with '%0'"});
1286 merge(config
.macros
.timeline
,{
1287 dateFormat
: "DD MMM YYYY"});
1289 merge(config
.macros
.allTags
,{
1290 tooltip
: "Show tiddlers tagged with '%0'",
1291 noTags
: "There are no tagged tiddlers"});
1293 config
.macros
.list
.all
.prompt
= "All tiddlers in alphabetical order";
1294 config
.macros
.list
.missing
.prompt
= "Tiddlers that have links to them but are not defined";
1295 config
.macros
.list
.orphans
.prompt
= "Tiddlers that are not linked to from any other tiddlers";
1296 config
.macros
.list
.shadowed
.prompt
= "Tiddlers shadowed with default contents";
1297 config
.macros
.list
.touched
.prompt
= "Tiddlers that have been modified locally";
1299 merge(config
.macros
.closeAll
,{
1301 prompt
: "Close all displayed tiddlers (except any that are being edited)"});
1303 merge(config
.macros
.permaview
,{
1305 prompt
: "Link to an URL that retrieves all the currently displayed tiddlers"});
1307 merge(config
.macros
.saveChanges
,{
1308 label
: "save changes",
1309 prompt
: "Save all tiddlers to create a new TiddlyWiki",
1312 merge(config
.macros
.newTiddler
,{
1313 label
: "new tiddler",
1314 prompt
: "Create a new tiddler",
1315 title
: "New Tiddler",
1318 merge(config
.macros
.newJournal
,{
1319 label
: "new journal",
1320 prompt
: "Create a new tiddler from the current date and time",
1323 merge(config
.macros
.options
,{
1324 wizardTitle
: "Tweak advanced options",
1325 step1Title
: "These options are saved in cookies in your browser",
1326 step1Html
: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='false' name='chkUnknown'>Show unknown options</input>",
1327 unknownDescription
: "//(unknown)//",
1330 {name
: 'Option', field
: 'option', title
: "Option", type
: 'String'},
1331 {name
: 'Description', field
: 'description', title
: "Description", type
: 'WikiText'},
1332 {name
: 'Name', field
: 'name', title
: "Name", type
: 'String'}
1335 {className
: 'lowlight', field
: 'lowlight'}
1339 merge(config
.macros
.plugins
,{
1340 wizardTitle
: "Manage plugins",
1341 step1Title
: "Currently loaded plugins",
1342 step1Html
: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
1343 skippedText
: "(This plugin has not been executed because it was added since startup)",
1344 noPluginText
: "There are no plugins installed",
1345 confirmDeleteText
: "Are you sure you want to delete these plugins:\n\n%0",
1346 removeLabel
: "remove systemConfig tag",
1347 removePrompt
: "Remove systemConfig tag",
1348 deleteLabel
: "delete",
1349 deletePrompt
: "Delete these tiddlers forever",
1352 {name
: 'Selected', field
: 'Selected', rowName
: 'title', type
: 'Selector'},
1353 {name
: 'Tiddler', field
: 'tiddler', title
: "Tiddler", type
: 'Tiddler'},
1354 {name
: 'Size', field
: 'size', tiddlerLink
: 'size', title
: "Size", type
: 'Size'},
1355 {name
: 'Forced', field
: 'forced', title
: "Forced", tag
: 'systemConfigForce', type
: 'TagCheckbox'},
1356 {name
: 'Disabled', field
: 'disabled', title
: "Disabled", tag
: 'systemConfigDisable', type
: 'TagCheckbox'},
1357 {name
: 'Executed', field
: 'executed', title
: "Loaded", type
: 'Boolean', trueText
: "Yes", falseText
: "No"},
1358 {name
: 'Startup Time', field
: 'startupTime', title
: "Startup Time", type
: 'String'},
1359 {name
: 'Error', field
: 'error', title
: "Status", type
: 'Boolean', trueText
: "Error", falseText
: "OK"},
1360 {name
: 'Log', field
: 'log', title
: "Log", type
: 'StringList'}
1363 {className
: 'error', field
: 'error'},
1364 {className
: 'warning', field
: 'warning'}
1368 merge(config
.macros
.toolbar
,{
1370 morePrompt
: "Reveal further commands"
1373 merge(config
.macros
.refreshDisplay
,{
1375 prompt
: "Redraw the entire TiddlyWiki display"
1378 merge(config
.macros
.importTiddlers
,{
1379 readOnlyWarning
: "You cannot import into a read-only TiddlyWiki file. Try opening it from a file:// URL",
1380 wizardTitle
: "Import tiddlers from another file or server",
1381 step1Title
: "Step 1: Locate the server or TiddlyWiki file",
1382 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>",
1384 openPrompt
: "Open the connection to this file or server",
1385 openError
: "There were problems fetching the tiddlywiki file",
1386 statusOpenHost
: "Opening the host",
1387 statusGetWorkspaceList
: "Getting the list of available workspaces",
1388 step2Title
: "Step 2: Choose the workspace",
1389 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>",
1390 cancelLabel
: "cancel",
1391 cancelPrompt
: "Cancel this import",
1392 statusOpenWorkspace
: "Opening the workspace",
1393 statusGetTiddlerList
: "Getting the list of available tiddlers",
1394 step3Title
: "Step 3: Choose the tiddlers to import",
1395 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'>",
1396 importLabel
: "import",
1397 importPrompt
: "Import these tiddlers",
1398 confirmOverwriteText
: "Are you sure you want to overwrite these tiddlers:\n\n%0",
1399 step4Title
: "Step 4: Importing %0 tiddler(s)",
1400 step4Html
: "<input type='hidden' name='markReport'></input>", // DO NOT TRANSLATE
1402 donePrompt
: "Close this wizard",
1403 statusDoingImport
: "Importing tiddlers",
1404 statusDoneImport
: "All tiddlers imported",
1405 systemServerNamePattern
: "%2 on %1",
1406 systemServerNamePatternNoWorkspace
: "%1",
1407 confirmOverwriteSaveTiddler
: "The tiddler '%0' already exists. Click 'OK' to overwrite it with the details of this server, or 'Cancel' to leave it unchanged",
1408 serverSaveTemplate
: "|''Type:''|%0|\n|''URL:''|%1|\n|''Workspace:''|%2|\n\nThis tiddler was automatically created to record the details of this server",
1409 serverSaveModifier
: "(System)",
1412 {name
: 'Selected', field
: 'Selected', rowName
: 'title', type
: 'Selector'},
1413 {name
: 'Tiddler', field
: 'tiddler', title
: "Tiddler", type
: 'Tiddler'},
1414 {name
: 'Size', field
: 'size', tiddlerLink
: 'size', title
: "Size", type
: 'Size'},
1415 {name
: 'Tags', field
: 'tags', title
: "Tags", type
: 'Tags'}
1421 merge(config
.macros
.sync
,{
1424 {name
: 'Selected', field
: 'selected', rowName
: 'title', type
: 'Selector'},
1425 {name
: 'Tiddler', field
: 'tiddler', title
: "Tiddler", type
: 'Tiddler'},
1426 {name
: 'Server Type', field
: 'serverType', title
: "Server type", type
: 'String'},
1427 {name
: 'Server Host', field
: 'serverHost', title
: "Server host", type
: 'String'},
1428 {name
: 'Server Workspace', field
: 'serverWorkspace', title
: "Server workspace", type
: 'String'},
1429 {name
: 'Status', field
: 'status', title
: "Synchronisation status", type
: 'String'},
1430 {name
: 'Server URL', field
: 'serverUrl', title
: "Server URL", text
: "View", type
: 'Link'}
1435 {caption
: "Sync these tiddlers", name
: 'sync'}
1437 wizardTitle
: "Synchronize with external servers and files",
1438 step1Title
: "Choose the tiddlers you want to synchronize",
1439 step1Html
: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
1441 syncPrompt
: "Sync these tiddlers",
1442 hasChanged
: "Changed while unplugged",
1443 hasNotChanged
: "Unchanged while unplugged",
1445 none
: {text
: "...", color
: "transparent"},
1446 changedServer
: {text
: "Changed on server", color
: '#80ff80'},
1447 changedLocally
: {text
: "Changed while unplugged", color
: '#80ff80'},
1448 changedBoth
: {text
: "Changed while unplugged and on server", color
: '#ff8080'},
1449 notFound
: {text
: "Not found on server", color
: '#ffff80'},
1450 putToServer
: {text
: "Saved update on server", color
: '#ff80ff'},
1451 gotFromServer
: {text
: "Retrieved update from server", color
: '#80ffff'}
1455 merge(config
.macros
.annotations
,{
1458 merge(config
.commands
.closeTiddler
,{
1460 tooltip
: "Close this tiddler"});
1462 merge(config
.commands
.closeOthers
,{
1463 text
: "close others",
1464 tooltip
: "Close all other tiddlers"});
1466 merge(config
.commands
.editTiddler
,{
1468 tooltip
: "Edit this tiddler",
1469 readOnlyText
: "view",
1470 readOnlyTooltip
: "View the source of this tiddler"});
1472 merge(config
.commands
.saveTiddler
,{
1474 tooltip
: "Save changes to this tiddler"});
1476 merge(config
.commands
.cancelTiddler
,{
1478 tooltip
: "Undo changes to this tiddler",
1479 warning
: "Are you sure you want to abandon your changes to '%0'?",
1480 readOnlyText
: "done",
1481 readOnlyTooltip
: "View this tiddler normally"});
1483 merge(config
.commands
.deleteTiddler
,{
1485 tooltip
: "Delete this tiddler",
1486 warning
: "Are you sure you want to delete '%0'?"});
1488 merge(config
.commands
.permalink
,{
1490 tooltip
: "Permalink for this tiddler"});
1492 merge(config
.commands
.references
,{
1494 tooltip
: "Show tiddlers that link to this one",
1495 popupNone
: "No references"});
1497 merge(config
.commands
.jump
,{
1499 tooltip
: "Jump to another open tiddler"});
1501 merge(config
.commands
.syncing
,{
1503 tooltip
: "Control synchronisation of this tiddler with a server or external file",
1504 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
1505 notCurrentlySyncing
: "Not currently syncing",
1506 captionUnSync
: "Stop synchronising this tiddler",
1507 chooseServer
: "Synchronise this tiddler with another server:",
1508 currServerMarker
: "\u25cf ",
1509 notCurrServerMarker
: " "});
1511 merge(config
.commands
.fields
,{
1513 tooltip
: "Show the extended fields of this tiddler",
1514 emptyText
: "There are no extended fields for this tiddler",
1517 {name
: 'Field', field
: 'field', title
: "Field", type
: 'String'},
1518 {name
: 'Value', field
: 'value', title
: "Value", type
: 'String'}
1525 merge(config
.shadowTiddlers
,{
1526 DefaultTiddlers
: "GettingStarted",
1527 MainMenu
: "GettingStarted",
1528 SiteTitle
: "My TiddlyWiki",
1529 SiteSubtitle
: "a reusable non-linear personal web notebook",
1530 SiteUrl
: "http://www.tiddlywiki.com/",
1531 SideBarOptions
: '<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal "DD MMM YYYY" "journal">><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel "options »" "Change TiddlyWiki advanced options">>',
1532 SideBarTabs
: '<<tabs txtMainTab "Timeline" "Timeline" TabTimeline "All" "All tiddlers" TabAll "Tags" "All tags" TabTags "More" "More lists" TabMore>>',
1533 TabMore
: '<<tabs txtMoreTab "Missing" "Missing tiddlers" TabMoreMissing "Orphans" "Orphaned tiddlers" TabMoreOrphans "Shadowed" "Shadowed tiddlers" TabMoreShadowed>>'});
1535 merge(config
.annotations
,{
1536 AdvancedOptions
: "This shadow tiddler provides access to several advanced options",
1537 ColorPalette
: "These values in this shadow tiddler determine the colour scheme of the ~TiddlyWiki user interface",
1538 DefaultTiddlers
: "The tiddlers listed in this shadow tiddler will be automatically displayed when ~TiddlyWiki starts up",
1539 EditTemplate
: "The HTML template in this shadow tiddler determines how tiddlers look while they are being edited",
1540 GettingStarted
: "This shadow tiddler provides basic usage instructions",
1541 ImportTiddlers
: "This shadow tiddler provides access to importing tiddlers",
1542 MainMenu
: "This shadow tiddler is used as the contents of the main menu in the left-hand column of the screen",
1543 MarkupPreHead
: "This tiddler is inserted at the top of the <head> section of the TiddlyWiki HTML file",
1544 MarkupPostHead
: "This tiddler is inserted at the bottom of the <head> section of the TiddlyWiki HTML file",
1545 MarkupPreBody
: "This tiddler is inserted at the top of the <body> section of the TiddlyWiki HTML file",
1546 MarkupPostBody
: "This tiddler is inserted at the end of the <body> section of the TiddlyWiki HTML file immediately before the script block",
1547 OptionsPanel
: "This shadow tiddler is used as the contents of the options panel slider in the right-hand sidebar",
1548 PageTemplate
: "The HTML template in this shadow tiddler determines the overall ~TiddlyWiki layout",
1549 PluginManager
: "This shadow tiddler provides access to the plugin manager",
1550 SideBarOptions
: "This shadow tiddler is used as the contents of the option panel in the right-hand sidebar",
1551 SideBarTabs
: "This shadow tiddler is used as the contents of the tabs panel in the right-hand sidebar",
1552 SiteSubtitle
: "This shadow tiddler is used as the second part of the page title",
1553 SiteTitle
: "This shadow tiddler is used as the first part of the page title",
1554 SiteUrl
: "This shadow tiddler should be set to the full target URL for publication",
1555 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.",
1556 StyleSheet
: "This tiddler can contain custom CSS definitions",
1557 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.",
1558 StyleSheetLocale
: "This shadow tiddler contains CSS definitions related to the translation locale",
1559 StyleSheetPrint
: "This shadow tiddler contains CSS definitions for printing",
1560 TabAll
: "This shadow tiddler contains the contents of the 'All' tab in the right-hand sidebar",
1561 TabMore
: "This shadow tiddler contains the contents of the 'More' tab in the right-hand sidebar",
1562 TabMoreMissing
: "This shadow tiddler contains the contents of the 'Missing' tab in the right-hand sidebar",
1563 TabMoreOrphans
: "This shadow tiddler contains the contents of the 'Orphans' tab in the right-hand sidebar",
1564 TabMoreShadowed
: "This shadow tiddler contains the contents of the 'Shadowed' tab in the right-hand sidebar",
1565 TabTags
: "This shadow tiddler contains the contents of the 'Tags' tab in the right-hand sidebar",
1566 TabTimeline
: "This shadow tiddler contains the contents of the 'Timeline' tab in the right-hand sidebar",
1567 ViewTemplate
: "The HTML template in this shadow tiddler determines how tiddlers look"
1574 var params
= null; // Command line parameters
1575 var store
= null; // TiddlyWiki storage
1576 var story
= null; // Main story
1577 var formatter
= null; // Default formatters for the wikifier
1578 config
.parsers
= {}; // Hashmap of alternative parsers for the wikifier
1579 var anim
= typeof Animator
== "function" ? new Animator() : null; // Animation engine
1580 var readOnly
= false; // Whether we're in readonly mode
1581 var highlightHack
= null; // Embarrassing hack department...
1582 var hadConfirmExit
= false; // Don't warn more than once
1583 var safeMode
= false; // Disable all plugins and cookies
1584 var showBackstage
; // Whether to include the backstage area
1585 var installedPlugins
= []; // Information filled in when plugins are executed
1586 var startingUp
= false; // Whether we're in the process of starting up
1587 var pluginInfo
,tiddler
; // Used to pass information to plugins in loadPlugins()
1589 // Whether to use the JavaSaver applet
1590 var useJavaSaver
= config
.browser
.isSafari
|| config
.browser
.isOpera
;
1595 var t10
,t9
,t8
,t7
,t6
,t5
,t4
,t3
,t2
,t1
,t0
= new Date();
1597 window
.onbeforeunload = function(e
) {if(window
.confirmExit
) return confirmExit();};
1598 params
= getParameters();
1600 params
= params
.parseParams("open",null,false);
1601 store
= new TiddlyWiki();
1602 invokeParamifier(params
,"oninit");
1603 story
= new Story("tiddlerDisplay","tiddler");
1604 addEvent(document
,"click",Popup
.onDocumentClick
);
1606 loadOptionsCookie();
1607 for(var s
=0; s
<config
.notifyTiddlers
.length
; s
++)
1608 store
.addNotification(config
.notifyTiddlers
[s
].name
,config
.notifyTiddlers
[s
].notify
);
1610 store
.loadFromDiv("storeArea","store",true);
1612 loadShadowTiddlers();
1614 invokeParamifier(params
,"onload");
1616 readOnly
= (window
.location
.protocol
== "file:") ? false : config
.options
.chkHttpReadOnly
;
1617 showBackstage
= !readOnly
;
1618 var pluginProblem
= loadPlugins();
1620 formatter
= new Formatter(config
.formatters
);
1621 story
.switchTheme(config
.options
.txtTheme
);
1622 invokeParamifier(params
,"onconfig");
1629 story
.displayTiddler(null,"PluginManager");
1630 displayMessage(config
.messages
.customConfigError
);
1632 for(var m
in config
.macros
) {
1633 if(config
.macros
[m
].init
)
1634 config
.macros
[m
].init();
1640 if(config
.options
.chkDisplayStartupTime
) {
1641 displayMessage("LoadFromDiv " + (t2
-t1
) + " ms");
1642 displayMessage("LoadShadows " + (t3
-t2
) + " ms");
1643 displayMessage("LoadPlugins " + (t5
-t4
) + " ms");
1644 displayMessage("Notify " + (t7
-t6
) + " ms");
1645 displayMessage("Restart " + (t8
-t7
) + " ms");
1646 displayMessage("Macro init " + (t9
-t8
) + " ms");
1647 displayMessage("Total: " + (t10
-t0
) + " ms");
1655 invokeParamifier(params
,"onstart");
1656 if(story
.isEmpty()) {
1657 var tiddlers
= store
.filterTiddlers(store
.getTiddlerText("DefaultTiddlers"));
1658 story
.displayTiddlers(null,tiddlers
);
1660 window
.scrollTo(0,0);
1665 var s
= document
.getElementById("saveTest");
1666 if(s
.hasChildNodes())
1667 alert(config
.messages
.savedSnapshotError
);
1668 s
.appendChild(document
.createTextNode("savetest"));
1671 function loadShadowTiddlers()
1673 var shadows
= new TiddlyWiki();
1674 shadows
.loadFromDiv("shadowArea","shadows",true);
1675 shadows
.forEachTiddler(function(title
,tiddler
){config
.shadowTiddlers
[title
] = tiddler
.text
;});
1679 function loadPlugins()
1683 var tiddlers
= store
.getTaggedTiddlers("systemConfig");
1687 var nPlugins
= tiddlers
.length
;
1688 installedPlugins
= [];
1689 for(var i
=0; i
<nPlugins
; i
++) {
1690 var p
= getPluginInfo(tiddlers
[i
]);
1691 installedPlugins
[i
] = p
;
1699 var visit = function(p
) {
1703 var reqs
= p
.Requires
;
1705 reqs
= reqs
.readBracketedList();
1706 for(var i
=0; i
<reqs
.length
; i
++)
1707 visit(map
[reqs
[i
]]);
1711 for(i
=0; i
<nPlugins
; i
++)
1712 visit(installedPlugins
[i
]);
1713 for(i
=0; i
<toLoad
.length
; i
++) {
1716 tiddler
= p
.tiddler
;
1717 if(isPluginExecutable(p
)) {
1718 if(isPluginEnabled(p
)) {
1720 var startTime
= new Date();
1723 window
.eval(tiddler
.text
);
1726 p
.log
.push(config
.messages
.pluginError
.format([exceptionText(ex
)]));
1729 pluginInfo
.startupTime
= String((new Date()) - startTime
) + "ms";
1737 return nLoaded
!= nPlugins
;
1740 function getPluginInfo(tiddler
)
1742 var p
= store
.getTiddlerSlices(tiddler
.title
,["Name","Description","Version","Requires","CoreVersion","Date","Source","Author","License","Browsers"]);
1743 p
.tiddler
= tiddler
;
1744 p
.title
= tiddler
.title
;
1749 // Check that a particular plugin is valid for execution
1750 function isPluginExecutable(plugin
)
1752 if(plugin
.tiddler
.isTagged("systemConfigForce"))
1753 return verifyTail(plugin
,true,config
.messages
.pluginForced
);
1754 if(plugin
["CoreVersion"]) {
1755 var coreVersion
= plugin
["CoreVersion"].split(".");
1756 var w
= parseInt(coreVersion
[0]) - version
.major
;
1757 if(w
== 0 && coreVersion
[1])
1758 w
= parseInt(coreVersion
[1]) - version
.minor
;
1759 if(w
== 0 && coreVersion
[2])
1760 w
= parseInt(coreVersion
[2]) - version
.revision
;
1762 return verifyTail(plugin
,false,config
.messages
.pluginVersionError
);
1767 function isPluginEnabled(plugin
)
1769 if(plugin
.tiddler
.isTagged("systemConfigDisable"))
1770 return verifyTail(plugin
,false,config
.messages
.pluginDisabled
);
1774 function verifyTail(plugin
,result
,message
)
1776 plugin
.log
.push(message
);
1780 function invokeMacro(place
,macro
,params
,wikifier
,tiddler
)
1783 var m
= config
.macros
[macro
];
1785 m
.handler(place
,macro
,params
.readMacroParams(),wikifier
,params
,tiddler
);
1787 createTiddlyError(place
,config
.messages
.macroError
.format([macro
]),config
.messages
.macroErrorDetails
.format([macro
,config
.messages
.missingMacro
]));
1789 createTiddlyError(place
,config
.messages
.macroError
.format([macro
]),config
.messages
.macroErrorDetails
.format([macro
,ex
.toString()]));
1797 function getParameters()
1800 if(window
.location
.hash
) {
1801 p
= decodeURI(window
.location
.hash
.substr(1));
1802 if(config
.browser
.firefoxDate
!= null && config
.browser
.firefoxDate
[1] < "20051111")
1803 p
= convertUTF8ToUnicode(p
);
1808 function invokeParamifier(params
,handler
)
1810 if(!params
|| params
.length
== undefined || params
.length
<= 1)
1812 for(var t
=1; t
<params
.length
; t
++) {
1813 var p
= config
.paramifiers
[params
[t
].name
];
1814 if(p
&& p
[handler
] instanceof Function
)
1815 p
[handler
](params
[t
].value
);
1819 config
.paramifiers
= {};
1821 config
.paramifiers
.start
= {
1822 oninit: function(v
) {
1823 safeMode
= v
.toLowerCase() == "safe";
1827 config
.paramifiers
.open
= {
1828 onstart: function(v
) {
1829 story
.displayTiddler("bottom",v
,null,false,null);
1833 config
.paramifiers
.story
= {
1834 onstart: function(v
) {
1835 var list
= store
.getTiddlerText(v
,"").parseParams("open",null,false);
1836 invokeParamifier(list
,"onstart");
1840 config
.paramifiers
.search
= {
1841 onstart: function(v
) {
1842 story
.search(v
,false,false);
1846 config
.paramifiers
.searchRegExp
= {
1847 onstart: function(v
) {
1848 story
.prototype.search(v
,false,true);
1852 config
.paramifiers
.tag
= {
1853 onstart: function(v
) {
1854 var tagged
= store
.getTaggedTiddlers(v
,"title");
1855 story
.displayTiddlers(null,tagged
,null,false,null);
1859 config
.paramifiers
.newTiddler
= {
1860 onstart: function(v
) {
1862 story
.displayTiddler(null,v
,DEFAULT_EDIT_TEMPLATE
);
1863 story
.focusTiddler(v
,"text");
1868 config
.paramifiers
.newJournal
= {
1869 onstart: function(v
) {
1871 var now
= new Date();
1872 var title
= now
.formatString(v
.trim());
1873 story
.displayTiddler(null,title
,DEFAULT_EDIT_TEMPLATE
);
1874 story
.focusTiddler(title
,"text");
1879 config
.paramifiers
.readOnly
= {
1880 onconfig: function(v
) {
1881 var p
= v
.toLowerCase();
1882 readOnly
= p
== "yes" ? true : (p
== "no" ? false : readOnly
);
1887 config
.paramifiers
.theme
= {
1888 onconfig: function(v
) {
1889 story
.switchTheme(v
);
1894 //-- Formatter helpers
1897 function Formatter(formatters
)
1899 this.formatters
= [];
1901 for(var n
=0; n
<formatters
.length
; n
++) {
1902 pattern
.push("(" + formatters
[n
].match
+ ")");
1903 this.formatters
.push(formatters
[n
]);
1905 this.formatterRegExp
= new RegExp(pattern
.join("|"),"mg");
1908 config
.formatterHelpers
= {
1910 createElementAndWikify: function(w
)
1912 w
.subWikifyTerm(createTiddlyElement(w
.output
,this.element
),this.termRegExp
);
1915 inlineCssHelper: function(w
)
1918 config
.textPrimitives
.cssLookaheadRegExp
.lastIndex
= w
.nextMatch
;
1919 var lookaheadMatch
= config
.textPrimitives
.cssLookaheadRegExp
.exec(w
.source
);
1920 while(lookaheadMatch
&& lookaheadMatch
.index
== w
.nextMatch
) {
1922 if(lookaheadMatch
[1]) {
1923 s
= lookaheadMatch
[1].unDash();
1924 v
= lookaheadMatch
[2];
1926 s
= lookaheadMatch
[3].unDash();
1927 v
= lookaheadMatch
[4];
1930 s
= "backgroundColor";
1931 styles
.push({style
: s
, value
: v
});
1932 w
.nextMatch
= lookaheadMatch
.index
+ lookaheadMatch
[0].length
;
1933 config
.textPrimitives
.cssLookaheadRegExp
.lastIndex
= w
.nextMatch
;
1934 lookaheadMatch
= config
.textPrimitives
.cssLookaheadRegExp
.exec(w
.source
);
1939 applyCssHelper: function(e
,styles
)
1941 for(var t
=0; t
< styles
.length
; t
++) {
1943 e
.style
[styles
[t
].style
] = styles
[t
].value
;
1949 enclosedTextHelper: function(w
)
1951 this.lookaheadRegExp
.lastIndex
= w
.matchStart
;
1952 var lookaheadMatch
= this.lookaheadRegExp
.exec(w
.source
);
1953 if(lookaheadMatch
&& lookaheadMatch
.index
== w
.matchStart
) {
1954 var text
= lookaheadMatch
[1];
1955 if(config
.browser
.isIE
)
1956 text
= text
.replace(/\n/g,"\r");
1957 createTiddlyElement(w
.output
,this.element
,null,null,text
);
1958 w
.nextMatch
= lookaheadMatch
.index
+ lookaheadMatch
[0].length
;
1962 isExternalLink: function(link
)
1964 if(store
.tiddlerExists(link
) || store
.isShadowTiddler(link
)) {
1967 var urlRegExp
= new RegExp(config
.textPrimitives
.urlPattern
,"mg");
1968 if(urlRegExp
.exec(link
)) {
1971 if (link
.indexOf(".")!=-1 || link
.indexOf("\\")!=-1 || link
.indexOf("/")!=-1 || link
.indexOf("#")!=-1) {
1980 //-- Standard formatters
1983 config
.formatters
= [
1986 match
: "^\\|(?:[^\\n]*)\\|(?:[fhck]?)$",
1987 lookaheadRegExp
: /^\|([^\n]*)\|([fhck]?)$/mg,
1988 rowTermRegExp
: /(\|(?:[fhck]?)$\n?)/mg,
1989 cellRegExp
: /(?:\|([^\n\|]*)\|)|(\|[fhck]?$\n?)/mg,
1990 cellTermRegExp
: /((?:\x20*)\|)/mg,
1991 rowTypes
: {"c":"caption", "h":"thead", "":"tbody", "f":"tfoot"},
1992 handler: function(w
)
1994 var table
= createTiddlyElement(w
.output
,"table",null,"twtable");
1995 var prevColumns
= [];
1996 var currRowType
= null;
1999 w
.nextMatch
= w
.matchStart
;
2000 this.lookaheadRegExp
.lastIndex
= w
.nextMatch
;
2001 var lookaheadMatch
= this.lookaheadRegExp
.exec(w
.source
);
2002 while(lookaheadMatch
&& lookaheadMatch
.index
== w
.nextMatch
) {
2003 var nextRowType
= lookaheadMatch
[2];
2004 if(nextRowType
== "k") {
2005 table
.className
= lookaheadMatch
[1];
2006 w
.nextMatch
+= lookaheadMatch
[0].length
+1;
2008 if(nextRowType
!= currRowType
) {
2009 rowContainer
= createTiddlyElement(table
,this.rowTypes
[nextRowType
]);
2010 currRowType
= nextRowType
;
2012 if(currRowType
== "c") {
2015 if(rowContainer
!= table
.firstChild
)
2016 table
.insertBefore(rowContainer
,table
.firstChild
);
2017 rowContainer
.setAttribute("align",rowCount
== 0?"top":"bottom");
2018 w
.subWikifyTerm(rowContainer
,this.rowTermRegExp
);
2020 var theRow
= createTiddlyElement(rowContainer
,"tr",null,(rowCount
&1)?"oddRow":"evenRow");
2021 theRow
.onmouseover = function() {addClass(this,"hoverRow");};
2022 theRow
.onmouseout = function() {removeClass(this,"hoverRow");};
2023 this.rowHandler(w
,theRow
,prevColumns
);
2027 this.lookaheadRegExp
.lastIndex
= w
.nextMatch
;
2028 lookaheadMatch
= this.lookaheadRegExp
.exec(w
.source
);
2031 rowHandler: function(w
,e
,prevColumns
)
2034 var colSpanCount
= 1;
2035 var prevCell
= null;
2036 this.cellRegExp
.lastIndex
= w
.nextMatch
;
2037 var cellMatch
= this.cellRegExp
.exec(w
.source
);
2038 while(cellMatch
&& cellMatch
.index
== w
.nextMatch
) {
2039 if(cellMatch
[1] == "~") {
2041 var last
= prevColumns
[col
];
2043 last
.rowSpanCount
++;
2044 last
.element
.setAttribute("rowspan",last
.rowSpanCount
);
2045 last
.element
.setAttribute("rowSpan",last
.rowSpanCount
); // Needed for IE
2046 last
.element
.valign
= "center";
2048 w
.nextMatch
= this.cellRegExp
.lastIndex
-1;
2049 } else if(cellMatch
[1] == ">") {
2052 w
.nextMatch
= this.cellRegExp
.lastIndex
-1;
2053 } else if(cellMatch
[2]) {
2055 if(prevCell
&& colSpanCount
> 1) {
2056 prevCell
.setAttribute("colspan",colSpanCount
);
2057 prevCell
.setAttribute("colSpan",colSpanCount
); // Needed for IE
2059 w
.nextMatch
= this.cellRegExp
.lastIndex
;
2064 var styles
= config
.formatterHelpers
.inlineCssHelper(w
);
2065 var spaceLeft
= false;
2066 var chr
= w
.source
.substr(w
.nextMatch
,1);
2070 chr
= w
.source
.substr(w
.nextMatch
,1);
2074 cell
= createTiddlyElement(e
,"th");
2077 cell
= createTiddlyElement(e
,"td");
2080 prevColumns
[col
] = {rowSpanCount
:1,element
:cell
};
2081 if(colSpanCount
> 1) {
2082 cell
.setAttribute("colspan",colSpanCount
);
2083 cell
.setAttribute("colSpan",colSpanCount
); // Needed for IE
2086 config
.formatterHelpers
.applyCssHelper(cell
,styles
);
2087 w
.subWikifyTerm(cell
,this.cellTermRegExp
);
2088 if(w
.matchText
.substr(w
.matchText
.length
-2,1) == " ") // spaceRight
2089 cell
.align
= spaceLeft
? "center" : "left";
2091 cell
.align
= "right";
2095 this.cellRegExp
.lastIndex
= w
.nextMatch
;
2096 cellMatch
= this.cellRegExp
.exec(w
.source
);
2104 termRegExp
: /(\n)/mg,
2105 handler: function(w
)
2107 w
.subWikifyTerm(createTiddlyElement(w
.output
,"h" + w
.matchLength
),this.termRegExp
);
2113 match
: "^(?:[\\*#;:]+)",
2114 lookaheadRegExp
: /^(?:(?:(\*)|(#)|(;)|(:))+)/mg,
2115 termRegExp
: /(\n)/mg,
2116 handler: function(w
)
2118 var stack
= [w
.output
];
2119 var currLevel
= 0, currType
= null;
2120 var listLevel
, listType
, itemType
, baseType
;
2121 w
.nextMatch
= w
.matchStart
;
2122 this.lookaheadRegExp
.lastIndex
= w
.nextMatch
;
2123 var lookaheadMatch
= this.lookaheadRegExp
.exec(w
.source
);
2124 while(lookaheadMatch
&& lookaheadMatch
.index
== w
.nextMatch
) {
2125 if(lookaheadMatch
[1]) {
2128 } else if(lookaheadMatch
[2]) {
2131 } else if(lookaheadMatch
[3]) {
2134 } else if(lookaheadMatch
[4]) {
2139 baseType
= listType
;
2140 listLevel
= lookaheadMatch
[0].length
;
2141 w
.nextMatch
+= lookaheadMatch
[0].length
;
2143 if(listLevel
> currLevel
) {
2144 for(t
=currLevel
; t
<listLevel
; t
++) {
2145 var target
= (currLevel
== 0) ? stack
[stack
.length
-1] : stack
[stack
.length
-1].lastChild
;
2146 stack
.push(createTiddlyElement(target
,listType
));
2148 } else if(listType
!=baseType
&& listLevel
==1) {
2149 w
.nextMatch
-= lookaheadMatch
[0].length
;
2151 } else if(listLevel
< currLevel
) {
2152 for(t
=currLevel
; t
>listLevel
; t
--)
2154 } else if(listLevel
== currLevel
&& listType
!= currType
) {
2156 stack
.push(createTiddlyElement(stack
[stack
.length
-1].lastChild
,listType
));
2158 currLevel
= listLevel
;
2159 currType
= listType
;
2160 var e
= createTiddlyElement(stack
[stack
.length
-1],itemType
);
2161 w
.subWikifyTerm(e
,this.termRegExp
);
2162 this.lookaheadRegExp
.lastIndex
= w
.nextMatch
;
2163 lookaheadMatch
= this.lookaheadRegExp
.exec(w
.source
);
2169 name
: "quoteByBlock",
2171 termRegExp
: /(^<<<(\n|$))/mg,
2172 element
: "blockquote",
2173 handler
: config
.formatterHelpers
.createElementAndWikify
2177 name
: "quoteByLine",
2179 lookaheadRegExp
: /^>+/mg,
2180 termRegExp
: /(\n)/mg,
2181 element
: "blockquote",
2182 handler: function(w
)
2184 var stack
= [w
.output
];
2186 var newLevel
= w
.matchLength
;
2189 if(newLevel
> currLevel
) {
2190 for(t
=currLevel
; t
<newLevel
; t
++)
2191 stack
.push(createTiddlyElement(stack
[stack
.length
-1],this.element
));
2192 } else if(newLevel
< currLevel
) {
2193 for(t
=currLevel
; t
>newLevel
; t
--)
2196 currLevel
= newLevel
;
2197 w
.subWikifyTerm(stack
[stack
.length
-1],this.termRegExp
);
2198 createTiddlyElement(stack
[stack
.length
-1],"br");
2199 this.lookaheadRegExp
.lastIndex
= w
.nextMatch
;
2200 var lookaheadMatch
= this.lookaheadRegExp
.exec(w
.source
);
2201 var matched
= lookaheadMatch
&& lookaheadMatch
.index
== w
.nextMatch
;
2203 newLevel
= lookaheadMatch
[0].length
;
2204 w
.nextMatch
+= lookaheadMatch
[0].length
;
2212 match
: "^----+$\\n?",
2213 handler: function(w
)
2215 createTiddlyElement(w
.output
,"hr");
2220 name
: "monospacedByLine",
2221 match
: "^(?:/\\*\\{\\{\\{\\*/|\\{\\{\\{|//\\{\\{\\{|<!--\\{\\{\\{-->)\\n",
2223 handler: function(w
)
2225 switch(w
.matchText
) {
2226 case "/*{{{*/\n": // CSS
2227 this.lookaheadRegExp
= /\/\*\{\{\{\*\/\n*((?:^[^\n]*\n)+?)(\n*^\/\*\}\}\}\*\/$\n?)/mg;
2229 case "{{{\n": // monospaced block
2230 this.lookaheadRegExp
= /^\{\{\{\n((?:^[^\n]*\n)+?)(^\}\}\}$\n?)/mg;
2232 case "//{{{\n": // plugin
2233 this.lookaheadRegExp
= /^\/\/\{\{\{\n\n*((?:^[^\n]*\n)+?)(\n*^\/\/\}\}\}$\n?)/mg;
2235 case "<!--{{{-->\n": //template
2236 this.lookaheadRegExp
= /<!--\{\{\{-->\n*((?:^[^\n]*\n)+?)(\n*^<!--\}\}\}-->$\n?)/mg;
2241 config
.formatterHelpers
.enclosedTextHelper
.call(this,w
);
2246 name
: "wikifyComment",
2247 match
: "^(?:/\\*\\*\\*|<!---)\\n",
2248 handler: function(w
)
2250 var termRegExp
= (w
.matchText
== "/***\n") ? (/(^\*\*\*\/\n)/mg) : (/(^--->\n)/mg);
2251 w
.subWikifyTerm(w
.output
,termRegExp
);
2258 lookaheadRegExp
: /<<([^>\s]+)(?:\s*)((?:[^>]|(?:>(?!>)))*)>>/mg,
2259 handler: function(w
)
2261 this.lookaheadRegExp
.lastIndex
= w
.matchStart
;
2262 var lookaheadMatch
= this.lookaheadRegExp
.exec(w
.source
);
2263 if(lookaheadMatch
&& lookaheadMatch
.index
== w
.matchStart
&& lookaheadMatch
[1]) {
2264 w
.nextMatch
= this.lookaheadRegExp
.lastIndex
;
2265 invokeMacro(w
.output
,lookaheadMatch
[1],lookaheadMatch
[2],w
,w
.tiddler
);
2273 lookaheadRegExp
: /\[\[(.*?)(?:\|(~)?(.*?))?\]\]/mg,
2274 handler: function(w
)
2276 this.lookaheadRegExp
.lastIndex
= w
.matchStart
;
2277 var lookaheadMatch
= this.lookaheadRegExp
.exec(w
.source
);
2278 if(lookaheadMatch
&& lookaheadMatch
.index
== w
.matchStart
) {
2280 var text
= lookaheadMatch
[1];
2281 if(lookaheadMatch
[3]) {
2282 // Pretty bracketted link
2283 var link
= lookaheadMatch
[3];
2284 e
= (!lookaheadMatch
[2] && config
.formatterHelpers
.isExternalLink(link
)) ?
2285 createExternalLink(w
.output
,link
) : createTiddlyLink(w
.output
,link
,false,null,w
.isStatic
,w
.tiddler
);
2287 // Simple bracketted link
2288 e
= createTiddlyLink(w
.output
,text
,false,null,w
.isStatic
,w
.tiddler
);
2290 createTiddlyText(e
,text
);
2291 w
.nextMatch
= this.lookaheadRegExp
.lastIndex
;
2298 match
: config
.textPrimitives
.unWikiLink
+"?"+config
.textPrimitives
.wikiLink
,
2299 handler: function(w
)
2301 if(w
.matchText
.substr(0,1) == config
.textPrimitives
.unWikiLink
) {
2302 w
.outputText(w
.output
,w
.matchStart
+1,w
.nextMatch
);
2305 if(w
.matchStart
> 0) {
2306 var preRegExp
= new RegExp(config
.textPrimitives
.anyLetterStrict
,"mg");
2307 preRegExp
.lastIndex
= w
.matchStart
-1;
2308 var preMatch
= preRegExp
.exec(w
.source
);
2309 if(preMatch
.index
== w
.matchStart
-1) {
2310 w
.outputText(w
.output
,w
.matchStart
,w
.nextMatch
);
2314 if(w
.autoLinkWikiWords
|| store
.isShadowTiddler(w
.matchText
)) {
2315 var link
= createTiddlyLink(w
.output
,w
.matchText
,false,null,w
.isStatic
,w
.tiddler
);
2316 w
.outputText(link
,w
.matchStart
,w
.nextMatch
);
2318 w
.outputText(w
.output
,w
.matchStart
,w
.nextMatch
);
2325 match
: config
.textPrimitives
.urlPattern
,
2326 handler: function(w
)
2328 w
.outputText(createExternalLink(w
.output
,w
.matchText
),w
.matchStart
,w
.nextMatch
);
2334 match
: "\\[[<>]?[Ii][Mm][Gg]\\[",
2335 lookaheadRegExp
: /\[([<]?)(>?)[Ii][Mm][Gg]\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg,
2336 handler: function(w
)
2338 this.lookaheadRegExp
.lastIndex
= w
.matchStart
;
2339 var lookaheadMatch
= this.lookaheadRegExp
.exec(w
.source
);
2340 if(lookaheadMatch
&& lookaheadMatch
.index
== w
.matchStart
) {
2342 if(lookaheadMatch
[5]) {
2343 var link
= lookaheadMatch
[5];
2344 e
= config
.formatterHelpers
.isExternalLink(link
) ? createExternalLink(w
.output
,link
) : createTiddlyLink(w
.output
,link
,false,null,w
.isStatic
,w
.tiddler
);
2345 addClass(e
,"imageLink");
2347 var img
= createTiddlyElement(e
,"img");
2348 if(lookaheadMatch
[1])
2350 else if(lookaheadMatch
[2])
2351 img
.align
= "right";
2352 if(lookaheadMatch
[3])
2353 img
.title
= lookaheadMatch
[3];
2354 img
.src
= lookaheadMatch
[4];
2355 w
.nextMatch
= this.lookaheadRegExp
.lastIndex
;
2362 match
: "<[Hh][Tt][Mm][Ll]>",
2363 lookaheadRegExp
: /<[Hh][Tt][Mm][Ll]>((?:.|\n)*?)<\/[Hh][Tt][Mm][Ll]>/mg,
2364 handler: function(w
)
2366 this.lookaheadRegExp
.lastIndex
= w
.matchStart
;
2367 var lookaheadMatch
= this.lookaheadRegExp
.exec(w
.source
);
2368 if(lookaheadMatch
&& lookaheadMatch
.index
== w
.matchStart
) {
2369 createTiddlyElement(w
.output
,"span").innerHTML
= lookaheadMatch
[1];
2370 w
.nextMatch
= this.lookaheadRegExp
.lastIndex
;
2376 name
: "commentByBlock",
2378 lookaheadRegExp
: /\/%((?:.|\n)*?)%\//mg,
2379 handler: function(w
)
2381 this.lookaheadRegExp
.lastIndex
= w
.matchStart
;
2382 var lookaheadMatch
= this.lookaheadRegExp
.exec(w
.source
);
2383 if(lookaheadMatch
&& lookaheadMatch
.index
== w
.matchStart
)
2384 w
.nextMatch
= this.lookaheadRegExp
.lastIndex
;
2389 name
: "characterFormat",
2390 match
: "''|//|__|\\^\\^|~~|--(?!\\s|$)|\\{\\{\\{",
2391 handler: function(w
)
2393 switch(w
.matchText
) {
2395 w
.subWikifyTerm(w
.output
.appendChild(document
.createElement("strong")),/('')/mg);
2398 w
.subWikifyTerm(createTiddlyElement(w
.output
,"em"),/(\/\/)/mg);
2401 w
.subWikifyTerm(createTiddlyElement(w
.output
,"u"),/(__)/mg);
2404 w
.subWikifyTerm(createTiddlyElement(w
.output
,"sup"),/(\^\^)/mg);
2407 w
.subWikifyTerm(createTiddlyElement(w
.output
,"sub"),/(~~)/mg);
2410 w
.subWikifyTerm(createTiddlyElement(w
.output
,"strike"),/(--)/mg);
2413 var lookaheadRegExp
= /\{\{\{((?:.|\n)*?)\}\}\}/mg;
2414 lookaheadRegExp
.lastIndex
= w
.matchStart
;
2415 var lookaheadMatch
= lookaheadRegExp
.exec(w
.source
);
2416 if(lookaheadMatch
&& lookaheadMatch
.index
== w
.matchStart
) {
2417 createTiddlyElement(w
.output
,"code",null,null,lookaheadMatch
[1]);
2418 w
.nextMatch
= lookaheadRegExp
.lastIndex
;
2426 name
: "customFormat",
2428 handler: function(w
)
2430 switch(w
.matchText
) {
2432 var e
= createTiddlyElement(w
.output
,"span");
2433 var styles
= config
.formatterHelpers
.inlineCssHelper(w
);
2434 if(styles
.length
== 0)
2435 e
.className
= "marked";
2437 config
.formatterHelpers
.applyCssHelper(e
,styles
);
2438 w
.subWikifyTerm(e
,/(@@)/mg);
2441 lookaheadRegExp
= /\{\{[\s]*([\w]+[\s\w]*)[\s]*\{(\n?)/mg;
2442 lookaheadRegExp
.lastIndex
= w
.matchStart
;
2443 lookaheadMatch
= lookaheadRegExp
.exec(w
.source
);
2444 if(lookaheadMatch
) {
2445 w
.nextMatch
= lookaheadRegExp
.lastIndex
;
2446 e
= createTiddlyElement(w
.output
,lookaheadMatch
[2] == "\n" ? "div" : "span",null,lookaheadMatch
[1]);
2447 w
.subWikifyTerm(e
,/(\}\}\})/mg);
2457 handler: function(w
)
2459 createTiddlyElement(w
.output
,"span").innerHTML
= "—";
2465 match
: "\\n|<br ?/?>",
2466 handler: function(w
)
2468 createTiddlyElement(w
.output
,"br");
2474 match
: "\\\"{3}|<nowiki>",
2475 lookaheadRegExp
: /(?:\"{3}|<nowiki>)((?:.|\n)*?)(?:\"{3}|<\/nowiki>)/mg,
2476 handler: function(w
)
2478 this.lookaheadRegExp
.lastIndex
= w
.matchStart
;
2479 var lookaheadMatch
= this.lookaheadRegExp
.exec(w
.source
);
2480 if(lookaheadMatch
&& lookaheadMatch
.index
== w
.matchStart
) {
2481 createTiddlyElement(w
.output
,"span",null,null,lookaheadMatch
[1]);
2482 w
.nextMatch
= this.lookaheadRegExp
.lastIndex
;
2488 name
: "htmlEntitiesEncoding",
2489 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};)",
2490 handler: function(w
)
2492 createTiddlyElement(w
.output
,"span").innerHTML
= w
.matchText
;
2502 function getParser(tiddler
,format
)
2506 format
= tiddler
.fields
["wikiformat"];
2509 for(i
in config
.parsers
) {
2510 if(format
== config
.parsers
[i
].format
)
2511 return config
.parsers
[i
];
2514 for(i
in config
.parsers
) {
2515 if(tiddler
.isTagged(config
.parsers
[i
].formatTag
))
2516 return config
.parsers
[i
];
2523 function wikify(source
,output
,highlightRegExp
,tiddler
)
2525 if(source
&& source
!= "") {
2526 var wikifier
= new Wikifier(source
,getParser(tiddler
),highlightRegExp
,tiddler
);
2527 wikifier
.subWikifyUnterm(output
);
2531 function wikifyStatic(source
,highlightRegExp
,tiddler
,format
)
2533 var e
= createTiddlyElement(document
.body
,"div");
2534 e
.style
.display
= "none";
2536 if(source
&& source
!= "") {
2537 var wikifier
= new Wikifier(source
,getParser(tiddler
,format
),highlightRegExp
,tiddler
);
2538 wikifier
.isStatic
= true;
2539 wikifier
.subWikifyUnterm(e
);
2546 function wikifyPlain(title
,theStore
,limit
)
2550 if(theStore
.tiddlerExists(title
) || theStore
.isShadowTiddler(title
)) {
2551 return wikifyPlainText(theStore
.getTiddlerText(title
),limit
,tiddler
);
2557 function wikifyPlainText(text
,limit
,tiddler
)
2560 text
= text
.substr(0,limit
);
2561 var wikifier
= new Wikifier(text
,formatter
,null,tiddler
);
2562 return wikifier
.wikifyPlain();
2565 function highlightify(source
,output
,highlightRegExp
,tiddler
)
2567 if(source
&& source
!= "") {
2568 var wikifier
= new Wikifier(source
,formatter
,highlightRegExp
,tiddler
);
2569 wikifier
.outputText(output
,0,source
.length
);
2573 function Wikifier(source
,formatter
,highlightRegExp
,tiddler
)
2575 this.source
= source
;
2577 this.formatter
= formatter
;
2579 this.autoLinkWikiWords
= tiddler
&& tiddler
.autoLinkWikiWords() == false ? false : true;
2580 this.highlightRegExp
= highlightRegExp
;
2581 this.highlightMatch
= null;
2582 this.isStatic
= false;
2583 if(highlightRegExp
) {
2584 highlightRegExp
.lastIndex
= 0;
2585 this.highlightMatch
= highlightRegExp
.exec(source
);
2587 this.tiddler
= tiddler
;
2590 Wikifier
.prototype.wikifyPlain = function()
2592 var e
= createTiddlyElement(document
.body
,"div");
2594 var text
= getPlainText(e
);
2599 Wikifier
.prototype.subWikify = function(output
,terminator
)
2602 this.subWikifyTerm(output
,new RegExp("(" + terminator
+ ")","mg"));
2604 this.subWikifyUnterm(output
);
2607 Wikifier
.prototype.subWikifyUnterm = function(output
)
2609 var oldOutput
= this.output
;
2610 this.output
= output
;
2611 this.formatter
.formatterRegExp
.lastIndex
= this.nextMatch
;
2612 var formatterMatch
= this.formatter
.formatterRegExp
.exec(this.source
);
2613 while(formatterMatch
) {
2614 // Output any text before the match
2615 if(formatterMatch
.index
> this.nextMatch
)
2616 this.outputText(this.output
,this.nextMatch
,formatterMatch
.index
);
2617 // Set the match parameters for the handler
2618 this.matchStart
= formatterMatch
.index
;
2619 this.matchLength
= formatterMatch
[0].length
;
2620 this.matchText
= formatterMatch
[0];
2621 this.nextMatch
= this.formatter
.formatterRegExp
.lastIndex
;
2622 for(var t
=1; t
<formatterMatch
.length
; t
++) {
2623 if(formatterMatch
[t
]) {
2624 this.formatter
.formatters
[t
-1].handler(this);
2625 this.formatter
.formatterRegExp
.lastIndex
= this.nextMatch
;
2629 formatterMatch
= this.formatter
.formatterRegExp
.exec(this.source
);
2631 if(this.nextMatch
< this.source
.length
) {
2632 this.outputText(this.output
,this.nextMatch
,this.source
.length
);
2633 this.nextMatch
= this.source
.length
;
2635 this.output
= oldOutput
;
2638 Wikifier
.prototype.subWikifyTerm = function(output
,terminatorRegExp
)
2640 var oldOutput
= this.output
;
2641 this.output
= output
;
2642 terminatorRegExp
.lastIndex
= this.nextMatch
;
2643 var terminatorMatch
= terminatorRegExp
.exec(this.source
);
2644 this.formatter
.formatterRegExp
.lastIndex
= this.nextMatch
;
2645 var formatterMatch
= this.formatter
.formatterRegExp
.exec(terminatorMatch
? this.source
.substr(0,terminatorMatch
.index
) : this.source
);
2646 while(terminatorMatch
|| formatterMatch
) {
2647 if(terminatorMatch
&& (!formatterMatch
|| terminatorMatch
.index
<= formatterMatch
.index
)) {
2648 if(terminatorMatch
.index
> this.nextMatch
)
2649 this.outputText(this.output
,this.nextMatch
,terminatorMatch
.index
);
2650 this.matchText
= terminatorMatch
[1];
2651 this.matchLength
= terminatorMatch
[1].length
;
2652 this.matchStart
= terminatorMatch
.index
;
2653 this.nextMatch
= this.matchStart
+ this.matchLength
;
2654 this.output
= oldOutput
;
2657 if(formatterMatch
.index
> this.nextMatch
)
2658 this.outputText(this.output
,this.nextMatch
,formatterMatch
.index
);
2659 this.matchStart
= formatterMatch
.index
;
2660 this.matchLength
= formatterMatch
[0].length
;
2661 this.matchText
= formatterMatch
[0];
2662 this.nextMatch
= this.formatter
.formatterRegExp
.lastIndex
;
2663 for(var t
=1; t
<formatterMatch
.length
; t
++) {
2664 if(formatterMatch
[t
]) {
2665 this.formatter
.formatters
[t
-1].handler(this);
2666 this.formatter
.formatterRegExp
.lastIndex
= this.nextMatch
;
2670 terminatorRegExp
.lastIndex
= this.nextMatch
;
2671 terminatorMatch
= terminatorRegExp
.exec(this.source
);
2672 formatterMatch
= this.formatter
.formatterRegExp
.exec(terminatorMatch
? this.source
.substr(0,terminatorMatch
.index
) : this.source
);
2674 if(this.nextMatch
< this.source
.length
) {
2675 this.outputText(this.output
,this.nextMatch
,this.source
.length
);
2676 this.nextMatch
= this.source
.length
;
2678 this.output
= oldOutput
;
2681 Wikifier
.prototype.outputText = function(place
,startPos
,endPos
)
2683 while(this.highlightMatch
&& (this.highlightRegExp
.lastIndex
> startPos
) && (this.highlightMatch
.index
< endPos
) && (startPos
< endPos
)) {
2684 if(this.highlightMatch
.index
> startPos
) {
2685 createTiddlyText(place
,this.source
.substring(startPos
,this.highlightMatch
.index
));
2686 startPos
= this.highlightMatch
.index
;
2688 var highlightEnd
= Math
.min(this.highlightRegExp
.lastIndex
,endPos
);
2689 var theHighlight
= createTiddlyElement(place
,"span",null,"highlight",this.source
.substring(startPos
,highlightEnd
));
2690 startPos
= highlightEnd
;
2691 if(startPos
>= this.highlightRegExp
.lastIndex
)
2692 this.highlightMatch
= this.highlightRegExp
.exec(this.source
);
2694 if(startPos
< endPos
) {
2695 createTiddlyText(place
,this.source
.substring(startPos
,endPos
));
2700 //-- Macro definitions
2703 config
.macros
.today
.handler = function(place
,macroName
,params
)
2705 var now
= new Date();
2706 var text
= params
[0] ? now
.formatString(params
[0].trim()) : text
= now
.toLocaleString();
2707 createTiddlyElement(place
,"span",null,null,text
);
2710 config
.macros
.version
.handler = function(place
)
2712 createTiddlyElement(place
,"span",null,null,version
.major
+ "." + version
.minor
+ "." + version
.revision
+ (version
.beta
? " (beta " + version
.beta
+ ")" : ""));
2715 config
.macros
.list
.handler = function(place
,macroName
,params
)
2717 var type
= params
[0] ? params
[0] : "all";
2718 var list
= document
.createElement("ul");
2719 place
.appendChild(list
);
2720 if(this[type
].prompt
)
2721 createTiddlyElement(list
,"li",null,"listTitle",this[type
].prompt
);
2723 if(this[type
].handler
)
2724 results
= this[type
].handler(params
);
2725 for(var t
= 0; t
< results
.length
; t
++) {
2726 var li
= document
.createElement("li");
2727 list
.appendChild(li
);
2728 createTiddlyLink(li
,typeof results
[t
] == "string" ? results
[t
] : results
[t
].title
,true);
2732 config
.macros
.list
.all
.handler = function(params
)
2734 return store
.reverseLookup("tags","excludeLists",false,"title");
2737 config
.macros
.list
.missing
.handler = function(params
)
2739 return store
.getMissingLinks();
2742 config
.macros
.list
.orphans
.handler = function(params
)
2744 return store
.getOrphans();
2747 config
.macros
.list
.shadowed
.handler = function(params
)
2749 return store
.getShadowed();
2752 config
.macros
.list
.touched
.handler = function(params
)
2754 return store
.getTouched();
2757 config
.macros
.list
.filter
.handler = function(params
)
2759 var filter
= params
[1];
2762 var tiddlers
= store
.filterTiddlers(filter
);
2763 for(var t
=0; t
<tiddlers
.length
; t
++)
2764 results
.push(tiddlers
[t
].title
);
2769 config
.macros
.allTags
.handler = function(place
,macroName
,params
)
2771 var tags
= store
.getTags(params
[0]);
2772 var ul
= createTiddlyElement(place
,"ul");
2773 if(tags
.length
== 0)
2774 createTiddlyElement(ul
,"li",null,"listTitle",this.noTags
);
2775 for(var t
=0; t
<tags
.length
; t
++) {
2776 var title
= tags
[t
][0];
2777 var info
= getTiddlyLinkInfo(title
);
2778 var li
=createTiddlyElement(ul
,"li");
2779 var btn
= createTiddlyButton(li
,title
+ " (" + tags
[t
][1] + ")",this.tooltip
.format([title
]),onClickTag
,info
.classes
);
2780 btn
.setAttribute("tag",title
);
2781 btn
.setAttribute("refresh","link");
2782 btn
.setAttribute("tiddlyLink",title
);
2786 config
.macros
.timeline
.handler = function(place
,macroName
,params
)
2788 var field
= params
[0] ? params
[0] : "modified";
2789 var tiddlers
= store
.reverseLookup("tags","excludeLists",false,field
);
2791 var last
= params
[1] ? tiddlers
.length
-Math
.min(tiddlers
.length
,parseInt(params
[1])) : 0;
2792 var dateFormat
= params
[2] ? params
[2] : this.dateFormat
;
2793 for(var t
=tiddlers
.length
-1; t
>=last
; t
--) {
2794 var tiddler
= tiddlers
[t
];
2795 var theDay
= tiddler
[field
].convertToLocalYYYYMMDDHHMM().substr(0,8);
2796 if(theDay
!= lastDay
) {
2797 var ul
= document
.createElement("ul");
2798 place
.appendChild(ul
);
2799 createTiddlyElement(ul
,"li",null,"listTitle",tiddler
[field
].formatString(dateFormat
));
2802 createTiddlyElement(ul
,"li",null,"listLink").appendChild(createTiddlyLink(place
,tiddler
.title
,true));
2806 config
.macros
.tiddler
.handler = function(place
,macroName
,params
,wikifier
,paramString
,tiddler
)
2808 params
= paramString
.parseParams("name",null,true,false,true);
2809 var names
= params
[0]["name"];
2810 var tiddlerName
= names
[0];
2811 var className
= names
[1] ? names
[1] : null;
2812 var args
= params
[0]["with"];
2813 var wrapper
= createTiddlyElement(place
,"span",null,className
);
2815 wrapper
.setAttribute("refresh","content");
2816 wrapper
.setAttribute("tiddler",tiddlerName
);
2818 var text
= store
.getTiddlerText(tiddlerName
);
2820 var stack
= config
.macros
.tiddler
.tiddlerStack
;
2821 if(stack
.indexOf(tiddlerName
) !== -1)
2823 stack
.push(tiddlerName
);
2825 var n
= args
? Math
.min(args
.length
,9) : 0;
2826 for(var i
=0; i
<n
; i
++) {
2827 var placeholderRE
= new RegExp("\\$" + (i
+ 1),"mg");
2828 text
= text
.replace(placeholderRE
,args
[i
]);
2830 config
.macros
.tiddler
.renderText(wrapper
,text
,tiddlerName
,params
);
2837 config
.macros
.tiddler
.renderText = function(place
,text
,tiddlerName
,params
)
2839 wikify(text
,place
,null,store
.getTiddler(tiddlerName
));
2842 config
.macros
.tiddler
.tiddlerStack
= [];
2844 config
.macros
.tag
.handler = function(place
,macroName
,params
)
2846 createTagButton(place
,params
[0]);
2849 config
.macros
.tags
.handler = function(place
,macroName
,params
,wikifier
,paramString
,tiddler
)
2851 params
= paramString
.parseParams("anon",null,true,false,false);
2852 var ul
= createTiddlyElement(place
,"ul");
2853 var title
= getParam(params
,"anon","");
2854 if(title
&& store
.tiddlerExists(title
))
2855 tiddler
= store
.getTiddler(title
);
2856 var sep
= getParam(params
,"sep"," ");
2857 var lingo
= config
.views
.wikified
.tag
;
2858 var prompt
= tiddler
.tags
.length
== 0 ? lingo
.labelNoTags
: lingo
.labelTags
;
2859 createTiddlyElement(ul
,"li",null,"listTitle",prompt
.format([tiddler
.title
]));
2860 for(var t
=0; t
<tiddler
.tags
.length
; t
++) {
2861 createTagButton(createTiddlyElement(ul
,"li"),tiddler
.tags
[t
],tiddler
.title
);
2862 if(t
<tiddler
.tags
.length
-1)
2863 createTiddlyText(ul
,sep
);
2867 config
.macros
.tagging
.handler = function(place
,macroName
,params
,wikifier
,paramString
,tiddler
)
2869 params
= paramString
.parseParams("anon",null,true,false,false);
2870 var ul
= createTiddlyElement(place
,"ul");
2871 var title
= getParam(params
,"anon","");
2872 if(title
== "" && tiddler
instanceof Tiddler
)
2873 title
= tiddler
.title
;
2874 var sep
= getParam(params
,"sep"," ");
2875 ul
.setAttribute("title",this.tooltip
.format([title
]));
2876 var tagged
= store
.getTaggedTiddlers(title
);
2877 var prompt
= tagged
.length
== 0 ? this.labelNotTag
: this.label
;
2878 createTiddlyElement(ul
,"li",null,"listTitle",prompt
.format([title
,tagged
.length
]));
2879 for(var t
=0; t
<tagged
.length
; t
++) {
2880 createTiddlyLink(createTiddlyElement(ul
,"li"),tagged
[t
].title
,true);
2881 if(t
<tagged
.length
-1)
2882 createTiddlyText(ul
,sep
);
2886 config
.macros
.closeAll
.handler = function(place
)
2888 createTiddlyButton(place
,this.label
,this.prompt
,this.onClick
);
2891 config
.macros
.closeAll
.onClick = function(e
)
2893 story
.closeAllTiddlers();
2897 config
.macros
.permaview
.handler = function(place
)
2899 createTiddlyButton(place
,this.label
,this.prompt
,this.onClick
);
2902 config
.macros
.permaview
.onClick = function(e
)
2908 config
.macros
.saveChanges
.handler = function(place
)
2911 createTiddlyButton(place
,this.label
,this.prompt
,this.onClick
,null,null,this.accessKey
);
2914 config
.macros
.saveChanges
.onClick = function(e
)
2920 config
.macros
.slider
.onClickSlider = function(ev
)
2922 var e
= ev
? ev
: window
.event
;
2923 var n
= this.nextSibling
;
2924 var cookie
= n
.getAttribute("cookie");
2925 var isOpen
= n
.style
.display
!= "none";
2926 if(config
.options
.chkAnimate
&& anim
&& typeof Slider
== "function")
2927 anim
.startAnimating(new Slider(n
,!isOpen
,null,"none"));
2929 n
.style
.display
= isOpen
? "none" : "block";
2930 config
.options
[cookie
] = !isOpen
;
2931 saveOptionCookie(cookie
);
2935 config
.macros
.slider
.createSlider = function(place
,cookie
,title
,tooltip
)
2937 var c
= cookie
? cookie
: "";
2938 var btn
= createTiddlyButton(place
,title
,tooltip
,this.onClickSlider
);
2939 var panel
= createTiddlyElement(null,"div",null,"sliderPanel");
2940 panel
.setAttribute("cookie",c
);
2941 panel
.style
.display
= config
.options
[c
] ? "block" : "none";
2942 place
.appendChild(panel
);
2946 config
.macros
.slider
.handler = function(place
,macroName
,params
)
2948 var panel
= this.createSlider(place
,params
[0],params
[2],params
[3]);
2949 var text
= store
.getTiddlerText(params
[1]);
2950 panel
.setAttribute("refresh","content");
2951 panel
.setAttribute("tiddler",params
[1]);
2953 wikify(text
,panel
,null,store
.getTiddler(params
[1]));
2956 // <<gradient [[tiddler name]] vert|horiz rgb rgb rgb rgb... >>
2957 config
.macros
.gradient
.handler = function(place
,macroName
,params
,wikifier
)
2959 var panel
= wikifier
? createTiddlyElement(place
,"div",null,"gradient") : place
;
2960 panel
.style
.position
= "relative";
2961 panel
.style
.overflow
= "hidden";
2962 panel
.style
.zIndex
= "0";
2964 var styles
= config
.formatterHelpers
.inlineCssHelper(wikifier
);
2965 config
.formatterHelpers
.applyCssHelper(panel
,styles
);
2968 for(var t
=1; t
<params
.length
; t
++) {
2969 var c
= new RGB(params
[t
]);
2973 drawGradient(panel
,params
[0] != "vert",colours
);
2975 wikifier
.subWikify(panel
,">>");
2977 panel
.style
.height
= "100%";
2978 panel
.style
.width
= "100%";
2982 config
.macros
.message
.handler = function(place
,macroName
,params
)
2986 var p
= params
[0].split(".");
2987 for(var t
=0; t
<p
.length
; t
++) {
2993 createTiddlyText(place
,m
.toString().format(params
.splice(1)));
2997 config
.macros
.view
.handler = function(place
,macroName
,params
,wikifier
,paramString
,tiddler
)
2999 if((tiddler
instanceof Tiddler
) && params
[0]) {
3000 var value
= store
.getValue(tiddler
,params
[0]);
3001 if(value
!= undefined) {
3004 highlightify(value
,place
,highlightHack
,tiddler
);
3007 createTiddlyLink(place
,value
,true);
3010 wikify(value
,place
,highlightHack
,tiddler
);
3013 value
= Date
.convertFromYYYYMMDDHHMM(value
);
3014 createTiddlyText(place
,value
.formatString(params
[2] ? params
[2] : config
.views
.wikified
.dateFormat
));
3021 config
.macros
.edit
.handler = function(place
,macroName
,params
,wikifier
,paramString
,tiddler
)
3023 var field
= params
[0];
3024 var rows
= params
[1] || 0;
3025 var defVal
= params
[2] || '';
3026 if((tiddler
instanceof Tiddler
) && field
) {
3027 story
.setDirty(tiddler
.title
,true);
3029 if(field
!= "text" && !rows
) {
3030 e
= createTiddlyElement(null,"input");
3031 if(tiddler
.isReadOnly())
3032 e
.setAttribute("readOnly","readOnly");
3033 e
.setAttribute("edit",field
);
3034 e
.setAttribute("type","text");
3035 e
.value
= store
.getValue(tiddler
,field
) || defVal
;
3036 e
.setAttribute("size","40");
3037 e
.setAttribute("autocomplete","off");
3038 place
.appendChild(e
);
3040 var wrapper1
= createTiddlyElement(null,"fieldset",null,"fieldsetFix");
3041 var wrapper2
= createTiddlyElement(wrapper1
,"div");
3042 e
= createTiddlyElement(wrapper2
,"textarea");
3043 if(tiddler
.isReadOnly())
3044 e
.setAttribute("readOnly","readOnly");
3045 e
.value
= v
= store
.getValue(tiddler
,field
) || defVal
;
3046 rows
= rows
? rows
: 10;
3047 var lines
= v
.match(/\n/mg);
3048 var maxLines
= Math
.max(parseInt(config
.options
.txtMaxEditRows
),5);
3049 if(lines
!= null && lines
.length
> rows
)
3050 rows
= lines
.length
+ 5;
3051 rows
= Math
.min(rows
,maxLines
);
3052 e
.setAttribute("rows",rows
);
3053 e
.setAttribute("edit",field
);
3054 place
.appendChild(wrapper1
);
3060 config
.macros
.tagChooser
.onClick = function(ev
)
3062 var e
= ev
? ev
: window
.event
;
3063 var lingo
= config
.views
.editor
.tagChooser
;
3064 var popup
= Popup
.create(this);
3065 var tags
= store
.getTags();
3066 if(tags
.length
== 0)
3067 createTiddlyText(createTiddlyElement(popup
,"li"),lingo
.popupNone
);
3068 for(var t
=0; t
<tags
.length
; t
++) {
3069 var tag
= createTiddlyButton(createTiddlyElement(popup
,"li"),tags
[t
][0],lingo
.tagTooltip
.format([tags
[t
][0]]),config
.macros
.tagChooser
.onTagClick
);
3070 tag
.setAttribute("tag",tags
[t
][0]);
3071 tag
.setAttribute("tiddler",this.getAttribute("tiddler"));
3074 e
.cancelBubble
= true;
3075 if(e
.stopPropagation
) e
.stopPropagation();
3079 config
.macros
.tagChooser
.onTagClick = function(ev
)
3081 var e
= ev
? ev
: window
.event
;
3082 var tag
= this.getAttribute("tag");
3083 var title
= this.getAttribute("tiddler");
3085 story
.setTiddlerTag(title
,tag
,0);
3089 config
.macros
.tagChooser
.handler = function(place
,macroName
,params
,wikifier
,paramString
,tiddler
)
3091 if(tiddler
instanceof Tiddler
) {
3092 var lingo
= config
.views
.editor
.tagChooser
;
3093 var btn
= createTiddlyButton(place
,lingo
.text
,lingo
.tooltip
,this.onClick
);
3094 btn
.setAttribute("tiddler",tiddler
.title
);
3098 config
.macros
.refreshDisplay
.handler = function(place
)
3100 createTiddlyButton(place
,this.label
,this.prompt
,this.onClick
);
3103 config
.macros
.refreshDisplay
.onClick = function(e
)
3109 config
.macros
.annotations
.handler = function(place
,macroName
,params
,wikifier
,paramString
,tiddler
)
3111 var title
= tiddler
? tiddler
.title
: null;
3112 var a
= title
? config
.annotations
[title
] : null;
3113 if(!tiddler
|| !title
|| !a
)
3115 var text
= a
.format([title
]);
3116 wikify(text
,createTiddlyElement(place
,"div",null,"annotation"),null,tiddler
);
3120 //-- NewTiddler and NewJournal macros
3123 config
.macros
.newTiddler
.createNewTiddlerButton = function(place
,title
,params
,label
,prompt
,accessKey
,newFocus
,isJournal
)
3126 for(var t
=1; t
<params
.length
; t
++) {
3127 if((params
[t
].name
== "anon" && t
!= 1) || (params
[t
].name
== "tag"))
3128 tags
.push(params
[t
].value
);
3130 label
= getParam(params
,"label",label
);
3131 prompt
= getParam(params
,"prompt",prompt
);
3132 accessKey
= getParam(params
,"accessKey",accessKey
);
3133 newFocus
= getParam(params
,"focus",newFocus
);
3134 var customFields
= getParam(params
,"fields","");
3135 if(!customFields
&& !store
.isShadowTiddler(title
))
3136 customFields
= String
.encodeHashMap(config
.defaultCustomFields
);
3137 var btn
= createTiddlyButton(place
,label
,prompt
,this.onClickNewTiddler
,null,null,accessKey
);
3138 btn
.setAttribute("newTitle",title
);
3139 btn
.setAttribute("isJournal",isJournal
? "true" : "false");
3141 btn
.setAttribute("params",tags
.join("|"));
3142 btn
.setAttribute("newFocus",newFocus
);
3143 btn
.setAttribute("newTemplate",getParam(params
,"template",DEFAULT_EDIT_TEMPLATE
));
3144 if(customFields
!== "")
3145 btn
.setAttribute("customFields",customFields
);
3146 var text
= getParam(params
,"text");
3147 if(text
!== undefined)
3148 btn
.setAttribute("newText",text
);
3152 config
.macros
.newTiddler
.onClickNewTiddler = function()
3154 var title
= this.getAttribute("newTitle");
3155 if(this.getAttribute("isJournal") == "true") {
3156 var now
= new Date();
3157 title
= now
.formatString(title
.trim());
3159 var params
= this.getAttribute("params");
3160 var tags
= params
? params
.split("|") : [];
3161 var focus
= this.getAttribute("newFocus");
3162 var template
= this.getAttribute("newTemplate");
3163 var customFields
= this.getAttribute("customFields");
3164 story
.displayTiddler(null,title
,template
,false,null,null);
3165 var tiddlerElem
= document
.getElementById(story
.idPrefix
+ title
);
3167 story
.addCustomFields(tiddlerElem
,customFields
);
3168 var text
= this.getAttribute("newText");
3169 if(typeof text
== "string")
3170 story
.getTiddlerField(title
,"text").value
= text
.format([title
]);
3171 for(var t
=0;t
<tags
.length
;t
++)
3172 story
.setTiddlerTag(title
,tags
[t
],+1);
3173 story
.focusTiddler(title
,focus
);
3177 config
.macros
.newTiddler
.handler = function(place
,macroName
,params
,wikifier
,paramString
,tiddler
)
3180 params
= paramString
.parseParams("anon",null,true,false,false);
3181 var title
= params
[1] && params
[1].name
== "anon" ? params
[1].value
: this.title
;
3182 title
= getParam(params
,"title",title
);
3183 this.createNewTiddlerButton(place
,title
,params
,this.label
,this.prompt
,this.accessKey
,"title",false);
3187 config
.macros
.newJournal
.handler = function(place
,macroName
,params
,wikifier
,paramString
,tiddler
)
3190 params
= paramString
.parseParams("anon",null,true,false,false);
3191 var title
= params
[1] && params
[1].name
== "anon" ? params
[1].value
: config
.macros
.timeline
.dateFormat
;
3192 title
= getParam(params
,"title",title
);
3193 config
.macros
.newTiddler
.createNewTiddlerButton(place
,title
,params
,this.label
,this.prompt
,this.accessKey
,"text",true);
3201 config
.macros
.search
.handler = function(place
,macroName
,params
)
3203 var searchTimeout
= null;
3204 var btn
= createTiddlyButton(place
,this.label
,this.prompt
,this.onClick
);
3205 var txt
= createTiddlyElement(place
,"input",null,"txtOptionInput");
3207 txt
.value
= params
[0];
3208 txt
.onkeyup
= this.onKeyPress
;
3209 txt
.onfocus
= this.onFocus
;
3210 txt
.setAttribute("size",this.sizeTextbox
);
3211 txt
.setAttribute("accessKey",this.accessKey
);
3212 txt
.setAttribute("autocomplete","off");
3213 txt
.setAttribute("lastSearchText","");
3214 if(config
.browser
.isSafari
) {
3215 txt
.setAttribute("type","search");
3216 txt
.setAttribute("results","5");
3218 txt
.setAttribute("type","text");
3222 // Global because there's only ever one outstanding incremental search timer
3223 config
.macros
.search
.timeout
= null;
3225 config
.macros
.search
.doSearch = function(txt
)
3227 if(txt
.value
.length
> 0) {
3228 story
.search(txt
.value
,config
.options
.chkCaseSensitiveSearch
,config
.options
.chkRegExpSearch
);
3229 txt
.setAttribute("lastSearchText",txt
.value
);
3233 config
.macros
.search
.onClick = function(e
)
3235 config
.macros
.search
.doSearch(this.nextSibling
);
3239 config
.macros
.search
.onKeyPress = function(ev
)
3241 var e
= ev
? ev
: window
.event
;
3243 case 13: // Ctrl-Enter
3244 case 10: // Ctrl-Enter on IE PC
3245 config
.macros
.search
.doSearch(this);
3252 if(this.value
.length
> 2) {
3253 if(this.value
!= this.getAttribute("lastSearchText")) {
3254 if(config
.macros
.search
.timeout
)
3255 clearTimeout(config
.macros
.search
.timeout
);
3257 config
.macros
.search
.timeout
= setTimeout(function() {config
.macros
.search
.doSearch(txt
);},500);
3260 if(config
.macros
.search
.timeout
)
3261 clearTimeout(config
.macros
.search
.timeout
);
3265 config
.macros
.search
.onFocus = function(e
)
3274 config
.macros
.tabs
.handler = function(place
,macroName
,params
)
3276 var cookie
= params
[0];
3277 var numTabs
= (params
.length
-1)/3;
3278 var wrapper
= createTiddlyElement(null,"div",null,cookie
);
3279 var tabset
= createTiddlyElement(wrapper
,"div",null,"tabset");
3280 tabset
.setAttribute("cookie",cookie
);
3281 var validTab
= false;
3282 for(var t
=0; t
<numTabs
; t
++) {
3283 var label
= params
[t
*3+1];
3284 var prompt
= params
[t
*3+2];
3285 var content
= params
[t
*3+3];
3286 var tab
= createTiddlyButton(tabset
,label
,prompt
,this.onClickTab
,"tab tabUnselected");
3287 tab
.setAttribute("tab",label
);
3288 tab
.setAttribute("content",content
);
3290 if(config
.options
[cookie
] == label
)
3294 config
.options
[cookie
] = params
[1];
3295 place
.appendChild(wrapper
);
3296 this.switchTab(tabset
,config
.options
[cookie
]);
3299 config
.macros
.tabs
.onClickTab = function(e
)
3301 config
.macros
.tabs
.switchTab(this.parentNode
,this.getAttribute("tab"));
3305 config
.macros
.tabs
.switchTab = function(tabset
,tab
)
3307 var cookie
= tabset
.getAttribute("cookie");
3309 var nodes
= tabset
.childNodes
;
3310 for(var t
=0; t
<nodes
.length
; t
++) {
3311 if(nodes
[t
].getAttribute
&& nodes
[t
].getAttribute("tab") == tab
) {
3313 theTab
.className
= "tab tabSelected";
3315 nodes
[t
].className
= "tab tabUnselected";
3319 if(tabset
.nextSibling
&& tabset
.nextSibling
.className
== "tabContents")
3320 removeNode(tabset
.nextSibling
);
3321 var tabContent
= createTiddlyElement(null,"div",null,"tabContents");
3322 tabset
.parentNode
.insertBefore(tabContent
,tabset
.nextSibling
);
3323 var contentTitle
= theTab
.getAttribute("content");
3324 wikify(store
.getTiddlerText(contentTitle
),tabContent
,null,store
.getTiddler(contentTitle
));
3326 config
.options
[cookie
] = tab
;
3327 saveOptionCookie(cookie
);
3333 //-- Tiddler toolbar
3336 // Create a toolbar command button
3337 config
.macros
.toolbar
.createCommand = function(place
,commandName
,tiddler
,theClass
)
3339 if(typeof commandName
!= "string") {
3341 for(var t
in config
.commands
) {
3342 if(config
.commands
[t
] == commandName
)
3347 if((tiddler
instanceof Tiddler
) && (typeof commandName
== "string")) {
3348 var command
= config
.commands
[commandName
];
3349 if(command
.isEnabled
? command
.isEnabled(tiddler
) : this.isCommandEnabled(command
,tiddler
)) {
3350 var text
= command
.getText
? command
.getText(tiddler
) : this.getCommandText(command
,tiddler
);
3351 var tooltip
= command
.getTooltip
? command
.getTooltip(tiddler
) : this.getCommandTooltip(command
,tiddler
);
3353 switch(command
.type
) {
3355 cmd
= this.onClickPopup
;
3359 cmd
= this.onClickCommand
;
3362 var btn
= createTiddlyButton(null,text
,tooltip
,cmd
);
3363 btn
.setAttribute("commandName",commandName
);
3364 btn
.setAttribute("tiddler",tiddler
.title
);
3366 addClass(btn
,theClass
);
3367 place
.appendChild(btn
);
3372 config
.macros
.toolbar
.isCommandEnabled = function(command
,tiddler
)
3374 var title
= tiddler
.title
;
3375 var ro
= tiddler
.isReadOnly();
3376 var shadow
= store
.isShadowTiddler(title
) && !store
.tiddlerExists(title
);
3377 return (!ro
|| (ro
&& !command
.hideReadOnly
)) && !(shadow
&& command
.hideShadow
);
3380 config
.macros
.toolbar
.getCommandText = function(command
,tiddler
)
3382 return tiddler
.isReadOnly() && command
.readOnlyText
? command
.readOnlyText
: command
.text
;
3385 config
.macros
.toolbar
.getCommandTooltip = function(command
,tiddler
)
3387 return tiddler
.isReadOnly() && command
.readOnlyTooltip
? command
.readOnlyTooltip
: command
.tooltip
;
3390 config
.macros
.toolbar
.onClickCommand = function(ev
)
3392 var e
= ev
? ev
: window
.event
;
3393 e
.cancelBubble
= true;
3394 if (e
.stopPropagation
) e
.stopPropagation();
3395 var command
= config
.commands
[this.getAttribute("commandName")];
3396 return command
.handler(e
,this,this.getAttribute("tiddler"));
3399 config
.macros
.toolbar
.onClickPopup = function(ev
)
3401 var e
= ev
? ev
: window
.event
;
3402 e
.cancelBubble
= true;
3403 if (e
.stopPropagation
) e
.stopPropagation();
3404 var popup
= Popup
.create(this);
3405 var command
= config
.commands
[this.getAttribute("commandName")];
3406 var title
= this.getAttribute("tiddler");
3407 var tiddler
= store
.fetchTiddler(title
);
3408 popup
.setAttribute("tiddler",title
);
3409 command
.handlePopup(popup
,title
);
3414 // Invoke the first command encountered from a given place that is tagged with a specified class
3415 config
.macros
.toolbar
.invokeCommand = function(place
,theClass
,event
)
3417 var children
= place
.getElementsByTagName("a");
3418 for(var t
=0; t
<children
.length
; t
++) {
3419 var c
= children
[t
];
3420 if(hasClass(c
,theClass
) && c
.getAttribute
&& c
.getAttribute("commandName")) {
3421 if(c
.onclick
instanceof Function
)
3422 c
.onclick
.call(c
,event
);
3428 config
.macros
.toolbar
.onClickMore = function(ev
)
3430 var e
= this.nextSibling
;
3431 e
.style
.display
= "inline";
3436 config
.macros
.toolbar
.handler = function(place
,macroName
,params
,wikifier
,paramString
,tiddler
)
3438 for(var t
=0; t
<params
.length
; t
++) {
3442 var btn
= createTiddlyButton(place
,this.moreLabel
,this.morePrompt
,config
.macros
.toolbar
.onClickMore
);
3443 addClass(btn
,"moreCommand");
3444 var e
= createTiddlyElement(place
,"span",null,"moreCommand");
3445 e
.style
.display
= "none";
3450 switch(c
.substr(0,1)) {
3452 theClass
= "defaultCommand";
3456 theClass
= "cancelCommand";
3460 if(c
in config
.commands
)
3461 this.createCommand(place
,c
,tiddler
,theClass
);
3468 //-- Menu and toolbar commands
3471 config
.commands
.closeTiddler
.handler = function(event
,src
,title
)
3473 story
.closeTiddler(title
,true);
3477 config
.commands
.closeOthers
.handler = function(event
,src
,title
)
3479 story
.closeAllTiddlers(title
);
3483 config
.commands
.editTiddler
.handler = function(event
,src
,title
)
3486 var tiddlerElem
= document
.getElementById(story
.idPrefix
+ title
);
3487 var fields
= tiddlerElem
.getAttribute("tiddlyFields");
3488 story
.displayTiddler(null,title
,DEFAULT_EDIT_TEMPLATE
,false,null,fields
);
3489 story
.focusTiddler(title
,"text");
3493 config
.commands
.saveTiddler
.handler = function(event
,src
,title
)
3495 var newTitle
= story
.saveTiddler(title
,event
.shiftKey
);
3497 story
.displayTiddler(null,newTitle
);
3501 config
.commands
.cancelTiddler
.handler = function(event
,src
,title
)
3503 if(story
.hasChanges(title
) && !readOnly
) {
3504 if(!confirm(this.warning
.format([title
])))
3507 story
.setDirty(title
,false);
3508 story
.displayTiddler(null,title
);
3512 config
.commands
.deleteTiddler
.handler = function(event
,src
,title
)
3514 var deleteIt
= true;
3515 if (config
.options
.chkConfirmDelete
)
3516 deleteIt
= confirm(this.warning
.format([title
]));
3518 store
.removeTiddler(title
);
3519 story
.closeTiddler(title
,true);
3525 config
.commands
.permalink
.handler = function(event
,src
,title
)
3527 var t
= encodeURIComponent(String
.encodeTiddlyLink(title
));
3528 if(window
.location
.hash
!= t
)
3529 window
.location
.hash
= t
;
3533 config
.commands
.references
.handlePopup = function(popup
,title
)
3535 var references
= store
.getReferringTiddlers(title
);
3537 for(var r
=0; r
<references
.length
; r
++) {
3538 if(references
[r
].title
!= title
&& !references
[r
].isTagged("excludeLists")) {
3539 createTiddlyLink(createTiddlyElement(popup
,"li"),references
[r
].title
,true);
3544 createTiddlyText(createTiddlyElement(popup
,"li",null,"disabled"),this.popupNone
);
3547 config
.commands
.jump
.handlePopup = function(popup
,title
)
3549 story
.forEachTiddler(function(title
,element
) {
3550 createTiddlyLink(createTiddlyElement(popup
,"li"),title
,true,null,false,null,true);
3554 config
.commands
.syncing
.handlePopup = function(popup
,title
)
3556 var tiddler
= store
.fetchTiddler(title
);
3559 var serverType
= tiddler
.getServerType();
3560 var serverHost
= tiddler
.fields
['server.host'];
3561 var serverWorkspace
= tiddler
.fields
['server.workspace'];
3562 if(!serverWorkspace
)
3563 serverWorkspace
= "";
3565 var e
= createTiddlyElement(popup
,"li",null,"popupMessage");
3566 e
.innerHTML
= config
.commands
.syncing
.currentlySyncing
.format([serverType
,serverHost
,serverWorkspace
]);
3568 createTiddlyElement(popup
,"li",null,"popupMessage",config
.commands
.syncing
.notCurrentlySyncing
);
3571 createTiddlyElement(createTiddlyElement(popup
,"li",null,"listBreak"),"div");
3572 var btn
= createTiddlyButton(createTiddlyElement(popup
,"li"),this.captionUnSync
,null,config
.commands
.syncing
.onChooseServer
);
3573 btn
.setAttribute("tiddler",title
);
3574 btn
.setAttribute("server.type","");
3576 createTiddlyElement(createTiddlyElement(popup
,"li",null,"listBreak"),"div");
3577 createTiddlyElement(popup
,"li",null,"popupMessage",config
.commands
.syncing
.chooseServer
);
3578 var feeds
= store
.getTaggedTiddlers("systemServer","title");
3579 for(var t
=0; t
<feeds
.length
; t
++) {
3581 var feedServerType
= store
.getTiddlerSlice(f
.title
,"Type");
3583 feedServerType
= "file";
3584 var feedServerHost
= store
.getTiddlerSlice(f
.title
,"URL");
3586 feedServerHost
= "";
3587 var feedServerWorkspace
= store
.getTiddlerSlice(f
.title
,"Workspace");
3588 if(!feedServerWorkspace
)
3589 feedServerWorkspace
= "";
3590 var caption
= f
.title
;
3591 if(serverType
== feedServerType
&& serverHost
== feedServerHost
&& serverWorkspace
== feedServerWorkspace
) {
3592 caption
= config
.commands
.syncing
.currServerMarker
+ caption
;
3594 caption
= config
.commands
.syncing
.notCurrServerMarker
+ caption
;
3596 btn
= createTiddlyButton(createTiddlyElement(popup
,"li"),caption
,null,config
.commands
.syncing
.onChooseServer
);
3597 btn
.setAttribute("tiddler",title
);
3598 btn
.setAttribute("server.type",feedServerType
);
3599 btn
.setAttribute("server.host",feedServerHost
);
3600 btn
.setAttribute("server.workspace",feedServerWorkspace
);
3604 config
.commands
.syncing
.onChooseServer = function(e
)
3606 var tiddler
= this.getAttribute("tiddler");
3607 var serverType
= this.getAttribute("server.type");
3609 store
.addTiddlerFields(tiddler
,{
3610 'server.type': serverType
,
3611 'server.host': this.getAttribute("server.host"),
3612 'server.workspace': this.getAttribute("server.workspace")
3615 store
.setValue(tiddler
,'server',null);
3620 config
.commands
.fields
.handlePopup = function(popup
,title
)
3622 var tiddler
= store
.fetchTiddler(title
);
3626 store
.forEachField(tiddler
,function(tiddler
,fieldName
,value
) {fields
[fieldName
] = value
;},true);
3628 for(var t
in fields
) {
3629 items
.push({field
: t
,value
: fields
[t
]});
3631 items
.sort(function(a
,b
) {return a
.field
< b
.field
? -1 : (a
.field
== b
.field
? 0 : +1);});
3632 if(items
.length
> 0)
3633 ListView
.create(popup
,items
,this.listViewTemplate
);
3635 createTiddlyElement(popup
,"div",null,null,this.emptyText
);
3639 //-- Tiddler() object
3642 function Tiddler(title
)
3646 this.modifier
= null;
3647 this.modified
= new Date();
3648 this.created
= new Date();
3650 this.linksUpdated
= false;
3656 Tiddler
.prototype.getLinks = function()
3658 if(this.linksUpdated
==false)
3663 // Returns the fields that are inherited in string field:"value" field2:"value2" format
3664 Tiddler
.prototype.getInheritedFields = function()
3667 for(i
in this.fields
) {
3668 if(i
=="server.host" || i
=="server.workspace" || i
=="wikiformat"|| i
=="server.type") {
3669 f
[i
] = this.fields
[i
];
3672 return String
.encodeHashMap(f
);
3675 // Increment the changeCount of a tiddler
3676 Tiddler
.prototype.incChangeCount = function()
3678 var c
= this.fields
['changecount'];
3679 c
= c
? parseInt(c
) : 0;
3680 this.fields
['changecount'] = String(c
+1);
3683 // Clear the changeCount of a tiddler
3684 Tiddler
.prototype.clearChangeCount = function()
3686 if(this.fields
['changecount']) {
3687 delete this.fields
['changecount'];
3691 // Returns true if the tiddler has been updated since the tiddler was created or downloaded
3692 Tiddler
.prototype.isTouched = function()
3694 var changeCount
= this.fields
['changecount'];
3695 if(changeCount
=== undefined)
3697 return changeCount
> 0;
3700 // Return the tiddler as an RSS item
3701 Tiddler
.prototype.toRssItem = function(uri
)
3704 s
.push("<title" + ">" + this.title
.htmlEncode() + "</title" + ">");
3705 s
.push("<description>" + wikifyStatic(this.text
,null,this).htmlEncode() + "</description>");
3706 for(var t
=0; t
<this.tags
.length
; t
++)
3707 s
.push("<category>" + this.tags
[t
] + "</category>");
3708 s
.push("<link>" + uri
+ "#" + encodeURIComponent(String
.encodeTiddlyLink(this.title
)) + "</link>");
3709 s
.push("<pubDate>" + this.modified
.toGMTString() + "</pubDate>");
3710 return s
.join("\n");
3713 // Format the text for storage in an RSS item
3714 Tiddler
.prototype.saveToRss = function(uri
)
3716 return "<item>\n" + this.toRssItem(uri
) + "\n</item>";
3719 // Change the text and other attributes of a tiddler
3720 Tiddler
.prototype.set = function(title
,text
,modifier
,modified
,tags
,created
,fields
)
3722 this.assign(title
,text
,modifier
,modified
,tags
,created
,fields
);
3727 // Change the text and other attributes of a tiddler without triggered a tiddler.changed() call
3728 Tiddler
.prototype.assign = function(title
,text
,modifier
,modified
,tags
,created
,fields
)
3730 if(title
!= undefined)
3732 if(text
!= undefined)
3734 if(modifier
!= undefined)
3735 this.modifier
= modifier
;
3736 if(modified
!= undefined)
3737 this.modified
= modified
;
3738 if(created
!= undefined)
3739 this.created
= created
;
3740 if(fields
!= undefined)
3741 this.fields
= fields
;
3742 if(tags
!= undefined)
3743 this.tags
= (typeof tags
== "string") ? tags
.readBracketedList() : tags
;
3744 else if(this.tags
== undefined)
3749 // Get the tags for a tiddler as a string (space delimited, using [[brackets]] for tags containing spaces)
3750 Tiddler
.prototype.getTags = function()
3752 return String
.encodeTiddlyLinkList(this.tags
);
3755 // Test if a tiddler carries a tag
3756 Tiddler
.prototype.isTagged = function(tag
)
3758 return this.tags
.indexOf(tag
) != -1;
3761 // Static method to convert "\n" to newlines, "\s" to "\"
3762 Tiddler
.unescapeLineBreaks = function(text
)
3764 return text
? text
.unescapeLineBreaks() : "";
3767 // Convert newlines to "\n", "\" to "\s"
3768 Tiddler
.prototype.escapeLineBreaks = function()
3770 return this.text
.escapeLineBreaks();
3773 // Updates the secondary information (like links[] array) after a change to a tiddler
3774 Tiddler
.prototype.changed = function()
3777 var t
= this.autoLinkWikiWords() ? 0 : 1;
3778 var tiddlerLinkRegExp
= t
==0 ? config
.textPrimitives
.tiddlerAnyLinkRegExp
: config
.textPrimitives
.tiddlerForcedLinkRegExp
;
3779 tiddlerLinkRegExp
.lastIndex
= 0;
3780 var formatMatch
= tiddlerLinkRegExp
.exec(this.text
);
3781 while(formatMatch
) {
3782 var lastIndex
= tiddlerLinkRegExp
.lastIndex
;
3783 if(t
==0 && formatMatch
[1] && formatMatch
[1] != this.title
) {
3785 if(formatMatch
.index
> 0) {
3786 var preRegExp
= new RegExp(config
.textPrimitives
.unWikiLink
+"|"+config
.textPrimitives
.anyLetter
,"mg");
3787 preRegExp
.lastIndex
= formatMatch
.index
-1;
3788 var preMatch
= preRegExp
.exec(this.text
);
3789 if(preMatch
.index
!= formatMatch
.index
-1)
3790 this.links
.pushUnique(formatMatch
[1]);
3792 this.links
.pushUnique(formatMatch
[1]);
3795 else if(formatMatch
[2-t
] && !config
.formatterHelpers
.isExternalLink(formatMatch
[3-t
])) // titledBrackettedLink
3796 this.links
.pushUnique(formatMatch
[3-t
]);
3797 else if(formatMatch
[4-t
] && formatMatch
[4-t
] != this.title
) // brackettedLink
3798 this.links
.pushUnique(formatMatch
[4-t
]);
3799 tiddlerLinkRegExp
.lastIndex
= lastIndex
;
3800 formatMatch
= tiddlerLinkRegExp
.exec(this.text
);
3802 this.linksUpdated
= true;
3805 Tiddler
.prototype.getSubtitle = function()
3807 var modifier
= this.modifier
;
3809 modifier
= config
.messages
.subtitleUnknown
;
3810 var modified
= this.modified
;
3812 modified
= modified
.toLocaleString();
3814 modified
= config
.messages
.subtitleUnknown
;
3815 return config
.messages
.tiddlerLinkTooltip
.format([this.title
,modifier
,modified
]);
3818 Tiddler
.prototype.isReadOnly = function()
3823 Tiddler
.prototype.autoLinkWikiWords = function()
3825 return !(this.isTagged("systemConfig") || this.isTagged("excludeMissing"));
3828 Tiddler
.prototype.generateFingerprint = function()
3830 return "0x" + Crypto
.hexSha1Str(this.text
);
3833 Tiddler
.prototype.getServerType = function()
3835 var serverType
= null;
3836 if(this.fields
&& this.fields
['server.type'])
3837 serverType
= this.fields
['server.type'];
3839 serverType
= this.fields
['wikiformat'];
3840 if(serverType
&& !config
.adaptors
[serverType
])
3845 Tiddler
.prototype.getAdaptor = function()
3847 var serverType
= this.getServerType();
3848 return serverType
? new config
.adaptors
[serverType
] : null;
3852 //-- TiddlyWiki() object contains Tiddler()s
3855 function TiddlyWiki()
3857 var tiddlers
= {}; // Hashmap by name of tiddlers
3858 this.tiddlersUpdated
= false;
3859 this.namedNotifications
= []; // Array of {name:,notify:} of notification functions
3860 this.notificationLevel
= 0;
3861 this.slices
= {}; // map tiddlerName->(map sliceName->sliceValue). Lazy.
3862 this.clear = function() {
3864 this.setDirty(false);
3866 this.fetchTiddler = function(title
) {
3867 var t
= tiddlers
[title
];
3868 return t
instanceof Tiddler
? t
: null;
3870 this.deleteTiddler = function(title
) {
3871 delete this.slices
[title
];
3872 delete tiddlers
[title
];
3874 this.addTiddler = function(tiddler
) {
3875 delete this.slices
[tiddler
.title
];
3876 tiddlers
[tiddler
.title
] = tiddler
;
3878 this.forEachTiddler = function(callback
) {
3879 for(var t
in tiddlers
) {
3880 var tiddler
= tiddlers
[t
];
3881 if(tiddler
instanceof Tiddler
)
3882 callback
.call(this,t
,tiddler
);
3887 TiddlyWiki
.prototype.setDirty = function(dirty
)
3892 TiddlyWiki
.prototype.isDirty = function()
3897 TiddlyWiki
.prototype.suspendNotifications = function()
3899 this.notificationLevel
--;
3902 TiddlyWiki
.prototype.resumeNotifications = function()
3904 this.notificationLevel
++;
3907 // Invoke the notification handlers for a particular tiddler
3908 TiddlyWiki
.prototype.notify = function(title
,doBlanket
)
3910 if(!this.notificationLevel
) {
3911 for(var t
=0; t
<this.namedNotifications
.length
; t
++) {
3912 var n
= this.namedNotifications
[t
];
3913 if((n
.name
== null && doBlanket
) || (n
.name
== title
))
3919 // Invoke the notification handlers for all tiddlers
3920 TiddlyWiki
.prototype.notifyAll = function()
3922 if(!this.notificationLevel
) {
3923 for(var t
=0; t
<this.namedNotifications
.length
; t
++) {
3924 var n
= this.namedNotifications
[t
];
3931 // Add a notification handler to a tiddler
3932 TiddlyWiki
.prototype.addNotification = function(title
,fn
)
3934 for(var i
=0; i
<this.namedNotifications
.length
; i
++) {
3935 if((this.namedNotifications
[i
].name
== title
) && (this.namedNotifications
[i
].notify
== fn
))
3938 this.namedNotifications
.push({name
: title
, notify
: fn
});
3942 TiddlyWiki
.prototype.removeTiddler = function(title
)
3944 var tiddler
= this.fetchTiddler(title
);
3946 this.deleteTiddler(title
);
3947 this.notify(title
,true);
3948 this.setDirty(true);
3952 TiddlyWiki
.prototype.tiddlerExists = function(title
)
3954 var t
= this.fetchTiddler(title
);
3955 return t
!= undefined;
3958 TiddlyWiki
.prototype.isShadowTiddler = function(title
)
3960 return typeof config
.shadowTiddlers
[title
] == "string";
3963 TiddlyWiki
.prototype.getTiddler = function(title
)
3965 var t
= this.fetchTiddler(title
);
3972 TiddlyWiki
.prototype.getTiddlerText = function(title
,defaultText
)
3976 var pos
= title
.indexOf(config
.textPrimitives
.sectionSeparator
);
3979 section
= title
.substr(pos
+ config
.textPrimitives
.sectionSeparator
.length
);
3980 title
= title
.substr(0,pos
);
3982 pos
= title
.indexOf(config
.textPrimitives
.sliceSeparator
);
3984 var slice
= this.getTiddlerSlice(title
.substr(0,pos
),title
.substr(pos
+ config
.textPrimitives
.sliceSeparator
.length
));
3988 var tiddler
= this.fetchTiddler(title
);
3991 return tiddler
.text
;
3992 var re
= new RegExp("(^!{1,6}" + section
.escapeRegExp() + ")","mg");
3994 var match
= re
.exec(tiddler
.text
);
3996 var t
= tiddler
.text
.substr(match
.index
+match
[1].length
);
3999 match
= re2
.exec(t
); //# search for the next heading
4001 t
= t
.substr(0,match
.index
);
4006 if(this.isShadowTiddler(title
))
4007 return config
.shadowTiddlers
[title
];
4008 if(defaultText
!= undefined)
4013 TiddlyWiki
.prototype.slicesRE
= /(?:[\'\/]*~?([\.\w]+)[\'\/]*\:[\'\/]*\s*(.*?)\s*$)|(?:\|[\'\/]*~?([\.\w]+)\:?[\'\/]*\|\s*(.*?)\s*\|)/gm;
4016 TiddlyWiki
.prototype.calcAllSlices = function(title
)
4019 var text
= this.getTiddlerText(title
,"");
4020 this.slicesRE
.lastIndex
= 0;
4022 var m
= this.slicesRE
.exec(text
);
4025 slices
[m
[1]] = m
[2];
4027 slices
[m
[3]] = m
[4];
4033 // Returns the slice of text of the given name
4034 TiddlyWiki
.prototype.getTiddlerSlice = function(title
,sliceName
)
4036 var slices
= this.slices
[title
];
4038 slices
= this.calcAllSlices(title
);
4039 this.slices
[title
] = slices
;
4041 return slices
[sliceName
];
4044 // Build an hashmap of the specified named slices of a tiddler
4045 TiddlyWiki
.prototype.getTiddlerSlices = function(title
,sliceNames
)
4048 for(var t
=0; t
<sliceNames
.length
; t
++) {
4049 var slice
= this.getTiddlerSlice(title
,sliceNames
[t
]);
4051 r
[sliceNames
[t
]] = slice
;
4056 TiddlyWiki
.prototype.getRecursiveTiddlerText = function(title
,defaultText
,depth
)
4058 var bracketRegExp
= new RegExp("(?:\\[\\[([^\\]]+)\\]\\])","mg");
4059 var text
= this.getTiddlerText(title
,null);
4065 var match
= bracketRegExp
.exec(text
);
4067 textOut
.push(text
.substr(lastPos
,match
.index
-lastPos
));
4070 textOut
.push(match
[1]);
4072 textOut
.push(this.getRecursiveTiddlerText(match
[1],"[[" + match
[1] + "]]",depth
-1));
4074 lastPos
= match
.index
+ match
[0].length
;
4076 textOut
.push(text
.substr(lastPos
));
4079 return textOut
.join("");
4082 TiddlyWiki
.prototype.setTiddlerTag = function(title
,status
,tag
)
4084 var tiddler
= this.fetchTiddler(title
);
4086 var t
= tiddler
.tags
.indexOf(tag
);
4088 tiddler
.tags
.splice(t
,1);
4090 tiddler
.tags
.push(tag
);
4092 this.incChangeCount(title
);
4093 this.notify(title
,true);
4094 this.setDirty(true);
4098 TiddlyWiki
.prototype.addTiddlerFields = function(title
,fields
)
4100 var tiddler
= this.fetchTiddler(title
);
4103 merge(tiddler
.fields
,fields
);
4105 this.incChangeCount(title
);
4106 this.notify(title
,true);
4107 this.setDirty(true);
4110 TiddlyWiki
.prototype.saveTiddler = function(title
,newTitle
,newBody
,modifier
,modified
,tags
,fields
,clearChangeCount
,created
)
4112 var tiddler
= this.fetchTiddler(title
);
4114 created
= created
? created
: tiddler
.created
; // Preserve created date
4115 this.deleteTiddler(title
);
4117 created
= created
? created
: modified
;
4118 tiddler
= new Tiddler();
4120 tiddler
.set(newTitle
,newBody
,modifier
,modified
,tags
,created
,fields
);
4121 this.addTiddler(tiddler
);
4122 if(clearChangeCount
)
4123 tiddler
.clearChangeCount();
4125 tiddler
.incChangeCount();
4126 if(title
!= newTitle
)
4127 this.notify(title
,true);
4128 this.notify(newTitle
,true);
4129 this.setDirty(true);
4133 // Reset the sync status of a freshly synced tiddler
4134 TiddlyWiki
.prototype.resetTiddler = function(title
)
4136 var tiddler
= this.fetchTiddler(title
);
4138 tiddler
.clearChangeCount();
4139 this.notify(title
,true);
4140 this.setDirty(true);
4144 TiddlyWiki
.prototype.incChangeCount = function(title
)
4146 var tiddler
= this.fetchTiddler(title
);
4148 tiddler
.incChangeCount();
4151 TiddlyWiki
.prototype.createTiddler = function(title
)
4153 var tiddler
= this.fetchTiddler(title
);
4155 tiddler
= new Tiddler();
4156 tiddler
.title
= title
;
4157 this.addTiddler(tiddler
);
4158 this.setDirty(true);
4163 // Load contents of a TiddlyWiki from an HTML DIV
4164 TiddlyWiki
.prototype.loadFromDiv = function(src
,idPrefix
,noUpdate
)
4166 this.idPrefix
= idPrefix
;
4167 var storeElem
= (typeof src
== "string") ? document
.getElementById(src
) : src
;
4170 var tiddlers
= this.getLoader().loadTiddlers(this,storeElem
.childNodes
);
4171 this.setDirty(false);
4173 for(var i
= 0;i
<tiddlers
.length
; i
++)
4174 tiddlers
[i
].changed();
4178 // Load contents of a TiddlyWiki from a string
4179 // Returns null if there's an error
4180 TiddlyWiki
.prototype.importTiddlyWiki = function(text
)
4182 var posDiv
= locateStoreArea(text
);
4185 var content
= "<" + "html><" + "body>" + text
.substring(posDiv
[0],posDiv
[1] + endSaveArea
.length
) + "<" + "/body><" + "/html>";
4186 // Create the iframe
4187 var iframe
= document
.createElement("iframe");
4188 iframe
.style
.display
= "none";
4189 document
.body
.appendChild(iframe
);
4190 var doc
= iframe
.document
;
4191 if(iframe
.contentDocument
)
4192 doc
= iframe
.contentDocument
; // For NS6
4193 else if(iframe
.contentWindow
)
4194 doc
= iframe
.contentWindow
.document
; // For IE5.5 and IE6
4195 // Put the content in the iframe
4197 doc
.writeln(content
);
4199 // Load the content into a TiddlyWiki() object
4200 var storeArea
= doc
.getElementById("storeArea");
4201 this.loadFromDiv(storeArea
,"store");
4202 // Get rid of the iframe
4203 iframe
.parentNode
.removeChild(iframe
);
4207 TiddlyWiki
.prototype.updateTiddlers = function()
4209 this.tiddlersUpdated
= true;
4210 this.forEachTiddler(function(title
,tiddler
) {
4215 // Return all tiddlers formatted as an HTML string
4216 TiddlyWiki
.prototype.allTiddlersAsHtml = function()
4218 return store
.getSaver().externalize(store
);
4221 // Return an array of tiddlers matching a search regular expression
4222 TiddlyWiki
.prototype.search = function(searchRegExp
,sortField
,excludeTag
,match
)
4224 var candidates
= this.reverseLookup("tags",excludeTag
,!!match
);
4226 for(var t
=0; t
<candidates
.length
; t
++) {
4227 if((candidates
[t
].title
.search(searchRegExp
) != -1) || (candidates
[t
].text
.search(searchRegExp
) != -1))
4228 results
.push(candidates
[t
]);
4231 sortField
= "title";
4232 results
.sort(function(a
,b
) {return a
[sortField
] < b
[sortField
] ? -1 : (a
[sortField
] == b
[sortField
] ? 0 : +1);});
4236 // Returns a list of all tags in use
4237 // excludeTag - if present, excludes tags that are themselves tagged with excludeTag
4238 // Returns an array of arrays where [tag][0] is the name of the tag and [tag][1] is the number of occurances
4239 TiddlyWiki
.prototype.getTags = function(excludeTag
)
4242 this.forEachTiddler(function(title
,tiddler
) {
4243 for(var g
=0; g
<tiddler
.tags
.length
; g
++) {
4244 var tag
= tiddler
.tags
[g
];
4246 for(var c
=0; c
<results
.length
; c
++) {
4247 if(results
[c
][0] == tag
) {
4252 if(n
&& excludeTag
) {
4253 var t
= store
.fetchTiddler(tag
);
4254 if(t
&& t
.isTagged(excludeTag
))
4258 results
.push([tag
,1]);
4261 results
.sort(function(a
,b
) {return a
[0].toLowerCase() < b
[0].toLowerCase() ? -1 : (a
[0].toLowerCase() == b
[0].toLowerCase() ? 0 : +1);});
4265 // Return an array of the tiddlers that are tagged with a given tag
4266 TiddlyWiki
.prototype.getTaggedTiddlers = function(tag
,sortField
)
4268 return this.reverseLookup("tags",tag
,true,sortField
);
4271 // Return an array of the tiddlers that link to a given tiddler
4272 TiddlyWiki
.prototype.getReferringTiddlers = function(title
,unusedParameter
,sortField
)
4274 if(!this.tiddlersUpdated
)
4275 this.updateTiddlers();
4276 return this.reverseLookup("links",title
,true,sortField
);
4279 // Return an array of the tiddlers that do or do not have a specified entry in the specified storage array (ie, "links" or "tags")
4280 // lookupMatch == true to match tiddlers, false to exclude tiddlers
4281 TiddlyWiki
.prototype.reverseLookup = function(lookupField
,lookupValue
,lookupMatch
,sortField
)
4284 this.forEachTiddler(function(title
,tiddler
) {
4285 var f
= !lookupMatch
;
4286 for(var lookup
=0; lookup
<tiddler
[lookupField
].length
; lookup
++) {
4287 if(tiddler
[lookupField
][lookup
] == lookupValue
)
4291 results
.push(tiddler
);
4294 sortField
= "title";
4295 results
.sort(function(a
,b
) {return a
[sortField
] < b
[sortField
] ? -1 : (a
[sortField
] == b
[sortField
] ? 0 : +1);});
4299 // Return the tiddlers as a sorted array
4300 TiddlyWiki
.prototype.getTiddlers = function(field
,excludeTag
)
4303 this.forEachTiddler(function(title
,tiddler
) {
4304 if(excludeTag
== undefined || !tiddler
.isTagged(excludeTag
))
4305 results
.push(tiddler
);
4308 results
.sort(function(a
,b
) {return a
[field
] < b
[field
] ? -1 : (a
[field
] == b
[field
] ? 0 : +1);});
4312 // Return array of names of tiddlers that are referred to but not defined
4313 TiddlyWiki
.prototype.getMissingLinks = function(sortField
)
4315 if(!this.tiddlersUpdated
)
4316 this.updateTiddlers();
4318 this.forEachTiddler(function (title
,tiddler
) {
4319 if(tiddler
.isTagged("excludeMissing") || tiddler
.isTagged("systemConfig"))
4321 for(var n
=0; n
<tiddler
.links
.length
;n
++) {
4322 var link
= tiddler
.links
[n
];
4323 if(this.fetchTiddler(link
) == null && !this.isShadowTiddler(link
))
4324 results
.pushUnique(link
);
4331 // Return an array of names of tiddlers that are defined but not referred to
4332 TiddlyWiki
.prototype.getOrphans = function()
4335 this.forEachTiddler(function (title
,tiddler
) {
4336 if(this.getReferringTiddlers(title
).length
== 0 && !tiddler
.isTagged("excludeLists"))
4337 results
.push(title
);
4343 // Return an array of names of all the shadow tiddlers
4344 TiddlyWiki
.prototype.getShadowed = function()
4347 for(var t
in config
.shadowTiddlers
) {
4348 if(typeof config
.shadowTiddlers
[t
] == "string")
4355 // Return an array of tiddlers that have been touched since they were downloaded or created
4356 TiddlyWiki
.prototype.getTouched = function()
4359 this.forEachTiddler(function(title
,tiddler
) {
4360 if(tiddler
.isTouched())
4361 results
.push(tiddler
);
4367 // Resolves a Tiddler reference or tiddler title into a Tiddler object, or null if it doesn't exist
4368 TiddlyWiki
.prototype.resolveTiddler = function(tiddler
)
4370 var t
= (typeof tiddler
== 'string') ? this.getTiddler(tiddler
) : tiddler
;
4371 return t
instanceof Tiddler
? t
: null;
4374 TiddlyWiki
.prototype.getLoader = function()
4377 this.loader
= new TW21Loader();
4381 TiddlyWiki
.prototype.getSaver = function()
4384 this.saver
= new TW21Saver();
4388 // Filter a list of tiddlers
4389 TiddlyWiki
.prototype.filterTiddlers = function(filter
)
4394 var re
= /([^ \[\]]+)|(?:\[([ \w]+)\[([^\]]+)\]\])|(?:\[\[([^\]]+)\]\])/mg;
4395 var match
= re
.exec(filter
);
4397 if(match
[1] || match
[4]) {
4398 var title
= match
[1] ? match
[1] : match
[4];
4399 tiddler
= this.fetchTiddler(title
);
4401 results
.pushUnique(tiddler
);
4402 } else if(store
.isShadowTiddler(title
)) {
4403 tiddler
= new Tiddler();
4404 tiddler
.set(title
,store
.getTiddlerText(title
));
4405 results
.pushUnique(tiddler
);
4407 } else if(match
[2]) {
4410 this.forEachTiddler(function(title
,tiddler
) {
4411 if(tiddler
.isTagged(match
[3]))
4412 results
.pushUnique(tiddler
);
4416 results
= this.sortTiddlers(results
,match
[3]);
4420 match
= re
.exec(filter
);
4426 // Sort a list of tiddlers
4427 TiddlyWiki
.prototype.sortTiddlers = function(tiddlers
,field
)
4430 switch(field
.substr(0,1)) {
4433 // Note: this fall-through is intentional
4435 field
= field
.substr(1);
4438 if(TiddlyWiki
.standardFieldAccess
[field
])
4439 tiddlers
.sort(function(a
,b
) {return a
[field
] < b
[field
] ? -asc
: (a
[field
] == b
[field
] ? 0 : asc
);});
4441 tiddlers
.sort(function(a
,b
) {return a
.fields
[field
] < b
.fields
[field
] ? -asc
: (a
.fields
[field
] == b
.fields
[field
] ? 0 : +asc
);});
4444 // Returns true if path is a valid field name (path),
4445 // i.e. a sequence of identifiers, separated by '.'
4446 TiddlyWiki
.isValidFieldName = function(name
)
4448 var match
= /[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*/.exec(name
);
4449 return match
&& (match
[0] == name
);
4452 // Throws an exception when name is not a valid field name.
4453 TiddlyWiki
.checkFieldName = function(name
)
4455 if(!TiddlyWiki
.isValidFieldName(name
))
4456 throw config
.messages
.invalidFieldName
.format([name
]);
4459 function StringFieldAccess(n
,readOnly
)
4461 this.set = readOnly
?
4462 function(t
,v
) {if(v
!= t
[n
]) throw config
.messages
.fieldCannotBeChanged
.format([n
]);} :
4463 function(t
,v
) {if(v
!= t
[n
]) {t
[n
] = v
; return true;}};
4464 this.get = function(t
) {return t
[n
];};
4467 function DateFieldAccess(n
)
4469 this.set = function(t
,v
) {
4470 var d
= v
instanceof Date
? v
: Date
.convertFromYYYYMMDDHHMM(v
);
4472 t
[n
] = d
; return true;
4475 this.get = function(t
) {return t
[n
].convertToYYYYMMDDHHMM();};
4478 function LinksFieldAccess(n
)
4480 this.set = function(t
,v
) {
4481 var s
= (typeof v
== "string") ? v
.readBracketedList() : v
;
4482 if(s
.toString() != t
[n
].toString()) {
4483 t
[n
] = s
; return true;
4486 this.get = function(t
) {return String
.encodeTiddlyLinkList(t
[n
]);};
4489 TiddlyWiki
.standardFieldAccess
= {
4490 // The set functions return true when setting the data has changed the value.
4491 "title": new StringFieldAccess("title",true),
4492 // Handle the "tiddler" field name as the title
4493 "tiddler": new StringFieldAccess("title",true),
4494 "text": new StringFieldAccess("text"),
4495 "modifier": new StringFieldAccess("modifier"),
4496 "modified": new DateFieldAccess("modified"),
4497 "created": new DateFieldAccess("created"),
4498 "tags": new LinksFieldAccess("tags")
4501 TiddlyWiki
.isStandardField = function(name
)
4503 return TiddlyWiki
.standardFieldAccess
[name
] != undefined;
4506 // Sets the value of the given field of the tiddler to the value.
4507 // Setting an ExtendedField's value to null or undefined removes the field.
4508 // Setting a namespace to undefined removes all fields of that namespace.
4509 // The fieldName is case-insensitive.
4510 // All values will be converted to a string value.
4511 TiddlyWiki
.prototype.setValue = function(tiddler
,fieldName
,value
)
4513 TiddlyWiki
.checkFieldName(fieldName
);
4514 var t
= this.resolveTiddler(tiddler
);
4517 fieldName
= fieldName
.toLowerCase();
4518 var isRemove
= (value
=== undefined) || (value
=== null);
4519 var accessor
= TiddlyWiki
.standardFieldAccess
[fieldName
];
4522 // don't remove StandardFields
4524 var h
= TiddlyWiki
.standardFieldAccess
[fieldName
];
4528 var oldValue
= t
.fields
[fieldName
];
4530 if(oldValue
!== undefined) {
4531 // deletes a single field
4532 delete t
.fields
[fieldName
];
4534 // no concrete value is defined for the fieldName
4535 // so we guess this is a namespace path.
4536 // delete all fields in a namespace
4537 var re
= new RegExp('^'+fieldName
+'\\.');
4539 for(var n
in t
.fields
) {
4549 // the "normal" set case. value is defined (not null/undefined)
4550 // For convenience provide a nicer conversion Date->String
4551 value
= value
instanceof Date
? value
.convertToYYYYMMDDHHMMSSMMM() : String(value
);
4552 if(oldValue
== value
)
4554 t
.fields
[fieldName
] = value
;
4557 // When we are here the tiddler/store really was changed.
4558 this.notify(t
.title
,true);
4559 if(!fieldName
.match(/^temp\./))
4560 this.setDirty(true);
4563 // Returns the value of the given field of the tiddler.
4564 // The fieldName is case-insensitive.
4565 // Will only return String values (or undefined).
4566 TiddlyWiki
.prototype.getValue = function(tiddler
,fieldName
)
4568 var t
= this.resolveTiddler(tiddler
);
4571 fieldName
= fieldName
.toLowerCase();
4572 var accessor
= TiddlyWiki
.standardFieldAccess
[fieldName
];
4574 return accessor
.get(t
);
4576 return t
.fields
[fieldName
];
4579 // Calls the callback function for every field in the tiddler.
4580 // When callback function returns a non-false value the iteration stops
4581 // and that value is returned.
4582 // The order of the fields is not defined.
4583 // @param callback a function(tiddler,fieldName,value).
4584 TiddlyWiki
.prototype.forEachField = function(tiddler
,callback
,onlyExtendedFields
)
4586 var t
= this.resolveTiddler(tiddler
);
4590 for(n
in t
.fields
) {
4591 result
= callback(t
,n
,t
.fields
[n
]);
4595 if(onlyExtendedFields
)
4597 for(n
in TiddlyWiki
.standardFieldAccess
) {
4599 // even though the "title" field can also be referenced through the name "tiddler"
4600 // we only visit this field once.
4602 result
= callback(t
,n
,TiddlyWiki
.standardFieldAccess
[n
].get(t
));
4610 //-- Story functions
4613 function Story(container
,idPrefix
)
4615 this.container
= container
;
4616 this.idPrefix
= idPrefix
;
4617 this.highlightRegExp
= null;
4620 Story
.prototype.forEachTiddler = function(fn
)
4622 var place
= document
.getElementById(this.container
);
4625 var e
= place
.firstChild
;
4627 var n
= e
.nextSibling
;
4628 var title
= e
.getAttribute("tiddler");
4629 fn
.call(this,title
,e
);
4634 Story
.prototype.displayTiddlers = function(srcElement
,titles
,template
,animate
,unused
,customFields
,toggle
)
4636 for(var t
= titles
.length
-1;t
>=0;t
--)
4637 this.displayTiddler(srcElement
,titles
[t
],template
,animate
,unused
,customFields
);
4640 Story
.prototype.displayTiddler = function(srcElement
,tiddler
,template
,animate
,unused
,customFields
,toggle
)
4642 var title
= (tiddler
instanceof Tiddler
)? tiddler
.title
: tiddler
;
4643 var place
= document
.getElementById(this.container
);
4644 var tiddlerElem
= document
.getElementById(this.idPrefix
+ title
);
4647 this.closeTiddler(title
,true);
4649 this.refreshTiddler(title
,template
,false,customFields
);
4651 var before
= this.positionTiddler(srcElement
);
4652 tiddlerElem
= this.createTiddler(place
,before
,title
,template
,customFields
);
4654 if(srcElement
&& typeof srcElement
!== "string") {
4655 if(config
.options
.chkAnimate
&& (animate
== undefined || animate
== true) && anim
&& typeof Zoomer
== "function" && typeof Scroller
== "function")
4656 anim
.startAnimating(new Zoomer(title
,srcElement
,tiddlerElem
),new Scroller(tiddlerElem
));
4658 window
.scrollTo(0,ensureVisible(tiddlerElem
));
4662 Story
.prototype.positionTiddler = function(srcElement
)
4664 var place
= document
.getElementById(this.container
);
4666 if(typeof srcElement
== "string") {
4667 switch(srcElement
) {
4669 before
= place
.firstChild
;
4676 var after
= this.findContainingTiddler(srcElement
);
4678 before
= place
.firstChild
;
4679 } else if(after
.nextSibling
) {
4680 before
= after
.nextSibling
;
4681 if(before
.nodeType
!= 1)
4688 Story
.prototype.createTiddler = function(place
,before
,title
,template
,customFields
)
4690 var tiddlerElem
= createTiddlyElement(null,"div",this.idPrefix
+ title
,"tiddler");
4691 tiddlerElem
.setAttribute("refresh","tiddler");
4693 tiddlerElem
.setAttribute("tiddlyFields",customFields
);
4694 place
.insertBefore(tiddlerElem
,before
);
4695 var defaultText
= null;
4696 if(!store
.tiddlerExists(title
) && !store
.isShadowTiddler(title
))
4697 defaultText
= this.loadMissingTiddler(title
,customFields
,tiddlerElem
);
4698 this.refreshTiddler(title
,template
,false,customFields
,defaultText
);
4702 Story
.prototype.loadMissingTiddler = function(title
,fields
,tiddlerElem
)
4704 var tiddler
= new Tiddler(title
);
4705 tiddler
.fields
= typeof fields
== "string" ? fields
.decodeHashMap() : (fields
? fields
: {});
4706 var serverType
= tiddler
.getServerType();
4707 var host
= tiddler
.fields
['server.host'];
4708 var workspace
= tiddler
.fields
['server.workspace'];
4709 if(!serverType
|| !host
)
4711 var sm
= new SyncMachine(serverType
,{
4713 return this.openHost(host
,"openWorkspace");
4715 openWorkspace: function() {
4716 return this.openWorkspace(workspace
,"getTiddler");
4718 getTiddler: function() {
4719 return this.getTiddler(title
,"onGetTiddler");
4721 onGetTiddler: function(context
) {
4722 var tiddler
= context
.tiddler
;
4723 if(tiddler
&& tiddler
.text
) {
4724 var downloaded
= new Date();
4725 if(!tiddler
.created
)
4726 tiddler
.created
= downloaded
;
4727 if(!tiddler
.modified
)
4728 tiddler
.modified
= tiddler
.created
;
4729 store
.saveTiddler(tiddler
.title
,tiddler
.title
,tiddler
.text
,tiddler
.modifier
,tiddler
.modified
,tiddler
.tags
,tiddler
.fields
,true,tiddler
.created
);
4735 error: function(message
) {
4736 displayMessage("Error loading missing tiddler from %0: %1".format([host
,message
]));
4740 return config
.messages
.loadingMissingTiddler
.format([title
,serverType
,host
,workspace
]);
4743 Story
.prototype.chooseTemplateForTiddler = function(title
,template
)
4746 template
= DEFAULT_VIEW_TEMPLATE
;
4747 if(template
== DEFAULT_VIEW_TEMPLATE
|| template
== DEFAULT_EDIT_TEMPLATE
)
4748 template
= config
.tiddlerTemplates
[template
];
4752 Story
.prototype.getTemplateForTiddler = function(title
,template
,tiddler
)
4754 return store
.getRecursiveTiddlerText(template
,null,10);
4757 Story
.prototype.refreshTiddler = function(title
,template
,force
,customFields
,defaultText
)
4759 var tiddlerElem
= document
.getElementById(this.idPrefix
+ title
);
4761 if(tiddlerElem
.getAttribute("dirty") == "true" && !force
)
4763 template
= this.chooseTemplateForTiddler(title
,template
);
4764 var currTemplate
= tiddlerElem
.getAttribute("template");
4765 if((template
!= currTemplate
) || force
) {
4766 var tiddler
= store
.getTiddler(title
);
4768 tiddler
= new Tiddler();
4769 if(store
.isShadowTiddler(title
)) {
4770 tiddler
.set(title
,store
.getTiddlerText(title
),config
.views
.wikified
.shadowModifier
,version
.date
,[],version
.date
);
4772 var text
= template
=="EditTemplate" ?
4773 config
.views
.editor
.defaultText
.format([title
]) :
4774 config
.views
.wikified
.defaultText
.format([title
]);
4775 text
= defaultText
? defaultText
: text
;
4776 var fields
= customFields
? customFields
.decodeHashMap() : null;
4777 tiddler
.set(title
,text
,config
.views
.wikified
.defaultModifier
,version
.date
,[],version
.date
,fields
);
4780 tiddlerElem
.setAttribute("tags",tiddler
.tags
.join(" "));
4781 tiddlerElem
.setAttribute("tiddler",title
);
4782 tiddlerElem
.setAttribute("template",template
);
4784 tiddlerElem
.onmouseover
= this.onTiddlerMouseOver
;
4785 tiddlerElem
.onmouseout
= this.onTiddlerMouseOut
;
4786 tiddlerElem
.ondblclick
= this.onTiddlerDblClick
;
4787 tiddlerElem
[window
.event
?"onkeydown":"onkeypress"] = this.onTiddlerKeyPress
;
4788 var html
= this.getTemplateForTiddler(title
,template
,tiddler
);
4789 tiddlerElem
.innerHTML
= html
;
4790 applyHtmlMacros(tiddlerElem
,tiddler
);
4791 if(store
.getTaggedTiddlers(title
).length
> 0)
4792 addClass(tiddlerElem
,"isTag");
4794 removeClass(tiddlerElem
,"isTag");
4795 if(!store
.tiddlerExists(title
)) {
4796 if(store
.isShadowTiddler(title
))
4797 addClass(tiddlerElem
,"shadow");
4799 addClass(tiddlerElem
,"missing");
4801 removeClass(tiddlerElem
,"shadow");
4802 removeClass(tiddlerElem
,"missing");
4805 this.addCustomFields(tiddlerElem
,customFields
);
4812 Story
.prototype.addCustomFields = function(place
,customFields
)
4814 var fields
= customFields
.decodeHashMap();
4815 var w
= document
.createElement("div");
4816 w
.style
.display
= "none";
4817 place
.appendChild(w
);
4818 for(var t
in fields
) {
4819 var e
= document
.createElement("input");
4820 e
.setAttribute("type","text");
4821 e
.setAttribute("value",fields
[t
]);
4823 e
.setAttribute("edit",t
);
4827 Story
.prototype.refreshAllTiddlers = function(force
)
4829 var place
= document
.getElementById(this.container
);
4830 var e
= place
.firstChild
;
4833 this.refreshTiddler(e
.getAttribute("tiddler"),force
? null : e
.getAttribute("template"),true);
4834 while((e
= e
.nextSibling
) != null)
4835 this.refreshTiddler(e
.getAttribute("tiddler"),force
? null : e
.getAttribute("template"),true);
4838 Story
.prototype.onTiddlerMouseOver = function(e
)
4840 if(window
.addClass
instanceof Function
)
4841 addClass(this,"selected");
4844 Story
.prototype.onTiddlerMouseOut = function(e
)
4846 if(window
.removeClass
instanceof Function
)
4847 removeClass(this,"selected");
4850 Story
.prototype.onTiddlerDblClick = function(ev
)
4852 var e
= ev
? ev
: window
.event
;
4853 var theTarget
= resolveTarget(e
);
4854 if(theTarget
&& theTarget
.nodeName
.toLowerCase() != "input" && theTarget
.nodeName
.toLowerCase() != "textarea") {
4855 if(document
.selection
&& document
.selection
.empty
)
4856 document
.selection
.empty();
4857 config
.macros
.toolbar
.invokeCommand(this,"defaultCommand",e
);
4858 e
.cancelBubble
= true;
4859 if(e
.stopPropagation
) e
.stopPropagation();
4866 Story
.prototype.onTiddlerKeyPress = function(ev
)
4868 var e
= ev
? ev
: window
.event
;
4870 var consume
= false;
4871 var title
= this.getAttribute("tiddler");
4872 var target
= resolveTarget(e
);
4875 if(config
.options
.chkInsertTabs
&& target
.tagName
.toLowerCase() == "textarea") {
4876 replaceSelection(target
,String
.fromCharCode(9));
4879 if(config
.isOpera
) {
4880 target
.onblur = function() {
4886 case 13: // Ctrl-Enter
4887 case 10: // Ctrl-Enter on IE PC
4888 case 77: // Ctrl-Enter is "M" on some platforms
4891 config
.macros
.toolbar
.invokeCommand(this,"defaultCommand",e
);
4897 config
.macros
.toolbar
.invokeCommand(this,"cancelCommand",e
);
4901 e
.cancelBubble
= consume
;
4903 if(e
.stopPropagation
) e
.stopPropagation(); // Stop Propagation
4904 e
.returnValue
= true; // Cancel The Event in IE
4905 if(e
.preventDefault
) e
.preventDefault(); // Cancel The Event in Moz
4910 Story
.prototype.getTiddlerField = function(title
,field
)
4912 var tiddlerElem
= document
.getElementById(this.idPrefix
+ title
);
4914 if(tiddlerElem
!= null) {
4915 var children
= tiddlerElem
.getElementsByTagName("*");
4916 for(var t
=0; t
<children
.length
; t
++) {
4917 var c
= children
[t
];
4918 if(c
.tagName
.toLowerCase() == "input" || c
.tagName
.toLowerCase() == "textarea") {
4921 if(c
.getAttribute("edit") == field
)
4929 Story
.prototype.focusTiddler = function(title
,field
)
4931 var e
= this.getTiddlerField(title
,field
);
4938 Story
.prototype.blurTiddler = function(title
)
4940 var tiddlerElem
= document
.getElementById(this.idPrefix
+ title
);
4941 if(tiddlerElem
!= null && tiddlerElem
.focus
&& tiddlerElem
.blur
) {
4942 tiddlerElem
.focus();
4947 Story
.prototype.setTiddlerField = function(title
,tag
,mode
,field
)
4949 var c
= story
.getTiddlerField(title
,field
);
4951 var tags
= c
.value
.readBracketedList();
4952 tags
.setItem(tag
,mode
);
4953 c
.value
= String
.encodeTiddlyLinkList(tags
);
4956 Story
.prototype.setTiddlerTag = function(title
,tag
,mode
)
4958 Story
.prototype.setTiddlerField(title
,tag
,mode
,"tags");
4961 Story
.prototype.closeTiddler = function(title
,animate
,unused
)
4963 var tiddlerElem
= document
.getElementById(this.idPrefix
+ title
);
4964 if(tiddlerElem
!= null) {
4966 this.scrubTiddler(tiddlerElem
);
4967 if(config
.options
.chkAnimate
&& animate
&& anim
&& typeof Slider
== "function")
4968 anim
.startAnimating(new Slider(tiddlerElem
,false,null,"all"));
4970 removeNode(tiddlerElem
);
4976 Story
.prototype.scrubTiddler = function(tiddlerElem
)
4978 tiddlerElem
.id
= null;
4981 Story
.prototype.setDirty = function(title
,dirty
)
4983 var tiddlerElem
= document
.getElementById(this.idPrefix
+ title
);
4984 if(tiddlerElem
!= null)
4985 tiddlerElem
.setAttribute("dirty",dirty
? "true" : "false");
4988 Story
.prototype.isDirty = function(title
)
4990 var tiddlerElem
= document
.getElementById(this.idPrefix
+ title
);
4991 if(tiddlerElem
!= null)
4992 return tiddlerElem
.getAttribute("dirty") == "true";
4996 Story
.prototype.areAnyDirty = function()
4999 this.forEachTiddler(function(title
,element
) {
5000 if(this.isDirty(title
))
5006 Story
.prototype.closeAllTiddlers = function(exclude
)
5009 this.forEachTiddler(function(title
,element
) {
5010 if((title
!= exclude
) && element
.getAttribute("dirty") != "true")
5011 this.closeTiddler(title
);
5013 window
.scrollTo(0,ensureVisible(this.container
));
5016 Story
.prototype.isEmpty = function()
5018 var place
= document
.getElementById(this.container
);
5019 return place
&& place
.firstChild
== null;
5022 Story
.prototype.search = function(text
,useCaseSensitive
,useRegExp
)
5024 this.closeAllTiddlers();
5025 highlightHack
= new RegExp(useRegExp
? text
: text
.escapeRegExp(),useCaseSensitive
? "mg" : "img");
5026 var matches
= store
.search(highlightHack
,"title","excludeSearch");
5027 this.displayTiddlers(null,matches
);
5028 highlightHack
= null;
5029 var q
= useRegExp
? "/" : "'";
5030 if(matches
.length
> 0)
5031 displayMessage(config
.macros
.search
.successMsg
.format([matches
.length
.toString(),q
+ text
+ q
]));
5033 displayMessage(config
.macros
.search
.failureMsg
.format([q
+ text
+ q
]));
5036 Story
.prototype.findContainingTiddler = function(e
)
5038 while(e
&& !hasClass(e
,"tiddler"))
5043 Story
.prototype.gatherSaveFields = function(e
,fields
)
5045 if(e
&& e
.getAttribute
) {
5046 var f
= e
.getAttribute("edit");
5048 fields
[f
] = e
.value
.replace(/\r/mg,"");
5049 if(e
.hasChildNodes()) {
5050 var c
= e
.childNodes
;
5051 for(var t
=0; t
<c
.length
; t
++)
5052 this.gatherSaveFields(c
[t
],fields
);
5057 Story
.prototype.hasChanges = function(title
)
5059 var e
= document
.getElementById(this.idPrefix
+ title
);
5062 this.gatherSaveFields(e
,fields
);
5063 var tiddler
= store
.fetchTiddler(title
);
5066 for(var n
in fields
) {
5067 if(store
.getValue(title
,n
) != fields
[n
])
5074 Story
.prototype.saveTiddler = function(title
,minorUpdate
)
5076 var tiddlerElem
= document
.getElementById(this.idPrefix
+ title
);
5077 if(tiddlerElem
!= null) {
5079 this.gatherSaveFields(tiddlerElem
,fields
);
5080 var newTitle
= fields
.title
? fields
.title
: title
;
5081 if(!store
.tiddlerExists(newTitle
))
5082 newTitle
= newTitle
.trim();
5083 if(store
.tiddlerExists(newTitle
) && newTitle
!= title
) {
5084 if(!confirm(config
.messages
.overwriteWarning
.format([newTitle
.toString()])))
5087 if(newTitle
!= title
)
5088 this.closeTiddler(newTitle
,false);
5089 tiddlerElem
.id
= this.idPrefix
+ newTitle
;
5090 tiddlerElem
.setAttribute("tiddler",newTitle
);
5091 tiddlerElem
.setAttribute("template",DEFAULT_VIEW_TEMPLATE
);
5092 tiddlerElem
.setAttribute("dirty","false");
5093 if(config
.options
.chkForceMinorUpdate
)
5094 minorUpdate
= !minorUpdate
;
5095 if(!store
.tiddlerExists(newTitle
))
5096 minorUpdate
= false;
5097 var newDate
= new Date();
5098 var extendedFields
= store
.tiddlerExists(newTitle
) ? store
.fetchTiddler(newTitle
).fields
: (newTitle
!=title
&& store
.tiddlerExists(title
) ? store
.fetchTiddler(title
).fields
: {});
5099 for(var n
in fields
) {
5100 if(!TiddlyWiki
.isStandardField(n
))
5101 extendedFields
[n
] = fields
[n
];
5103 var tiddler
= store
.saveTiddler(title
,newTitle
,fields
.text
,minorUpdate
? undefined : config
.options
.txtUserName
,minorUpdate
? undefined : newDate
,fields
.tags
,extendedFields
);
5104 autoSaveChanges(null,[tiddler
]);
5110 Story
.prototype.permaView = function()
5113 this.forEachTiddler(function(title
,element
) {
5114 links
.push(String
.encodeTiddlyLink(title
));
5116 var t
= encodeURIComponent(links
.join(" "));
5119 if(window
.location
.hash
!= t
)
5120 window
.location
.hash
= t
;
5124 Story
.prototype.switchTheme = function(theme
)
5129 isAvailable = function(title
) {
5130 var s
= title
? title
.indexOf(config
.textPrimitives
.sectionSeparator
) : -1;
5132 title
= title
.substr(0,s
);
5133 return store
.tiddlerExists(title
) || store
.isShadowTiddler(title
);
5136 getSlice = function(theme
,slice
) {
5137 var r
= store
.getTiddlerSlice(theme
,slice
);
5138 if(r
&& r
.indexOf(config
.textPrimitives
.sectionSeparator
)==0)
5140 return isAvailable(r
) ? r
: slice
;
5143 replaceNotification = function(i
,name
,newName
) {
5146 if(store
.namedNotifications
[i
].name
== name
) {
5147 store
.namedNotifications
[i
].name
= newName
;
5153 for(var i
=0; i
<config
.notifyTiddlers
.length
; i
++) {
5154 var name
= config
.notifyTiddlers
[i
].name
;
5156 case "PageTemplate":
5157 config
.refreshers
.pageTemplate
= replaceNotification(i
,config
.refreshers
.pageTemplate
,getSlice(theme
,name
));
5160 removeStyleSheet(config
.refreshers
.styleSheet
);
5161 config
.refreshers
.styleSheet
= replaceNotification(i
,config
.refreshers
.styleSheet
,getSlice(theme
,name
));
5163 case "ColorPalette":
5164 config
.refreshers
.colorPalette
= replaceNotification(i
,config
.refreshers
.colorPalette
,getSlice(theme
,name
));
5170 config
.tiddlerTemplates
[DEFAULT_VIEW_TEMPLATE
] = getSlice(theme
,"ViewTemplate");
5171 config
.tiddlerTemplates
[DEFAULT_EDIT_TEMPLATE
] = getSlice(theme
,"EditTemplate");
5174 story
.refreshAllTiddlers(true);
5175 config
.options
.txtTheme
= theme
;
5176 saveOptionCookie("txtTheme");
5199 var cmb
= config
.messages
.backstage
;
5200 this.area
= document
.getElementById("backstageArea");
5201 this.toolbar
= document
.getElementById("backstageToolbar");
5202 this.button
= document
.getElementById("backstageButton");
5203 this.button
.style
.display
= "block";
5204 var t
= cmb
.open
.text
+ " " + glyph("bentArrowLeft");
5205 this.showButton
= createTiddlyButton(this.button
,t
,cmb
.open
.tooltip
,
5206 function (e
) {backstage
.show(); return false;},null,"backstageShow");
5207 t
= glyph("bentArrowRight") + " " + cmb
.close
.text
;
5208 this.hideButton
= createTiddlyButton(this.button
,t
,cmb
.close
.tooltip
,
5209 function (e
) {backstage
.hide(); return false;},null,"backstageHide");
5210 this.cloak
= document
.getElementById("backstageCloak");
5211 this.panel
= document
.getElementById("backstagePanel");
5212 this.panelFooter
= createTiddlyElement(this.panel
,"div",null,"backstagePanelFooter");
5213 this.panelBody
= createTiddlyElement(this.panel
,"div",null,"backstagePanelBody");
5214 this.cloak
.onmousedown = function(e
) {
5215 backstage
.switchTab(null);
5217 createTiddlyText(this.toolbar
,cmb
.prompt
);
5218 for(t
=0; t
<config
.backstageTasks
.length
; t
++) {
5219 var taskName
= config
.backstageTasks
[t
];
5220 var task
= config
.tasks
[taskName
];
5221 var handler
= task
.action
? this.onClickCommand
: this.onClickTab
;
5222 var text
= task
.text
+ (task
.action
? "" : glyph("downTriangle"));
5223 var btn
= createTiddlyButton(this.toolbar
,text
,task
.tooltip
,handler
,"backstageTab");
5224 btn
.setAttribute("task",taskName
);
5225 addClass(btn
,task
.action
? "backstageAction" : "backstageTask");
5227 this.content
= document
.getElementById("contentWrapper");
5228 if(config
.options
.chkBackstage
)
5234 isVisible: function() {
5235 return this.area
? this.area
.style
.display
== "block" : false;
5239 this.area
.style
.display
= "block";
5240 if(anim
&& config
.options
.chkAnimate
) {
5241 backstage
.toolbar
.style
.left
= findWindowWidth() + "px";
5243 {style
: "left", start
: findWindowWidth(), end
: 0, template
: "%0px"}
5245 anim
.startAnimating(new Morpher(backstage
.toolbar
,config
.animDuration
,p
));
5247 backstage
.area
.style
.left
= "0px";
5249 this.showButton
.style
.display
= "none";
5250 this.hideButton
.style
.display
= "block";
5251 config
.options
.chkBackstage
= true;
5252 saveOptionCookie("chkBackstage");
5253 addClass(this.content
,"backstageVisible");
5257 if(this.currTabElem
) {
5258 this.switchTab(null);
5260 backstage
.toolbar
.style
.left
= "0px";
5261 if(anim
&& config
.options
.chkAnimate
) {
5263 {style
: "left", start
: 0, end
: findWindowWidth(), template
: "%0px"}
5265 var c = function(element
,properties
) {backstage
.area
.style
.display
= "none";};
5266 anim
.startAnimating(new Morpher(backstage
.toolbar
,config
.animDuration
,p
,c
));
5268 this.area
.style
.display
= "none";
5270 this.showButton
.style
.display
= "block";
5271 this.hideButton
.style
.display
= "none";
5272 config
.options
.chkBackstage
= false;
5273 saveOptionCookie("chkBackstage");
5274 removeClass(this.content
,"backstageVisible");
5278 onClickCommand: function(e
) {
5279 var task
= config
.tasks
[this.getAttribute("task")];
5280 displayMessage(task
);
5282 backstage
.switchTab(null);
5288 onClickTab: function(e
) {
5289 backstage
.switchTab(this.getAttribute("task"));
5293 // Switch to a given tab, or none if null is passed
5294 switchTab: function(tabName
) {
5296 var e
= this.toolbar
.firstChild
;
5299 if(e
.getAttribute
&& e
.getAttribute("task") == tabName
)
5303 if(tabName
== backstage
.currTabName
)
5305 if(backstage
.currTabElem
) {
5306 removeClass(this.currTabElem
,"backstageSelTab");
5308 if(tabElem
&& tabName
) {
5309 backstage
.preparePanel();
5310 addClass(tabElem
,"backstageSelTab");
5311 var task
= config
.tasks
[tabName
];
5312 wikify(task
.content
,backstage
.panelBody
,null,null);
5313 backstage
.showPanel();
5314 } else if(backstage
.currTabElem
) {
5315 backstage
.hidePanel();
5317 backstage
.currTabName
= tabName
;
5318 backstage
.currTabElem
= tabElem
;
5321 isPanelVisible: function() {
5322 return backstage
.panel
? backstage
.panel
.style
.display
== "block" : false;
5325 preparePanel: function() {
5326 backstage
.cloak
.style
.height
= findWindowHeight() + "px";
5327 backstage
.cloak
.style
.display
= "block";
5328 removeChildren(backstage
.panelBody
);
5329 return backstage
.panelBody
;
5332 showPanel: function() {
5333 backstage
.panel
.style
.display
= "block";
5334 if(anim
&& config
.options
.chkAnimate
) {
5335 backstage
.panel
.style
.top
= (-backstage
.panel
.offsetHeight
) + "px";
5337 {style
: "top", start
: -backstage
.panel
.offsetHeight
, end
: 0, template
: "%0px"}
5339 anim
.startAnimating(new Morpher(backstage
.panel
,config
.animDuration
,p
),new Scroller(backstage
.panel
,false));
5341 backstage
.panel
.style
.top
= "0px";
5343 return backstage
.panelBody
;
5346 hidePanel: function() {
5347 backstage
.currTabName
= null;
5348 backstage
.currTabElem
= null;
5349 if(anim
&& config
.options
.chkAnimate
) {
5351 {style
: "top", start
: 0, end
: -(backstage
.panel
.offsetHeight
), template
: "%0px"},
5352 {style
: "display", atEnd
: "none"}
5354 var c = function(element
,properties
) {backstage
.cloak
.style
.display
= "none";};
5355 anim
.startAnimating(new Morpher(backstage
.panel
,config
.animDuration
,p
,c
));
5357 backstage
.panel
.style
.display
= "none";
5358 backstage
.cloak
.style
.display
= "none";
5363 config
.macros
.backstage
= {};
5365 config
.macros
.backstage
.handler = function(place
,macroName
,params
,wikifier
,paramString
,tiddler
)
5367 var backstageTask
= config
.tasks
[params
[0]];
5369 createTiddlyButton(place
,backstageTask
.text
,backstageTask
.tooltip
,function(e
) {backstage
.switchTab(params
[0]); return false;});
5373 //-- ImportTiddlers macro
5376 config
.macros
.importTiddlers
.handler = function(place
,macroName
,params
,wikifier
,paramString
,tiddler
)
5379 createTiddlyElement(place
,"div",null,"marked",this.readOnlyWarning
);
5382 var w
= new Wizard();
5383 w
.createWizard(place
,this.wizardTitle
);
5387 config
.macros
.importTiddlers
.onCancel = function(e
)
5389 var wizard
= new Wizard(this);
5390 var place
= wizard
.clear();
5391 config
.macros
.importTiddlers
.restart(wizard
);
5395 config
.macros
.importTiddlers
.restart = function(wizard
)
5397 wizard
.addStep(this.step1Title
,this.step1Html
);
5398 var s
= wizard
.getElement("selTypes");
5399 for(var t
in config
.adaptors
) {
5400 var e
= createTiddlyElement(s
,"option",null,null,t
);
5403 s
= wizard
.getElement("selFeeds");
5404 var feeds
= this.getFeeds();
5406 e
= createTiddlyElement(s
,"option",null,null,t
);
5409 wizard
.setValue("feeds",feeds
);
5410 s
.onchange
= config
.macros
.importTiddlers
.onFeedChange
;
5411 var fileInput
= wizard
.getElement("txtBrowse");
5412 fileInput
.onchange
= config
.macros
.importTiddlers
.onBrowseChange
;
5413 fileInput
.onkeyup
= config
.macros
.importTiddlers
.onBrowseChange
;
5414 wizard
.setButtons([{caption
: this.openLabel
, tooltip
: this.openPrompt
, onClick
: config
.macros
.importTiddlers
.onOpen
}]);
5417 config
.macros
.importTiddlers
.getFeeds = function()
5420 var tagged
= store
.getTaggedTiddlers("systemServer","title");
5421 for(var t
=0; t
<tagged
.length
; t
++) {
5422 var title
= tagged
[t
].title
;
5423 var serverType
= store
.getTiddlerSlice(title
,"Type");
5425 serverType
= "file";
5426 feeds
[title
] = {title
: title
,
5427 url
: store
.getTiddlerSlice(title
,"URL"),
5428 workspace
: store
.getTiddlerSlice(title
,"Workspace"),
5429 workspaceList
: store
.getTiddlerSlice(title
,"WorkspaceList"),
5430 tiddlerFilter
: store
.getTiddlerSlice(title
,"TiddlerFilter"),
5431 serverType
: serverType
,
5432 description
: store
.getTiddlerSlice(title
,"Description")};
5437 config
.macros
.importTiddlers
.onFeedChange = function(e
)
5439 var wizard
= new Wizard(this);
5440 var selTypes
= wizard
.getElement("selTypes");
5441 var fileInput
= wizard
.getElement("txtPath");
5442 var feeds
= wizard
.getValue("feeds");
5443 var f
= feeds
[this.value
];
5445 selTypes
.value
= f
.serverType
;
5446 fileInput
.value
= f
.url
;
5447 this.selectedIndex
= 0;
5448 wizard
.setValue("feedName",f
.serverType
);
5449 wizard
.setValue("feedHost",f
.url
);
5450 wizard
.setValue("feedWorkspace",f
.workspace
);
5451 wizard
.setValue("feedWorkspaceList",f
.workspaceList
);
5452 wizard
.setValue("feedTiddlerFilter",f
.tiddlerFilter
);
5457 config
.macros
.importTiddlers
.onBrowseChange = function(e
)
5459 var wizard
= new Wizard(this);
5460 var fileInput
= wizard
.getElement("txtPath");
5461 fileInput
.value
= "file://" + this.value
;
5462 var serverType
= wizard
.getElement("selTypes");
5463 serverType
.value
= "file";
5467 config
.macros
.importTiddlers
.onOpen = function(e
)
5469 var wizard
= new Wizard(this);
5470 var fileInput
= wizard
.getElement("txtPath");
5471 var url
= fileInput
.value
;
5472 var serverType
= wizard
.getElement("selTypes").value
;
5473 var adaptor
= new config
.adaptors
[serverType
];
5474 wizard
.setValue("adaptor",adaptor
);
5475 wizard
.setValue("serverType",serverType
);
5476 wizard
.setValue("host",url
);
5477 var ret
= adaptor
.openHost(url
,null,wizard
,config
.macros
.importTiddlers
.onOpenHost
);
5479 displayMessage(ret
);
5480 wizard
.setButtons([{caption
: config
.macros
.importTiddlers
.cancelLabel
, tooltip
: config
.macros
.importTiddlers
.cancelPrompt
, onClick
: config
.macros
.importTiddlers
.onCancel
}],config
.macros
.importTiddlers
.statusOpenHost
);
5484 config
.macros
.importTiddlers
.onOpenHost = function(context
,wizard
)
5486 var adaptor
= wizard
.getValue("adaptor");
5487 if(context
.status
!== true)
5488 displayMessage("Error in importTiddlers.onOpenHost: " + context
.statusText
);
5489 var ret
= adaptor
.getWorkspaceList(context
,wizard
,config
.macros
.importTiddlers
.onGetWorkspaceList
);
5491 displayMessage(ret
);
5492 wizard
.setButtons([{caption
: config
.macros
.importTiddlers
.cancelLabel
, tooltip
: config
.macros
.importTiddlers
.cancelPrompt
, onClick
: config
.macros
.importTiddlers
.onCancel
}],config
.macros
.importTiddlers
.statusGetWorkspaceList
);
5495 config
.macros
.importTiddlers
.onGetWorkspaceList = function(context
,wizard
)
5497 if(context
.status
!== true)
5498 displayMessage("Error in importTiddlers.onGetWorkspaceList: " + context
.statusText
);
5499 wizard
.setValue("context",context
);
5500 var workspace
= wizard
.getValue("feedWorkspace");
5501 if(!workspace
&& context
.workspaces
.length
==1)
5502 workspace
= context
.workspaces
[0].title
;
5504 var ret
= context
.adaptor
.openWorkspace(workspace
,context
,wizard
,config
.macros
.importTiddlers
.onOpenWorkspace
);
5506 displayMessage(ret
);
5507 wizard
.setValue("workspace",workspace
);
5508 wizard
.setButtons([{caption
: config
.macros
.importTiddlers
.cancelLabel
, tooltip
: config
.macros
.importTiddlers
.cancelPrompt
, onClick
: config
.macros
.importTiddlers
.onCancel
}],config
.macros
.importTiddlers
.statusOpenWorkspace
);
5511 wizard
.addStep(config
.macros
.importTiddlers
.step2Title
,config
.macros
.importTiddlers
.step2Html
);
5512 var s
= wizard
.getElement("selWorkspace");
5513 s
.onchange
= config
.macros
.importTiddlers
.onWorkspaceChange
;
5514 for(var t
=0; t
<context
.workspaces
.length
; t
++) {
5515 var e
= createTiddlyElement(s
,"option",null,null,context
.workspaces
[t
].title
);
5516 e
.value
= context
.workspaces
[t
].title
;
5518 var workspaceList
= wizard
.getValue("feedWorkspaceList");
5520 var list
= workspaceList
.parseParams("workspace",null,false,true);
5521 for(var n
=1; n
<list
.length
; n
++) {
5522 if(context
.workspaces
.findByField("title",list
[n
].value
) == null) {
5523 e
= createTiddlyElement(s
,"option",null,null,list
[n
].value
);
5524 e
.value
= list
[n
].value
;
5529 t
= wizard
.getElement("txtWorkspace");
5530 t
.value
= workspace
;
5532 wizard
.setButtons([{caption
: config
.macros
.importTiddlers
.openLabel
, tooltip
: config
.macros
.importTiddlers
.openPrompt
, onClick
: config
.macros
.importTiddlers
.onChooseWorkspace
}]);
5535 config
.macros
.importTiddlers
.onWorkspaceChange = function(e
)
5537 var wizard
= new Wizard(this);
5538 var t
= wizard
.getElement("txtWorkspace");
5539 t
.value
= this.value
;
5540 this.selectedIndex
= 0;
5544 config
.macros
.importTiddlers
.onChooseWorkspace = function(e
)
5546 var wizard
= new Wizard(this);
5547 var adaptor
= wizard
.getValue("adaptor");
5548 var workspace
= wizard
.getElement("txtWorkspace").value
;
5549 wizard
.setValue("workspace",workspace
);
5550 var context
= wizard
.getValue("context");
5551 var ret
= adaptor
.openWorkspace(workspace
,context
,wizard
,config
.macros
.importTiddlers
.onOpenWorkspace
);
5553 displayMessage(ret
);
5554 wizard
.setButtons([{caption
: config
.macros
.importTiddlers
.cancelLabel
, tooltip
: config
.macros
.importTiddlers
.cancelPrompt
, onClick
: config
.macros
.importTiddlers
.onCancel
}],config
.macros
.importTiddlers
.statusOpenWorkspace
);
5558 config
.macros
.importTiddlers
.onOpenWorkspace = function(context
,wizard
)
5560 if(context
.status
!== true)
5561 displayMessage("Error in importTiddlers.onOpenWorkspace: " + context
.statusText
);
5562 var adaptor
= wizard
.getValue("adaptor");
5563 var ret
= adaptor
.getTiddlerList(context
,wizard
,config
.macros
.importTiddlers
.onGetTiddlerList
,wizard
.getValue("feedTiddlerFilter"));
5565 displayMessage(ret
);
5566 wizard
.setButtons([{caption
: config
.macros
.importTiddlers
.cancelLabel
, tooltip
: config
.macros
.importTiddlers
.cancelPrompt
, onClick
: config
.macros
.importTiddlers
.onCancel
}],config
.macros
.importTiddlers
.statusGetTiddlerList
);
5569 config
.macros
.importTiddlers
.onGetTiddlerList = function(context
,wizard
)
5571 if(context
.status
!== true)
5572 displayMessage("Error in importTiddlers.onGetTiddlerList: " + context
.statusText
);
5573 // Extract data for the listview
5574 var listedTiddlers
= [];
5575 if(context
.tiddlers
) {
5576 for(var n
=0; n
<context
.tiddlers
.length
; n
++) {
5577 var tiddler
= context
.tiddlers
[n
];
5578 listedTiddlers
.push({
5579 title
: tiddler
.title
,
5580 modified
: tiddler
.modified
,
5581 modifier
: tiddler
.modifier
,
5582 text
: tiddler
.text
? wikifyPlainText(tiddler
.text
,100) : "",
5584 size
: tiddler
.text
? tiddler
.text
.length
: 0,
5589 listedTiddlers
.sort(function(a
,b
) {return a
.title
< b
.title
? -1 : (a
.title
== b
.title
? 0 : +1);});
5590 // Display the listview
5591 wizard
.addStep(config
.macros
.importTiddlers
.step3Title
,config
.macros
.importTiddlers
.step3Html
);
5592 var markList
= wizard
.getElement("markList");
5593 var listWrapper
= document
.createElement("div");
5594 markList
.parentNode
.insertBefore(listWrapper
,markList
);
5595 var listView
= ListView
.create(listWrapper
,listedTiddlers
,config
.macros
.importTiddlers
.listViewTemplate
);
5596 wizard
.setValue("listView",listView
);
5597 var txtSaveTiddler
= wizard
.getElement("txtSaveTiddler");
5598 txtSaveTiddler
.value
= config
.macros
.importTiddlers
.generateSystemServerName(wizard
);
5600 {caption
: config
.macros
.importTiddlers
.cancelLabel
, tooltip
: config
.macros
.importTiddlers
.cancelPrompt
, onClick
: config
.macros
.importTiddlers
.onCancel
},
5601 {caption
: config
.macros
.importTiddlers
.importLabel
, tooltip
: config
.macros
.importTiddlers
.importPrompt
, onClick
: config
.macros
.importTiddlers
.doImport
}
5605 config
.macros
.importTiddlers
.generateSystemServerName = function(wizard
)
5607 var serverType
= wizard
.getValue("serverType");
5608 var host
= wizard
.getValue("host");
5609 var workspace
= wizard
.getValue("workspace");
5610 var pattern
= config
.macros
.importTiddlers
[workspace
? "systemServerNamePattern" : "systemServerNamePatternNoWorkspace"];
5611 return pattern
.format([serverType
,host
,workspace
]);
5614 config
.macros
.importTiddlers
.saveServerTiddler = function(wizard
)
5616 var txtSaveTiddler
= wizard
.getElement("txtSaveTiddler").value
;
5617 if(store
.tiddlerExists(txtSaveTiddler
)) {
5618 if(!confirm(config
.macros
.importTiddlers
.confirmOverwriteSaveTiddler
.format([txtSaveTiddler
])))
5620 store
.suspendNotifications();
5621 store
.removeTiddler(txtSaveTiddler
);
5622 store
.resumeNotifications();
5624 var serverType
= wizard
.getValue("serverType");
5625 var host
= wizard
.getValue("host");
5626 var workspace
= wizard
.getValue("workspace");
5627 var text
= config
.macros
.importTiddlers
.serverSaveTemplate
.format([serverType
,host
,workspace
]);
5628 store
.saveTiddler(txtSaveTiddler
,txtSaveTiddler
,text
,config
.macros
.importTiddlers
.serverSaveModifier
,new Date(),["systemServer"]);
5631 config
.macros
.importTiddlers
.doImport = function(e
)
5633 var wizard
= new Wizard(this);
5634 if(wizard
.getElement("chkSave").checked
)
5635 config
.macros
.importTiddlers
.saveServerTiddler(wizard
);
5636 var chkSync
= wizard
.getElement("chkSync").checked
;
5637 wizard
.setValue("sync",chkSync
);
5638 var listView
= wizard
.getValue("listView");
5639 var rowNames
= ListView
.getSelectedRows(listView
);
5640 var adaptor
= wizard
.getValue("adaptor");
5641 var overwrite
= new Array();
5643 for(t
=0; t
<rowNames
.length
; t
++) {
5644 if(store
.tiddlerExists(rowNames
[t
]))
5645 overwrite
.push(rowNames
[t
]);
5647 if(overwrite
.length
> 0) {
5648 if(!confirm(config
.macros
.importTiddlers
.confirmOverwriteText
.format([overwrite
.join(", ")])))
5651 wizard
.addStep(config
.macros
.importTiddlers
.step4Title
.format([rowNames
.length
]),config
.macros
.importTiddlers
.step4Html
);
5652 for(t
=0; t
<rowNames
.length
; t
++) {
5653 var link
= document
.createElement("div");
5654 createTiddlyLink(link
,rowNames
[t
],true);
5655 var place
= wizard
.getElement("markReport");
5656 place
.parentNode
.insertBefore(link
,place
);
5658 wizard
.setValue("remainingImports",rowNames
.length
);
5660 {caption
: config
.macros
.importTiddlers
.cancelLabel
, tooltip
: config
.macros
.importTiddlers
.cancelPrompt
, onClick
: config
.macros
.importTiddlers
.onCancel
}
5661 ],config
.macros
.importTiddlers
.statusDoingImport
);
5662 for(t
=0; t
<rowNames
.length
; t
++) {
5664 context
.allowSynchronous
= true;
5665 var inbound
= adaptor
.getTiddler(rowNames
[t
],context
,wizard
,config
.macros
.importTiddlers
.onGetTiddler
);
5670 config
.macros
.importTiddlers
.onGetTiddler = function(context
,wizard
)
5673 displayMessage("Error in importTiddlers.onGetTiddler: " + context
.statusText
);
5674 var tiddler
= context
.tiddler
;
5675 store
.suspendNotifications();
5676 store
.saveTiddler(tiddler
.title
, tiddler
.title
, tiddler
.text
, tiddler
.modifier
, tiddler
.modified
, tiddler
.tags
, tiddler
.fields
, true, tiddler
.created
);
5677 if(!wizard
.getValue("sync")) {
5678 store
.setValue(tiddler
.title
,'server',null);
5680 store
.resumeNotifications();
5681 if(!context
.isSynchronous
)
5682 store
.notify(tiddler
.title
,true);
5683 var remainingImports
= wizard
.getValue("remainingImports")-1;
5684 wizard
.setValue("remainingImports",remainingImports
);
5685 if(remainingImports
== 0) {
5686 if(context
.isSynchronous
) {
5691 {caption
: config
.macros
.importTiddlers
.doneLabel
, tooltip
: config
.macros
.importTiddlers
.donePrompt
, onClick
: config
.macros
.importTiddlers
.onCancel
}
5692 ],config
.macros
.importTiddlers
.statusDoneImport
);
5701 // Synchronisation handlers
5702 config
.syncers
= {};
5705 var currSync
= null;
5708 config
.macros
.sync
.handler = function(place
,macroName
,params
,wikifier
,paramString
,tiddler
)
5710 if(!wikifier
.isStatic
)
5711 this.startSync(place
);
5714 config
.macros
.sync
.startSync = function(place
)
5717 config
.macros
.sync
.cancelSync();
5719 currSync
.syncList
= this.getSyncableTiddlers();
5720 this.createSyncTasks();
5721 this.preProcessSyncableTiddlers();
5722 var wizard
= new Wizard();
5723 currSync
.wizard
= wizard
;
5724 wizard
.createWizard(place
,this.wizardTitle
);
5725 wizard
.addStep(this.step1Title
,this.step1Html
);
5726 var markList
= wizard
.getElement("markList");
5727 var listWrapper
= document
.createElement("div");
5728 markList
.parentNode
.insertBefore(listWrapper
,markList
);
5729 currSync
.listView
= ListView
.create(listWrapper
,currSync
.syncList
,this.listViewTemplate
);
5730 this.processSyncableTiddlers();
5732 {caption
: this.syncLabel
, tooltip
: this.syncPrompt
, onClick
: this.doSync
}
5736 config
.macros
.sync
.getSyncableTiddlers = function()
5739 store
.forEachTiddler(function(title
,tiddler
) {
5741 syncItem
.serverType
= tiddler
.getServerType();
5742 syncItem
.serverHost
= tiddler
.fields
['server.host'];
5743 syncItem
.serverWorkspace
= tiddler
.fields
['server.workspace'];
5744 syncItem
.tiddler
= tiddler
;
5745 syncItem
.title
= tiddler
.title
;
5746 syncItem
.isTouched
= tiddler
.isTouched();
5747 syncItem
.selected
= syncItem
.isTouched
;
5748 syncItem
.syncStatus
= config
.macros
.sync
.syncStatusList
[syncItem
.isTouched
? "changedLocally" : "none"];
5749 syncItem
.status
= syncItem
.syncStatus
.text
;
5750 if(syncItem
.serverType
&& syncItem
.serverHost
)
5751 list
.push(syncItem
);
5753 list
.sort(function(a
,b
) {return a
.title
< b
.title
? -1 : (a
.title
== b
.title
? 0 : +1);});
5757 config
.macros
.sync
.preProcessSyncableTiddlers = function()
5759 for(var t
=0; t
<currSync
.syncList
.length
; t
++) {
5760 si
= currSync
.syncList
[t
];
5761 var ti
= si
.syncTask
.syncMachine
.generateTiddlerInfo(si
.tiddler
);
5762 si
.serverUrl
= ti
.uri
;
5766 config
.macros
.sync
.processSyncableTiddlers = function()
5768 for(var t
=0; t
<currSync
.syncList
.length
; t
++) {
5769 si
= currSync
.syncList
[t
];
5770 si
.rowElement
.style
.backgroundColor
= si
.syncStatus
.color
;
5774 config
.macros
.sync
.createSyncTasks = function()
5776 currSync
.syncTasks
= [];
5777 for(var t
=0; t
<currSync
.syncList
.length
; t
++) {
5778 var si
= currSync
.syncList
[t
];
5780 for(var st
=0; st
<currSync
.syncTasks
.length
; st
++) {
5781 var cst
= currSync
.syncTasks
[st
];
5782 if(si
.serverType
== cst
.serverType
&& si
.serverHost
== cst
.serverHost
&& si
.serverWorkspace
== cst
.serverWorkspace
)
5786 si
.syncTask
= this.createSyncTask(si
);
5787 currSync
.syncTasks
.push(si
.syncTask
);
5790 r
.syncItems
.push(si
);
5795 config
.macros
.sync
.createSyncTask = function(syncItem
)
5798 st
.serverType
= syncItem
.serverType
;
5799 st
.serverHost
= syncItem
.serverHost
;
5800 st
.serverWorkspace
= syncItem
.serverWorkspace
;
5801 st
.syncItems
= [syncItem
];
5802 st
.syncMachine
= new SyncMachine(st
.serverType
,{
5804 return this.openHost(st
.serverHost
,"openWorkspace");
5806 openWorkspace: function() {
5807 return this.openWorkspace(st
.serverWorkspace
,"getTiddlerList");
5809 getTiddlerList: function() {
5810 return this.getTiddlerList("onGetTiddlerList");
5812 onGetTiddlerList: function(context
) {
5813 var tiddlers
= context
.tiddlers
;
5814 for(var t
=0; t
<st
.syncItems
.length
; t
++) {
5815 var si
= st
.syncItems
[t
];
5816 var f
= tiddlers
.findByField("title",si
.title
);
5818 if(tiddlers
[f
].fields
['server.page.revision'] > si
.tiddler
.fields
['server.page.revision']) {
5819 si
.syncStatus
= config
.macros
.sync
.syncStatusList
[si
.isTouched
? 'changedBoth' : 'changedServer'];
5822 si
.syncStatus
= config
.macros
.sync
.syncStatusList
.notFound
;
5824 config
.macros
.sync
.updateSyncStatus(si
);
5827 getTiddler: function(title
) {
5828 return this.getTiddler(title
,"onGetTiddler");
5830 onGetTiddler: function(context
) {
5831 var tiddler
= context
.tiddler
;
5832 var syncItem
= st
.syncItems
.findByField("title",tiddler
.title
);
5833 if(syncItem
!== null) {
5834 syncItem
= st
.syncItems
[syncItem
];
5835 store
.saveTiddler(tiddler
.title
, tiddler
.title
, tiddler
.text
, tiddler
.modifier
, tiddler
.modified
, tiddler
.tags
, tiddler
.fields
, true, tiddler
.created
);
5836 syncItem
.syncStatus
= config
.macros
.sync
.syncStatusList
.gotFromServer
;
5837 config
.macros
.sync
.updateSyncStatus(syncItem
);
5840 putTiddler: function(tiddler
) {
5841 return this.putTiddler(tiddler
,"onPutTiddler");
5843 onPutTiddler: function(context
) {
5844 var title
= context
.title
;
5845 var syncItem
= st
.syncItems
.findByField("title",title
);
5846 if(syncItem
!== null) {
5847 syncItem
= st
.syncItems
[syncItem
];
5848 store
.resetTiddler(title
);
5849 syncItem
.syncStatus
= config
.macros
.sync
.syncStatusList
.putToServer
;
5850 config
.macros
.sync
.updateSyncStatus(syncItem
);
5854 st
.syncMachine
.go();
5858 config
.macros
.sync
.updateSyncStatus = function(syncItem
)
5860 var e
= syncItem
.colElements
["status"];
5862 createTiddlyText(e
,syncItem
.syncStatus
.text
);
5863 syncItem
.rowElement
.style
.backgroundColor
= syncItem
.syncStatus
.color
;
5866 config
.macros
.sync
.doSync = function(e
)
5868 var rowNames
= ListView
.getSelectedRows(currSync
.listView
);
5869 for(var t
=0; t
<currSync
.syncList
.length
; t
++) {
5870 var si
= currSync
.syncList
[t
];
5871 if(rowNames
.indexOf(si
.title
) != -1) {
5872 config
.macros
.sync
.doSyncItem(si
);
5878 config
.macros
.sync
.doSyncItem = function(syncItem
)
5881 var sl
= config
.macros
.sync
.syncStatusList
;
5882 switch(syncItem
.syncStatus
) {
5883 case sl
.changedServer
:
5884 r
= syncItem
.syncTask
.syncMachine
.go("getTiddler",syncItem
.title
);
5887 case sl
.changedLocally
:
5888 case sl
.changedBoth
:
5889 r
= syncItem
.syncTask
.syncMachine
.go("putTiddler",syncItem
.tiddler
);
5895 displayMessage("Error in doSyncItem: " + r
);
5898 config
.macros
.sync
.cancelSync = function()
5903 function SyncMachine(serverType
,steps
)
5905 this.serverType
= serverType
;
5906 this.adaptor
= new config
.adaptors
[serverType
];
5910 SyncMachine
.prototype.go = function(step
,context
)
5912 var r
= context
? context
.status
: null;
5913 if(typeof r
== "string") {
5914 this.invokeError(r
);
5917 var h
= this.steps
[step
? step
: "start"];
5920 r
= h
.call(this,context
);
5921 if(typeof r
== "string")
5922 this.invokeError(r
);
5926 SyncMachine
.prototype.invokeError = function(message
)
5928 if(this.steps
.error
)
5929 this.steps
.error(message
);
5932 SyncMachine
.prototype.openHost = function(host
,nextStep
)
5935 return me
.adaptor
.openHost(host
,null,null,function(context
) {me
.go(nextStep
,context
);});
5938 SyncMachine
.prototype.getWorkspaceList = function(nextStep
)
5941 return me
.adaptor
.getWorkspaceList(null,null,function(context
) {me
.go(nextStep
,context
);});
5944 SyncMachine
.prototype.openWorkspace = function(workspace
,nextStep
)
5947 return me
.adaptor
.openWorkspace(workspace
,null,null,function(context
) {me
.go(nextStep
,context
);});
5950 SyncMachine
.prototype.getTiddlerList = function(nextStep
)
5953 return me
.adaptor
.getTiddlerList(null,null,function(context
) {me
.go(nextStep
,context
);});
5956 SyncMachine
.prototype.generateTiddlerInfo = function(tiddler
)
5958 return this.adaptor
.generateTiddlerInfo(tiddler
);
5961 SyncMachine
.prototype.getTiddler = function(title
,nextStep
)
5964 return me
.adaptor
.getTiddler(title
,null,null,function(context
) {me
.go(nextStep
,context
);});
5967 SyncMachine
.prototype.putTiddler = function(tiddler
,nextStep
)
5970 return me
.adaptor
.putTiddler(tiddler
,null,null,function(context
) {me
.go(nextStep
,context
);});
5974 //-- Manager UI for groups of tiddlers
5977 config
.macros
.plugins
.handler = function(place
,macroName
,params
,wikifier
,paramString
,tiddler
)
5979 var wizard
= new Wizard();
5980 wizard
.createWizard(place
,this.wizardTitle
);
5981 wizard
.addStep(this.step1Title
,this.step1Html
);
5982 var markList
= wizard
.getElement("markList");
5983 var listWrapper
= document
.createElement("div");
5984 markList
.parentNode
.insertBefore(listWrapper
,markList
);
5985 listWrapper
.setAttribute("refresh","macro");
5986 listWrapper
.setAttribute("macroName","plugins");
5987 listWrapper
.setAttribute("params",paramString
);
5988 this.refresh(listWrapper
,paramString
);
5991 config
.macros
.plugins
.refresh = function(listWrapper
,params
)
5993 var wizard
= new Wizard(listWrapper
);
5994 var selectedRows
= [];
5995 ListView
.forEachSelector(listWrapper
,function(e
,rowName
) {
5997 selectedRows
.push(e
.getAttribute("rowName"));
5999 removeChildren(listWrapper
);
6000 params
= params
.parseParams("anon");
6001 var plugins
= installedPlugins
.slice(0);
6003 var configTiddlers
= store
.getTaggedTiddlers("systemConfig");
6004 for(t
=0; t
<configTiddlers
.length
; t
++) {
6005 tiddler
= configTiddlers
[t
];
6006 if(plugins
.findByField("title",tiddler
.title
) == null) {
6007 p
= getPluginInfo(tiddler
);
6009 p
.log
.splice(0,0,this.skippedText
);
6013 for(t
=0; t
<plugins
.length
; t
++) {
6015 p
.size
= p
.tiddler
.text
? p
.tiddler
.text
.length
: 0;
6016 p
.forced
= p
.tiddler
.isTagged("systemConfigForce");
6017 p
.disabled
= p
.tiddler
.isTagged("systemConfigDisable");
6018 p
.Selected
= selectedRows
.indexOf(plugins
[t
].title
) != -1;
6020 if(plugins
.length
== 0) {
6021 createTiddlyElement(listWrapper
,"em",null,null,this.noPluginText
);
6022 wizard
.setButtons([]);
6024 var listView
= ListView
.create(listWrapper
,plugins
,this.listViewTemplate
,this.onSelectCommand
);
6025 wizard
.setValue("listView",listView
);
6027 {caption
: config
.macros
.plugins
.removeLabel
, tooltip
: config
.macros
.plugins
.removePrompt
, onClick
: config
.macros
.plugins
.doRemoveTag
},
6028 {caption
: config
.macros
.plugins
.deleteLabel
, tooltip
: config
.macros
.plugins
.deletePrompt
, onClick
: config
.macros
.plugins
.doDelete
}
6033 config
.macros
.plugins
.doRemoveTag = function(e
)
6035 var wizard
= new Wizard(this);
6036 var listView
= wizard
.getValue("listView");
6037 var rowNames
= ListView
.getSelectedRows(listView
);
6038 if(rowNames
.length
== 0) {
6039 alert(config
.messages
.nothingSelected
);
6041 for(var t
=0; t
<rowNames
.length
; t
++)
6042 store
.setTiddlerTag(rowNames
[t
],false,"systemConfig");
6046 config
.macros
.plugins
.doDelete = function(e
)
6048 var wizard
= new Wizard(this);
6049 var listView
= wizard
.getValue("listView");
6050 var rowNames
= ListView
.getSelectedRows(listView
);
6051 if(rowNames
.length
== 0) {
6052 alert(config
.messages
.nothingSelected
);
6054 if(confirm(config
.macros
.plugins
.confirmDeleteText
.format([rowNames
.join(", ")]))) {
6055 for(t
=0; t
<rowNames
.length
; t
++) {
6056 store
.removeTiddler(rowNames
[t
]);
6057 story
.closeTiddler(rowNames
[t
],true);
6067 function getMessageDiv()
6069 var msgArea
= document
.getElementById("messageArea");
6072 if(!msgArea
.hasChildNodes())
6073 createTiddlyButton(createTiddlyElement(msgArea
,"div",null,"messageToolbar"),
6074 config
.messages
.messageClose
.text
,
6075 config
.messages
.messageClose
.tooltip
,
6077 msgArea
.style
.display
= "block";
6078 return createTiddlyElement(msgArea
,"div");
6081 function displayMessage(text
,linkText
)
6083 var e
= getMessageDiv();
6089 var link
= createTiddlyElement(e
,"a",null,null,text
);
6090 link
.href
= linkText
;
6091 link
.target
= "_blank";
6093 e
.appendChild(document
.createTextNode(text
));
6097 function clearMessage()
6099 var msgArea
= document
.getElementById("messageArea");
6101 removeChildren(msgArea
);
6102 msgArea
.style
.display
= "none";
6108 //-- Refresh mechanism
6111 config
.refreshers
= {
6112 link: function(e
,changeList
)
6114 var title
= e
.getAttribute("tiddlyLink");
6115 refreshTiddlyLink(e
,title
);
6119 tiddler: function(e
,changeList
)
6121 var title
= e
.getAttribute("tiddler");
6122 var template
= e
.getAttribute("template");
6123 if(changeList
&& changeList
.indexOf(title
) != -1 && !story
.isDirty(title
))
6124 story
.refreshTiddler(title
,template
,true);
6126 refreshElements(e
,changeList
);
6130 content: function(e
,changeList
)
6132 var title
= e
.getAttribute("tiddler");
6133 var force
= e
.getAttribute("force");
6134 if(force
!= null || changeList
== null || changeList
.indexOf(title
) != -1) {
6136 wikify(store
.getTiddlerText(title
,title
),e
,null);
6142 macro: function(e
,changeList
)
6144 var macro
= e
.getAttribute("macroName");
6145 var params
= e
.getAttribute("params");
6147 macro
= config
.macros
[macro
];
6148 if(macro
&& macro
.refresh
)
6149 macro
.refresh(e
,params
);
6152 styleSheet
: "StyleSheet",
6153 defaultStyleSheet
: "StyleSheet",
6154 pageTemplate
: "PageTemplate",
6155 defaultPageTemplate
: "PageTemplate",
6156 colorPalette
: "ColorPalette",
6157 defaultColorPalette
: "ColorPalette"
6160 function refreshElements(root
,changeList
)
6162 var nodes
= root
.childNodes
;
6163 for(var c
=0; c
<nodes
.length
; c
++) {
6164 var e
= nodes
[c
], type
= null;
6165 if(e
.getAttribute
&& (e
.tagName
? e
.tagName
!= "IFRAME" : true))
6166 type
= e
.getAttribute("refresh");
6167 var refresher
= config
.refreshers
[type
];
6168 var refreshed
= false;
6169 if(refresher
!= undefined)
6170 refreshed
= refresher(e
,changeList
);
6171 if(e
.hasChildNodes() && !refreshed
)
6172 refreshElements(e
,changeList
);
6176 function applyHtmlMacros(root
,tiddler
)
6178 var e
= root
.firstChild
;
6180 var nextChild
= e
.nextSibling
;
6181 if(e
.getAttribute
) {
6182 var macro
= e
.getAttribute("macro");
6185 var p
= macro
.indexOf(" ");
6187 params
= macro
.substr(p
+1);
6188 macro
= macro
.substr(0,p
);
6190 invokeMacro(e
,macro
,params
,null,tiddler
);
6193 if(e
.hasChildNodes())
6194 applyHtmlMacros(e
,tiddler
);
6199 function refreshPageTemplate(title
)
6201 var stash
= createTiddlyElement(document
.body
,"div");
6202 stash
.style
.display
= "none";
6203 var display
= document
.getElementById("tiddlerDisplay");
6206 nodes
= display
.childNodes
;
6207 for(t
=nodes
.length
-1; t
>=0; t
--)
6208 stash
.appendChild(nodes
[t
]);
6210 var wrapper
= document
.getElementById("contentWrapper");
6212 isAvailable = function(title
) {
6213 var s
= title
? title
.indexOf(config
.textPrimitives
.sectionSeparator
) : -1;
6215 title
= title
.substr(0,s
);
6216 return store
.tiddlerExists(title
) || store
.isShadowTiddler(title
);
6218 if(!title
|| !isAvailable(title
))
6219 title
= config
.refreshers
.pageTemplate
;
6220 if(!isAvailable(title
))
6221 title
= config
.refreshers
.defaultPageTemplate
; //# this one is always avaialable
6222 html
= store
.getRecursiveTiddlerText(title
,null,10);
6223 wrapper
.innerHTML
= html
;
6224 applyHtmlMacros(wrapper
);
6225 refreshElements(wrapper
);
6226 display
= document
.getElementById("tiddlerDisplay");
6227 removeChildren(display
);
6229 display
= createTiddlyElement(wrapper
,"div","tiddlerDisplay");
6230 nodes
= stash
.childNodes
;
6231 for(t
=nodes
.length
-1; t
>=0; t
--)
6232 display
.appendChild(nodes
[t
]);
6236 function refreshDisplay(hint
)
6238 if(typeof hint
== "string")
6240 var e
= document
.getElementById("contentWrapper");
6241 refreshElements(e
,hint
);
6242 if(backstage
.isPanelVisible()) {
6243 e
= document
.getElementById("backstage");
6244 refreshElements(e
,hint
);
6248 function refreshPageTitle()
6250 document
.title
= getPageTitle();
6253 function getPageTitle()
6255 var st
= wikifyPlain("SiteTitle");
6256 var ss
= wikifyPlain("SiteSubtitle");
6257 return st
+ ((st
== "" || ss
== "") ? "" : " - ") + ss
;
6260 function refreshStyles(title
,doc
)
6262 setStylesheet(title
== null ? "" : store
.getRecursiveTiddlerText(title
,"",10),title
,doc
? doc
: document
);
6265 function refreshColorPalette(title
)
6271 function refreshAll()
6273 refreshPageTemplate();
6275 refreshStyles("StyleSheetLayout");
6276 refreshStyles("StyleSheetColors");
6277 refreshStyles(config
.refreshers
.styleSheet
);
6278 refreshStyles("StyleSheetPrint");
6285 config
.optionHandlers
= {
6287 get: function(name
) {return encodeCookie(config
.options
[name
].toString());},
6288 set: function(name
,value
) {config
.options
[name
] = decodeCookie(value
);}
6291 get: function(name
) {return config
.options
[name
] ? "true" : "false";},
6292 set: function(name
,value
) {config
.options
[name
] = value
== "true";}
6296 function loadOptionsCookie()
6300 var cookies
= document
.cookie
.split(";");
6301 for(var c
=0; c
<cookies
.length
; c
++) {
6302 var p
= cookies
[c
].indexOf("=");
6304 var name
= cookies
[c
].substr(0,p
).trim();
6305 var value
= cookies
[c
].substr(p
+1).trim();
6306 var optType
= name
.substr(0,3);
6307 if(config
.optionHandlers
[optType
] && config
.optionHandlers
[optType
].set)
6308 config
.optionHandlers
[optType
].set(name
,value
);
6313 function saveOptionCookie(name
)
6318 var optType
= name
.substr(0,3);
6319 if(config
.optionHandlers
[optType
] && config
.optionHandlers
[optType
].get)
6320 c
+= config
.optionHandlers
[optType
].get(name
);
6321 c
+= "; expires=Fri, 1 Jan 2038 12:00:00 UTC; path=/";
6322 document
.cookie
= c
;
6325 function encodeCookie(s
)
6327 return escape(manualConvertUnicodeToUTF8(s
));
6330 function decodeCookie(s
)
6333 var re
= /&#[0-9]{1,5};/g;
6334 return s
.replace(re
,function($0) {return String
.fromCharCode(eval($0.replace(/[&#;]/g,"")));});
6338 config
.macros
.option
.genericCreate = function(place
,type
,opt
,className
,desc
)
6340 var typeInfo
= config
.macros
.option
.types
[type
];
6341 var c
= document
.createElement(typeInfo
.elementType
);
6342 if(typeInfo
.typeValue
)
6343 c
.setAttribute("type",typeInfo
.typeValue
);
6344 c
[typeInfo
.eventName
] = typeInfo
.onChange
;
6345 c
.setAttribute("option",opt
);
6347 c
.className
= className
;
6349 c
.className
= typeInfo
.className
;
6350 if(config
.optionsDesc
[opt
])
6351 c
.setAttribute("title",config
.optionsDesc
[opt
]);
6352 place
.appendChild(c
);
6354 createTiddlyText(place
,config
.optionsDesc
[opt
] ? config
.optionsDesc
[opt
] : opt
);
6355 c
[typeInfo
.valueField
] = config
.options
[opt
];
6359 config
.macros
.option
.genericOnChange = function(e
)
6361 var opt
= this.getAttribute("option");
6363 var optType
= opt
.substr(0,3);
6364 var handler
= config
.macros
.option
.types
[optType
];
6365 if (handler
.elementType
&& handler
.valueField
)
6366 config
.macros
.option
.propagateOption(opt
,handler
.valueField
,this[handler
.valueField
],handler
.elementType
);
6371 config
.macros
.option
.types
= {
6373 elementType
: "input",
6374 valueField
: "value",
6375 eventName
: "onkeyup",
6376 className
: "txtOptionInput",
6377 create
: config
.macros
.option
.genericCreate
,
6378 onChange
: config
.macros
.option
.genericOnChange
6381 elementType
: "input",
6382 valueField
: "checked",
6383 eventName
: "onclick",
6384 className
: "chkOptionInput",
6385 typeValue
: "checkbox",
6386 create
: config
.macros
.option
.genericCreate
,
6387 onChange
: config
.macros
.option
.genericOnChange
6391 config
.macros
.option
.propagateOption = function(opt
,valueField
,value
,elementType
)
6393 config
.options
[opt
] = value
;
6394 saveOptionCookie(opt
);
6395 var nodes
= document
.getElementsByTagName(elementType
);
6396 for(var t
=0; t
<nodes
.length
; t
++) {
6397 var optNode
= nodes
[t
].getAttribute("option");
6399 nodes
[t
][valueField
] = value
;
6403 config
.macros
.option
.handler = function(place
,macroName
,params
,wikifier
,paramString
,tiddler
)
6405 params
= paramString
.parseParams("anon",null,true,false,false);
6406 var opt
= (params
[1] && params
[1].name
== "anon") ? params
[1].value
: getParam(params
,"name",null);
6407 var className
= (params
[2] && params
[2].name
== "anon") ? params
[2].value
: getParam(params
,"class",null);
6408 var desc
= getParam(params
,"desc","no");
6409 var type
= opt
.substr(0,3);
6410 var h
= config
.macros
.option
.types
[type
];
6412 h
.create(place
,type
,opt
,className
,desc
);
6415 config
.macros
.options
.handler = function(place
,macroName
,params
,wikifier
,paramString
,tiddler
)
6417 params
= paramString
.parseParams("anon",null,true,false,false);
6418 var showUnknown
= getParam(params
,"showUnknown","no");
6419 var wizard
= new Wizard();
6420 wizard
.createWizard(place
,this.wizardTitle
);
6421 wizard
.addStep(this.step1Title
,this.step1Html
);
6422 var markList
= wizard
.getElement("markList");
6423 var chkUnknown
= wizard
.getElement("chkUnknown");
6424 chkUnknown
.checked
= showUnknown
== "yes";
6425 chkUnknown
.onchange
= this.onChangeUnknown
;
6426 var listWrapper
= document
.createElement("div");
6427 markList
.parentNode
.insertBefore(listWrapper
,markList
);
6428 wizard
.setValue("listWrapper",listWrapper
);
6429 this.refreshOptions(listWrapper
,showUnknown
== "yes");
6432 config
.macros
.options
.refreshOptions = function(listWrapper
,showUnknown
)
6435 for(var n
in config
.options
) {
6439 opt
.lowlight
= !config
.optionsDesc
[n
];
6440 opt
.description
= opt
.lowlight
? this.unknownDescription
: config
.optionsDesc
[n
];
6441 if(!opt
.lowlight
|| showUnknown
)
6444 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);});
6445 var listview
= ListView
.create(listWrapper
,opts
,this.listViewTemplate
);
6446 for(n
=0; n
<opts
.length
; n
++) {
6447 var type
= opts
[n
].name
.substr(0,3);
6448 var h
= config
.macros
.option
.types
[type
];
6449 if (h
&& h
.create
) {
6450 h
.create(opts
[n
].colElements
['option'],type
,opts
[n
].name
,null,"no");
6455 config
.macros
.options
.onChangeUnknown = function(e
)
6457 var wizard
= new Wizard(this);
6458 var listWrapper
= wizard
.getValue("listWrapper");
6459 removeChildren(listWrapper
);
6460 config
.macros
.options
.refreshOptions(listWrapper
,this.checked
);
6468 var saveUsingSafari
= false;
6470 var startSaveArea
= '<div id="' + 'storeArea">'; // Split up into two so that indexOf() of this source doesn't find it
6471 var endSaveArea
= '</d' + 'iv>';
6473 // If there are unsaved changes, force the user to confirm before exitting
6474 function confirmExit()
6476 hadConfirmExit
= true;
6477 if((store
&& store
.isDirty
&& store
.isDirty()) || (story
&& story
.areAnyDirty
&& story
.areAnyDirty()))
6478 return config
.messages
.confirmExit
;
6481 // Give the user a chance to save changes before exitting
6482 function checkUnsavedChanges()
6484 if(store
&& store
.isDirty
&& store
.isDirty() && window
.hadConfirmExit
=== false) {
6485 if(confirm(config
.messages
.unsavedChangesWarning
))
6490 function updateLanguageAttribute(s
)
6493 var mRE
= /(<html(?:.*?)?)(?: xml:lang\="([a-z]+)")?(?: lang\="([a-z]+)")?>/;
6494 var m
= mRE
.exec(s
);
6498 t
+= ' xml:lang="' + config
.locale
+ '"';
6500 t
+= ' lang="' + config
.locale
+ '"';
6502 s
= s
.substr(0,m
.index
) + t
+ s
.substr(m
.index
+m
[0].length
);
6508 function updateMarkupBlock(s
,blockName
,tiddlerName
)
6510 return s
.replaceChunk(
6511 "<!--%0-START-->".format([blockName
]),
6512 "<!--%0-END-->".format([blockName
]),
6513 "\n" + store
.getRecursiveTiddlerText(tiddlerName
,"") + "\n");
6516 function updateOriginal(original
,posDiv
)
6519 posDiv
= locateStoreArea(original
);
6521 alert(config
.messages
.invalidFileError
.format([localPath
]));
6524 var revised
= original
.substr(0,posDiv
[0] + startSaveArea
.length
) + "\n" +
6525 convertUnicodeToUTF8(store
.allTiddlersAsHtml()) + "\n" +
6526 original
.substr(posDiv
[1]);
6527 var newSiteTitle
= convertUnicodeToUTF8(getPageTitle()).htmlEncode();
6528 revised
= revised
.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle
+ " ");
6529 revised
= updateLanguageAttribute(revised
);
6530 revised
= updateMarkupBlock(revised
,"PRE-HEAD","MarkupPreHead");
6531 revised
= updateMarkupBlock(revised
,"POST-HEAD","MarkupPostHead");
6532 revised
= updateMarkupBlock(revised
,"PRE-BODY","MarkupPreBody");
6533 revised
= updateMarkupBlock(revised
,"POST-SCRIPT","MarkupPostBody");
6537 function locateStoreArea(original
)
6539 // Locate the storeArea div's
6540 var posOpeningDiv
= original
.indexOf(startSaveArea
);
6541 var limitClosingDiv
= original
.indexOf("<"+"!--POST-STOREAREA--"+">");
6542 if(limitClosingDiv
== -1)
6543 limitClosingDiv
= original
.indexOf("<"+"!--POST-BODY-START--"+">");
6544 var posClosingDiv
= original
.lastIndexOf(endSaveArea
,limitClosingDiv
== -1 ? original
.length
: limitClosingDiv
);
6545 return (posOpeningDiv
!= -1 && posClosingDiv
!= -1) ? [posOpeningDiv
,posClosingDiv
] : null;
6548 function autoSaveChanges(onlyIfDirty
,tiddlers
)
6550 if(config
.options
.chkAutoSave
)
6551 saveChanges(onlyIfDirty
,tiddlers
);
6554 // Save this tiddlywiki with the pending changes
6555 function saveChanges(onlyIfDirty
,tiddlers
)
6557 if(onlyIfDirty
&& !store
.isDirty())
6560 // Get the URL of the document
6561 var originalPath
= document
.location
.toString();
6562 // Check we were loaded from a file URL
6563 if(originalPath
.substr(0,5) != "file:") {
6564 alert(config
.messages
.notFileUrlError
);
6565 if(store
.tiddlerExists(config
.messages
.saveInstructions
))
6566 story
.displayTiddler(null,config
.messages
.saveInstructions
);
6569 var localPath
= getLocalPath(originalPath
);
6570 // Load the original file
6571 var original
= loadFile(localPath
);
6572 if(original
== null) {
6573 alert(config
.messages
.cantSaveError
);
6574 if(store
.tiddlerExists(config
.messages
.saveInstructions
))
6575 story
.displayTiddler(null,config
.messages
.saveInstructions
);
6578 // Locate the storeArea div's
6579 var posDiv
= locateStoreArea(original
);
6581 alert(config
.messages
.invalidFileError
.format([localPath
]));
6584 saveBackup(localPath
,original
);
6586 saveEmpty(localPath
,original
,posDiv
);
6587 saveMain(localPath
,original
,posDiv
);
6590 function saveBackup(localPath
,original
)
6592 if(config
.options
.chkSaveBackups
) {
6593 var backupPath
= getBackupPath(localPath
);
6594 var backup
= config
.browser
.isIE
? ieCopyFile(backupPath
,localPath
) : saveFile(backupPath
,original
);
6596 displayMessage(config
.messages
.backupSaved
,"file://" + backupPath
);
6598 alert(config
.messages
.backupFailed
);
6602 function saveRss(localPath
)
6604 if(config
.options
.chkGenerateAnRssFeed
) {
6605 var rssPath
= localPath
.substr(0,localPath
.lastIndexOf(".")) + ".xml";
6606 var rssSave
= saveFile(rssPath
,convertUnicodeToUTF8(generateRss()));
6608 displayMessage(config
.messages
.rssSaved
,"file://" + rssPath
);
6610 alert(config
.messages
.rssFailed
);
6614 function saveEmpty(localPath
,original
,posDiv
)
6616 if(config
.options
.chkSaveEmptyTemplate
) {
6618 if((p
= localPath
.lastIndexOf("/")) != -1)
6619 emptyPath
= localPath
.substr(0,p
) + "/empty.html";
6620 else if((p
= localPath
.lastIndexOf("\\")) != -1)
6621 emptyPath
= localPath
.substr(0,p
) + "\\empty.html";
6623 emptyPath
= localPath
+ ".empty.html";
6624 var empty
= original
.substr(0,posDiv
[0] + startSaveArea
.length
) + original
.substr(posDiv
[1]);
6625 var emptySave
= saveFile(emptyPath
,empty
);
6627 displayMessage(config
.messages
.emptySaved
,"file://" + emptyPath
);
6629 alert(config
.messages
.emptyFailed
);
6633 function saveMain(localPath
,original
,posDiv
)
6637 var revised
= updateOriginal(original
,posDiv
);
6638 save
= saveFile(localPath
,revised
);
6643 displayMessage(config
.messages
.mainSaved
,"file://" + localPath
);
6644 store
.setDirty(false);
6646 alert(config
.messages
.mainFailed
);
6650 function getLocalPath(origPath
)
6652 var originalPath
= convertUriToUTF8(origPath
,config
.options
.txtFileSystemCharSet
);
6653 // Remove any location or query part of the URL
6654 var argPos
= originalPath
.indexOf("?");
6656 originalPath
= originalPath
.substr(0,argPos
);
6657 var hashPos
= originalPath
.indexOf("#");
6659 originalPath
= originalPath
.substr(0,hashPos
);
6660 // Convert file://localhost/ to file:///
6661 if(originalPath
.indexOf("file://localhost/") == 0)
6662 originalPath
= "file://" + originalPath
.substr(16);
6663 // Convert to a native file format
6665 if(originalPath
.charAt(9) == ":") // pc local file
6666 localPath
= unescape(originalPath
.substr(8)).replace(new RegExp("/","g"),"\\");
6667 else if(originalPath
.indexOf("file://///") == 0) // FireFox pc network file
6668 localPath
= "\\\\" + unescape(originalPath
.substr(10)).replace(new RegExp("/","g"),"\\");
6669 else if(originalPath
.indexOf("file:///") == 0) // mac/unix local file
6670 localPath
= unescape(originalPath
.substr(7));
6671 else if(originalPath
.indexOf("file:/") == 0) // mac/unix local file
6672 localPath
= unescape(originalPath
.substr(5));
6673 else // pc network file
6674 localPath
= "\\\\" + unescape(originalPath
.substr(7)).replace(new RegExp("/","g"),"\\");
6678 function getBackupPath(localPath
,title
,extension
)
6681 var dirPathPos
= localPath
.lastIndexOf("\\");
6682 if(dirPathPos
== -1) {
6683 dirPathPos
= localPath
.lastIndexOf("/");
6686 var backupFolder
= config
.options
.txtBackupFolder
;
6687 if(!backupFolder
|| backupFolder
== "")
6689 var backupPath
= localPath
.substr(0,dirPathPos
) + slash
+ backupFolder
+ localPath
.substr(dirPathPos
);
6690 backupPath
= backupPath
.substr(0,backupPath
.lastIndexOf(".")) + ".";
6692 backupPath
+= title
.replace(/[\\\/\*\?\":<> ]/g,"_") + ".";
6693 backupPath
+= (new Date()).convertToYYYYMMDDHHMMSSMMM() + "." + (extension
? extension
: "html");
6697 function generateRss()
6701 var u
= store
.getTiddlerText("SiteUrl");
6702 // Assemble the header
6703 s
.push("<" + "?xml version=\"1.0\"?" + ">");
6704 s
.push("<rss version=\"2.0\">");
6705 s
.push("<channel>");
6706 s
.push("<title" + ">" + wikifyPlain("SiteTitle").htmlEncode() + "</title" + ">");
6708 s
.push("<link>" + u
.htmlEncode() + "</link>");
6709 s
.push("<description>" + wikifyPlain("SiteSubtitle").htmlEncode() + "</description>");
6710 s
.push("<language>en-us</language>");
6711 s
.push("<copyright>Copyright " + d
.getFullYear() + " " + config
.options
.txtUserName
.htmlEncode() + "</copyright>");
6712 s
.push("<pubDate>" + d
.toGMTString() + "</pubDate>");
6713 s
.push("<lastBuildDate>" + d
.toGMTString() + "</lastBuildDate>");
6714 s
.push("<docs>http://blogs.law.harvard.edu/tech/rss</docs>");
6715 s
.push("<generator>TiddlyWiki " + version
.major
+ "." + version
.minor
+ "." + version
.revision
+ "</generator>");
6717 var tiddlers
= store
.getTiddlers("modified","excludeLists");
6718 var n
= config
.numRssItems
> tiddlers
.length
? 0 : tiddlers
.length
-config
.numRssItems
;
6719 for (var t
=tiddlers
.length
-1; t
>=n
; t
--) {
6720 s
.push("<item>\n" + tiddlers
[t
].toRssItem(u
) + "\n</item>");
6723 s
.push("</channel>");
6726 return s
.join("\n");
6730 //-- Filesystem code
6733 function convertUTF8ToUnicode(u
)
6735 return window
.netscape
== undefined ? manualConvertUTF8ToUnicode(u
) : mozConvertUTF8ToUnicode(u
);
6738 function manualConvertUTF8ToUnicode(utf
)
6745 while(src
< utf
.length
) {
6746 b1
= utf
.charCodeAt(src
++);
6749 } else if(b1
< 0xE0) {
6750 b2
= utf
.charCodeAt(src
++);
6751 c
= String
.fromCharCode(((b1
& 0x1F) << 6) | (b2
& 0x3F));
6752 uni
= uni
.substring(0,dst
++).concat(c
,utf
.substr(src
));
6754 b2
= utf
.charCodeAt(src
++);
6755 b3
= utf
.charCodeAt(src
++);
6756 c
= String
.fromCharCode(((b1
& 0xF) << 12) | ((b2
& 0x3F) << 6) | (b3
& 0x3F));
6757 uni
= uni
.substring(0,dst
++).concat(c
,utf
.substr(src
));
6763 function mozConvertUTF8ToUnicode(u
)
6766 netscape
.security
.PrivilegeManager
.enablePrivilege("UniversalXPConnect");
6767 var converter
= Components
.classes
["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components
.interfaces
.nsIScriptableUnicodeConverter
);
6768 converter
.charset
= "UTF-8";
6770 return manualConvertUTF8ToUnicode(u
);
6772 var s
= converter
.ConvertToUnicode(u
);
6773 var fin
= converter
.Finish();
6774 return (fin
.length
> 0) ? s
+fin
: s
;
6777 function convertUnicodeToUTF8(s
)
6779 if(window
.netscape
== undefined)
6780 return manualConvertUnicodeToUTF8(s
);
6782 return mozConvertUnicodeToUTF8(s
);
6785 function manualConvertUnicodeToUTF8(s
)
6787 var re
= /[^\u0000-\u007F]/g ;
6788 return s
.replace(re
,function($0) {return "&#" + $0.charCodeAt(0).toString() + ";";});
6791 function mozConvertUnicodeToUTF8(s
)
6794 netscape
.security
.PrivilegeManager
.enablePrivilege("UniversalXPConnect");
6795 var converter
= Components
.classes
["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components
.interfaces
.nsIScriptableUnicodeConverter
);
6796 converter
.charset
= "UTF-8";
6798 return manualConvertUnicodeToUTF8(s
);
6800 var u
= converter
.ConvertFromUnicode(s
);
6801 var fin
= converter
.Finish();
6802 return fin
.length
> 0 ? u
+ fin
: u
;
6805 function convertUriToUTF8(uri
,charSet
)
6807 if(window
.netscape
== undefined || charSet
== undefined || charSet
== "")
6810 netscape
.security
.PrivilegeManager
.enablePrivilege("UniversalXPConnect");
6811 var converter
= Components
.classes
["@mozilla.org/intl/utf8converterservice;1"].getService(Components
.interfaces
.nsIUTF8ConverterService
);
6815 return converter
.convertURISpecToUTF8(uri
,charSet
);
6818 function saveFile(fileUrl
,content
)
6820 var r
= mozillaSaveFile(fileUrl
,content
);
6822 r
= ieSaveFile(fileUrl
,content
);
6824 r
= javaSaveFile(fileUrl
,content
);
6828 function loadFile(fileUrl
)
6830 var r
= mozillaLoadFile(fileUrl
);
6831 if((r
== null) || (r
== false))
6832 r
= ieLoadFile(fileUrl
);
6833 if((r
== null) || (r
== false))
6834 r
= javaLoadFile(fileUrl
);
6838 function ieCreatePath(path
)
6841 var fso
= new ActiveXObject("Scripting.FileSystemObject");
6846 var pos
= path
.lastIndexOf("\\");
6848 path
= path
.substring(0, pos
+1);
6854 var parent
= fso
.GetParentFolderName(scan
[i
++]);
6855 if (fso
.FolderExists(parent
))
6860 for(i
=scan
.length
-1;i
>=0;i
--) {
6861 if (!fso
.FolderExists(scan
[i
]))
6862 fso
.CreateFolder(scan
[i
]);
6867 // Returns null if it can't do it, false if there's an error, true if it saved OK
6868 function ieSaveFile(filePath
,content
)
6870 ieCreatePath(filePath
);
6872 var fso
= new ActiveXObject("Scripting.FileSystemObject");
6876 var file
= fso
.OpenTextFile(filePath
,2,-1,0);
6877 file
.Write(content
);
6882 // Returns null if it can't do it, false if there's an error, or a string of the content if successful
6883 function ieLoadFile(filePath
)
6886 var fso
= new ActiveXObject("Scripting.FileSystemObject");
6887 var file
= fso
.OpenTextFile(filePath
,1);
6888 var content
= file
.ReadAll();
6896 function ieCopyFile(dest
,source
)
6900 var fso
= new ActiveXObject("Scripting.FileSystemObject");
6901 fso
.GetFile(source
).Copy(dest
);
6908 // Returns null if it can't do it, false if there's an error, true if it saved OK
6909 function mozillaSaveFile(filePath
,content
)
6911 if(window
.Components
) {
6913 netscape
.security
.PrivilegeManager
.enablePrivilege("UniversalXPConnect");
6914 var file
= Components
.classes
["@mozilla.org/file/local;1"].createInstance(Components
.interfaces
.nsILocalFile
);
6915 file
.initWithPath(filePath
);
6917 file
.create(0,0664);
6918 var out
= Components
.classes
["@mozilla.org/network/file-output-stream;1"].createInstance(Components
.interfaces
.nsIFileOutputStream
);
6919 out
.init(file
,0x20|0x02,00004,null);
6920 out
.write(content
,content
.length
);
6931 // Returns null if it can't do it, false if there's an error, or a string of the content if successful
6932 function mozillaLoadFile(filePath
)
6934 if(window
.Components
) {
6936 netscape
.security
.PrivilegeManager
.enablePrivilege("UniversalXPConnect");
6937 var file
= Components
.classes
["@mozilla.org/file/local;1"].createInstance(Components
.interfaces
.nsILocalFile
);
6938 file
.initWithPath(filePath
);
6941 var inputStream
= Components
.classes
["@mozilla.org/network/file-input-stream;1"].createInstance(Components
.interfaces
.nsIFileInputStream
);
6942 inputStream
.init(file
,0x01,00004,null);
6943 var sInputStream
= Components
.classes
["@mozilla.org/scriptableinputstream;1"].createInstance(Components
.interfaces
.nsIScriptableInputStream
);
6944 sInputStream
.init(inputStream
);
6945 return sInputStream
.read(sInputStream
.available());
6953 function javaUrlToFilename(url
)
6955 var f
= "//localhost";
6956 if(url
.indexOf(f
) == 0)
6957 return url
.substring(f
.length
);
6958 var i
= url
.indexOf(":");
6960 return url
.substring(i
-1);
6964 function javaSaveFile(filePath
,content
)
6967 if(document
.applets
["TiddlySaver"])
6968 return document
.applets
["TiddlySaver"].saveFile(javaUrlToFilename(filePath
),"UTF-8",content
);
6972 var s
= new java
.io
.PrintStream(new java
.io
.FileOutputStream(javaUrlToFilename(filePath
)));
6981 function javaLoadFile(filePath
)
6984 if(document
.applets
["TiddlySaver"])
6985 return String(document
.applets
["TiddlySaver"].loadFile(javaUrlToFilename(filePath
),"UTF-8"));
6990 var r
= new java
.io
.BufferedReader(new java
.io
.FileReader(javaUrlToFilename(filePath
)));
6992 while((line
= r
.readLine()) != null)
6993 content
.push(new String(line
));
6998 return content
.join("\n");
7002 //-- Server adaptor for talking to static TiddlyWiki files
7005 function FileAdaptor()
7012 FileAdaptor
.serverType
= 'file';
7014 FileAdaptor
.prototype.setContext = function(context
,userParams
,callback
)
7016 if(!context
) context
= {};
7017 context
.userParams
= userParams
;
7018 if(callback
) context
.callback
= callback
;
7019 context
.adaptor
= this;
7021 context
.host
= this.host
;
7022 context
.host
= FileAdaptor
.fullHostName(context
.host
);
7023 if(!context
.workspace
)
7024 context
.workspace
= this.workspace
;
7028 FileAdaptor
.fullHostName = function(host
)
7032 if(!host
.match(/:\/\//))
7033 host
= 'http://' + host
;
7037 FileAdaptor
.minHostName = function(host
)
7039 return host
? host
.replace(/^http:\/\//,'').replace(/\/$/,'') : '';
7042 // Open the specified host
7043 FileAdaptor
.prototype.openHost = function(host
,context
,userParams
,callback
)
7046 context
= this.setContext(context
,userParams
,callback
);
7047 context
.status
= true;
7049 window
.setTimeout(function() {callback(context
,userParams
);},10);
7053 FileAdaptor
.loadTiddlyWikiCallback = function(status
,context
,responseText
,url
,xhr
)
7055 context
.status
= status
;
7057 context
.statusText
= "Error reading file: " + xhr
.statusText
;
7059 context
.adaptor
.store
= new TiddlyWiki();
7060 if(!context
.adaptor
.store
.importTiddlyWiki(responseText
))
7061 context
.statusText
= config
.messages
.invalidFileError
.format([url
]);
7063 context
.complete(context
,context
.userParams
);
7066 // Get the list of workspaces on a given server
7067 FileAdaptor
.prototype.getWorkspaceList = function(context
,userParams
,callback
)
7069 context
= this.setContext(context
,userParams
,callback
);
7070 context
.workspaces
= [{title
:"(default)"}];
7071 context
.status
= true;
7073 window
.setTimeout(function() {callback(context
,userParams
);},10);
7077 // Open the specified workspace
7078 FileAdaptor
.prototype.openWorkspace = function(workspace
,context
,userParams
,callback
)
7080 this.workspace
= workspace
;
7081 context
= this.setContext(context
,userParams
,callback
);
7082 context
.status
= true;
7084 window
.setTimeout(function() {callback(context
,userParams
);},10);
7088 // Gets the list of tiddlers within a given workspace
7089 FileAdaptor
.prototype.getTiddlerList = function(context
,userParams
,callback
,filter
)
7091 context
= this.setContext(context
,userParams
,callback
);
7093 context
.filter
= filter
;
7094 context
.complete
= FileAdaptor
.getTiddlerListComplete
;
7096 var ret
= context
.complete(context
,context
.userParams
);
7098 ret
= loadRemoteFile(context
.host
,FileAdaptor
.loadTiddlyWikiCallback
,context
);
7099 if(typeof ret
!= "string")
7105 FileAdaptor
.getTiddlerListComplete = function(context
,userParams
)
7107 if(context
.filter
) {
7108 context
.tiddlers
= context
.adaptor
.store
.filterTiddlers(context
.filter
);
7110 context
.tiddlers
= [];
7111 context
.adaptor
.store
.forEachTiddler(function(title
,tiddler
) {context
.tiddlers
.push(tiddler
);});
7113 for(var i
=0; i
<context
.tiddlers
.length
; i
++) {
7114 context
.tiddlers
[i
].fields
['server.type'] = FileAdaptor
.serverType
;
7115 context
.tiddlers
[i
].fields
['server.host'] = FileAdaptor
.minHostName(context
.host
);
7116 context
.tiddlers
[i
].fields
['server.page.revision'] = context
.tiddlers
[i
].modified
.convertToYYYYMMDDHHMM();
7118 context
.status
= true;
7119 if(context
.callback
) {
7120 window
.setTimeout(function() {context
.callback(context
,userParams
);},10);
7125 FileAdaptor
.prototype.generateTiddlerInfo = function(tiddler
)
7128 info
.uri
= tiddler
.fields
['server.host'] + "#" + tiddler
.title
;
7132 // Retrieve a tiddler from a given workspace on a given server
7133 FileAdaptor
.prototype.getTiddler = function(title
,context
,userParams
,callback
)
7135 context
= this.setContext(context
,userParams
,callback
);
7136 context
.title
= title
;
7137 context
.complete
= FileAdaptor
.getTiddlerComplete
;
7138 return context
.adaptor
.store
?
7139 context
.complete(context
,context
.userParams
) :
7140 loadRemoteFile(context
.host
,FileAdaptor
.loadTiddlyWikiCallback
,context
);
7143 FileAdaptor
.getTiddlerComplete = function(context
,userParams
)
7145 var t
= context
.adaptor
.store
.fetchTiddler(context
.title
);
7146 t
.fields
['server.type'] = FileAdaptor
.serverType
;
7147 t
.fields
['server.host'] = FileAdaptor
.minHostName(context
.host
);
7148 t
.fields
['server.page.revision'] = t
.modified
.convertToYYYYMMDDHHMM();
7149 context
.tiddler
= t
;
7150 context
.status
= true;
7151 if(context
.allowSynchronous
) {
7152 context
.isSynchronous
= true;
7153 context
.callback(context
,userParams
);
7155 window
.setTimeout(function() {callback(context
,userParams
);},10);
7160 FileAdaptor
.prototype.close = function()
7166 config
.adaptors
[FileAdaptor
.serverType
] = FileAdaptor
;
7169 //-- Remote HTTP requests
7172 function loadRemoteFile(url
,callback
,params
)
7174 return doHttp("GET",url
,null,null,null,null,callback
,params
,null);
7177 // HTTP status codes
7180 ContentCreated
: 201,
7186 MethodNotAllowed
: 405
7189 function doHttp(type
,url
,data
,contentType
,username
,password
,callback
,params
,headers
)
7191 var x
= getXMLHttpRequest();
7193 return "Can't create XMLHttpRequest object";
7194 x
.onreadystatechange = function() {
7196 var status
= x
.status
;
7200 if (x
.readyState
== 4 && callback
&& (status
!== undefined)) {
7201 if([0, httpStatus
.OK
, httpStatus
.ContentCreated
, httpStatus
.NoContent
, httpStatus
.MultiStatus
].contains(status
))
7202 callback(true,params
,x
.responseText
,url
,x
);
7204 callback(false,params
,null,url
,x
);
7205 x
.onreadystatechange = function(){};
7209 if(window
.Components
&& window
.netscape
&& window
.netscape
.security
&& document
.location
.protocol
.indexOf("http") == -1)
7210 window
.netscape
.security
.PrivilegeManager
.enablePrivilege("UniversalBrowserRead");
7212 url
= url
+ (url
.indexOf("?") < 0 ? "?" : "&") + "nocache=" + Math
.random();
7213 x
.open(type
,url
,true,username
,password
);
7215 x
.setRequestHeader("Content-Type", contentType
? contentType
: "application/x-www-form-urlencoded");
7216 if(x
.overrideMimeType
)
7217 x
.setRequestHeader("Connection", "close");
7219 for(var n
in headers
)
7220 x
.setRequestHeader(n
,headers
[n
]);
7222 x
.setRequestHeader("X-Requested-With", "TiddlyWiki " + version
.major
+ "." + version
.minor
+ "." + version
.revision
+ (version
.beta
? " (beta " + version
.beta
+ ")" : ""));
7225 return exceptionText(ex
);
7230 function getXMLHttpRequest()
7233 var x
= new XMLHttpRequest(); // Modern
7236 x
= new ActiveXObject("Msxml2.XMLHTTP"); // IE 6
7245 //-- TiddlyWiki-specific utility functions
7248 function createTiddlyButton(parent
,text
,tooltip
,action
,className
,id
,accessKey
,attribs
)
7250 var btn
= document
.createElement("a");
7252 btn
.onclick
= action
;
7253 btn
.setAttribute("href","javascript:;");
7256 btn
.setAttribute("title",tooltip
);
7258 btn
.appendChild(document
.createTextNode(text
));
7259 btn
.className
= className
? className
: "button";
7263 for(var n
in attribs
) {
7264 btn
.setAttribute(n
,attribs
[n
]);
7268 parent
.appendChild(btn
);
7270 btn
.setAttribute("accessKey",accessKey
);
7274 function createTiddlyLink(place
,title
,includeText
,className
,isStatic
,linkedFromTiddler
,noToggle
)
7276 var text
= includeText
? title
: null;
7277 var i
= getTiddlyLinkInfo(title
,className
);
7278 var btn
= isStatic
? createExternalLink(place
,store
.getTiddlerText("SiteUrl",null) + "#" + title
) : createTiddlyButton(place
,text
,i
.subTitle
,onClickTiddlerLink
,i
.classes
);
7279 btn
.setAttribute("refresh","link");
7280 btn
.setAttribute("tiddlyLink",title
);
7282 btn
.setAttribute("noToggle","true");
7283 if(linkedFromTiddler
) {
7284 var fields
= linkedFromTiddler
.getInheritedFields();
7286 btn
.setAttribute("tiddlyFields",fields
);
7291 function refreshTiddlyLink(e
,title
)
7293 var i
= getTiddlyLinkInfo(title
,e
.className
);
7294 e
.className
= i
.classes
;
7295 e
.title
= i
.subTitle
;
7298 function getTiddlyLinkInfo(title
,currClasses
)
7300 var classes
= currClasses
? currClasses
.split(" ") : [];
7301 classes
.pushUnique("tiddlyLink");
7302 var tiddler
= store
.fetchTiddler(title
);
7305 subTitle
= tiddler
.getSubtitle();
7306 classes
.pushUnique("tiddlyLinkExisting");
7307 classes
.remove("tiddlyLinkNonExisting");
7308 classes
.remove("shadow");
7310 classes
.remove("tiddlyLinkExisting");
7311 classes
.pushUnique("tiddlyLinkNonExisting");
7312 if(store
.isShadowTiddler(title
)) {
7313 subTitle
= config
.messages
.shadowedTiddlerToolTip
.format([title
]);
7314 classes
.pushUnique("shadow");
7316 subTitle
= config
.messages
.undefinedTiddlerToolTip
.format([title
]);
7317 classes
.remove("shadow");
7320 if(typeof config
.annotations
[title
]=="string")
7321 subTitle
= config
.annotations
[title
];
7322 return {classes
: classes
.join(" "),subTitle
: subTitle
};
7325 function createExternalLink(place
,url
)
7327 var link
= document
.createElement("a");
7328 link
.className
= "externalLink";
7330 link
.title
= config
.messages
.externalLinkTooltip
.format([url
]);
7331 if(config
.options
.chkOpenInNewWindow
)
7332 link
.target
= "_blank";
7333 place
.appendChild(link
);
7337 // Event handler for clicking on a tiddly link
7338 function onClickTiddlerLink(ev
)
7340 var e
= ev
? ev
: window
.event
;
7341 var target
= resolveTarget(e
);
7345 var noToggle
= null;
7347 title
= link
.getAttribute("tiddlyLink");
7348 fields
= link
.getAttribute("tiddlyFields");
7349 noToggle
= link
.getAttribute("noToggle");
7350 link
= link
.parentNode
;
7351 } while(title
== null && link
!= null);
7352 if(!store
.isShadowTiddler(title
)) {
7353 var f
= fields
? fields
.decodeHashMap() : {};
7354 fields
= String
.encodeHashMap(merge(f
,config
.defaultCustomFields
,true));
7357 var toggling
= e
.metaKey
|| e
.ctrlKey
;
7358 if(config
.options
.chkToggleLinks
)
7359 toggling
= !toggling
;
7362 if(store
.getTiddler(title
))
7364 story
.displayTiddler(target
,title
,null,true,null,fields
,toggling
);
7370 // Create a button for a tag with a popup listing all the tiddlers that it tags
7371 function createTagButton(place
,tag
,excludeTiddler
)
7373 var btn
= createTiddlyButton(place
,tag
,config
.views
.wikified
.tag
.tooltip
.format([tag
]),onClickTag
);
7374 btn
.setAttribute("tag",tag
);
7376 btn
.setAttribute("tiddler",excludeTiddler
);
7380 // Event handler for clicking on a tiddler tag
7381 function onClickTag(ev
)
7383 var e
= ev
? ev
: window
.event
;
7384 var popup
= Popup
.create(this);
7385 var tag
= this.getAttribute("tag");
7386 var title
= this.getAttribute("tiddler");
7388 var tagged
= store
.getTaggedTiddlers(tag
);
7391 for(r
=0;r
<tagged
.length
;r
++) {
7392 if(tagged
[r
].title
!= title
)
7393 titles
.push(tagged
[r
].title
);
7395 var lingo
= config
.views
.wikified
.tag
;
7396 if(titles
.length
> 0) {
7397 var openAll
= createTiddlyButton(createTiddlyElement(popup
,"li"),lingo
.openAllText
.format([tag
]),lingo
.openAllTooltip
,onClickTagOpenAll
);
7398 openAll
.setAttribute("tag",tag
);
7399 createTiddlyElement(createTiddlyElement(popup
,"li",null,"listBreak"),"div");
7400 for(r
=0; r
<titles
.length
; r
++) {
7401 createTiddlyLink(createTiddlyElement(popup
,"li"),titles
[r
],true);
7404 createTiddlyText(createTiddlyElement(popup
,"li",null,"disabled"),lingo
.popupNone
.format([tag
]));
7406 createTiddlyElement(createTiddlyElement(popup
,"li",null,"listBreak"),"div");
7407 var h
= createTiddlyLink(createTiddlyElement(popup
,"li"),tag
,false);
7408 createTiddlyText(h
,lingo
.openTag
.format([tag
]));
7411 e
.cancelBubble
= true;
7412 if(e
.stopPropagation
) e
.stopPropagation();
7416 // Event handler for 'open all' on a tiddler popup
7417 function onClickTagOpenAll(ev
)
7419 var e
= ev
? ev
: window
.event
;
7420 var tag
= this.getAttribute("tag");
7421 var tagged
= store
.getTaggedTiddlers(tag
);
7422 story
.displayTiddlers(this,tagged
);
7426 function onClickError(ev
)
7428 var e
= ev
? ev
: window
.event
;
7429 var popup
= Popup
.create(this);
7430 var lines
= this.getAttribute("errorText").split("\n");
7431 for(var t
=0; t
<lines
.length
; t
++)
7432 createTiddlyElement(popup
,"li",null,null,lines
[t
]);
7434 e
.cancelBubble
= true;
7435 if(e
.stopPropagation
) e
.stopPropagation();
7439 function createTiddlyDropDown(place
,onchange
,options
,defaultValue
)
7441 var sel
= createTiddlyElement(place
,"select");
7442 sel
.onchange
= onchange
;
7443 for(var t
=0; t
<options
.length
; t
++) {
7444 var e
= createTiddlyElement(sel
,"option",null,null,options
[t
].caption
);
7445 e
.value
= options
[t
].name
;
7446 if(options
[t
].name
== defaultValue
)
7452 function createTiddlyPopup(place
,caption
,tooltip
,tiddler
)
7455 createTiddlyLink(place
,caption
,true);
7456 var btn
= createTiddlyButton(place
,glyph("downArrow"),tooltip
,onClickTiddlyPopup
,"tiddlerPopupButton");
7457 btn
.tiddler
= tiddler
;
7459 createTiddlyText(place
,caption
);
7463 function onClickTiddlyPopup(ev
)
7465 var e
= ev
? ev
: window
.event
;
7466 var tiddler
= this.tiddler
;
7468 var popup
= Popup
.create(this,"div","popupTiddler");
7469 wikify(tiddler
.text
,popup
,null,tiddler
);
7472 if(e
) e
.cancelBubble
= true;
7473 if(e
&& e
.stopPropagation
) e
.stopPropagation();
7477 function createTiddlyError(place
,title
,text
)
7479 var btn
= createTiddlyButton(place
,title
,null,onClickError
,"errorButton");
7480 if(text
) btn
.setAttribute("errorText",text
);
7483 function merge(dst
,src
,preserveExisting
)
7486 if(!preserveExisting
|| dst
[p
] === undefined)
7492 // Returns a string containing the description of an exception, optionally prepended by a message
7493 function exceptionText(e
,message
)
7495 var s
= e
.description
? e
.description
: e
.toString();
7496 return message
? "%0:\n%1".format([message
,s
]) : s
;
7499 // Displays an alert of an exception description with optional message
7500 function showException(e
,message
)
7502 alert(exceptionText(e
,message
));
7505 function alertAndThrow(m
)
7511 function glyph(name
)
7513 var g
= config
.glyphs
;
7514 var b
= g
.currBrowser
;
7517 while(!g
.browsers
[b
]() && b
< g
.browsers
.length
-1)
7523 return g
.codes
[name
][b
];
7528 //- Animation engine
7533 this.running
= 0; // Incremented at start of each animation, decremented afterwards. If zero, the interval timer is disabled
7534 this.timerID
= 0; // ID of the timer used for animating
7535 this.animations
= []; // List of animations in progress
7539 // Start animation engine
7540 Animator
.prototype.startAnimating = function() //# Variable number of arguments
7542 for(var t
=0; t
<arguments
.length
; t
++)
7543 this.animations
.push(arguments
[t
]);
7544 if(this.running
== 0) {
7546 this.timerID
= window
.setInterval(function() {me
.doAnimate(me
);},10);
7548 this.running
+= arguments
.length
;
7551 // Perform an animation engine tick, calling each of the known animation modules
7552 Animator
.prototype.doAnimate = function(me
)
7555 while(a
< me
.animations
.length
) {
7556 var animation
= me
.animations
[a
];
7557 if(animation
.tick()) {
7560 me
.animations
.splice(a
,1);
7561 if(--me
.running
== 0)
7562 window
.clearInterval(me
.timerID
);
7567 Animator
.slowInSlowOut = function(progress
)
7569 return(1-((Math
.cos(progress
* Math
.PI
)+1)/2));
7573 //-- Morpher animation
7576 // Animate a set of properties of an element
7577 function Morpher(element
,duration
,properties
,callback
)
7579 this.element
= element
;
7580 this.duration
= duration
;
7581 this.properties
= properties
;
7582 this.startTime
= new Date();
7583 this.endTime
= Number(this.startTime
) + duration
;
7584 this.callback
= callback
;
7589 Morpher
.prototype.assignStyle = function(element
,style
,value
)
7592 case "-tw-vertScroll":
7593 window
.scrollTo(findScrollX(),value
);
7595 case "-tw-horizScroll":
7596 window
.scrollTo(value
,findScrollY());
7599 element
.style
[style
] = value
;
7604 Morpher
.prototype.stop = function()
7606 for(var t
=0; t
<this.properties
.length
; t
++) {
7607 var p
= this.properties
[t
];
7608 if(p
.atEnd
!== undefined) {
7609 this.assignStyle(this.element
,p
.style
,p
.atEnd
);
7613 this.callback(this.element
,this.properties
);
7616 Morpher
.prototype.tick = function()
7618 var currTime
= Number(new Date());
7619 progress
= Animator
.slowInSlowOut(Math
.min(1,(currTime
-this.startTime
)/this.duration
));
7620 for(var t
=0; t
<this.properties
.length
; t
++) {
7621 var p
= this.properties
[t
];
7622 if(p
.start
!== undefined && p
.end
!== undefined) {
7623 var template
= p
.template
? p
.template
: "%0";
7627 var v
= p
.start
+ (p
.end
-p
.start
) * progress
;
7628 this.assignStyle(this.element
,p
.style
,template
.format([v
]));
7635 if(currTime
>= this.endTime
) {
7643 //-- Zoomer animation
7646 function Zoomer(text
,startElement
,targetElement
,unused
)
7648 var e
= createTiddlyElement(document
.body
,"div",null,"zoomer");
7649 createTiddlyElement(e
,"div",null,null,text
);
7650 var winWidth
= findWindowWidth();
7651 var winHeight
= findWindowHeight();
7653 {style
: 'left', start
: findPosX(startElement
), end
: findPosX(targetElement
), template
: '%0px'},
7654 {style
: 'top', start
: findPosY(startElement
), end
: findPosY(targetElement
), template
: '%0px'},
7655 {style
: 'width', start
: Math
.min(startElement
.scrollWidth
,winWidth
), end
: Math
.min(targetElement
.scrollWidth
,winWidth
), template
: '%0px', atEnd
: 'auto'},
7656 {style
: 'height', start
: Math
.min(startElement
.scrollHeight
,winHeight
), end
: Math
.min(targetElement
.scrollHeight
,winHeight
), template
: '%0px', atEnd
: 'auto'},
7657 {style
: 'fontSize', start
: 8, end
: 24, template
: '%0pt'}
7659 var c = function(element
,properties
) {removeNode(element
);};
7660 return new Morpher(e
,config
.animDuration
,p
,c
);
7664 //-- Scroller animation
7667 function Scroller(targetElement
,unused
)
7670 {style
: '-tw-vertScroll', start
: findScrollY(), end
: ensureVisible(targetElement
)}
7672 return new Morpher(targetElement
,config
.animDuration
,p
);
7676 //-- Slider animation
7679 // deleteMode - "none", "all" [delete target element and it's children], [only] "children" [but not the target element]
7680 function Slider(element
,opening
,unused
,deleteMode
)
7682 element
.style
.overflow
= 'hidden';
7684 element
.style
.height
= '0px'; // Resolves a Firefox flashing bug
7685 element
.style
.display
= 'block';
7686 var left
= findPosX(element
);
7687 var width
= element
.scrollWidth
;
7688 var height
= element
.scrollHeight
;
7689 var winWidth
= findWindowWidth();
7693 p
.push({style
: 'height', start
: 0, end
: height
, template
: '%0px', atEnd
: 'auto'});
7694 p
.push({style
: 'opacity', start
: 0, end
: 1, template
: '%0'});
7695 p
.push({style
: 'filter', start
: 0, end
: 100, template
: 'alpha(opacity:%0)'});
7697 p
.push({style
: 'height', start
: height
, end
: 0, template
: '%0px'});
7698 p
.push({style
: 'display', atEnd
: 'none'});
7699 p
.push({style
: 'opacity', start
: 1, end
: 0, template
: '%0'});
7700 p
.push({style
: 'filter', start
: 100, end
: 0, template
: 'alpha(opacity:%0)'});
7701 switch(deleteMode
) {
7703 c = function(element
,properties
) {removeNode(element
);};
7706 c = function(element
,properties
) {removeChildren(element
);};
7710 return new Morpher(element
,config
.animDuration
,p
,c
);
7718 stack
: [] // Array of objects with members root: and popup:
7721 Popup
.create = function(root
,elem
,theClass
)
7724 var popup
= createTiddlyElement(document
.body
,elem
? elem
: "ol","popup",theClass
? theClass
: "popup");
7725 Popup
.stack
.push({root
: root
, popup
: popup
});
7729 Popup
.onDocumentClick = function(ev
)
7731 var e
= ev
? ev
: window
.event
;
7732 if(e
.eventPhase
== undefined)
7734 else if(e
.eventPhase
== Event
.BUBBLING_PHASE
|| e
.eventPhase
== Event
.AT_TARGET
)
7739 Popup
.show = function(unused1
,unused2
)
7741 var curr
= Popup
.stack
[Popup
.stack
.length
-1];
7742 this.place(curr
.root
,curr
.popup
);
7743 addClass(curr
.root
,"highlight");
7744 if(config
.options
.chkAnimate
&& anim
&& typeof Scroller
== "function")
7745 anim
.startAnimating(new Scroller(curr
.popup
));
7747 window
.scrollTo(0,ensureVisible(curr
.popup
));
7750 Popup
.place = function(root
,popup
,offset
)
7752 if(!offset
) var offset
= {x
:0, y
:0};
7753 var rootLeft
= findPosX(root
);
7754 var rootTop
= findPosY(root
);
7755 var rootHeight
= root
.offsetHeight
;
7756 var popupLeft
= rootLeft
+ offset
.x
;
7757 var popupTop
= rootTop
+ rootHeight
+ offset
.y
;
7758 var winWidth
= findWindowWidth();
7759 if(popup
.offsetWidth
> winWidth
*0.75)
7760 popup
.style
.width
= winWidth
*0.75 + "px";
7761 var popupWidth
= popup
.offsetWidth
;
7762 if(popupLeft
+ popupWidth
> winWidth
)
7763 popupLeft
= winWidth
- popupWidth
;
7764 popup
.style
.left
= popupLeft
+ "px";
7765 popup
.style
.top
= popupTop
+ "px";
7766 popup
.style
.display
= "block";
7769 Popup
.remove = function()
7771 if(Popup
.stack
.length
> 0) {
7772 Popup
.removeFrom(0);
7776 Popup
.removeFrom = function(from)
7778 for(var t
=Popup
.stack
.length
-1; t
>=from; t
--) {
7779 var p
= Popup
.stack
[t
];
7780 removeClass(p
.root
,"highlight");
7781 removeNode(p
.popup
);
7783 Popup
.stack
= Popup
.stack
.slice(0,from);
7790 function Wizard(elem
)
7793 this.formElem
= findRelated(elem
,"wizard","className");
7794 this.bodyElem
= findRelated(this.formElem
.firstChild
,"wizardBody","className","nextSibling");
7795 this.footElem
= findRelated(this.formElem
.firstChild
,"wizardFooter","className","nextSibling");
7797 this.formElem
= null;
7798 this.bodyElem
= null;
7799 this.footElem
= null;
7803 Wizard
.prototype.setValue = function(name
,value
)
7806 this.formElem
[name
] = value
;
7809 Wizard
.prototype.getValue = function(name
)
7811 return this.formElem
? this.formElem
[name
] : null;
7814 Wizard
.prototype.createWizard = function(place
,title
)
7816 this.formElem
= createTiddlyElement(place
,"form",null,"wizard");
7817 createTiddlyElement(this.formElem
,"h1",null,null,title
);
7818 this.bodyElem
= createTiddlyElement(this.formElem
,"div",null,"wizardBody");
7819 this.footElem
= createTiddlyElement(this.formElem
,"div",null,"wizardFooter");
7822 Wizard
.prototype.clear = function()
7824 removeChildren(this.bodyElem
);
7827 Wizard
.prototype.setButtons = function(buttonInfo
,status
)
7829 removeChildren(this.footElem
);
7830 for(var t
=0; t
<buttonInfo
.length
; t
++) {
7831 createTiddlyButton(this.footElem
,buttonInfo
[t
].caption
,buttonInfo
[t
].tooltip
,buttonInfo
[t
].onClick
);
7832 insertSpacer(this.footElem
);
7834 if(typeof status
== "string") {
7835 createTiddlyElement(this.footElem
,"span",null,"status",status
);
7839 Wizard
.prototype.addStep = function(stepTitle
,html
)
7841 removeChildren(this.bodyElem
);
7842 var w
= createTiddlyElement(this.bodyElem
,"div");
7843 createTiddlyElement(w
,"h2",null,null,stepTitle
);
7844 var step
= createTiddlyElement(w
,"div",null,"wizardStep");
7845 step
.innerHTML
= html
;
7846 applyHtmlMacros(step
,tiddler
);
7849 Wizard
.prototype.getElement = function(name
)
7851 return this.formElem
.elements
[name
];
7855 //-- ListView gadget
7860 // Create a listview
7861 ListView
.create = function(place
,listObject
,listTemplate
,callback
,className
)
7863 var table
= createTiddlyElement(place
,"table",null,className
? className
: "listView twtable");
7864 var thead
= createTiddlyElement(table
,"thead");
7865 var r
= createTiddlyElement(thead
,"tr");
7866 for(var t
=0; t
<listTemplate
.columns
.length
; t
++) {
7867 var columnTemplate
= listTemplate
.columns
[t
];
7868 var c
= createTiddlyElement(r
,"th");
7869 var colType
= ListView
.columnTypes
[columnTemplate
.type
];
7870 if(colType
&& colType
.createHeader
)
7871 colType
.createHeader(c
,columnTemplate
,t
);
7873 var tbody
= createTiddlyElement(table
,"tbody");
7874 for(var rc
=0; rc
<listObject
.length
; rc
++) {
7875 rowObject
= listObject
[rc
];
7876 r
= createTiddlyElement(tbody
,"tr");
7877 for(c
=0; c
<listTemplate
.rowClasses
.length
; c
++) {
7878 if(rowObject
[listTemplate
.rowClasses
[c
].field
])
7879 addClass(r
,listTemplate
.rowClasses
[c
].className
);
7881 rowObject
.rowElement
= r
;
7882 rowObject
.colElements
= {};
7883 for(var cc
=0; cc
<listTemplate
.columns
.length
; cc
++) {
7884 c
= createTiddlyElement(r
,"td");
7885 columnTemplate
= listTemplate
.columns
[cc
];
7886 var field
= columnTemplate
.field
;
7887 colType
= ListView
.columnTypes
[columnTemplate
.type
];
7888 if(colType
&& colType
.createItem
)
7889 colType
.createItem(c
,rowObject
,field
,columnTemplate
,cc
,rc
);
7890 rowObject
.colElements
[field
] = c
;
7893 if(callback
&& listTemplate
.actions
)
7894 createTiddlyDropDown(place
,ListView
.getCommandHandler(callback
),listTemplate
.actions
);
7895 if(callback
&& listTemplate
.buttons
) {
7896 for(t
=0; t
<listTemplate
.buttons
.length
; t
++) {
7897 var a
= listTemplate
.buttons
[t
];
7898 if(a
&& a
.name
!= "")
7899 createTiddlyButton(place
,a
.caption
,null,ListView
.getCommandHandler(callback
,a
.name
,a
.allowEmptySelection
));
7905 ListView
.getCommandHandler = function(callback
,name
,allowEmptySelection
)
7907 return function(e
) {
7908 var view
= findRelated(this,"TABLE",null,"previousSibling");
7910 ListView
.forEachSelector(view
,function(e
,rowName
) {
7912 tiddlers
.push(rowName
);
7914 if(tiddlers
.length
== 0 && !allowEmptySelection
) {
7915 alert(config
.messages
.nothingSelected
);
7917 if(this.nodeName
.toLowerCase() == "select") {
7918 callback(view
,this.value
,tiddlers
);
7919 this.selectedIndex
= 0;
7921 callback(view
,name
,tiddlers
);
7927 // Invoke a callback for each selector checkbox in the listview
7928 ListView
.forEachSelector = function(view
,callback
)
7930 var checkboxes
= view
.getElementsByTagName("input");
7932 for(var t
=0; t
<checkboxes
.length
; t
++) {
7933 var cb
= checkboxes
[t
];
7934 if(cb
.getAttribute("type") == "checkbox") {
7935 var rn
= cb
.getAttribute("rowName");
7945 ListView
.getSelectedRows = function(view
)
7948 ListView
.forEachSelector(view
,function(e
,rowName
) {
7950 rowNames
.push(rowName
);
7955 ListView
.columnTypes
= {};
7957 ListView
.columnTypes
.String
= {
7958 createHeader: function(place
,columnTemplate
,col
)
7960 createTiddlyText(place
,columnTemplate
.title
);
7962 createItem: function(place
,listObject
,field
,columnTemplate
,col
,row
)
7964 var v
= listObject
[field
];
7966 createTiddlyText(place
,v
);
7970 ListView
.columnTypes
.WikiText
= {
7971 createHeader
: ListView
.columnTypes
.String
.createHeader
,
7972 createItem: function(place
,listObject
,field
,columnTemplate
,col
,row
)
7974 var v
= listObject
[field
];
7976 wikify(v
,place
,null,null);
7980 ListView
.columnTypes
.Tiddler
= {
7981 createHeader
: ListView
.columnTypes
.String
.createHeader
,
7982 createItem: function(place
,listObject
,field
,columnTemplate
,col
,row
)
7984 var v
= listObject
[field
];
7985 if(v
!= undefined && v
.title
)
7986 createTiddlyPopup(place
,v
.title
,config
.messages
.listView
.tiddlerTooltip
,v
);
7990 ListView
.columnTypes
.Size
= {
7991 createHeader
: ListView
.columnTypes
.String
.createHeader
,
7992 createItem: function(place
,listObject
,field
,columnTemplate
,col
,row
)
7994 var v
= listObject
[field
];
7995 if(v
!= undefined) {
7997 while(t
<config
.messages
.sizeTemplates
.length
-1 && v
<config
.messages
.sizeTemplates
[t
].unit
)
7999 createTiddlyText(place
,config
.messages
.sizeTemplates
[t
].template
.format([Math
.round(v
/config
.messages
.sizeTemplates
[t
].unit
)]));
8004 ListView
.columnTypes
.Link
= {
8005 createHeader
: ListView
.columnTypes
.String
.createHeader
,
8006 createItem: function(place
,listObject
,field
,columnTemplate
,col
,row
)
8008 var v
= listObject
[field
];
8009 var c
= columnTemplate
.text
;
8011 createTiddlyText(createExternalLink(place
,v
),c
? c
: v
);
8015 ListView
.columnTypes
.Date
= {
8016 createHeader
: ListView
.columnTypes
.String
.createHeader
,
8017 createItem: function(place
,listObject
,field
,columnTemplate
,col
,row
)
8019 var v
= listObject
[field
];
8021 createTiddlyText(place
,v
.formatString(columnTemplate
.dateFormat
));
8025 ListView
.columnTypes
.StringList
= {
8026 createHeader
: ListView
.columnTypes
.String
.createHeader
,
8027 createItem: function(place
,listObject
,field
,columnTemplate
,col
,row
)
8029 var v
= listObject
[field
];
8030 if(v
!= undefined) {
8031 for(var t
=0; t
<v
.length
; t
++) {
8032 createTiddlyText(place
,v
[t
]);
8033 createTiddlyElement(place
,"br");
8039 ListView
.columnTypes
.Selector
= {
8040 createHeader: function(place
,columnTemplate
,col
)
8042 createTiddlyCheckbox(place
,null,false,this.onHeaderChange
);
8044 createItem: function(place
,listObject
,field
,columnTemplate
,col
,row
)
8046 var e
= createTiddlyCheckbox(place
,null,listObject
[field
],null);
8047 e
.setAttribute("rowName",listObject
[columnTemplate
.rowName
]);
8049 onHeaderChange: function(e
)
8051 var state
= this.checked
;
8052 var view
= findRelated(this,"TABLE");
8055 ListView
.forEachSelector(view
,function(e
,rowName
) {
8061 ListView
.columnTypes
.Tags
= {
8062 createHeader
: ListView
.columnTypes
.String
.createHeader
,
8063 createItem: function(place
,listObject
,field
,columnTemplate
,col
,row
)
8065 var tags
= listObject
[field
];
8066 createTiddlyText(place
,String
.encodeTiddlyLinkList(tags
));
8070 ListView
.columnTypes
.Boolean
= {
8071 createHeader
: ListView
.columnTypes
.String
.createHeader
,
8072 createItem: function(place
,listObject
,field
,columnTemplate
,col
,row
)
8074 if(listObject
[field
] == true)
8075 createTiddlyText(place
,columnTemplate
.trueText
);
8076 if(listObject
[field
] == false)
8077 createTiddlyText(place
,columnTemplate
.falseText
);
8081 ListView
.columnTypes
.TagCheckbox
= {
8082 createHeader
: ListView
.columnTypes
.String
.createHeader
,
8083 createItem: function(place
,listObject
,field
,columnTemplate
,col
,row
)
8085 var e
= createTiddlyCheckbox(place
,null,listObject
[field
],this.onChange
);
8086 e
.setAttribute("tiddler",listObject
.title
);
8087 e
.setAttribute("tag",columnTemplate
.tag
);
8089 onChange : function(e
)
8091 var tag
= this.getAttribute("tag");
8092 var tiddler
= this.getAttribute("tiddler");
8093 store
.setTiddlerTag(tiddler
,this.checked
,tag
);
8097 ListView
.columnTypes
.TiddlerLink
= {
8098 createHeader
: ListView
.columnTypes
.String
.createHeader
,
8099 createItem: function(place
,listObject
,field
,columnTemplate
,col
,row
)
8101 var v
= listObject
[field
];
8102 if(v
!= undefined) {
8103 var link
= createTiddlyLink(place
,listObject
[columnTemplate
.tiddlerLink
],false,null);
8104 createTiddlyText(link
,listObject
[field
]);
8110 //-- Augmented methods for the JavaScript Number(), Array(), String() and Date() objects
8113 // Clamp a number to a range
8114 Number
.prototype.clamp = function(min
,max
)
8124 // Add indexOf function if browser does not support it
8125 if(!Array
.indexOf
) {
8126 Array
.prototype.indexOf = function(item
,from)
8130 for(var i
=from; i
<this.length
; i
++) {
8131 if(this[i
] === item
)
8137 // Find an entry in a given field of the members of an array
8138 Array
.prototype.findByField = function(field
,value
)
8140 for(var t
=0; t
<this.length
; t
++) {
8141 if(this[t
][field
] == value
)
8147 // Return whether an entry exists in an array
8148 Array
.prototype.contains = function(item
)
8150 return this.indexOf(item
) != -1;
8153 // Adds, removes or toggles a particular value within an array
8154 // value - value to add
8155 // mode - +1 to add value, -1 to remove value, 0 to toggle it
8156 Array
.prototype.setItem = function(value
,mode
)
8158 var p
= this.indexOf(value
);
8160 mode
= (p
== -1) ? +1 : -1;
8164 } else if(mode
== -1) {
8170 // Return whether one of a list of values exists in an array
8171 Array
.prototype.containsAny = function(items
)
8173 for(var i
=0; i
<items
.length
; i
++) {
8174 if (this.indexOf(items
[i
]) != -1)
8180 // Return whether all of a list of values exists in an array
8181 Array
.prototype.containsAll = function(items
)
8183 for (var i
= 0; i
<items
.length
; i
++) {
8184 if (this.indexOf(items
[i
]) == -1)
8190 // 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
8191 Array
.prototype.pushUnique = function(item
,unique
)
8193 if(unique
=== false) {
8196 if(this.indexOf(item
) == -1)
8201 Array
.prototype.remove = function(item
)
8203 var p
= this.indexOf(item
);
8208 if(!Array
.prototype.map
){
8209 Array
.prototype.map = function(fn
, thisObj
) {
8210 var scope
= thisObj
|| window
;
8212 for ( var i
=0, j
=this.length
; i
< j
; ++i
) {
8213 a
.push(fn
.call(scope
, this[i
], i
, this));
8216 };}// Get characters from the right end of a string
8217 String
.prototype.right = function(n
)
8219 return n
< this.length
? this.slice(this.length
-n
) : this;
8222 // Trim whitespace from both ends of a string
8223 String
.prototype.trim = function()
8225 return this.replace(/^\s*|\s*$/g,"");
8228 // Convert a string from a CSS style property name to a JavaScript style name ("background-color" -> "backgroundColor")
8229 String
.prototype.unDash = function()
8231 var s
= this.split("-");
8233 for(var t
=1; t
<s
.length
; t
++)
8234 s
[t
] = s
[t
].substr(0,1).toUpperCase() + s
[t
].substr(1);
8239 // Substitute substrings from an array into a format string that includes '%1'-type specifiers
8240 String
.prototype.format = function(substrings
)
8242 var subRegExp
= /(?:%(\d+))/mg;
8246 var match
= subRegExp
.exec(this);
8247 if(match
&& match
[1]) {
8248 if(match
.index
> currPos
)
8249 r
.push(this.substring(currPos
,match
.index
));
8250 r
.push(substrings
[parseInt(match
[1])]);
8251 currPos
= subRegExp
.lastIndex
;
8254 if(currPos
< this.length
)
8255 r
.push(this.substring(currPos
,this.length
));
8259 // Escape any special RegExp characters with that character preceded by a backslash
8260 String
.prototype.escapeRegExp = function()
8262 var s
= "\\^$*+?()=!|,{}[].";
8264 for(var t
=0; t
<s
.length
; t
++)
8265 c
= c
.replace(new RegExp("\\" + s
.substr(t
,1),"g"),"\\" + s
.substr(t
,1));
8269 // Convert "\" to "\s", newlines to "\n" (and remove carriage returns)
8270 String
.prototype.escapeLineBreaks = function()
8272 return this.replace(/\\/mg,"\\s").replace(/\n/mg,"\\n").replace(/\r/mg
,"");
8275 // Convert "\n" to newlines, "\b" to " ", "\s" to "\" (and remove carriage returns)
8276 String
.prototype.unescapeLineBreaks = function()
8278 return this.replace(/\\n/mg,"\n").replace(/\\b/mg," ").replace(/\\s/mg,"\\").replace(/\r/mg,"");
8281 // Convert & to "&", < to "<", > to ">" and " to """
8282 String
.prototype.htmlEncode = function()
8284 return this.replace(/&/mg,"&").replace(/</mg,"<").replace(/>/mg,">").replace(/\"/mg
,""");
8287 // Convert "&" to &, "<" to <, ">" to > and """ to "
8288 String
.prototype.htmlDecode = function()
8290 return this.replace(/</mg,"<").replace(/>/mg,">").replace(/"/mg,"\"").replace(/&/mg,"&");
8293 // Convert a string to it's JSON representation by encoding control characters, double quotes and backslash. See json.org
8294 String
.prototype.toJSONString = function()
8305 var replaceFn = function(a
,b
) {
8310 return '\\u00' + Math
.floor(c
/ 16).toString(16) + (c
% 16).toString(16);
8312 if(/["\\\x00-\x1f]/.test(this))
8313 return '"' + this.replace(/([\x00-\x1f\\"])/g,replaceFn
) + '"';
8314 return '"' + this + '"';
8317 // Parse a space-separated string of name:value parameters
8318 // The result is an array of objects:
8319 // result[0] = object with a member for each parameter name, value of that member being an array of values
8320 // result[1..n] = one object for each parameter, with 'name' and 'value' members
8321 String
.prototype.parseParams = function(defaultName
,defaultValue
,allowEval
,noNames
,cascadeDefaults
)
8323 var parseToken = function(match
,p
) {
8325 if(match
[p
]) // Double quoted
8327 else if(match
[p
+1]) // Single quoted
8329 else if(match
[p
+2]) // Double-square-bracket quoted
8331 else if(match
[p
+3]) // Double-brace quoted
8337 throw "Unable to evaluate {{" + match
[p
+3] + "}}: " + exceptionText(ex
);
8339 else if(match
[p
+4]) // Unquoted
8341 else if(match
[p
+5]) // empty quote
8346 var dblQuote
= "(?:\"((?:(?:\\\\\")|[^\"])+)\")";
8347 var sngQuote
= "(?:'((?:(?:\\\\\')|[^'])+)')";
8348 var dblSquare
= "(?:\\[\\[((?:\\s|\\S)*?)\\]\\])";
8349 var dblBrace
= "(?:\\{\\{((?:\\s|\\S)*?)\\}\\})";
8350 var unQuoted
= noNames
? "([^\"'\\s]\\S*)" : "([^\"':\\s][^\\s:]*)";
8351 var emptyQuote
= "((?:\"\")|(?:''))";
8352 var skipSpace
= "(?:\\s*)";
8353 var token
= "(?:" + dblQuote
+ "|" + sngQuote
+ "|" + dblSquare
+ "|" + dblBrace
+ "|" + unQuoted
+ "|" + emptyQuote
+ ")";
8354 var re
= noNames
? new RegExp(token
,"mg") : new RegExp(skipSpace
+ token
+ skipSpace
+ "(?:(\\:)" + skipSpace
+ token
+ ")?","mg");
8357 var match
= re
.exec(this);
8359 var n
= parseToken(match
,1);
8361 r
.push({name
:"",value
:n
});
8363 var v
= parseToken(match
,8);
8364 if(v
== null && defaultName
) {
8367 } else if(v
== null && defaultValue
) {
8370 r
.push({name
:n
,value
:v
});
8371 if(cascadeDefaults
) {
8378 // Summarise parameters into first element
8379 for(var t
=1; t
<r
.length
; t
++) {
8381 r
[0][r
[t
].name
].push(r
[t
].value
);
8383 r
[0][r
[t
].name
] = [r
[t
].value
];
8388 // Process a string list of macro parameters into an array. Parameters can be quoted with "", '',
8389 // [[]], {{ }} or left unquoted (and therefore space-separated). Double-braces {{}} results in
8390 // an *evaluated* parameter: e.g. {{config.options.txtUserName}} results in the current user's name.
8391 String
.prototype.readMacroParams = function()
8393 var p
= this.parseParams("list",null,true,true);
8395 for(var t
=1; t
<p
.length
; t
++)
8400 // Process a string list of unique tiddler names into an array. Tiddler names that have spaces in them must be [[bracketed]]
8401 String
.prototype.readBracketedList = function(unique
)
8403 var p
= this.parseParams("list",null,false,true);
8405 for(var t
=1; t
<p
.length
; t
++) {
8407 n
.pushUnique(p
[t
].value
,unique
);
8412 // Returns array with start and end index of chunk between given start and end marker, or undefined.
8413 String
.prototype.getChunkRange = function(start
,end
)
8415 var s
= this.indexOf(start
);
8418 var e
= this.indexOf(end
,s
);
8424 // Replace a chunk of a string given start and end markers
8425 String
.prototype.replaceChunk = function(start
,end
,sub
)
8427 var r
= this.getChunkRange(start
,end
);
8428 return r
? this.substring(0,r
[0]) + sub
+ this.substring(r
[1]) : this;
8431 // Returns a chunk of a string between start and end markers, or undefined
8432 String
.prototype.getChunk = function(start
,end
)
8434 var r
= this.getChunkRange(start
,end
);
8436 return this.substring(r
[0],r
[1]);
8440 // Static method to bracket a string with double square brackets if it contains a space
8441 String
.encodeTiddlyLink = function(title
)
8443 return title
.indexOf(" ") == -1 ? title
: "[[" + title
+ "]]";
8446 // Static method to encodeTiddlyLink for every item in an array and join them with spaces
8447 String
.encodeTiddlyLinkList = function(list
)
8451 for(var t
=0; t
<list
.length
; t
++)
8452 results
.push(String
.encodeTiddlyLink(list
[t
]));
8453 return results
.join(" ");
8459 // Convert a string as a sequence of name:"value" pairs into a hashmap
8460 String
.prototype.decodeHashMap = function()
8462 var fields
= this.parseParams("anon","",false);
8464 for(var t
=1; t
<fields
.length
; t
++)
8465 r
[fields
[t
].name
] = fields
[t
].value
;
8469 // Static method to encode a hashmap into a name:"value"... string
8470 String
.encodeHashMap = function(hashmap
)
8473 for(var t
in hashmap
)
8474 r
.push(t
+ ':"' + hashmap
[t
] + '"');
8478 // Static method to left-pad a string with 0s to a certain width
8479 String
.zeroPad = function(n
,d
)
8481 var s
= n
.toString();
8483 s
= "000000000000000000000000000".substr(0,d
-s
.length
) + s
;
8487 String
.prototype.startsWith = function(prefix
)
8489 return !prefix
|| this.substring(0,prefix
.length
) == prefix
;
8492 // Returns the first value of the given named parameter.
8493 function getParam(params
,name
,defaultValue
)
8496 return defaultValue
;
8497 var p
= params
[0][name
];
8498 return p
? p
[0] : defaultValue
;
8501 // Returns the first value of the given boolean named parameter.
8502 function getFlag(params
,name
,defaultValue
)
8504 return !!getParam(params
,name
,defaultValue
);
8507 // Substitute date components into a string
8508 Date
.prototype.formatString = function(template
)
8510 var t
= template
.replace(/0hh12/g,String
.zeroPad(this.getHours12(),2));
8511 t
= t
.replace(/hh12/g,this.getHours12());
8512 t
= t
.replace(/0hh/g,String
.zeroPad(this.getHours(),2));
8513 t
= t
.replace(/hh/g,this.getHours());
8514 t
= t
.replace(/mmm/g,config
.messages
.dates
.shortMonths
[this.getMonth()]);
8515 t
= t
.replace(/0mm/g,String
.zeroPad(this.getMinutes(),2));
8516 t
= t
.replace(/mm/g,this.getMinutes());
8517 t
= t
.replace(/0ss/g,String
.zeroPad(this.getSeconds(),2));
8518 t
= t
.replace(/ss/g,this.getSeconds());
8519 t
= t
.replace(/[ap]m/g,this.getAmPm().toLowerCase());
8520 t
= t
.replace(/[AP]M/g,this.getAmPm().toUpperCase());
8521 t
= t
.replace(/wYYYY/g,this.getYearForWeekNo());
8522 t
= t
.replace(/wYY/g,String
.zeroPad(this.getYearForWeekNo()-2000,2));
8523 t
= t
.replace(/YYYY/g,this.getFullYear());
8524 t
= t
.replace(/YY/g,String
.zeroPad(this.getFullYear()-2000,2));
8525 t
= t
.replace(/MMM/g,config
.messages
.dates
.months
[this.getMonth()]);
8526 t
= t
.replace(/0MM/g,String
.zeroPad(this.getMonth()+1,2));
8527 t
= t
.replace(/MM/g,this.getMonth()+1);
8528 t
= t
.replace(/0WW/g,String
.zeroPad(this.getWeek(),2));
8529 t
= t
.replace(/WW/g,this.getWeek());
8530 t
= t
.replace(/DDD/g,config
.messages
.dates
.days
[this.getDay()]);
8531 t
= t
.replace(/ddd/g,config
.messages
.dates
.shortDays
[this.getDay()]);
8532 t
= t
.replace(/0DD/g,String
.zeroPad(this.getDate(),2));
8533 t
= t
.replace(/DDth/g,this.getDate()+this.daySuffix());
8534 t
= t
.replace(/DD/g,this.getDate());
8538 Date
.prototype.getWeek = function()
8540 var dt
= new Date(this.getTime());
8541 var d
= dt
.getDay();
8542 if (d
==0) d
=7;// JavaScript Sun=0, ISO Sun=7
8543 dt
.setTime(dt
.getTime()+(4-d
)*86400000);// shift day to Thurs of same week to calculate weekNo
8544 var n
= Math
.floor((dt
.getTime()-new Date(dt
.getFullYear(),0,1)+3600000)/86400000);
8545 return Math
.floor(n
/7)+1;
8548 Date
.prototype.getYearForWeekNo = function()
8550 var dt
= new Date(this.getTime());
8551 var d
= dt
.getDay();
8552 if (d
==0) d
=7;// JavaScript Sun=0, ISO Sun=7
8553 dt
.setTime(dt
.getTime()+(4-d
)*86400000);// shift day to Thurs of same week
8554 return dt
.getFullYear();
8557 Date
.prototype.getHours12 = function()
8559 var h
= this.getHours();
8560 return h
> 12 ? h
-12 : ( h
> 0 ? h
: 12 );
8563 Date
.prototype.getAmPm = function()
8565 return this.getHours() >= 12 ? config
.messages
.dates
.pm
: config
.messages
.dates
.am
;
8568 Date
.prototype.daySuffix = function()
8570 return config
.messages
.dates
.daySuffixes
[this.getDate()-1];
8573 // Convert a date to local YYYYMMDDHHMM string format
8574 Date
.prototype.convertToLocalYYYYMMDDHHMM = function()
8576 return String
.zeroPad(this.getFullYear(),4) + String
.zeroPad(this.getMonth()+1,2) + String
.zeroPad(this.getDate(),2) + String
.zeroPad(this.getHours(),2) + String
.zeroPad(this.getMinutes(),2);
8579 // Convert a date to UTC YYYYMMDDHHMM string format
8580 Date
.prototype.convertToYYYYMMDDHHMM = function()
8582 return String
.zeroPad(this.getUTCFullYear(),4) + String
.zeroPad(this.getUTCMonth()+1,2) + String
.zeroPad(this.getUTCDate(),2) + String
.zeroPad(this.getUTCHours(),2) + String
.zeroPad(this.getUTCMinutes(),2);
8585 // Convert a date to UTC YYYYMMDD.HHMMSSMMM string format
8586 Date
.prototype.convertToYYYYMMDDHHMMSSMMM = function()
8588 return String
.zeroPad(this.getUTCFullYear(),4) + String
.zeroPad(this.getUTCMonth()+1,2) + String
.zeroPad(this.getUTCDate(),2) + "." + String
.zeroPad(this.getUTCHours(),2) + String
.zeroPad(this.getUTCMinutes(),2) + String
.zeroPad(this.getUTCSeconds(),2) + String
.zeroPad(this.getUTCMilliseconds(),4);
8591 // Static method to create a date from a UTC YYYYMMDDHHMM format string
8592 Date
.convertFromYYYYMMDDHHMM = function(d
)
8594 return new Date(Date
.UTC(parseInt(d
.substr(0,4),10),
8595 parseInt(d
.substr(4,2),10)-1,
8596 parseInt(d
.substr(6,2),10),
8597 parseInt(d
.substr(8,2),10),
8598 parseInt(d
.substr(10,2),10),0,0));
8602 //-- RGB colour object
8605 // Construct an RGB colour object from a '#rrggbb', '#rgb' or 'rgb(n,n,n)' string or from separate r,g,b values
8611 if(typeof r
== "string") {
8612 if(r
.substr(0,1) == "#") {
8614 this.r
= parseInt(r
.substr(1,2),16)/255;
8615 this.g
= parseInt(r
.substr(3,2),16)/255;
8616 this.b
= parseInt(r
.substr(5,2),16)/255;
8618 this.r
= parseInt(r
.substr(1,1),16)/15;
8619 this.g
= parseInt(r
.substr(2,1),16)/15;
8620 this.b
= parseInt(r
.substr(3,1),16)/15;
8623 var rgbPattern
= /rgb\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/;
8624 var c
= r
.match(rgbPattern
);
8626 this.r
= parseInt(c
[1],10)/255;
8627 this.g
= parseInt(c
[2],10)/255;
8628 this.b
= parseInt(c
[3],10)/255;
8639 // Mixes this colour with another in a specified proportion
8640 // c = other colour to mix
8641 // f = 0..1 where 0 is this colour and 1 is the new colour
8642 // Returns an RGB object
8643 RGB
.prototype.mix = function(c
,f
)
8645 return new RGB(this.r
+ (c
.r
-this.r
) * f
,this.g
+ (c
.g
-this.g
) * f
,this.b
+ (c
.b
-this.b
) * f
);
8648 // Return an rgb colour as a #rrggbb format hex string
8649 RGB
.prototype.toString = function()
8651 return "#" + ("0" + Math
.floor(this.r
.clamp(0,1) * 255).toString(16)).right(2) +
8652 ("0" + Math
.floor(this.g
.clamp(0,1) * 255).toString(16)).right(2) +
8653 ("0" + Math
.floor(this.b
.clamp(0,1) * 255).toString(16)).right(2);
8657 //-- DOM utilities - many derived from www.quirksmode.org
8660 function drawGradient(place
,horiz
,colours
)
8662 for(var t
=0; t
<= 100; t
+=2) {
8663 var bar
= document
.createElement("div");
8664 place
.appendChild(bar
);
8665 bar
.style
.position
= "absolute";
8666 bar
.style
.left
= horiz
? t
+ "%" : 0;
8667 bar
.style
.top
= horiz
? 0 : t
+ "%";
8668 bar
.style
.width
= horiz
? (101-t
) + "%" : "100%";
8669 bar
.style
.height
= horiz
? "100%" : (101-t
) + "%";
8670 bar
.style
.zIndex
= -1;
8672 var p
= f
*(colours
.length
-1);
8673 bar
.style
.backgroundColor
= colours
[Math
.floor(p
)].mix(colours
[Math
.ceil(p
)],p
-Math
.floor(p
)).toString();
8677 function createTiddlyText(theParent
,theText
)
8679 return theParent
.appendChild(document
.createTextNode(theText
));
8682 function createTiddlyCheckbox(theParent
,caption
,checked
,onChange
)
8684 var cb
= document
.createElement("input");
8685 cb
.setAttribute("type","checkbox");
8686 cb
.onclick
= onChange
;
8687 theParent
.appendChild(cb
);
8688 cb
.checked
= checked
;
8689 cb
.className
= "chkOptionInput";
8691 wikify(caption
,theParent
);
8695 function createTiddlyElement(theParent
,theElement
,theID
,theClass
,theText
,attribs
)
8697 var e
= document
.createElement(theElement
);
8698 if(theClass
!= null)
8699 e
.className
= theClass
;
8701 e
.setAttribute("id",theID
);
8703 e
.appendChild(document
.createTextNode(theText
));
8705 for(var n
in attribs
){
8706 e
.setAttribute(n
,attribs
[n
]);
8709 if(theParent
!= null)
8710 theParent
.appendChild(e
);
8714 function addEvent(obj
,type
,fn
)
8716 if(obj
.attachEvent
) {
8717 obj
['e'+type
+fn
] = fn
;
8718 obj
[type
+fn
] = function(){obj
['e'+type
+fn
](window
.event
);};
8719 obj
.attachEvent('on'+type
,obj
[type
+fn
]);
8721 obj
.addEventListener(type
,fn
,false);
8725 function removeEvent(obj
,type
,fn
)
8727 if(obj
.detachEvent
) {
8728 obj
.detachEvent('on'+type
,obj
[type
+fn
]);
8729 obj
[type
+fn
] = null;
8731 obj
.removeEventListener(type
,fn
,false);
8735 function addClass(e
,theClass
)
8737 var currClass
= e
.className
.split(" ");
8738 if(currClass
.indexOf(theClass
) == -1)
8739 e
.className
+= " " + theClass
;
8742 function removeClass(e
,theClass
)
8744 var currClass
= e
.className
.split(" ");
8745 var i
= currClass
.indexOf(theClass
);
8747 currClass
.splice(i
,1);
8748 i
= currClass
.indexOf(theClass
);
8750 e
.className
= currClass
.join(" ");
8753 function hasClass(e
,theClass
)
8756 if(e
.className
.split(" ").indexOf(theClass
) != -1)
8762 // Find the closest relative with a given property value (property defaults to tagName, relative defaults to parentNode)
8763 function findRelated(e
,value
,name
,relative
)
8765 name
= name
? name
: "tagName";
8766 relative
= relative
? relative
: "parentNode";
8767 if(name
== "className") {
8768 while(e
&& !hasClass(e
,value
)) {
8772 while(e
&& e
[name
] != value
) {
8779 // Resolve the target object of an event
8780 function resolveTarget(e
)
8785 else if(e
.srcElement
)
8787 if(obj
.nodeType
== 3) // defeat Safari bug
8788 obj
= obj
.parentNode
;
8792 // Prevent an event from bubbling
8793 function stopEvent(e
){
8794 var ev
= e
? e
: window
.event
;
8795 ev
.cancelBubble
= true;
8796 if (ev
.stopPropagation
) ev
.stopPropagation();
8800 // Return the content of an element as plain text with no formatting
8801 function getPlainText(e
)
8806 else if(e
.textContent
)
8807 text
= e
.textContent
;
8811 // Get the scroll position for window.scrollTo necessary to scroll a given element into view
8812 function ensureVisible(e
)
8814 var posTop
= findPosY(e
);
8815 var posBot
= posTop
+ e
.offsetHeight
;
8816 var winTop
= findScrollY();
8817 var winHeight
= findWindowHeight();
8818 var winBot
= winTop
+ winHeight
;
8819 if(posTop
< winTop
) {
8821 } else if(posBot
> winBot
) {
8822 if(e
.offsetHeight
< winHeight
)
8823 return posTop
- (winHeight
- e
.offsetHeight
);
8831 // Get the current width of the display window
8832 function findWindowWidth()
8834 return window
.innerWidth
? window
.innerWidth
: document
.documentElement
.clientWidth
;
8837 // Get the current height of the display window
8838 function findWindowHeight()
8840 return window
.innerHeight
? window
.innerHeight
: document
.documentElement
.clientHeight
;
8843 // Get the current horizontal page scroll position
8844 function findScrollX()
8846 return window
.scrollX
? window
.scrollX
: document
.documentElement
.scrollLeft
;
8849 // Get the current vertical page scroll position
8850 function findScrollY()
8852 return window
.scrollY
? window
.scrollY
: document
.documentElement
.scrollTop
;
8855 function findPosX(obj
)
8858 while(obj
.offsetParent
) {
8859 curleft
+= obj
.offsetLeft
;
8860 obj
= obj
.offsetParent
;
8865 function findPosY(obj
)
8868 while(obj
.offsetParent
) {
8869 curtop
+= obj
.offsetTop
;
8870 obj
= obj
.offsetParent
;
8875 // Blur a particular element
8876 function blurElement(e
)
8878 if(e
!= null && e
.focus
&& e
.blur
) {
8884 // Create a non-breaking space
8885 function insertSpacer(place
)
8887 var e
= document
.createTextNode(String
.fromCharCode(160));
8889 place
.appendChild(e
);
8893 // Remove all children of a node
8894 function removeChildren(e
)
8896 while(e
&& e
.hasChildNodes())
8897 removeNode(e
.firstChild
);
8900 // Remove a node and all it's children
8901 function removeNode(e
)
8904 e
.parentNode
.removeChild(e
);
8907 // Remove any event handlers or non-primitve custom attributes
8908 function scrubNode(e
)
8910 if(!config
.browser
.isIE
)
8912 var att
= e
.attributes
;
8914 for(var t
=0; t
<att
.length
; t
++) {
8915 var n
= att
[t
].name
;
8916 if(n
!== 'style' && (typeof e
[n
] === 'function' || (typeof e
[n
] === 'object' && e
[n
] != null))) {
8924 var c
= e
.firstChild
;
8931 // Add a stylesheet, replacing any previous custom stylesheet
8932 function setStylesheet(s
,id
,doc
)
8935 id
= "customStyleSheet";
8938 var n
= doc
.getElementById(id
);
8939 if(doc
.createStyleSheet
) {
8940 // Test for IE's non-standard createStyleSheet method
8942 n
.parentNode
.removeChild(n
);
8943 // This failed without the
8944 doc
.getElementsByTagName("head")[0].insertAdjacentHTML("beforeEnd"," <style id='" + id
+ "'>" + s
+ "</style>");
8947 n
.replaceChild(doc
.createTextNode(s
),n
.firstChild
);
8949 n
= doc
.createElement("style");
8950 n
.type
= "text/css";
8952 n
.appendChild(doc
.createTextNode(s
));
8953 doc
.getElementsByTagName("head")[0].appendChild(n
);
8958 function removeStyleSheet(id
)
8960 var e
= document
.getElementById(id
);
8962 e
.parentNode
.removeChild(e
);
8965 // Force the browser to do a document reflow when needed to workaround browser bugs
8966 function forceReflow()
8968 if(config
.browser
.isGecko
) {
8969 setStylesheet("body {top:-1em;margin-top:1em;}");
8974 // Replace the current selection of a textarea or text input and scroll it into view
8975 function replaceSelection(e
,text
)
8977 if(e
.setSelectionRange
) {
8978 var oldpos
= e
.selectionStart
;
8979 var isRange
= e
.selectionEnd
> e
.selectionStart
;
8980 e
.value
= e
.value
.substr(0,e
.selectionStart
) + text
+ e
.value
.substr(e
.selectionEnd
);
8981 e
.setSelectionRange(isRange
? oldpos
: oldpos
+ text
.length
,oldpos
+ text
.length
);
8982 var linecount
= e
.value
.split('\n').length
;
8983 var thisline
= e
.value
.substr(0,e
.selectionStart
).split('\n').length
-1;
8984 e
.scrollTop
= Math
.floor((thisline
- e
.rows
/ 2) * e
.scrollHeight
/ linecount
);
8985 } else if(document
.selection
) {
8986 var range
= document
.selection
.createRange();
8987 if(range
.parentElement() == e
) {
8988 var isCollapsed
= range
.text
== "";
8991 range
.moveStart('character', -text
.length
);
8998 // Returns the text of the given (text) node, possibly merging subsequent text nodes
8999 function getNodeText(e
)
9002 while(e
&& e
.nodeName
== "#text") {
9010 //-- LoaderBase and SaverBase
9013 function LoaderBase() {}
9015 LoaderBase
.prototype.loadTiddler = function(store
,node
,tiddlers
)
9017 var title
= this.getTitle(store
,node
);
9019 var tiddler
= store
.createTiddler(title
);
9020 this.internalizeTiddler(store
,tiddler
,title
,node
);
9021 tiddlers
.push(tiddler
);
9025 LoaderBase
.prototype.loadTiddlers = function(store
,nodes
)
9028 for(var t
= 0; t
< nodes
.length
; t
++) {
9030 this.loadTiddler(store
,nodes
[t
],tiddlers
);
9032 showException(ex
,config
.messages
.tiddlerLoadError
.format([this.getTitle(store
,nodes
[t
])]));
9038 function SaverBase() {}
9040 SaverBase
.prototype.externalize = function(store
)
9043 var tiddlers
= store
.getTiddlers("title");
9044 for(var t
= 0; t
< tiddlers
.length
; t
++)
9045 results
.push(this.externalizeTiddler(store
,tiddlers
[t
]));
9046 return results
.join("\n");
9050 //-- TW21Loader (inherits from LoaderBase)
9053 function TW21Loader() {}
9055 TW21Loader
.prototype = new LoaderBase();
9057 TW21Loader
.prototype.getTitle = function(store
,node
)
9060 if(node
.getAttribute
) {
9061 title
= node
.getAttribute("title");
9063 title
= node
.getAttribute("tiddler");
9065 if(!title
&& node
.id
) {
9066 var lenPrefix
= store
.idPrefix
.length
;
9067 if (node
.id
.substr(0,lenPrefix
) == store
.idPrefix
)
9068 title
= node
.id
.substr(lenPrefix
);
9073 TW21Loader
.prototype.internalizeTiddler = function(store
,tiddler
,title
,node
)
9075 var e
= node
.firstChild
;
9077 if(node
.getAttribute("tiddler")) {
9078 text
= getNodeText(e
).unescapeLineBreaks();
9080 while(e
.nodeName
!="PRE" && e
.nodeName
!="pre") {
9083 text
= e
.innerHTML
.replace(/\r/mg,"").htmlDecode();
9085 var modifier
= node
.getAttribute("modifier");
9086 var c
= node
.getAttribute("created");
9087 var m
= node
.getAttribute("modified");
9088 var created
= c
? Date
.convertFromYYYYMMDDHHMM(c
) : version
.date
;
9089 var modified
= m
? Date
.convertFromYYYYMMDDHHMM(m
) : created
;
9090 var tags
= node
.getAttribute("tags");
9092 var attrs
= node
.attributes
;
9093 for(var i
= attrs
.length
-1; i
>= 0; i
--) {
9094 var name
= attrs
[i
].name
;
9095 if (attrs
[i
].specified
&& !TiddlyWiki
.isStandardField(name
)) {
9096 fields
[name
] = attrs
[i
].value
.unescapeLineBreaks();
9099 tiddler
.assign(title
,text
,modifier
,modified
,tags
,created
,fields
);
9104 //-- TW21Saver (inherits from SaverBase)
9107 function TW21Saver() {}
9109 TW21Saver
.prototype = new SaverBase();
9111 TW21Saver
.prototype.externalizeTiddler = function(store
,tiddler
)
9114 var extendedAttributes
= "";
9115 var usePre
= config
.options
.chkUsePreForStorage
;
9116 store
.forEachField(tiddler
,
9117 function(tiddler
,fieldName
,value
) {
9118 // don't store stuff from the temp namespace
9119 if(typeof value
!= "string")
9121 if (!fieldName
.match(/^temp\./))
9122 extendedAttributes
+= ' %0="%1"'.format([fieldName
,value
.escapeLineBreaks().htmlEncode()]);
9124 var created
= tiddler
.created
.convertToYYYYMMDDHHMM();
9125 var modified
= tiddler
.modified
.convertToYYYYMMDDHHMM();
9126 var vdate
= version
.date
.convertToYYYYMMDDHHMM();
9127 var attributes
= tiddler
.modifier
? ' modifier="' + tiddler
.modifier
.htmlEncode() + '"' : "";
9128 attributes
+= (usePre
&& modified
== created
) ? "" : ' modified="' + modified
+'"';
9129 attributes
+= (usePre
&& created
== vdate
) ? "" :' created="' + created
+ '"';
9130 var tags
= tiddler
.getTags();
9132 attributes
+= ' tags="' + tags
.htmlEncode() + '"';
9133 return ('<div %0="%1"%2%3>%4</'+'div>').format([
9134 usePre
? "title" : "tiddler",
9135 tiddler
.title
.htmlEncode(),
9138 usePre
? "\n<pre>" + tiddler
.text
.htmlEncode() + "</pre>\n" : tiddler
.text
.escapeLineBreaks().htmlEncode()
9141 throw exceptionText(ex
,config
.messages
.tiddlerSaveError
.format([tiddler
.title
]));
9150 <script type=
"text/javascript">
9153 document
.write("<applet style='position:absolute;left:-1px' name='TiddlySaver' code='TiddlySaver.class' archive='TiddlySaver.jar' width='1' height='1'></applet>");
9156 <!--POST-SCRIPT-START-->
9158 <!--POST-SCRIPT-END-->