PageRenderTime 78ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/rdbms/doc/rdbms.html

http://github.com/gebi/jungerl
HTML | 4272 lines | 4045 code | 194 blank | 33 comment | 0 complexity | 717ff9ac828a9d56723462f69c458b51 MD5 | raw file
Possible License(s): AGPL-1.0, JSON, LGPL-2.1, BSD-3-Clause
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  2. <script type="text/javascript">
  3. var version = {major: 1, minor: 2, revision: 32, date: new Date("Aug 18, 2005"), extensions: {}};
  4. </script>
  5. <!--
  6. TiddlyWiki 1.2.32 by Jeremy Ruston, (jeremy [at] osmosoft [dot] com)
  7. Published under a BSD open source license
  8. Incorporating improvements by Isao Sonobe, http://www-gauge.scphys.kyoto-u.ac.jp/~sonobe/OgreKit/OgreKitWiki.html
  9. Copyright (c) Osmosoft Limited, 14 April 2005
  10. Redistribution and use in source and binary forms, with or without modification,
  11. are permitted provided that the following conditions are met:
  12. Redistributions of source code must retain the above copyright notice, this
  13. list of conditions and the following disclaimer.
  14. Redistributions in binary form must reproduce the above copyright notice, this
  15. list of conditions and the following disclaimer in the documentation and/or other
  16. materials provided with the distribution.
  17. Neither the name of the Osmosoft Limited nor the names of its contributors may be
  18. used to endorse or promote products derived from this software without specific
  19. prior written permission.
  20. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
  21. EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  22. OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
  23. SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  24. INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
  25. TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
  26. BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  27. CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  28. ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  29. DAMAGE.
  30. -->
  31. <html>
  32. <head>
  33. <meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
  34. <title>RDBMS</title>
  35. <script type="text/javascript">
  36. // ---------------------------------------------------------------------------------
  37. // Configuration repository
  38. // ---------------------------------------------------------------------------------
  39. var config = {
  40. // Options that can be set in the options panel and/or cookies
  41. options: {
  42. chkRegExpSearch: false,
  43. chkCaseSensitiveSearch: false,
  44. chkAnimate: true,
  45. txtUserName: "YourName",
  46. chkSaveBackups: true,
  47. chkAutoSave: false,
  48. chkGenerateAnRssFeed: false,
  49. chkSaveEmptyTemplate: false,
  50. chkOpenInNewWindow: true,
  51. chkToggleLinks: false,
  52. chkHttpReadOnly: false,
  53. txtMainTab: "tabTimeline",
  54. txtMoreTab: "moreTabAll"
  55. },
  56. // Hashmap of notification functions to be called when certain tiddlers are changed or deleted
  57. notifyNamedTiddlers: {
  58. SiteTitle: refreshTitle,
  59. SiteSubtitle: refreshSubtitle,
  60. SideBarOptions: refreshSidebar,
  61. StyleSheet: refreshStyles
  62. },
  63. // List of notification functions to be called when any tiddler is changed or deleted
  64. notifyTiddlers: [
  65. refreshMenu,
  66. refreshStory,
  67. refreshTabs
  68. ],
  69. // Shadow tiddlers for emergencies
  70. shadowTiddlers: {
  71. SideBarOptions: "<<search>><<closeAll>><<permaview>><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel options 'Change TiddlyWiki advanced options'>>",
  72. OptionsPanel: "These InterfaceOptions for customising TiddlyWiki are saved in your browser\n\nYour username for signing your edits. Write it as a WikiWord (eg JoeBloggs)\n\n<<option txtUserName>>\n<<option chkSaveBackups>> SaveBackups\n<<option chkAutoSave>> AutoSave\n<<option chkGenerateAnRssFeed>> GenerateAnRssFeed\n<<option chkRegExpSearch>> RegExpSearch\n<<option chkCaseSensitiveSearch>> CaseSensitiveSearch\n<<option chkAnimate>> EnableAnimations\n\nSee AdvancedOptions",
  73. AdvancedOptions: "<<option chkOpenInNewWindow>> OpenLinksInNewWindow\n<<option chkSaveEmptyTemplate>> SaveEmptyTemplate\n<<option chkToggleLinks>> Clicking on links to tiddlers that are already open causes them to close\n^^(override with Control or other modifier key)^^\n<<option chkHttpReadOnly>> HideEditingFeatures when viewed over HTTP",
  74. SideBarTabs: "<<tabs txtMainTab Timeline Timeline TabTimeline All 'All tiddlers' TabAll Tags 'All tags' TabTags More 'More lists' TabMore>>",
  75. TabTimeline: "<<timeline>>",
  76. TabAll: "<<list all>>",
  77. TabTags: "<<allTags>>",
  78. TabMore: "<<tabs txtMoreTab Missing 'Missing tiddlers' TabMoreMissing Orphans 'Orphaned tiddlers' TabMoreOrphans>>",
  79. TabMoreMissing: "<<list missing>>",
  80. TabMoreOrphans: "<<list orphans>>"
  81. },
  82. // Miscellaneous options
  83. numRssItems: 20, // Number of items in the RSS feed
  84. animFast: 0.12, // Speed for animations (lower == slower)
  85. animSlow: 0.01, // Speed for EasterEgg animations
  86. // Messages
  87. messages: {
  88. customConfigError: "Error in customConfig - %0",
  89. savedSnapshotError: "It appears that this TiddlyWiki has been incorrectly saved. Please see http://www.tiddlywiki.com/#DownloadSoftware for details",
  90. subtitleUnknown: "(unknown)",
  91. undefinedTiddlerToolTip: "The tiddler '%0' doesn't yet exist",
  92. externalLinkTooltip: "External link to %0",
  93. noTags: "There are no tagged tiddlers",
  94. notFileUrlError: "You need to save this TiddlyWiki to a file before you can save changes",
  95. cantSaveError: "It's not possible to save changes using this browser. Use FireFox if you can",
  96. invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
  97. backupSaved: "Backup saved",
  98. backupFailed: "Failed to save backup file",
  99. rssSaved: "RSS feed saved",
  100. rssFailed: "Failed to save RSS feed file",
  101. emptySaved: "Empty template saved",
  102. emptyFailed: "Failed to save empty template file",
  103. mainSaved: "Main TiddlyWiki file saved",
  104. mainFailed: "Failed to save main TiddlyWiki file. Your changes have not been saved",
  105. macroError: "Error executing macro '%0'",
  106. overwriteWarning: "A tiddler named '%0' already exists. Choose OK to overwrite it",
  107. unsavedChangesWarning: "WARNING! There are unsaved changes in TiddlyWiki\n\nChoose OK to save\nChoose CANCEL to discard",
  108. dates: {
  109. months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November","December"],
  110. days: ["Sunday", "Monday","Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
  111. }
  112. },
  113. views: {
  114. wikified: {
  115. tag: {labelNoTags: "no tags", labelTags: "tags: ", tooltip: "Show tiddlers tagged with '%0'", openAllText: "Open all", openAllTooltip: "Open all of these tiddlers", popupNone: "No other tiddlers tagged with '%0'"},
  116. toolbarClose: {text: "close", tooltip: "Close this tiddler"},
  117. toolbarEdit: {text: "edit", tooltip: "Edit this tiddler"},
  118. toolbarPermalink: {text: "permalink", tooltip: "Permalink for this tiddler"},
  119. toolbarReferences: {text: "references", tooltip: "Show tiddlers that link to this one", popupNone: "No references"},
  120. defaultText: "The tiddler '%0' doesn't yet exist. Double-click to create it"
  121. },
  122. editor: {
  123. tagPrompt: "Type tags separated with spaces, [[use double square brackets]] if necessary, or add existing",
  124. tagChooser: {text: "tags", tooltip: "Choose existing tags to add to this tiddler", popupNone: "There are no tags defined", tagTooltip: "Add the tag '%0'"},
  125. toolbarDone: {text: "done", tooltip: "Save changes to this tiddler"},
  126. toolbarCancel: {text: "cancel", tooltip: "Undo changes to this tiddler"},
  127. toolbarDelete: {text: "delete", tooltip: "Delete this tiddler"},
  128. defaultText: "Type the text for '%0'"
  129. }
  130. },
  131. macros: { // Each has a 'handler' member that is inserted later
  132. today: {},
  133. version: {},
  134. search: {label: "search", prompt: "Search this TiddlyWiki", sizeTextbox: 15, successMsg: "%0 tiddlers found matching %1", failureMsg: "No tiddlers found matching %0"},
  135. tiddler: {},
  136. tag: {},
  137. timeline: {dateFormat: "DD MMM YYYY"},
  138. allTags: {tooltip: "Show tiddlers tagged with '%0'", noTags: "There are no tagged tiddlers"},
  139. list: {
  140. all: {prompt: "All tiddlers in alphabetical order"},
  141. missing: {prompt: "Tiddlers that have links to them but are not defined"},
  142. orphans: {prompt: "Tiddlers that are not linked to from any other tiddlers"}
  143. },
  144. closeAll: {label: "close all", prompt: "Close all displayed tiddlers (except any that are being edited)"},
  145. permaview: {label: "permaview", prompt: "Link to an URL that retrieves all the currently displayed tiddlers"},
  146. saveChanges: {label: "save changes", prompt: "Save all tiddlers to create a new TiddlyWiki"},
  147. slider: {},
  148. option: {},
  149. newTiddler: {label: "new tiddler", prompt: "Create a new tiddler", title: "New Tiddler"},
  150. newJournal: {label: "new journal", prompt: "Create a new tiddler from the current date and time"},
  151. sparkline: {},
  152. tabs: {}
  153. }
  154. };
  155. // ---------------------------------------------------------------------------------
  156. // Main
  157. // ---------------------------------------------------------------------------------
  158. // TiddlyWiki storage
  159. var store = new TiddlyWiki();
  160. // Animation engine
  161. var anim = new Animator();
  162. var readOnly = false;
  163. // Starting up
  164. function main()
  165. {
  166. setupRegexp();
  167. saveTest();
  168. loadOptionsCookie();
  169. var s;
  170. for(s in config.notifyNamedTiddlers)
  171. store.addNotification(s,config.notifyNamedTiddlers[s]);
  172. for(s=0; s<config.notifyTiddlers.length; s++)
  173. store.addNotification(null,config.notifyTiddlers[s]);
  174. store.loadFromDiv("storeArea","store");
  175. loadSystemConfig();
  176. readOnly = (document.location.toString().substr(0,7) == "http://") ? config.options.chkHttpReadOnly : false;
  177. store.notifyAll();
  178. var start = store.getTiddlerText("DefaultTiddlers");
  179. if(window.location.hash)
  180. displayTiddlers(null,convertUTF8ToUnicode(decodeURI(window.location.hash.substr(1))),1,null,null);
  181. else if(start)
  182. displayTiddlers(null,start,1,null,null);
  183. }
  184. function saveTest()
  185. {
  186. var saveTest = document.getElementById("saveTest");
  187. if(saveTest.hasChildNodes())
  188. alert(config.messages.savedSnapshotError);
  189. saveTest.appendChild(document.createTextNode("savetest"));
  190. }
  191. function loadSystemConfig()
  192. {
  193. var configTiddlers = store.getTaggedTiddlers("systemConfig");
  194. for(var t=0; t<configTiddlers.length; t++)
  195. {
  196. var ex = processConfig(configTiddlers[t].text);
  197. if(ex)
  198. displayMessage(config.messages.customConfigError.format([ex]));
  199. }
  200. }
  201. // ---------------------------------------------------------------------------------
  202. // Macro definitions
  203. // ---------------------------------------------------------------------------------
  204. config.macros.today.handler = function(place)
  205. {
  206. createTiddlyElement(place,"span",null,null,(new Date()).toLocaleString());
  207. }
  208. config.macros.version.handler = function(place)
  209. {
  210. createTiddlyElement(place,"span",null,null,version.major + "." + version.minor + "." + version.revision);
  211. }
  212. config.macros.list.handler = function(place,macroName,params)
  213. {
  214. var type = params[0] ? params[0] : "all";
  215. var theList = document.createElement("ul");
  216. place.appendChild(theList);
  217. if(this[type].prompt)
  218. createTiddlyElement(theList,"li",null,"listTitle",this[type].prompt);
  219. var results;
  220. if(this[type].handler)
  221. results = this[type].handler(params);
  222. for (t = 0; t < results.length; t++)
  223. {
  224. theListItem = document.createElement("li")
  225. theList.appendChild(theListItem);
  226. if(typeof results[t] == "string")
  227. createTiddlyLink(theListItem,results[t],true);
  228. else
  229. createTiddlyLink(theListItem,results[t].title,true);
  230. }
  231. }
  232. config.macros.list.all.handler = function(params)
  233. {
  234. return store.reverseLookup("tags","excludeLists",false,"title");
  235. }
  236. config.macros.list.missing.handler = function(params)
  237. {
  238. return store.getMissingLinks();
  239. }
  240. config.macros.list.orphans.handler = function(params)
  241. {
  242. return store.getOrphans();
  243. }
  244. config.macros.allTags.handler = function(place,macroName,params)
  245. {
  246. var tags = store.getTags();
  247. var theDateList = createTiddlyElement(place,"ul",null,null,null);
  248. if(tags.length == 0)
  249. createTiddlyElement(theDateList,"li",null,"listTitle",this.noTags);
  250. for (t=0; t<tags.length; t++)
  251. {
  252. var theListItem =createTiddlyElement(theDateList,"li",null,null,null);
  253. var theTag = createTiddlyButton(theListItem,tags[t][0] + " (" + tags[t][1] + ")",this.tooltip.format([tags[t][0]]),onClickTag);
  254. theTag.setAttribute("tag",tags[t][0]);
  255. }
  256. }
  257. config.macros.timeline.handler = function(place,macroName,params)
  258. {
  259. var tiddlers = store.reverseLookup("tags","excludeLists",false,"modified");
  260. var lastDay = "";
  261. for (t=tiddlers.length-1; t>=0; t--)
  262. {
  263. var tiddler = tiddlers[t];
  264. var theDay = tiddler.modified.convertToYYYYMMDDHHMM().substr(0,8);
  265. if(theDay != lastDay)
  266. {
  267. var theDateList = document.createElement("ul");
  268. place.appendChild(theDateList);
  269. createTiddlyElement(theDateList,"li",null,"listTitle",tiddler.modified.formatString(this.dateFormat));
  270. lastDay = theDay;
  271. }
  272. var theDateListItem = createTiddlyElement(theDateList,"li",null,"listLink",null);
  273. theDateListItem.appendChild(createTiddlyLink(place,tiddler.title,true));
  274. }
  275. }
  276. config.macros.search.handler = function(place,macroName,params)
  277. {
  278. var lastSearchText = "";
  279. var searchTimeout = null;
  280. var doSearch = function(txt)
  281. {
  282. closeAllTiddlers();
  283. var matches = store.search(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch,"title","excludeSearch");
  284. for(var t=matches.length-1; t>=0; t--)
  285. displayTiddler(null,matches[t].title,0,txt.value,config.options.chkCaseSensitiveSearch,false,false);
  286. var q = config.options.chkRegExpSearch ? "/" : "'";
  287. if(matches.length > 0)
  288. displayMessage(config.macros.search.successMsg.format([matches.length.toString(),q + txt.value + q]));
  289. else
  290. displayMessage(config.macros.search.failureMsg.format([q + txt.value + q]));
  291. lastSearchText = txt.value;
  292. };
  293. var clickHandler = function(e)
  294. {
  295. doSearch(this.nextSibling);
  296. };
  297. var keyHandler = function(e)
  298. {
  299. if (!e) var e = window.event;
  300. switch(e.keyCode)
  301. {
  302. case 27:
  303. this.value = "";
  304. clearMessage();
  305. break;
  306. }
  307. if((this.value.length > 2) && (this.value != lastSearchText))
  308. {
  309. if(searchTimeout)
  310. clearTimeout(searchTimeout);
  311. var txt = this;
  312. searchTimeout = setTimeout(function() {doSearch(txt);},200);
  313. }
  314. };
  315. var focusHandler = function(e)
  316. {
  317. this.select();
  318. };
  319. var btn = createTiddlyButton(place,this.label,this.prompt,clickHandler);
  320. var txt = createTiddlyElement(place,"input",null,null,null);
  321. if(params[0])
  322. txt.value = params[0];
  323. txt.onkeyup = keyHandler;
  324. txt.onfocus = focusHandler;
  325. txt.setAttribute("size",this.sizeTextbox);
  326. txt.setAttribute("autocomplete","off");
  327. if(navigator.userAgent.toLowerCase().indexOf("safari") == -1)
  328. txt.setAttribute("type","text");
  329. else
  330. {
  331. txt.setAttribute("type","search");
  332. txt.setAttribute("results","5");
  333. }
  334. }
  335. config.macros.tiddler.handler = function(place,macroName,params)
  336. {
  337. var wrapper = createTiddlyElement(place,"span",null,params[1] ? params[1] : null,null);
  338. var text = store.getTiddlerText(params[0]);
  339. if(text)
  340. wikify(text,wrapper,null,null);
  341. }
  342. config.macros.tag.handler = function(place,macroName,params)
  343. {
  344. createTagButton(place,params[0]);
  345. }
  346. config.macros.closeAll.handler = function(place)
  347. {
  348. createTiddlyButton(place,this.label,this.prompt,closeAllTiddlers);
  349. }
  350. config.macros.permaview.handler = function(place)
  351. {
  352. createTiddlyButton(place,this.label,this.prompt,onClickPermaView);
  353. }
  354. config.macros.saveChanges.handler = function(place)
  355. {
  356. if(!readOnly)
  357. createTiddlyButton(place,this.label,this.prompt,saveChanges);
  358. }
  359. config.macros.slider.onClickSlider = function(e)
  360. {
  361. if (!e) var e = window.event;
  362. var n = this.nextSibling;
  363. var cookie = n.getAttribute("cookie");
  364. var isOpen = n.style.display != "none";
  365. if(config.options.chkAnimate)
  366. anim.startAnimating(new Slider(n,!isOpen,e.shiftKey || e.altKey,"none"));
  367. else
  368. n.style.display = isOpen ? "none" : "block";
  369. config.options[cookie] = !isOpen;
  370. saveOptionCookie(cookie);
  371. }
  372. config.macros.slider.handler = function(place,macroName,params)
  373. {
  374. var cookie = params[0] ? params[0] : "";
  375. var text = store.getTiddlerText(params[1]);
  376. var btn = createTiddlyButton(place,params[2],params[3],this.onClickSlider);
  377. var panel = createTiddlyElement(place,"div",null,"sliderPanel",null);
  378. panel.setAttribute("cookie",cookie);
  379. panel.style.display = config.options[cookie] ? "block" : "none";
  380. if(text)
  381. wikify(text,panel,null,null);
  382. }
  383. config.macros.option.onChangeOption = function(e)
  384. {
  385. var opt = this.getAttribute("option");
  386. var elementType,valueField;
  387. if(opt)
  388. {
  389. switch(opt.substr(0,3))
  390. {
  391. case "txt":
  392. elementType = "input";
  393. valueField = "value";
  394. break;
  395. case "chk":
  396. elementType = "input";
  397. valueField = "checked";
  398. break;
  399. }
  400. config.options[opt] = this[valueField];
  401. saveOptionCookie(opt);
  402. var nodes = document.getElementsByTagName(elementType);
  403. for(var t=0; t<nodes.length; t++)
  404. {
  405. var optNode = nodes[t].getAttribute("option");
  406. if(opt == optNode)
  407. nodes[t][valueField] = this[valueField];
  408. }
  409. }
  410. return(true);
  411. }
  412. config.macros.option.handler = function(place,macroName,params)
  413. {
  414. var opt = params[0];
  415. if(config.options[opt] == undefined)
  416. return;
  417. var c;
  418. switch(opt.substr(0,3))
  419. {
  420. case "txt":
  421. c = document.createElement("input");
  422. c.onkeyup = this.onChangeOption;
  423. c.setAttribute("option",opt);
  424. c.size = 15;
  425. c.value = config.options[opt];
  426. place.appendChild(c);
  427. break;
  428. case "chk":
  429. c = document.createElement("input");
  430. c.setAttribute("type","checkbox");
  431. c.onclick = this.onChangeOption;
  432. c.setAttribute("option",opt);
  433. c.checked = config.options[opt];
  434. place.appendChild(c);
  435. break;
  436. }
  437. }
  438. config.macros.newTiddler.onClick = function()
  439. {
  440. displayTiddler(null,config.macros.newTiddler.title,2,null,null,false,false);
  441. var e = document.getElementById("editorTitle" + config.macros.newTiddler.title);
  442. e.focus();
  443. e.select();
  444. }
  445. config.macros.newTiddler.handler = function(place)
  446. {
  447. if(!readOnly)
  448. createTiddlyButton(place,this.label,this.prompt,this.onClick);
  449. }
  450. config.macros.newJournal.handler = function(place,macroName,params)
  451. {
  452. if(!readOnly)
  453. {
  454. var now = new Date();
  455. var title = now.formatString(params[0].trim());
  456. var createJournal = function() {
  457. displayTiddler(null,title,2,null,null,false,false);
  458. var tagsBox = document.getElementById("editorTags" + title);
  459. if(tagsBox && params[1])
  460. tagsBox.value += " " + String.encodeTiddlyLink(params[1]);
  461. };
  462. createTiddlyButton(place,this.label,this.prompt,createJournal);
  463. }
  464. }
  465. config.macros.sparkline.handler = function(place,macroName,params)
  466. {
  467. var data = [];
  468. var min = 0;
  469. var max = 0;
  470. for(var t=0; t<params.length; t++)
  471. {
  472. var v = parseInt(params[t]);
  473. if(v < min)
  474. min = v;
  475. if(v > max)
  476. max = v;
  477. data.push(v);
  478. }
  479. if(data.length < 1)
  480. return;
  481. var box = createTiddlyElement(place,"span",null,"sparkline",String.fromCharCode(160));
  482. box.title = data.join(",");
  483. var w = box.offsetWidth;
  484. var h = box.offsetHeight;
  485. box.style.paddingRight = (data.length * 2 - w) + "px";
  486. box.style.position = "relative";
  487. for(var d=0; d<data.length; d++)
  488. {
  489. var tick = document.createElement("img");
  490. tick.border = 0;
  491. tick.className = "sparktick";
  492. tick.style.position = "absolute";
  493. 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";
  494. tick.style.left = d*2 + "px";
  495. tick.style.width = "2px";
  496. var v = Math.floor(((data[d] - min)/(max-min)) * h);
  497. tick.style.top = (h-v) + "px";
  498. tick.style.height = v + "px";
  499. box.appendChild(tick);
  500. }
  501. }
  502. config.macros.tabs.handler = function(place,macroName,params)
  503. {
  504. var cookie = params[0];
  505. var numTabs = (params.length-1)/3;
  506. var wrapper = createTiddlyElement(place,"div",null,cookie,null);
  507. var tabset = createTiddlyElement(wrapper,"div",null,"tabset",null);
  508. tabset.setAttribute("cookie",cookie);
  509. var validTab = false;
  510. for(var t=0; t<numTabs; t++)
  511. {
  512. var label = params[t*3+1];
  513. var prompt = params[t*3+2];
  514. var content = params[t*3+3];
  515. var tab = createTiddlyButton(tabset,label,prompt,this.onClickTab,"tab tabUnselected");
  516. tab.setAttribute("href","javascript:;");
  517. tab.onclick = this.onClickTab;
  518. tab.setAttribute("tab",label);
  519. tab.setAttribute("content",content);
  520. tab.title = prompt;
  521. if(config.options[cookie] == label)
  522. validTab = true;
  523. }
  524. if(!validTab)
  525. config.options[cookie] = params[1];
  526. this.switchTab(tabset,config.options[cookie]);
  527. }
  528. config.macros.tabs.onClickTab = function(e)
  529. {
  530. config.macros.tabs.switchTab(this.parentNode,this.getAttribute("tab"));
  531. }
  532. config.macros.tabs.switchTab = function(tabset,tab)
  533. {
  534. var cookie = tabset.getAttribute("cookie");
  535. var theTab = null
  536. var nodes = tabset.childNodes;
  537. for(var t=0; t<nodes.length; t++)
  538. if(nodes[t].getAttribute && nodes[t].getAttribute("tab") == tab)
  539. {
  540. theTab = nodes[t];
  541. theTab.className = "tab tabSelected";
  542. }
  543. else
  544. nodes[t].className = "tab tabUnselected"
  545. if(theTab)
  546. {
  547. if(tabset.nextSibling && tabset.nextSibling.className == "tabContents")
  548. tabset.parentNode.removeChild(tabset.nextSibling);
  549. var tabContent = createTiddlyElement(null,"div",null,"tabContents",null);
  550. tabset.parentNode.insertBefore(tabContent,tabset.nextSibling);
  551. wikify(store.getTiddlerText(theTab.getAttribute("content")),tabContent,null,null);
  552. if(cookie)
  553. {
  554. config.options[cookie] = tab;
  555. saveOptionCookie(cookie);
  556. }
  557. }
  558. }
  559. // ---------------------------------------------------------------------------------
  560. // Config and macro stuff
  561. // ---------------------------------------------------------------------------------
  562. // Merge a custom configuration over the top of the current configuration
  563. // Returns a string error message or null if it went OK
  564. function processConfig(customConfig)
  565. {
  566. try
  567. {
  568. if(customConfig && customConfig != "")
  569. window.eval(customConfig);
  570. }
  571. catch(e)
  572. {
  573. return(e.toString());
  574. }
  575. return null;
  576. }
  577. // Render a macro
  578. function insertMacro(place,macroName,macroParams)
  579. {
  580. var params = macroParams.readMacroParams();
  581. try
  582. {
  583. var macro = config.macros[macroName];
  584. if(macro && macro.handler)
  585. macro.handler(place,macroName,params);
  586. else
  587. createTiddlyElement(place,"span",null,"errorNoSuchMacro","<<" + macroName + ">>");
  588. }
  589. catch(e)
  590. {
  591. displayMessage(config.messages.macroError.format([macroName]));
  592. displayMessage(e.toString());
  593. }
  594. }
  595. // ---------------------------------------------------------------------------------
  596. // Tiddler() object
  597. // ---------------------------------------------------------------------------------
  598. function Tiddler()
  599. {
  600. this.title = null;
  601. this.text = null;
  602. this.modifier = null;
  603. this.modified = new Date();
  604. this.links = [];
  605. this.tags = [];
  606. return this;
  607. }
  608. // Load a tiddler from an HTML DIV
  609. Tiddler.prototype.loadFromDiv = function(divRef,title)
  610. {
  611. var text = Tiddler.unescapeLineBreaks(divRef.firstChild ? divRef.firstChild.nodeValue : "");
  612. var modifier = divRef.getAttribute("modifier");
  613. var modified = Date.convertFromYYYYMMDDHHMM(divRef.getAttribute("modified"));
  614. var tags = divRef.getAttribute("tags");
  615. this.set(title,text,modifier,modified,tags);
  616. return this;
  617. }
  618. // Format the text for storage in an HTML DIV
  619. Tiddler.prototype.saveToDiv = function()
  620. {
  621. return '<div tiddler="' + this.title + '" modified="' +
  622. this.modified.convertToYYYYMMDDHHMM() + '" modifier="' + this.modifier +
  623. '" tags="' + this.getTags() + '">' +
  624. this.escapeLineBreaks().htmlEncode() + '</div>';
  625. }
  626. // Format the text for storage in an RSS item
  627. Tiddler.prototype.saveToRss = function(url)
  628. {
  629. var s = [];
  630. s.push("<item>");
  631. s.push("<title>" + this.title.htmlEncode() + "</title>");
  632. s.push("<description>" + this.text.replace(regexpNewLine,"<br />").htmlEncode() + "</description>");
  633. for(var t=0; t<this.tags.length; t++)
  634. s.push("<category>" + this.tags[t] + "</category>");
  635. s.push("<link>" + url + "#" + encodeURIComponent(String.encodeTiddlyLink(this.title)) + "</link>");
  636. s.push("<pubDate>" + this.modified.toGMTString() + "</pubDate>");
  637. s.push("</item>");
  638. return(s.join("\n"));
  639. }
  640. // Change the text and other attributes of a tiddler
  641. Tiddler.prototype.set = function(title,text,modifier,modified,tags)
  642. {
  643. if(title != undefined)
  644. this.title = title;
  645. if(text != undefined)
  646. this.text = text;
  647. if(modifier != undefined)
  648. this.modifier = modifier;
  649. if(modified != undefined)
  650. this.modified = modified;
  651. if(tags != undefined)
  652. this.tags = (typeof tags == "string") ? tags.readBracketedList() : tags;
  653. else
  654. this.tags = [];
  655. this.changed();
  656. return this;
  657. }
  658. // Get the tags for a tiddler as a string (space delimited, using [[brackets]] for tags containing spaces)
  659. Tiddler.prototype.getTags = function()
  660. {
  661. if(this.tags)
  662. {
  663. var results = [];
  664. for(var t=0; t<this.tags.length; t++)
  665. results.push(String.encodeTiddlyLink(this.tags[t]));
  666. return results.join(" ");
  667. }
  668. else
  669. return "";
  670. }
  671. var regexpBackSlashEn = new RegExp("\\\\n","mg");
  672. var regexpBackSlash = new RegExp("\\\\","mg");
  673. var regexpBackSlashEss = new RegExp("\\\\s","mg");
  674. var regexpNewLine = new RegExp("\n","mg");
  675. var regexpCarriageReturn = new RegExp("\r","mg");
  676. // Static method to Convert "\n" to newlines, "\s" to "\"
  677. Tiddler.unescapeLineBreaks = function(text)
  678. {
  679. if(text && text != "")
  680. return text.replace(regexpBackSlashEn,"\n").replace(regexpBackSlashEss,"\\").replace(regexpCarriageReturn,"");
  681. else
  682. return "";
  683. }
  684. // Convert newlines to "\n", "\" to "\s"
  685. Tiddler.prototype.escapeLineBreaks = function()
  686. {
  687. return this.text.replace(regexpBackSlash,"\\s").replace(regexpNewLine,"\\n").replace(regexpCarriageReturn,"");
  688. }
  689. // Updates the secondary information (like links[] array) after a change to a tiddler
  690. Tiddler.prototype.changed = function()
  691. {
  692. this.links = [];
  693. var nextPos = 0;
  694. var theLink;
  695. do {
  696. var formatMatch = wikiNameRegExp.exec(this.text);
  697. if(formatMatch)
  698. {
  699. if(!formatMatch[1] && formatMatch[2] && formatMatch[2] != this.title)
  700. this.links.pushUnique(formatMatch[2]);
  701. else if(formatMatch[4] && store.tiddlers[formatMatch[5]] != undefined)
  702. this.links.pushUnique(formatMatch[5]);
  703. else if(formatMatch[6] && formatMatch[6] != this.title)
  704. this.links.pushUnique(formatMatch[6]);
  705. }
  706. } while(formatMatch);
  707. return;
  708. }
  709. Tiddler.prototype.getSubtitle = function()
  710. {
  711. var theModifier = this.modifier;
  712. if(!theModifier)
  713. theModifier = config.messages.subtitleUnknown;
  714. var theModified = this.modified;
  715. if(theModified)
  716. theModified = theModified.toLocaleString();
  717. else
  718. theModified = config.messages.subtitleUnknown;
  719. return(theModifier + ", " + theModified);
  720. }
  721. // ---------------------------------------------------------------------------------
  722. // TiddlyWiki() object contains Tiddler()s
  723. // ---------------------------------------------------------------------------------
  724. function TiddlyWiki()
  725. {
  726. this.tiddlers = {}; // Hashmap by name of tiddlers
  727. this.namedNotifications = {}; // Hashmap by name of array of notification functions
  728. this.blanketNotifications = []; // Array of blanket notifications to be invoked on any change
  729. this.dirty = false;
  730. }
  731. // Set the dirty flag
  732. TiddlyWiki.prototype.setDirty = function(dirty)
  733. {
  734. this.dirty = dirty;
  735. }
  736. // Invoke the notification handlers for a particular tiddler
  737. TiddlyWiki.prototype.notify = function(title,doBlanket)
  738. {
  739. var notification = this.namedNotifications[title];
  740. if(notification)
  741. for(var t=0; t<notification.length; t++)
  742. notification[t](title);
  743. if(doBlanket)
  744. for(var n=0; n<this.blanketNotifications.length; n++)
  745. this.blanketNotifications[n](title);
  746. }
  747. // Invoke the notification handlers for all tiddlers
  748. TiddlyWiki.prototype.notifyAll = function()
  749. {
  750. var notifyTitle;
  751. for(notifyTitle in this.tiddlers)
  752. this.notify(notifyTitle,false);
  753. for(notifyTitle in config.shadowTiddlers)
  754. if(this.tiddlers[notifyTitle] == undefined)
  755. this.notify(notifyTitle,false);
  756. for(var n=0; n<this.blanketNotifications.length; n++)
  757. this.blanketNotifications[n]();
  758. }
  759. // Add a notification handler to a tiddler
  760. TiddlyWiki.prototype.addNotification = function(title,fn)
  761. {
  762. var notification;
  763. if(title)
  764. {
  765. notification = this.namedNotifications[title];
  766. if(!notification)
  767. {
  768. notification = [];
  769. this.namedNotifications[title] = notification;
  770. }
  771. }
  772. else
  773. notification = this.blanketNotifications;
  774. notification.push(fn);
  775. return this;
  776. }
  777. // Clear a TiddlyWiki so that it contains no tiddlers
  778. TiddlyWiki.prototype.clear = function(src)
  779. {
  780. this.tiddlers = {};
  781. this.dirty = false;
  782. }
  783. TiddlyWiki.prototype.removeTiddler = function(title)
  784. {
  785. var tiddler = this.tiddlers[title];
  786. if(tiddler)
  787. {
  788. delete this.tiddlers[title];
  789. this.notify(title,true);
  790. this.dirty = true;
  791. }
  792. }
  793. TiddlyWiki.prototype.getTiddlerText = function(title,defaultText)
  794. {
  795. if(!title)
  796. return(defaultText);
  797. var tiddler = this.tiddlers[title];
  798. if(tiddler)
  799. return tiddler.text;
  800. else if(config.shadowTiddlers[title])
  801. return config.shadowTiddlers[title];
  802. else if(defaultText)
  803. return defaultText;
  804. else
  805. return null;
  806. }
  807. TiddlyWiki.prototype.getRecursiveTiddlerText = function(title,defaultText,ignoreList)
  808. {
  809. var childIgnoreList = ignoreList ? ignoreList : {};
  810. childIgnoreList[title] = true;
  811. var bracketRegExp = new RegExp("(?:\\[\\[([^\\]]+)\\]\\])","mg");
  812. var text = this.getTiddlerText(title,defaultText);
  813. if(text == null)
  814. return "";
  815. var textOut = [];
  816. var lastPos = 0;
  817. do {
  818. var match = bracketRegExp.exec(text);
  819. if(match)
  820. {
  821. textOut.push(text.substr(lastPos,match.index-lastPos));
  822. if(match[1])
  823. {
  824. if(childIgnoreList[match[1]])
  825. textOut.push(match[1]);
  826. else
  827. {
  828. var subText = this.getRecursiveTiddlerText(match[1],match[1],childIgnoreList);
  829. textOut.push(subText);
  830. }
  831. }
  832. lastPos = match.index + match[1].length + 4;
  833. }
  834. else
  835. textOut.push(text.substr(lastPos));
  836. } while(match);
  837. delete childIgnoreList[title];
  838. return(textOut.join(""));
  839. }
  840. TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags)
  841. {
  842. var tiddler = this.tiddlers[title];
  843. if(tiddler)
  844. delete this.tiddlers[title];
  845. else
  846. tiddler = new Tiddler();
  847. tiddler.set(newTitle,newBody,modifier,modified,tags);
  848. this.tiddlers[newTitle] = tiddler;
  849. if(title != newTitle)
  850. this.notify(title,true);
  851. this.notify(newTitle,true);
  852. this.dirty = true;
  853. return tiddler;
  854. }
  855. TiddlyWiki.prototype.createTiddler = function(title)
  856. {
  857. tiddler = this.tiddlers[title];
  858. if(!tiddler)
  859. {
  860. tiddler = new Tiddler();
  861. this.tiddlers[title] = tiddler;
  862. this.dirty = true;
  863. }
  864. return tiddler;
  865. }
  866. // Load contents of a tiddlywiki from an HTML DIV
  867. TiddlyWiki.prototype.loadFromDiv = function(srcID,idPrefix)
  868. {
  869. if(document.normalize)
  870. document.normalize();
  871. var lenPrefix = idPrefix.length;
  872. var store = document.getElementById(srcID).childNodes;
  873. for(var t = 0; t < store.length; t++)
  874. {
  875. var e = store[t];
  876. var title = null;
  877. if(e.getAttribute)
  878. title = e.getAttribute("tiddler");
  879. if(!title && e.id && e.id.substr(0,lenPrefix) == idPrefix)
  880. title = e.id.substr(lenPrefix);
  881. if(title && title != "")
  882. {
  883. var tiddler = this.createTiddler(title);
  884. tiddler.loadFromDiv(e,title);
  885. }
  886. }
  887. this.dirty = false;
  888. }
  889. // Return an array of tiddlers matching a search string
  890. TiddlyWiki.prototype.search = function(searchText,caseSensitive,useRegExp,sortField,excludeTag)
  891. {
  892. if (!useRegExp)
  893. searchText = searchText.escapeRegExp();
  894. var regExp = new RegExp(searchText,caseSensitive ? "m" : "im");
  895. var candidates = this.reverseLookup("tags",excludeTag,false);
  896. var results = [];
  897. for(var t=0; t<candidates.length; t++)
  898. {
  899. if(regExp.test(candidates[t].title) || regExp.test(candidates[t].text))
  900. results.push(candidates[t]);
  901. }
  902. if(!sortField)
  903. sortField = "title";
  904. results.sort(function (a,b) {if(a[sortField] == b[sortField]) return(0); else return (a[sortField] < b[sortField]) ? -1 : +1; });
  905. return results;
  906. }
  907. // Return an array of all the tags in use. Each member of the array is another array where [0] is the name of the tag and [1] is the number of occurances
  908. TiddlyWiki.prototype.getTags = function()
  909. {
  910. var results = [];
  911. for(var t in this.tiddlers)
  912. {
  913. var tiddler = this.tiddlers[t];
  914. for(g=0; g<tiddler.tags.length; g++)
  915. {
  916. var tag = tiddler.tags[g];
  917. var f = false;
  918. for(var c=0; c<results.length; c++)
  919. if(results[c][0] == tag)
  920. {
  921. f = true;
  922. results[c][1]++;
  923. }
  924. if(!f)
  925. results.push([tag,1]);
  926. }
  927. }
  928. results.sort(function (a,b) {if(a[0] == b[0]) return(0); else return (a[0] < b[0]) ? -1 : +1; });
  929. return results;
  930. }
  931. // Return an array of the tiddlers that are tagged with a given tag
  932. TiddlyWiki.prototype.getTaggedTiddlers = function(tag,sortField)
  933. {
  934. return this.reverseLookup("tags",tag,true,sortField);
  935. }
  936. // Return an array of the tiddlers that link to a given tiddler
  937. TiddlyWiki.prototype.getReferringTiddlers = function(title,exclude,sortField)
  938. {
  939. return this.reverseLookup("links",title,true,sortField);
  940. }
  941. // Return an array of the tiddlers that do or do not have a specified entry in the specified storage array (ie, "links" or "tags")
  942. // lookupMatch == true to match tiddlers, false to exclude tiddlers
  943. TiddlyWiki.prototype.reverseLookup = function(lookupField,lookupValue,lookupMatch,sortField)
  944. {
  945. var results = [];
  946. for(var t in this.tiddlers)
  947. {
  948. var tiddler = this.tiddlers[t];
  949. var f = !lookupMatch;
  950. for(var lookup=0; lookup<tiddler[lookupField].length; lookup++)
  951. if(tiddler[lookupField][lookup] == lookupValue)
  952. f = lookupMatch;
  953. if(f)
  954. results.push(tiddler);
  955. }
  956. if(!sortField)
  957. sortField = "title";
  958. results.sort(function (a,b) {if(a[sortField] == b[sortField]) return(0); else return (a[sortField] < b[sortField]) ? -1 : +1; });
  959. return results;
  960. }
  961. // Return the tiddlers as a sorted array
  962. TiddlyWiki.prototype.getTiddlers = function(field)
  963. {
  964. var results = [];
  965. for(var t in this.tiddlers)
  966. results.push(this.tiddlers[t]);
  967. if(field)
  968. results.sort(function (a,b) {if(a[field] == b[field]) return(0); else return (a[field] < b[field]) ? -1 : +1; });
  969. return results;
  970. }
  971. // Return array of names of tiddlers that are referred to but not defined
  972. TiddlyWiki.prototype.getMissingLinks = function(sortField)
  973. {
  974. var results = [];
  975. for(var t in this.tiddlers)
  976. {
  977. var tiddler = this.tiddlers[t];
  978. for(var n=0; n<tiddler.links.length;n++)
  979. {
  980. var link = tiddler.links[n];
  981. if(this.tiddlers[link] == null)
  982. results.pushUnique(link);
  983. }
  984. }
  985. results.sort();
  986. return results;
  987. }
  988. // Return an array of names of tiddlers that are defined but not referred to
  989. TiddlyWiki.prototype.getOrphans = function()
  990. {
  991. var results = [];
  992. for(var t in this.tiddlers)
  993. if(this.getReferringTiddlers(t).length == 0)
  994. results.push(t);
  995. results.sort();
  996. return results;
  997. }
  998. // ---------------------------------------------------------------------------------
  999. // Tiddler functions
  1000. // ---------------------------------------------------------------------------------
  1001. // Display several tiddlers from a list of space separated titles
  1002. function displayTiddlers(src,titles,state,highlightText,highlightCaseSensitive,animate,slowly)
  1003. {
  1004. var tiddlerNames = titles.readBracketedList();
  1005. for(var t = tiddlerNames.length-1;t>=0;t--)
  1006. displayTiddler(src,tiddlerNames[t],state,highlightText,highlightCaseSensitive,animate,slowly);
  1007. }
  1008. // Display a tiddler with animation and scrolling, as though a link to it has been clicked on
  1009. // src = source element object (eg link) for animation effects and positioning
  1010. // title = title of tiddler to display
  1011. // state = 0 is default or current state, 1 is read only and 2 is edittable
  1012. // highlightText = text to highlight in the displayed tiddler
  1013. // highlightCaseSensitive = flag for whether the highlight text is case sensitive
  1014. function displayTiddler(src,title,state,highlightText,highlightCaseSensitive,animate,slowly)
  1015. {
  1016. var place = document.getElementById("tiddlerDisplay");
  1017. var after = findContainingTiddler(src); // Which tiddler this one will be positioned after
  1018. var before;
  1019. if(after == null)
  1020. before = place.firstChild;
  1021. else if(after.nextSibling)
  1022. before = after.nextSibling;
  1023. else
  1024. before = null;
  1025. var theTiddler = createTiddler(place,before,title,state,highlightText,highlightCaseSensitive);
  1026. if(src)
  1027. {
  1028. if(config.options.chkAnimate && (animate == undefined || animate == true))
  1029. anim.startAnimating(new Zoomer(title,src,theTiddler,slowly),new Scroller(theTiddler,slowly));
  1030. else
  1031. window.scrollTo(0,ensureVisible(theTiddler));
  1032. }
  1033. }
  1034. // Create a tiddler if it doesn't exist (with no fancy animating)
  1035. // place = parent element
  1036. // before = node before which to create/move the tiddler
  1037. // title = title of tiddler to display
  1038. // state = 0 is default or current state, 1 is read only and 2 is edittable
  1039. // highlightText = text to highlight in the displayed tiddler
  1040. // highlightCaseSensitive = flag for whether the highlight text is case sensitive
  1041. function createTiddler(place,before,title,state,highlightText,highlightCaseSensitive)
  1042. {
  1043. var theTiddler = createTiddlerSkeleton(place,before,title);
  1044. createTiddlerTitle(title,highlightText,highlightCaseSensitive);
  1045. var theViewer = document.getElementById("viewer" + title);
  1046. var theEditor = document.getElementById("editorWrapper" + title);
  1047. switch(state)
  1048. {
  1049. case 0:
  1050. if(!theViewer && !theEditor)
  1051. {
  1052. createTiddlerToolbar(title,false);
  1053. createTiddlerViewer(title,highlightText,highlightCaseSensitive);
  1054. createTiddlerFooter(title,false);
  1055. }
  1056. break;
  1057. case 1: // Viewer
  1058. if(theViewer)
  1059. theViewer.parentNode.removeChild(theViewer);
  1060. if(theEditor)
  1061. theEditor.parentNode.removeChild(theEditor);
  1062. createTiddlerToolbar(title,false);
  1063. createTiddlerViewer(title,highlightText,highlightCaseSensitive);
  1064. createTiddlerFooter(title,false);
  1065. break;
  1066. case 2: // Editor
  1067. if(!theEditor)
  1068. {
  1069. if(theViewer)
  1070. theViewer.parentNode.removeChild(theViewer);
  1071. createTiddlerToolbar(title,true);
  1072. createTiddlerEditor(title);
  1073. createTiddlerFooter(title,true);
  1074. }
  1075. break;
  1076. }
  1077. return(theTiddler);
  1078. }
  1079. function refreshTiddler(title)
  1080. {
  1081. var theViewer = document.getElementById("viewer" + title);
  1082. if(theViewer)
  1083. {
  1084. theViewer.parentNode.removeChild(theViewer);
  1085. createTiddlerViewer(title,null,null);
  1086. }
  1087. }
  1088. function createTiddlerSkeleton(place,before,title)
  1089. {
  1090. var theTiddler = document.getElementById("tiddler" + title);
  1091. if(!theTiddler)
  1092. {
  1093. theTiddler = createTiddlyElement(null,"div","tiddler" + title,"tiddler",null);
  1094. theTiddler.onmouseover = onMouseOverTiddler;
  1095. theTiddler.onmouseout = onMouseOutTiddler;
  1096. theTiddler.ondblclick = onDblClickTiddler;
  1097. var theInnerTiddler = createTiddlyElement(theTiddler,"div",null,"unselectedTiddler",null);
  1098. var theToolbar = createTiddlyElement(theInnerTiddler,"div","toolbar" + title,"toolbar", null);
  1099. var theTitle = createTiddlyElement(theInnerTiddler,"div","title" + title,"title",null);
  1100. var theBody = createTiddlyElement(theInnerTiddler,"div","body" + title,"body",null);
  1101. var theFooter = createTiddlyElement(theInnerTiddler,"div","footer" + title,"footer",null);
  1102. place.insertBefore(theTiddler,before);
  1103. }
  1104. return(theTiddler);
  1105. }
  1106. function createTiddlerTitle(title,highlightText,highlightCaseSensitive)
  1107. {
  1108. var theTitle = document.getElementById("title" + title);
  1109. if(theTitle)
  1110. {
  1111. removeChildren(theTitle);
  1112. if(highlightText == "")
  1113. highlightText = null;
  1114. var highlightRegExp,highlightMatch;
  1115. if(highlightText)
  1116. {
  1117. highlightRegExp = new RegExp(highlightText,highlightCaseSensitive ? "mg" : "img");
  1118. highlightMatch = highlightRegExp.exec(title);
  1119. }
  1120. highlightMatch = subWikify(theTitle,title,0,title.length,highlightRegExp,highlightMatch);
  1121. var tiddler = store.tiddlers[title];
  1122. if(tiddler)
  1123. theTitle.title = tiddler.getSubtitle();
  1124. }
  1125. }
  1126. // Create a tiddler toolbar according to whether it's an editor or not
  1127. function createTiddlerToolbar(title,isEditor)
  1128. {
  1129. var theToolbar = document.getElementById("toolbar" + title);
  1130. var lingo = config.views;
  1131. if(theToolbar)
  1132. {
  1133. removeChildren(theToolbar);
  1134. insertSpacer(theToolbar);
  1135. if(isEditor)
  1136. {
  1137. // Editor toolbar
  1138. lingo = lingo.editor;
  1139. createTiddlyButton(theToolbar,lingo.toolbarDone.text,lingo.toolbarDone.tooltip,onClickToolbarSave);
  1140. insertSpacer(theToolbar);
  1141. createTiddlyButton(theToolbar,lingo.toolbarCancel.text,lingo.toolbarCancel.tooltip,onClickToolbarUndo);
  1142. insertSpacer(theToolbar);
  1143. createTiddlyButton(theToolbar,lingo.toolbarDelete.text,lingo.toolbarDelete.tooltip,onClickToolbarDelete);
  1144. }
  1145. else
  1146. {
  1147. // Viewer toolbar
  1148. lingo = lingo.wikified;
  1149. createTiddlyButton(theToolbar,lingo.toolbarClose.text,lingo.toolbarClose.tooltip,onClickToolbarClose);
  1150. insertSpacer(theToolbar);
  1151. if(!readOnly)
  1152. {
  1153. createTiddlyButton(theToolbar,lingo.toolbarEdit.text,lingo.toolbarEdit.tooltip,onClickToolbarEdit);
  1154. insertSpacer(theToolbar);
  1155. }
  1156. createTiddlyButton(theToolbar,lingo.toolbarPermalink.text,lingo.toolbarPermalink.tooltip,onClickToolbarPermaLink);
  1157. insertSpacer(theToolbar);
  1158. createTiddlyButton(theToolbar,lingo.toolbarReferences.text,lingo.toolbarReferences.tooltip,onClickToolbarReferences);
  1159. }
  1160. insertSpacer(theToolbar);
  1161. }
  1162. }
  1163. function createTiddlerPopup(srcElement)
  1164. {
  1165. var popup = document.getElementById("popup");
  1166. if(popup && popup.nextSibling == srcElement)
  1167. {
  1168. hideTiddlerPopup();
  1169. return null;
  1170. }
  1171. if(popup)
  1172. popup.parentNode.removeChild(popup);
  1173. popup = createTiddlyElement(null,"div","popup",null,null);
  1174. var leftPx = srcElement.offsetLeft;
  1175. var topPx = srcElement.offsetTop;
  1176. var heightPx = srcElement.offsetHeight;
  1177. if (leftPx <= 1 && srcElement.parentNode.offsetLeft > 0)
  1178. leftPx = srcElement.parentNode.offsetLeft;
  1179. if (topPx <= 1 && srcElement.parentNode.offsetTop > 0)
  1180. topPx = srcElement.parentNode.offsetTop;
  1181. if (heightPx <= 1 && srcElement.parentNode.offsetHeight > 0)
  1182. heightPx = srcElement.parentNode.offsetHeight;
  1183. popup.style.left = leftPx + "px";
  1184. popup.style.top = topPx + heightPx + "px";
  1185. popup.style.display = "block";
  1186. srcElement.onmouseout = onMouseOutTiddlerPopup;
  1187. srcElement.appendChild(popup);
  1188. return popup;
  1189. }
  1190. function scrollToTiddlerPopup(popup,slowly)
  1191. {
  1192. if(config.options.chkAnimate)
  1193. anim.startAnimating(new Scroller(popup,slowly));
  1194. else
  1195. window.scrollTo(0,ensureVisible(popup));
  1196. }
  1197. function onMouseOutTiddlerPopup(e)
  1198. {
  1199. if (!e) var e = window.event;
  1200. var related = (e.relatedTarget) ? e.relatedTarget : e.toElement;
  1201. try
  1202. {
  1203. while (related != this && related && related.nodeName && related.nodeName.toLowerCase() != "body")
  1204. related = related.parentNode;
  1205. }
  1206. catch(e)
  1207. {
  1208. related = null;
  1209. }
  1210. if(related != this)
  1211. {
  1212. this.onmouseout = null;
  1213. hideTiddlerPopup();
  1214. }
  1215. e.cancelBubble = true;
  1216. if (e.stopPropagation) e.stopPropagation();
  1217. return(false);
  1218. }
  1219. function hideTiddlerPopup()
  1220. {
  1221. var popup = document.getElementById("popup");
  1222. if(popup)
  1223. popup.parentNode.removeChild(popup);
  1224. }
  1225. // Create the body section of a read-only tiddler
  1226. function createTiddlerViewer(title,highlightText,highlightCaseSensitive,htmlElement)
  1227. {
  1228. var theBody = document.getElementById("body" + title);
  1229. if(theBody)
  1230. {
  1231. var tiddler = store.tiddlers[title];
  1232. var tiddlerText = store.getTiddlerText(title);
  1233. var theViewer = createTiddlyElement(theBody,htmlElement ? htmlElement : "div","viewer" + title,"viewer",null);
  1234. if(tiddler)
  1235. theViewer.setAttribute("tags",tiddler.tags.join(" "));
  1236. if(tiddlerText == null)
  1237. {
  1238. tiddlerText = config.views.wikified.defaultText.format([title]);
  1239. theViewer.style.fontStyle = "italic";
  1240. }
  1241. wikify(tiddlerText,theViewer,highlightText,highlightCaseSensitive);
  1242. }
  1243. }
  1244. // Create the footer section of a tiddler
  1245. function createTiddlerFooter(title,isEditor)
  1246. {
  1247. var theFooter = document.getElementById("footer" + title);
  1248. var tiddler = store.tiddlers[title];
  1249. if(theFooter && tiddler)
  1250. {
  1251. removeChildren(theFooter);
  1252. insertSpacer(theFooter);
  1253. if(isEditor)
  1254. {
  1255. }
  1256. else
  1257. {
  1258. var lingo = config.views.wikified.tag;
  1259. var prompt = tiddler.tags.length == 0 ? lingo.labelNoTags : lingo.labelTags;
  1260. var theTags = createTiddlyElement(theFooter,"div",null,null,prompt);
  1261. for(var t=0; t<tiddler.tags.length; t++)
  1262. {
  1263. var theTag = createTagButton(theTags,tiddler.tags[t],tiddler.title);
  1264. insertSpacer(theTags);
  1265. }
  1266. }
  1267. }
  1268. }
  1269. // Create a button for a tag with a popup listing all the tiddlers that it tags
  1270. function createTagButton(place,tag,excludeTiddler)
  1271. {
  1272. var theTag = createTiddlyButton(place,tag,config.views.wikified.tag.tooltip.format([tag]),onClickTag);
  1273. theTag.setAttribute("tag",tag);
  1274. if(excludeTiddler)
  1275. theTag.setAttribute("tiddler",excludeTiddler);
  1276. return(theTag);
  1277. }
  1278. // Create the body section of an edittable tiddler
  1279. function createTiddlerEditor(title)
  1280. {
  1281. var theBody = document.getElementById("body" + title);
  1282. if(theBody)
  1283. {
  1284. var tiddlerText = store.getTiddlerText(title);
  1285. var tiddlerExists = (tiddlerText != null);
  1286. if(!tiddlerExists)
  1287. tiddlerText = config.views.editor.defaultText.format([title]);
  1288. var theEditor = createTiddlyElement(theBody,"div","editorWrapper" + title,"editor",null);
  1289. theEditor.onkeypress = onEditKey;
  1290. var theTitleBox = createTiddlyElement(theEditor,"input","editorTitle" + title,null,null);
  1291. theTitleBox.setAttribute("type","text");
  1292. theTitleBox.value = title;
  1293. theTitleBox.setAttribute("size","40");
  1294. var theBodyBox = createTiddlyElement(theEditor,"textarea","editorBody" + title,null,null);
  1295. theBodyBox.value = tiddlerText;
  1296. var rows = 10;
  1297. var lines = tiddlerText.match(regexpNewLine);
  1298. if(lines != null && lines.length > rows)
  1299. rows = lines.length + 5;
  1300. theBodyBox.setAttribute("rows",rows);
  1301. var theTagsBox = createTiddlyElement(theEditor,"input","editorTags" + title,null,null);
  1302. theTagsBox.setAttribute("type","text");
  1303. var tiddler = store.tiddlers[title];
  1304. theTagsBox.value = tiddler ? tiddler.getTags() : "";
  1305. theTagsBox.setAttribute("size","40");
  1306. var tagPrompt = createTiddlyElement(theEditor,"div",null,"editorFooter",config.views.editor.tagPrompt);
  1307. insertSpacer(tagPrompt);
  1308. var lingo = config.views.editor.tagChooser;
  1309. var addTag = createTiddlyButton(tagPrompt,lingo.text,lingo.tooltip,onClickAddTag);
  1310. addTag.setAttribute("tiddler",title);
  1311. theBodyBox.focus();
  1312. }
  1313. }
  1314. function saveTiddler(title)
  1315. {
  1316. var titleBox = document.getElementById("editorTitle" + title);
  1317. var newTitle = titleBox.value;
  1318. if(store.tiddlers[newTitle])
  1319. {
  1320. if(newTitle != title && !confirm(config.messages.overwriteWarning.format([newTitle.toString()])))
  1321. {
  1322. titleBox.focus();
  1323. titleBox.select();
  1324. return;
  1325. }
  1326. }
  1327. var body = document.getElementById("editorBody" + title);
  1328. var newBody = body.value;
  1329. var newTags = document.getElementById("editorTags" + title).value;
  1330. blurTiddler(title);
  1331. store.saveTiddler(title,newTitle,newBody.replace(regexpCarriageReturn,""),config.options.txtUserName,new Date(),newTags);
  1332. displayTiddler(null,newTitle,1,null,null,null,false,false);
  1333. // Close the old tiddler if this is a rename
  1334. if(title != newTitle)
  1335. {
  1336. var oldTiddler = document.getElementById("tiddler" + title);
  1337. var newTiddler = document.getElementById("tiddler" + newTitle);
  1338. oldTiddler.parentNode.replaceChild(newTiddler,oldTiddler);
  1339. }
  1340. if(config.options.chkAutoSave)
  1341. saveChanges();
  1342. }
  1343. function selectTiddler(title)
  1344. {
  1345. var e = document.getElementById("tiddler" + title);
  1346. if(e != null)
  1347. e.firstChild.className = "selectedTiddler";
  1348. }
  1349. function deselectTiddler(title)
  1350. {
  1351. var e = document.getElementById("tiddler" + title);
  1352. if(e != null)
  1353. e.firstChild.className = "unselectedTiddler";
  1354. }
  1355. function blurTiddler(title)
  1356. {
  1357. var body = document.getElementById("editorBody" + title);
  1358. if(title)
  1359. {
  1360. body.focus();
  1361. body.blur();
  1362. }
  1363. }
  1364. function deleteTiddler(title)
  1365. {
  1366. closeTiddler(title,false);
  1367. store.removeTiddler(title);
  1368. // Autosave
  1369. if(config.options.chkAutoSave)
  1370. saveChanges();
  1371. }
  1372. function closeTiddler(title,slowly)
  1373. {
  1374. var tiddler = document.getElementById("tiddler" + title);
  1375. if(tiddler != null)
  1376. {
  1377. scrubIds(tiddler);
  1378. if(config.options.chkAnimate)
  1379. anim.startAnimating(new Slider(tiddler,false,slowly,"all"));
  1380. else
  1381. tiddler.parentNode.removeChild(tiddler);
  1382. }
  1383. }
  1384. function scrubIds(e)
  1385. {
  1386. if(e.id)
  1387. e.id = null;
  1388. var children = e.childNodes;
  1389. for(var t=0; t<children.length; t++)
  1390. {
  1391. var c = children[t];
  1392. if(c.id)
  1393. c.id = null;
  1394. }
  1395. }
  1396. function closeAllTiddlers()
  1397. {
  1398. clearMessage();
  1399. var place = document.getElementById("tiddlerDisplay");
  1400. var tiddler = place.firstChild;
  1401. var nextTiddler;
  1402. while(tiddler)
  1403. {
  1404. nextTiddler = tiddler.nextSibling;
  1405. if(tiddler.id)
  1406. if(tiddler.id.substr(0,7) == "tiddler")
  1407. {
  1408. var title = tiddler.id.substr(7);
  1409. if(!document.getElementById("editorWrapper" + title))
  1410. place.removeChild(tiddler);
  1411. }
  1412. tiddler = nextTiddler;
  1413. }
  1414. }
  1415. // ---------------------------------------------------------------------------------
  1416. // Regular expression stuff
  1417. // ---------------------------------------------------------------------------------
  1418. var upperLetter = "[A-Z\u00c0-\u00de\u0150\u0170]";
  1419. var lowerLetter = "[a-z\u00df-\u00ff_0-9\\-\u0151\u0171]";
  1420. var anyLetter = "[A-Za-z\u00c0-\u00de\u00df-\u00ff_0-9\\-\u0150\u0170\u0151\u0171]";
  1421. var anyDigit = "[0-9]";
  1422. var anyNumberChar = "[0-9\\.E]";
  1423. var wikiNamePattern = "(~?)((?:" + upperLetter + "+" + lowerLetter + "+" + upperLetter + anyLetter + "*)|(?:" + upperLetter + "{2,}" + lowerLetter + "+))";
  1424. var urlPattern = "((?:http|https|mailto|ftp):[^\\s'\"]+(?:/|\\b))";
  1425. var explicitLinkPattern = "\\[\\[([^\\[\\]\\|]+)\\|([^\\[\\]\\|]+)\\]\\]";
  1426. var bracketNamePattern = "\\[\\[([^\\]]+)\\]\\]";
  1427. var wikiNamePatterns;
  1428. var wikiNameRegExp;
  1429. var structurePatterns;
  1430. var stylePatterns;
  1431. var tableRegExp;
  1432. var tableRowColRegExp;
  1433. var invalidPreWikiNamePattern;
  1434. function setupRegexp()
  1435. {
  1436. // Table rows pattern
  1437. var rowPattern = "^\\|([^\\n]*\\|)([fhc]?)$";
  1438. tableRegExp = new RegExp(rowPattern,"mg");
  1439. // Table columns pattern
  1440. var elementPattern = "(?:(?:BGCOLOR|bgcolor)\\(([^\\)]+)\\):)?" +
  1441. "("+
  1442. "("+explicitLinkPattern+")?"+
  1443. "("+bracketNamePattern+")?" +
  1444. "[^\\|]*"+
  1445. ")\\|";
  1446. tableRowColRegExp = new RegExp(elementPattern,"g");
  1447. // Link patterns
  1448. wikiNamePatterns = "(?:" + wikiNamePattern +
  1449. ")|(?:" + urlPattern +
  1450. ")|(?:" + explicitLinkPattern +
  1451. ")|(?:" + bracketNamePattern +
  1452. ")";
  1453. wikiNameRegExp = new RegExp(wikiNamePatterns,"mg");
  1454. invalidPreWikiNamePattern = anyLetter;
  1455. // Structural patterns
  1456. var breakPattern = "\\n";
  1457. var horizontalRulePattern = "^----$\\n?";
  1458. var headerPattern = "^!{1,5}";
  1459. var bulletListItemPattern = "^\\*+";
  1460. var numberedListItemPattern = "^#+";
  1461. var tablePattern = "(?:^\\|[^\\n]*$\\n?)+";
  1462. var blockquotePattern = "(?:^>[^\\n]*$\\n?)+";
  1463. var blockquotePattern2 = "^<<<\\n((?:^(?!<<<)[^\\n]*\\n)+)(^<<<$\\n?)";
  1464. var imagePattern = "\\[[Ii][Mm][Gg]\\[(?:([^\\|\\]]+)\\|)?([^\\[\\]\\|]+)\\]\\]";
  1465. var verbatimPattern = "^\\{\\{\\{\\n((?:^[^\\n]*\\n)+?)(^\\}\\}\\}$\\n?)";
  1466. var macroPattern = "<<([^>\\s]+)(?:\\s*)([^>]*)>>";
  1467. structurePatterns = "(" + breakPattern +
  1468. ")|(" + horizontalRulePattern +
  1469. ")|(" + headerPattern +
  1470. ")|(" + bulletListItemPattern +
  1471. ")|(" + numberedListItemPattern +
  1472. ")|(" + tablePattern +
  1473. ")|(" + blockquotePattern +
  1474. ")|(?:" + blockquotePattern2 +
  1475. ")|(?:" + imagePattern +
  1476. ")|(?:" + verbatimPattern +
  1477. ")|(?:" + macroPattern +
  1478. ")";
  1479. // Style patterns
  1480. var boldPattern = "''((?:[^']+(?:'[^'])?)+)''";
  1481. var strikePattern = "==([^=]+)==";
  1482. var underlinePattern = "__([^_]+)__";
  1483. var italicPattern = "//([^/]+)//";
  1484. var supPattern = "\\^\\^([^\\^]+)\\^\\^";
  1485. var subPattern = "~~([^~]+)~~";
  1486. var monoPattern = "\\{\\{\\{(.*?)\\}\\}\\}";
  1487. var colorPattern = "@@(?:color\\(([^\\)]+)\\):|bgcolor\\(([^\\)]+)\\):){0,2}([^@]+)@@";
  1488. stylePatterns = "(?:" + boldPattern +
  1489. ")|(?:" + strikePattern +
  1490. ")|(?:" + underlinePattern +
  1491. ")|(?:" + italicPattern +
  1492. ")|(?:" + supPattern +
  1493. ")|(?:" + subPattern +
  1494. ")|(?:" + colorPattern +
  1495. ")|(?:" + monoPattern +
  1496. ")";
  1497. }
  1498. // Create child text nodes and link elements to represent a wiki-fied version of some text
  1499. function wikify(text,parent,highlightText,highlightCaseSensitive)
  1500. {
  1501. // Prepare the regexp for the highlighted selection
  1502. if(highlightText == "")
  1503. highlightText = null;
  1504. var highlightRegExp,highlightMatch;
  1505. if(highlightText)
  1506. {
  1507. highlightRegExp = new RegExp(highlightText,highlightCaseSensitive ? "mg" : "img");
  1508. highlightMatch = highlightRegExp.exec(text);
  1509. }
  1510. wikifyStructures(parent,text,text,0,text.length,highlightRegExp,highlightMatch);
  1511. }
  1512. function wikifyStructures(parent,text,targetText,startPos,endPos,highlightRegExp,highlightMatch)
  1513. {
  1514. var body = parent;
  1515. var structureRegExp = new RegExp(structurePatterns,"mg");
  1516. var theList = []; // theList[0]: don't use
  1517. var isInListMode = false;
  1518. var isInHeaderMode = false;
  1519. var isNewline = false;
  1520. // The start of the fragment of the text being considered
  1521. var nextPos = 0;
  1522. // Loop through the bits of the body text
  1523. do {
  1524. // Get the next formatting match
  1525. var formatMatch = structureRegExp.exec(targetText);
  1526. var matchPos = formatMatch ? formatMatch.index : targetText.length;
  1527. // Subwikify the plain text before the match
  1528. if(nextPos < matchPos)
  1529. {
  1530. isNewline = false;
  1531. highlightMatch = wikifyStyles(body,text,targetText.substring(nextPos,matchPos),startPos+nextPos,startPos+matchPos,highlightRegExp,highlightMatch);
  1532. }
  1533. // Dump out the formatted match
  1534. var level;
  1535. var theBlockquote;
  1536. if(formatMatch)
  1537. {
  1538. // Dump out the link itself in the appropriate format
  1539. if(formatMatch[1])
  1540. {
  1541. if(isNewline && isInListMode)
  1542. {
  1543. theList = [];
  1544. body = parent;
  1545. isInListMode = false;
  1546. }
  1547. else if(isInHeaderMode)
  1548. {
  1549. body = parent;
  1550. isInHeaderMode = false;
  1551. }
  1552. else
  1553. {
  1554. isNewline = true;
  1555. body.appendChild(document.createElement("br"));
  1556. }
  1557. }
  1558. else if(formatMatch[2])
  1559. {
  1560. isNewline = false;
  1561. body.appendChild(document.createElement("hr"));
  1562. }
  1563. else if(formatMatch[3])
  1564. {
  1565. level = formatMatch[3].length;
  1566. isNewline = false;
  1567. isInHeaderMode = true;
  1568. var theHeader = document.createElement("h" + level);
  1569. parent.appendChild(theHeader);
  1570. body = theHeader;
  1571. }
  1572. else if(formatMatch[4])
  1573. {
  1574. level = formatMatch[4].length;
  1575. isNewline = false;
  1576. isInListMode = true;
  1577. if (theList[level] == null)
  1578. {
  1579. theList[level] = document.createElement("ul");
  1580. body.appendChild(theList[level]);
  1581. }
  1582. theList = theList.slice(0,level + 1);
  1583. body = document.createElement("li");
  1584. theList[level].appendChild(body);
  1585. }
  1586. else if(formatMatch[5])
  1587. {
  1588. level = formatMatch[5].length;
  1589. isNewline = false;
  1590. isInListMode = true;
  1591. if (theList[level] == null)
  1592. {
  1593. theList[level] = document.createElement("ol");
  1594. body.appendChild(theList[level]);
  1595. }
  1596. theList = theList.slice(0,level + 1);
  1597. body = document.createElement("li");
  1598. theList[level].appendChild(body);
  1599. }
  1600. else if(formatMatch[6])
  1601. {
  1602. isNewline = false;
  1603. highlightMatch = wikifyTable(body,text,formatMatch[6],startPos+matchPos,startPos+structureRegExp.lastIndex,highlightRegExp,highlightMatch);
  1604. }
  1605. else if(formatMatch[7])
  1606. {
  1607. isNewline = false;
  1608. var quotedText = formatMatch[7].replace(new RegExp("^>(>*)","mg"),"$1");
  1609. theBlockquote = document.createElement("blockquote");
  1610. var newHighlightRegExp,newHighlightMatch;
  1611. if (highlightRegExp) {
  1612. newHighlightRegExp = new RegExp(highlightRegExp.toString(), "img");
  1613. newHighlightMatch = newHighlightRegExp.exec(quotedText);
  1614. }
  1615. wikifyStructures(theBlockquote,quotedText,quotedText,0,quotedText.length,newHighlightRegExp,newHighlightMatch);
  1616. body.appendChild(theBlockquote);
  1617. }
  1618. else if(formatMatch[8])
  1619. {
  1620. isNewline = false;
  1621. theBlockquote = document.createElement("blockquote");
  1622. highlightMatch = wikifyStructures(theBlockquote,text,formatMatch[8],startPos+matchPos+4,startPos+structureRegExp.lastIndex-formatMatch[9].length,highlightRegExp,highlightMatch);
  1623. body.appendChild(theBlockquote);
  1624. }
  1625. else if(formatMatch[11])
  1626. {
  1627. isNewline = false;
  1628. var theImage = document.createElement("img");
  1629. theImage.alt = formatMatch[10];
  1630. theImage.src = formatMatch[11];
  1631. body.appendChild(theImage);
  1632. }
  1633. else if(formatMatch[12])
  1634. {
  1635. isNewline = false;
  1636. var theVerbatim = document.createElement("pre");
  1637. out = text.substr(startPos+matchPos+4,startPos+structureRegExp.lastIndex-formatMatch[13].length-startPos-matchPos-4);
  1638. out = out.replace(/\n/g,"\r\n");
  1639. theVerbatim.appendChild(document.createTextNode(out));
  1640. body.appendChild(theVerbatim);
  1641. }
  1642. else if(formatMatch[14])
  1643. {
  1644. isNewline = false;
  1645. insertMacro(body,formatMatch[14],formatMatch[15]);
  1646. }
  1647. }
  1648. // Move the next position past the formatting match
  1649. nextPos = structureRegExp.lastIndex;
  1650. } while(formatMatch);
  1651. return highlightMatch;
  1652. }
  1653. function wikifyLinks(parent,text,targetText,startPos,endPos,highlightRegExp,highlightMatch)
  1654. {
  1655. // The start of the fragment of the text being considered
  1656. var nextPos = 0;
  1657. // Loop through the bits of the body text
  1658. var theLink;
  1659. do {
  1660. // Get the next formatting match
  1661. var formatMatch = wikiNameRegExp.exec(targetText);
  1662. var matchPos = formatMatch ? formatMatch.index : targetText.length;
  1663. // Subwikify the plain text before the match
  1664. if(nextPos < matchPos)
  1665. highlightMatch = subWikify(parent,text,startPos+nextPos,startPos+matchPos,highlightRegExp,highlightMatch);
  1666. // Dump out the formatted match
  1667. if(formatMatch)
  1668. {
  1669. // Dump out the link itself in the appropriate format
  1670. if(formatMatch[2])
  1671. {
  1672. if(formatMatch[1])
  1673. {
  1674. theLink = parent;
  1675. matchPos++;
  1676. }
  1677. else if(matchPos > 0 && new RegExp(invalidPreWikiNamePattern,"").exec(targetText.charAt(matchPos - 1)))
  1678. theLink = parent;
  1679. else
  1680. theLink = createTiddlyLink(parent,formatMatch[2],false);
  1681. highlightMatch = subWikify(theLink,text,startPos+matchPos,startPos+wikiNameRegExp.lastIndex,highlightRegExp,highlightMatch);
  1682. }
  1683. else if(formatMatch[3])
  1684. {
  1685. theLink = createExternalLink(parent,formatMatch[3]);
  1686. highlightMatch = subWikify(theLink,text,startPos+matchPos,startPos+wikiNameRegExp.lastIndex,highlightRegExp,highlightMatch);
  1687. }
  1688. else if(formatMatch[4])
  1689. {
  1690. if(store.tiddlers[formatMatch[5]] != undefined)
  1691. theLink = createTiddlyLink(parent,formatMatch[5],false);
  1692. else
  1693. theLink = createExternalLink(parent,formatMatch[5]);
  1694. highlightMatch = subWikify(theLink,text,startPos+matchPos+2,startPos+matchPos+2+formatMatch[4].length,highlightRegExp,highlightMatch);
  1695. }
  1696. else if(formatMatch[6])
  1697. {
  1698. theLink = createTiddlyLink(parent,formatMatch[6],false);
  1699. highlightMatch = subWikify(theLink,text,startPos+matchPos+2,startPos+wikiNameRegExp.lastIndex-2,highlightRegExp,highlightMatch);
  1700. }
  1701. }
  1702. // Move the next position past the formatting match
  1703. nextPos = wikiNameRegExp.lastIndex;
  1704. } while(formatMatch);
  1705. return highlightMatch;
  1706. }
  1707. function wikifyStyles(parent,text,targetText,startPos,endPos,highlightRegExp,highlightMatch)
  1708. {
  1709. var formatRegExp = new RegExp(stylePatterns,"mg");
  1710. // The start of the fragment of the text being considered
  1711. var nextPos = 0;
  1712. // Loop through the bits of the body text
  1713. do {
  1714. // Get the next formatting match
  1715. var formatMatch = formatRegExp.exec(targetText);
  1716. var matchPos = formatMatch ? formatMatch.index : targetText.length;
  1717. // Subwikify the plain text before the match
  1718. if(nextPos < matchPos)
  1719. highlightMatch = wikifyLinks(parent,text,targetText.substring(nextPos,matchPos),startPos+nextPos,startPos+matchPos,highlightRegExp,highlightMatch);
  1720. // Dump out the formatted match
  1721. if(formatMatch)
  1722. {
  1723. // Dump out the link itself in the appropriate format
  1724. if(formatMatch[1])
  1725. {
  1726. var theBold = createTiddlyElement(parent,"b",null,null,null);
  1727. highlightMatch = wikifyStyles(theBold,text,formatMatch[1],startPos+matchPos+2,startPos+formatRegExp.lastIndex-2,highlightRegExp,highlightMatch);
  1728. }
  1729. else if(formatMatch[2])
  1730. {
  1731. var theStrike = createTiddlyElement(parent,"strike",null,null,null);
  1732. highlightMatch = wikifyStyles(theStrike,text,formatMatch[2],startPos+matchPos+2,startPos+formatRegExp.lastIndex-2,highlightRegExp,highlightMatch);
  1733. }
  1734. else if(formatMatch[3])
  1735. {
  1736. var theUnderline = createTiddlyElement(parent,"u",null,null,null);
  1737. highlightMatch = wikifyStyles(theUnderline,text,formatMatch[3],startPos+matchPos+2,startPos+formatRegExp.lastIndex-2,highlightRegExp,highlightMatch);
  1738. }
  1739. else if(formatMatch[4])
  1740. {
  1741. var theItalic = createTiddlyElement(parent,"i",null,null,null);
  1742. highlightMatch = wikifyStyles(theItalic,text,formatMatch[4],startPos+matchPos+2,startPos+formatRegExp.lastIndex-2,highlightRegExp,highlightMatch);
  1743. }
  1744. else if(formatMatch[5])
  1745. {
  1746. var theSup = createTiddlyElement(parent,"sup",null,null,null);
  1747. highlightMatch = wikifyStyles(theSup,text,formatMatch[5],startPos+matchPos+2,startPos+formatRegExp.lastIndex-2,highlightRegExp,highlightMatch);
  1748. }
  1749. else if(formatMatch[6])
  1750. {
  1751. var theSub = createTiddlyElement(parent,"sub",null,null,null);
  1752. highlightMatch = wikifyStyles(theSub,text,formatMatch[6],startPos+matchPos+2,startPos+formatRegExp.lastIndex-2,highlightRegExp,highlightMatch);
  1753. }
  1754. else if(formatMatch[9])
  1755. {
  1756. var theSpan;
  1757. if ((formatMatch[7] == "" || formatMatch[7] == null) && (formatMatch[8] == "" || formatMatch[8] == null))
  1758. {
  1759. theSpan = createTiddlyElement(parent,"span",null,"marked",null);
  1760. }
  1761. else
  1762. {
  1763. theSpan = createTiddlyElement(parent,"span",null,null,null);
  1764. if (formatMatch[7] != "") theSpan.style.color = formatMatch[7];
  1765. if (formatMatch[8] != "") theSpan.style.background = formatMatch[8];
  1766. }
  1767. highlightMatch = wikifyStyles(theSpan,text,formatMatch[9],startPos+formatRegExp.lastIndex-2-formatMatch[9].length,startPos+formatRegExp.lastIndex-2,highlightRegExp,highlightMatch);
  1768. }
  1769. else if(formatMatch[10])
  1770. {
  1771. var theCode = createTiddlyElement(parent,"code",null,null,null);
  1772. highlightMatch = wikifyStyles(theCode,text,formatMatch[10],startPos+matchPos+3,startPos+formatRegExp.lastIndex-3,highlightRegExp,highlightMatch);
  1773. }
  1774. }
  1775. // Move the next position past the formatting match
  1776. nextPos = formatRegExp.lastIndex;
  1777. } while(formatMatch);
  1778. return highlightMatch;
  1779. }
  1780. // Create a table
  1781. function wikifyTable(parent,text,targetText,startPos,endPos,highlightRegExp,highlightMatch)
  1782. {
  1783. // The start of the fragment of the text being considered
  1784. var nextPos = 0;
  1785. var theTable = document.createElement("table");
  1786. var bodyRowLen = 0;
  1787. var headRowLen = 0;
  1788. var footRowLen = 0;
  1789. var bodyRows = [];
  1790. var headRows = [];
  1791. var footRows = [];
  1792. var theCaption = null;
  1793. // Loop through the bits of the body text
  1794. do {
  1795. // Get the next formatting match
  1796. var formatMatch = tableRegExp.exec(targetText);
  1797. var matchPos = formatMatch ? formatMatch.index : targetText.length;
  1798. // Dump out the formatted match
  1799. if(formatMatch) {
  1800. if (formatMatch[2] == "c") {
  1801. var cap = formatMatch[1].substring(0,formatMatch[1].length-1);
  1802. theCaption = document.createElement("caption");
  1803. highlightMatch = wikifyStyles(theCaption,text,cap,startPos+matchPos+1,startPos+cap.length,highlightRegExp,highlightMatch);
  1804. if (bodyRowLen == 0 && headRowLen == 0 && footRowLen == 0) {
  1805. theCaption.setAttribute("align", "top");
  1806. } else {
  1807. theCaption.setAttribute("align", "bottom");
  1808. }
  1809. } else if (formatMatch[2] == "h") {
  1810. highlightMatch = wikifyTableRow(headRows,headRowLen,text,formatMatch[1],startPos+matchPos,startPos+matchPos+formatMatch[1].length,highlightRegExp,highlightMatch);
  1811. headRowLen++;
  1812. } else if (formatMatch[2] == "f") {
  1813. highlightMatch = wikifyTableRow(footRows,footRowLen,text,formatMatch[1],startPos+matchPos,startPos+matchPos+formatMatch[1].length,highlightRegExp,highlightMatch);
  1814. footRowLen++;
  1815. } else {
  1816. highlightMatch = wikifyTableRow(bodyRows,bodyRowLen,text,formatMatch[1],startPos+matchPos,startPos+matchPos+formatMatch[1].length,highlightRegExp,highlightMatch);
  1817. bodyRowLen++;
  1818. }
  1819. }
  1820. nextPos = tableRegExp.lastIndex;
  1821. } while(formatMatch);
  1822. if (theCaption != null) {
  1823. theTable.appendChild(theCaption);
  1824. }
  1825. if (headRowLen > 0) {
  1826. var theTableHead = document.createElement("thead");
  1827. createTableRows(headRows,theTableHead);
  1828. theTable.appendChild(theTableHead);
  1829. }
  1830. if (bodyRowLen > 0) {
  1831. var theTableBody = document.createElement("tbody");
  1832. createTableRows(bodyRows,theTableBody);
  1833. theTable.appendChild(theTableBody);
  1834. }
  1835. if (footRowLen > 0) {
  1836. var theTableFoot = document.createElement("tfoot");
  1837. createTableRows(footRows,theTableFoot);
  1838. theTable.appendChild(theTableFoot);
  1839. }
  1840. parent.appendChild(theTable);
  1841. return highlightMatch;
  1842. }
  1843. function wikifyTableRow(rows,rowIndex,text,targetText,startPos,endPos,highlightRegExp,highlightMatch)
  1844. {
  1845. // The start of the fragment of the text being considered
  1846. var eIndex = 0;
  1847. var elements = [];
  1848. // Loop through the bits of the body text
  1849. do {
  1850. // Get the next formatting match
  1851. var formatMatch = tableRowColRegExp.exec(targetText);
  1852. var matchPos = formatMatch ? formatMatch.index : targetText.length;
  1853. if(formatMatch) {
  1854. var eText = formatMatch[2];
  1855. if (eText == "~" || eText == ">") {
  1856. elements[eIndex] = eText;
  1857. } else {
  1858. var eTextLen = eText.length;
  1859. var align = "";
  1860. if (eTextLen >= 1 && eText.charAt(0) == " ") {
  1861. if (eTextLen >= 3 && eText.charAt(eTextLen - 1) == " ") {
  1862. align = "center";
  1863. eText = eText.substring(1,eTextLen - 1);
  1864. //eTextLen -= 2;
  1865. eTextLen--;
  1866. } else {
  1867. align = "right";
  1868. eText = eText.substring(1);
  1869. eTextLen--;
  1870. }
  1871. } else if (eTextLen >= 2 && eText.charAt(eTextLen - 1) == " ") {
  1872. align = "left";
  1873. eText = eText.substring(0,eTextLen - 1);
  1874. //eTextLen--;
  1875. }
  1876. var theElement;
  1877. if (eTextLen >= 1 && eText.charAt(0) == "!") {
  1878. theElement = document.createElement("th");
  1879. eText = eText.substring(1);
  1880. eTextLen--;
  1881. } else {
  1882. theElement = document.createElement("td");
  1883. }
  1884. if (align != "") {
  1885. theElement.align = align;
  1886. }
  1887. if (formatMatch[1]) {
  1888. theElement.style.background = formatMatch[1];
  1889. }
  1890. highlightMatch = wikifyStyles(theElement,text,eText,startPos+tableRowColRegExp.lastIndex-eTextLen,startPos+tableRowColRegExp.lastIndex-1,highlightRegExp,highlightMatch);
  1891. elements[eIndex] = theElement;
  1892. }
  1893. eIndex++;
  1894. }
  1895. } while(formatMatch);
  1896. rows[rowIndex] = elements;
  1897. return highlightMatch;
  1898. }
  1899. function createTableRows(rows,parent)
  1900. {
  1901. var i, j, k, cols;
  1902. for (i = 0; i < rows.length; i++) {
  1903. cols = rows[i];
  1904. var theRow = document.createElement("tr");
  1905. for (j = 0; j < cols.length; j++) {
  1906. if (cols[j] == "~") continue;
  1907. var rowspan = 1;
  1908. for (k = i+1; k < rows.length; k++) {
  1909. if (rows[k][j] != "~") break;
  1910. rowspan++;
  1911. }
  1912. var colspan = 1;
  1913. for (; j < cols.length - 1; j++) {
  1914. if (cols[j] != ">") break;
  1915. colspan++;
  1916. }
  1917. var theElement = cols[j];
  1918. if (rowspan > 1) {
  1919. theElement.setAttribute("rowSpan",rowspan);
  1920. theElement.setAttribute("rowspan",rowspan);
  1921. theElement.valign = "center";
  1922. }
  1923. if (colspan > 1) {
  1924. theElement.setAttribute("colSpan",colspan);
  1925. theElement.setAttribute("colspan",colspan);
  1926. }
  1927. theRow.appendChild(theElement);
  1928. }
  1929. parent.appendChild(theRow);
  1930. }
  1931. }
  1932. // Helper for wikify that handles highlights within runs of text
  1933. function subWikify(parent,text,startPos,endPos,highlightRegExp,highlightMatch)
  1934. {
  1935. // Check for highlights
  1936. while(highlightMatch && (highlightRegExp.lastIndex > startPos) && (highlightMatch.index < endPos) && (startPos < endPos))
  1937. {
  1938. // Deal with the plain text before the highlight
  1939. if(highlightMatch.index > startPos)
  1940. {
  1941. parent.appendChild(document.createTextNode(text.substring(startPos,highlightMatch.index)));
  1942. startPos = highlightMatch.index;
  1943. }
  1944. // Deal with the highlight
  1945. var highlightEnd = Math.min(highlightRegExp.lastIndex,endPos);
  1946. var theHighlight = createTiddlyElement(parent,"span",null,"highlight",text.substring(startPos,highlightEnd));
  1947. startPos = highlightEnd;
  1948. // Nudge along to the next highlight if we're done with this one
  1949. if(startPos >= highlightRegExp.lastIndex)
  1950. highlightMatch = highlightRegExp.exec(text);
  1951. }
  1952. // Do the unhighlighted text left over
  1953. if(startPos < endPos)
  1954. {
  1955. parent.appendChild(document.createTextNode(text.substring(startPos,endPos)));
  1956. //startPos = endPos;
  1957. }
  1958. return(highlightMatch);
  1959. }
  1960. // ---------------------------------------------------------------------------------
  1961. // Message area
  1962. // ---------------------------------------------------------------------------------
  1963. function displayMessage(text,linkText)
  1964. {
  1965. var msgArea = document.getElementById("messageArea");
  1966. var msg;
  1967. if(linkText)
  1968. {
  1969. msg = createTiddlyElement(msgArea,"div",null,null,null);
  1970. var link = createTiddlyElement(msg,"a",null,null,text);
  1971. link.href = linkText;
  1972. link.target = "_blank";
  1973. }
  1974. else
  1975. msg = createTiddlyElement(msgArea,"div",null,null,text);
  1976. msgArea.style.display = "block";
  1977. }
  1978. function clearMessage()
  1979. {
  1980. var msgArea = document.getElementById("messageArea");
  1981. removeChildren(msgArea);
  1982. msgArea.style.display = "none";
  1983. }
  1984. // ---------------------------------------------------------------------------------
  1985. // Menu and sidebar functions
  1986. // ---------------------------------------------------------------------------------
  1987. function refreshStory(hint)
  1988. {
  1989. var hits = hint ? store.getReferringTiddlers(hint) : null;
  1990. var displayNodes = document.getElementById("tiddlerDisplay").childNodes;
  1991. for(var t=0;t<displayNodes.length;t++)
  1992. {
  1993. var theId = displayNodes[t].id;
  1994. if(theId && theId.substr(0,7) == "tiddler")
  1995. {
  1996. var title = theId.substr(7);
  1997. if(hint)
  1998. {
  1999. var f = false;
  2000. for(var h=0; h<hits.length; h++)
  2001. if(hits[h].title == title)
  2002. f = true
  2003. if(f)
  2004. refreshTiddler(title);
  2005. }
  2006. else
  2007. refreshTiddler(title);
  2008. }
  2009. }
  2010. }
  2011. function refreshTabs(hint)
  2012. {
  2013. refreshSpecialItem("sidebarTabs","SideBarTabs","SideBarTabs");
  2014. }
  2015. function refreshMenu(hint)
  2016. {
  2017. refreshSpecialItem("mainMenu","MainMenu","MainMenu");
  2018. }
  2019. function refreshTitle(title)
  2020. {
  2021. refreshSpecialItem("siteTitle",title,"SiteTitle");
  2022. refreshPageTitle();
  2023. }
  2024. function refreshSubtitle(title)
  2025. {
  2026. refreshSpecialItem("siteSubtitle",title,"SiteSubtitle");
  2027. refreshPageTitle();
  2028. }
  2029. function refreshPageTitle()
  2030. {
  2031. document.title = getElementText("siteTitle") + " - " + getElementText("siteSubtitle");
  2032. }
  2033. function refreshSidebar(title)
  2034. {
  2035. refreshSpecialItem("sidebarOptions",title,"SideBarOptions");
  2036. }
  2037. function refreshSpecialItem(elementID,title,defaultText)
  2038. {
  2039. var place = document.getElementById(elementID);
  2040. removeChildren(place);
  2041. wikify(store.getTiddlerText(title,defaultText),place,null,null);
  2042. }
  2043. function refreshStyles(title)
  2044. {
  2045. setStylesheet(title == null ? "" : store.getRecursiveTiddlerText(title,""));
  2046. }
  2047. // ---------------------------------------------------------------------------------
  2048. // Options cookie stuff
  2049. // ---------------------------------------------------------------------------------
  2050. function loadOptionsCookie()
  2051. {
  2052. var cookies = document.cookie.split(";");
  2053. for(var c=0; c<cookies.length; c++)
  2054. {
  2055. var p = cookies[c].indexOf("=");
  2056. if(p != -1)
  2057. {
  2058. var name = cookies[c].substr(0,p).trim();
  2059. var value = cookies[c].substr(p+1).trim();
  2060. switch(name.substr(0,3))
  2061. {
  2062. case "txt":
  2063. config.options[name] = unescape(value);
  2064. break;
  2065. case "chk":
  2066. config.options[name] = value == "true";
  2067. break;
  2068. }
  2069. }
  2070. }
  2071. }
  2072. function saveOptionCookie(name)
  2073. {
  2074. var c = name + "=";
  2075. switch(name.substr(0,3))
  2076. {
  2077. case "txt":
  2078. c += escape(config.options[name].toString());
  2079. break;
  2080. case "chk":
  2081. c += config.options[name] ? "true" : "false";
  2082. break;
  2083. }
  2084. c += "; expires=Fri, 1 Jan 2038 12:00:00 UTC; path=/";
  2085. document.cookie = c;
  2086. }
  2087. // ---------------------------------------------------------------------------------
  2088. // Saving
  2089. // ---------------------------------------------------------------------------------
  2090. var saveUsingSafari = false;
  2091. var startSaveArea = '<div id="' + 'storeArea">'; // Split up into two so that indexOf() of this source doesn't find it
  2092. var endSaveArea = '</d' + 'iv>';
  2093. // Check if there any unsaved changes before exitting
  2094. function checkUnsavedChanges()
  2095. {
  2096. if(store.dirty)
  2097. {
  2098. if(confirm(config.messages.unsavedChangesWarning))
  2099. saveChanges();
  2100. }
  2101. }
  2102. // Save this tiddlywiki with the pending changes
  2103. function saveChanges()
  2104. {
  2105. clearMessage();
  2106. // Get the URL of the document
  2107. var originalPath = document.location.toString();
  2108. // Check we were loaded from a file URL
  2109. if(originalPath.substr(0,5) != "file:")
  2110. {
  2111. alert(config.messages.notFileUrlError);
  2112. displayTiddler(null,"SaveChanges",0,null,null,false,false);
  2113. return;
  2114. }
  2115. // Remove any location part of the URL
  2116. var hashPos = originalPath.indexOf("#");
  2117. if(hashPos != -1)
  2118. originalPath = originalPath.substr(0,hashPos);
  2119. // Convert to a native file format assuming
  2120. // "file:///x:/path/path/path..." - pc local file --> "x:\path\path\path..."
  2121. // "file://///server/share/path/path/path..." - FireFox pc network file --> "\\server\share\path\path\path..."
  2122. // "file:///path/path/path..." - mac/unix local file --> "/path/path/path..."
  2123. // "file://server/share/path/path/path..." - pc network file --> "\\server\share\path\path\path..."
  2124. var localPath;
  2125. if(originalPath.charAt(9) == ":") // pc local file
  2126. localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\");
  2127. else if(originalPath.indexOf("file://///") == 0) // FireFox pc network file
  2128. localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\");
  2129. else if(originalPath.indexOf("file:///") == 0) // mac/unix local file
  2130. localPath = unescape(originalPath.substr(7));
  2131. else if(originalPath.indexOf("file:/") == 0) // mac/unix local file
  2132. localPath = unescape(originalPath.substr(5));
  2133. else // pc network file
  2134. localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\");
  2135. // Load the original file
  2136. var original = loadFile(localPath);
  2137. if(original == null)
  2138. {
  2139. alert(config.messages.cantSaveError);
  2140. displayTiddler(null,"SaveChanges",0,null,null,false,false);
  2141. return;
  2142. }
  2143. // Locate the storeArea div's
  2144. var posOpeningDiv = original.indexOf(startSaveArea);
  2145. var posClosingDiv = original.lastIndexOf(endSaveArea);
  2146. if((posOpeningDiv == -1) || (posClosingDiv == -1))
  2147. {
  2148. alert(config.messages.invalidFileError.format([localPath]));
  2149. return;
  2150. }
  2151. // Save the backup
  2152. if(config.options.chkSaveBackups)
  2153. {
  2154. var backupPath = localPath.substr(0,localPath.lastIndexOf(".")) + "." + (new Date()).convertToYYYYMMDDHHMMSSMMM() + ".html";
  2155. var backup = saveFile(backupPath,original);
  2156. if(backup)
  2157. displayMessage(config.messages.backupSaved,"file://" + backupPath);
  2158. else
  2159. alert(config.messages.backupFailed);
  2160. }
  2161. // Save Rss
  2162. if(config.options.chkGenerateAnRssFeed)
  2163. {
  2164. var rssPath = localPath.substr(0,localPath.lastIndexOf(".")) + ".xml";
  2165. var rssSave = saveFile(rssPath,convertUnicodeToUTF8(generateRss()));
  2166. if(rssSave)
  2167. displayMessage(config.messages.rssSaved,"file://" + rssPath);
  2168. else
  2169. alert(config.messages.rssFailed);
  2170. }
  2171. // Save empty template
  2172. if(config.options.chkSaveEmptyTemplate)
  2173. {
  2174. var emptyPath,p;
  2175. if((p = localPath.lastIndexOf("/")) != -1)
  2176. emptyPath = localPath.substr(0,p) + "/empty.html";
  2177. else if((p = localPath.lastIndexOf("\\")) != -1)
  2178. emptyPath = localPath.substr(0,p) + "\\empty.html";
  2179. else
  2180. emptyPath = localPath + ".empty.html";
  2181. var empty = original.substr(0,posOpeningDiv + startSaveArea.length) + convertUnicodeToUTF8(generateEmpty()) + original.substr(posClosingDiv);
  2182. var emptySave = saveFile(emptyPath,empty);
  2183. if(emptySave)
  2184. displayMessage(config.messages.emptySaved,"file://" + emptyPath);
  2185. else
  2186. alert(config.messages.emptyFailed);
  2187. }
  2188. // Save new file
  2189. var revised = original.substr(0,posOpeningDiv + startSaveArea.length) +
  2190. convertUnicodeToUTF8(allTiddlersAsHtml()) + "\n\t\t" +
  2191. original.substr(posClosingDiv);
  2192. var newSiteTitle = store.getTiddlerText("SiteTitle","TiddlyWiki").htmlEncode();
  2193. revised = revised.replace(new RegExp("<title>[^<]*</title>", "im"),"<title>"+ newSiteTitle +"</title>");
  2194. var save = saveFile(localPath,revised);
  2195. if(save)
  2196. {
  2197. displayMessage(config.messages.mainSaved,"file://" + localPath);
  2198. store.setDirty(false);
  2199. }
  2200. else
  2201. alert(config.messages.mainFailed);
  2202. }
  2203. function generateRss()
  2204. {
  2205. var s = [];
  2206. var d = new Date();
  2207. var u = store.getTiddlerText("SiteUrl",null);
  2208. // Assemble the header
  2209. s.push("<" + "?xml version=\"1.0\"?" + ">");
  2210. s.push("<rss version=\"2.0\">");
  2211. s.push("<channel>");
  2212. s.push("<title>" + store.getTiddlerText("SiteTitle","").htmlEncode() + "</title>");
  2213. if(u)
  2214. s.push("<link>" + u.htmlEncode() + "</link>");
  2215. s.push("<description>" + store.getTiddlerText("SiteSubtitle","").htmlEncode() + "</description>");
  2216. s.push("<language>en-us</language>");
  2217. s.push("<copyright>Copyright " + d.getFullYear() + " " + config.options.txtUserName.htmlEncode() + "</copyright>");
  2218. s.push("<pubDate>" + d.toGMTString() + "</pubDate>");
  2219. s.push("<lastBuildDate>" + d.toGMTString() + "</lastBuildDate>");
  2220. s.push("<docs>http://blogs.law.harvard.edu/tech/rss</docs>");
  2221. s.push("<generator>TiddlyWiki " + version.major + "." + version.minor + "." + version.revision + "</generator>");
  2222. // The body
  2223. var tiddlers = store.getTiddlers("modified");
  2224. var n = config.numRssItems > tiddlers.length ? 0 : tiddlers.length-config.numRssItems;
  2225. for (var t=tiddlers.length-1; t>=n; t--)
  2226. s.push(tiddlers[t].saveToRss(u));
  2227. // And footer
  2228. s.push("</channel>");
  2229. s.push("</rss>");
  2230. // Save it all
  2231. return s.join("\n");
  2232. }
  2233. function generateEmpty()
  2234. {
  2235. var systemTiddlers = store.getTaggedTiddlers("systemTiddlers");
  2236. var savedTiddlers = [];
  2237. for(var s=0;s<systemTiddlers.length;s++)
  2238. savedTiddlers.push(systemTiddlers[s].saveToDiv());
  2239. return savedTiddlers.join("\n");
  2240. }
  2241. function allTiddlersAsHtml()
  2242. {
  2243. var savedTiddlers = [];
  2244. var tiddlers = store.getTiddlers("modified");
  2245. for (var t = 0; t < tiddlers.length; t++)
  2246. savedTiddlers.push(tiddlers[t].saveToDiv());
  2247. return savedTiddlers.join("\n");
  2248. }
  2249. // UTF-8 encoding rules:
  2250. // 0x0000 - 0x007F: 0xxxxxxx
  2251. // 0x0080 - 0x07FF: 110xxxxx 10xxxxxx
  2252. // 0x0800 - 0xFFFF: 1110xxxx 10xxxxxx 10xxxxxx
  2253. function convertUTF8ToUnicode(u)
  2254. {
  2255. var s = "";
  2256. var t = 0;
  2257. var b1, b2, b3;
  2258. while(t < u.length)
  2259. {
  2260. b1 = u.charCodeAt(t++);
  2261. if(b1 < 0x80)
  2262. s += String.fromCharCode(b1);
  2263. else if(b1 < 0xE0)
  2264. {
  2265. b2 = u.charCodeAt(t++);
  2266. s += String.fromCharCode(((b1 & 0x1F) << 6) | (b2 & 0x3F));
  2267. }
  2268. else
  2269. {
  2270. b2 = u.charCodeAt(t++);
  2271. b3 = u.charCodeAt(t++);
  2272. s += String.fromCharCode(((b1 & 0xF) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F));
  2273. }
  2274. }
  2275. return(s);
  2276. }
  2277. function convertUnicodeToUTF8(s)
  2278. {
  2279. if(saveUsingSafari)
  2280. return s;
  2281. else if(window.Components)
  2282. return mozConvertUnicodeToUTF8(s);
  2283. else
  2284. return manualConvertUnicodeToUTF8(s);
  2285. }
  2286. function manualConvertUnicodeToUTF8(s)
  2287. {
  2288. var u = [];
  2289. for(var t=0;t<s.length;t++)
  2290. {
  2291. var c = s.charCodeAt(t);
  2292. if(c <= 0x7F)
  2293. u.push(String.fromCharCode(c));
  2294. else
  2295. u.push("&#" + c.toString() + ";");
  2296. }
  2297. return(u.join(""));
  2298. }
  2299. function mozConvertUnicodeToUTF8(s)
  2300. {
  2301. netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
  2302. var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
  2303. converter.charset = "UTF-8";
  2304. var u = converter.ConvertFromUnicode(s);
  2305. var fin = converter.Finish();
  2306. if(fin.length > 0)
  2307. return u + fin;
  2308. else
  2309. return u;
  2310. }
  2311. function saveFile(fileUrl, content)
  2312. {
  2313. var r = null;
  2314. if(saveUsingSafari)
  2315. r = safariSaveFile(fileUrl, content);
  2316. if((r == null) || (r == false))
  2317. r = mozillaSaveFile(fileUrl, content);
  2318. if((r == null) || (r == false))
  2319. r = ieSaveFile(fileUrl, content);
  2320. return(r);
  2321. }
  2322. function loadFile(fileUrl)
  2323. {
  2324. var r = null;
  2325. if(saveUsingSafari)
  2326. r = safariLoadFile(fileUrl);
  2327. if((r == null) || (r == false))
  2328. r = mozillaLoadFile(fileUrl);
  2329. if((r == null) || (r == false))
  2330. r = ieLoadFile(fileUrl);
  2331. return(r);
  2332. }
  2333. // Returns null if it can't do it, false if there's an error, true if it saved OK
  2334. function ieSaveFile(filePath, content)
  2335. {
  2336. try
  2337. {
  2338. var fso = new ActiveXObject("Scripting.FileSystemObject");
  2339. }
  2340. catch(e)
  2341. {
  2342. //alert("Exception while attempting to save\n\n" + e.toString());
  2343. return(null);
  2344. }
  2345. var file = fso.OpenTextFile(filePath,2,-1,0);
  2346. file.Write(content);
  2347. file.Close();
  2348. return(true);
  2349. }
  2350. // Returns null if it can't do it, false if there's an error, or a string of the content if successful
  2351. function ieLoadFile(filePath)
  2352. {
  2353. try
  2354. {
  2355. var fso = new ActiveXObject("Scripting.FileSystemObject");
  2356. }
  2357. catch(e)
  2358. {
  2359. //alert("Exception while attempting to load\n\n" + e.toString());
  2360. return(null);
  2361. }
  2362. var file = fso.OpenTextFile(filePath,1);
  2363. var content = file.ReadAll();
  2364. file.Close();
  2365. return(content);
  2366. }
  2367. // Returns null if it can't do it, false if there's an error, true if it saved OK
  2368. function mozillaSaveFile(filePath, content)
  2369. {
  2370. if(window.Components)
  2371. try
  2372. {
  2373. netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
  2374. var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
  2375. file.initWithPath(filePath);
  2376. if (!file.exists())
  2377. file.create(0, 0664);
  2378. var out = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
  2379. out.init(file, 0x20 | 0x02, 00004,null);
  2380. out.write(content, content.length);
  2381. out.flush();
  2382. out.close();
  2383. return(true);
  2384. }
  2385. catch(e)
  2386. {
  2387. //alert("Exception while attempting to save\n\n" + e);
  2388. return(false);
  2389. }
  2390. return(null);
  2391. }
  2392. // Returns null if it can't do it, false if there's an error, or a string of the content if successful
  2393. function mozillaLoadFile(filePath)
  2394. {
  2395. if(window.Components)
  2396. try
  2397. {
  2398. netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
  2399. var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
  2400. file.initWithPath(filePath);
  2401. if (!file.exists())
  2402. return(null);
  2403. var inputStream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
  2404. inputStream.init(file, 0x01, 00004, null);
  2405. var sInputStream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
  2406. sInputStream.init(inputStream);
  2407. return(sInputStream.read(sInputStream.available()));
  2408. }
  2409. catch(e)
  2410. {
  2411. //alert("Exception while attempting to load\n\n" + e);
  2412. return(false);
  2413. }
  2414. return(null);
  2415. }
  2416. function safariFilenameToUrl(filename) {
  2417. return ("file://" + filename);
  2418. }
  2419. function safariLoadFile(url)
  2420. {
  2421. url = safariFilenameToUrl(url);
  2422. var plugin = document.embeds["tiddlyWikiSafariSaver"];
  2423. return plugin.readURL(url);
  2424. }
  2425. function safariSaveFile(url,content)
  2426. {
  2427. url = safariFilenameToUrl(url);
  2428. var plugin = document.embeds["tiddlyWikiSafariSaver"];
  2429. return plugin.writeStringToURL(content,url);
  2430. }
  2431. // Lifted from http://developer.apple.com/internet/webcontent/detectplugins.html
  2432. function detectPlugin()
  2433. {
  2434. var daPlugins = detectPlugin.arguments;
  2435. var pluginFound = false;
  2436. if (navigator.plugins && navigator.plugins.length > 0)
  2437. {
  2438. var pluginsArrayLength = navigator.plugins.length;
  2439. for (pluginsArrayCounter=0; pluginsArrayCounter < pluginsArrayLength; pluginsArrayCounter++ )
  2440. {
  2441. var numFound = 0;
  2442. for(namesCounter=0; namesCounter < daPlugins.length; namesCounter++)
  2443. {
  2444. if( (navigator.plugins[pluginsArrayCounter].name.indexOf(daPlugins[namesCounter]) >= 0) ||
  2445. (navigator.plugins[pluginsArrayCounter].description.indexOf(daPlugins[namesCounter]) >= 0) )
  2446. numFound++;
  2447. }
  2448. if(numFound == daPlugins.length)
  2449. {
  2450. pluginFound = true;
  2451. break;
  2452. }
  2453. }
  2454. }
  2455. return pluginFound;
  2456. }
  2457. // ---------------------------------------------------------------------------------
  2458. // Event handlers
  2459. // ---------------------------------------------------------------------------------
  2460. function onEditKey(e)
  2461. {
  2462. if (!e) var e = window.event;
  2463. clearMessage();
  2464. var consume = false;
  2465. switch(e.keyCode)
  2466. {
  2467. case 13: // Ctrl-Enter
  2468. case 10: // Ctrl-Enter on IE PC
  2469. case 77: // Ctrl-Enter is "M" on some platforms
  2470. if(e.ctrlKey && this.id && this.id.substr(0,13) == "editorWrapper")
  2471. {
  2472. blurTiddler(this.id.substr(13));
  2473. saveTiddler(this.id.substr(13));
  2474. consume = true;
  2475. }
  2476. break;
  2477. case 27: // Escape
  2478. if(this.id && this.id.substr(0,13) == "editorWrapper")
  2479. {
  2480. blurTiddler(this.id.substr(13));
  2481. displayTiddler(null,this.id.substr(13),1,null,null,false,false);
  2482. consume = true;
  2483. }
  2484. break;
  2485. }
  2486. e.cancelBubble = consume;
  2487. if(consume)
  2488. if (e.stopPropagation) e.stopPropagation();
  2489. return(!consume);
  2490. }
  2491. // Event handler for clicking on a tiddly link
  2492. function onClickTiddlerLink(e)
  2493. {
  2494. if (!e) var e = window.event;
  2495. var theTarget = resolveTarget(e);
  2496. var theLink = theTarget;
  2497. var title = null;
  2498. do {
  2499. title = theLink.getAttribute("tiddlyLink");
  2500. theLink = theLink.parentNode;
  2501. } while(title == null && theLink != null);
  2502. if(title)
  2503. {
  2504. var toggling = e.metaKey || e.ctrlKey;
  2505. if(config.options.chkToggleLinks)
  2506. toggling = !toggling;
  2507. var opening;
  2508. if(toggling && document.getElementById("tiddler" + title))
  2509. closeTiddler(title,e.shiftKey || e.altKey);
  2510. else
  2511. displayTiddler(theTarget,title,0,null,null,true,e.shiftKey || e.altKey);
  2512. }
  2513. clearMessage();
  2514. e.cancelBubble = true;
  2515. if (e.stopPropagation) e.stopPropagation();
  2516. return(false);
  2517. }
  2518. // Event handler for mouse over a tiddler
  2519. function onMouseOverTiddler(e)
  2520. {
  2521. var tiddler;
  2522. if(this.id.substr(0,7) == "tiddler")
  2523. tiddler = this.id.substr(7);
  2524. if(tiddler)
  2525. selectTiddler(tiddler);
  2526. }
  2527. // Event handler for mouse out of a tiddler
  2528. function onMouseOutTiddler(e)
  2529. {
  2530. var tiddler;
  2531. if(this.id.substr(0,7) == "tiddler")
  2532. tiddler = this.id.substr(7);
  2533. if(tiddler)
  2534. deselectTiddler(tiddler);
  2535. }
  2536. // Event handler for double click on a tiddler
  2537. function onDblClickTiddler(e)
  2538. {
  2539. if(!readOnly)
  2540. {
  2541. clearMessage();
  2542. if(document.selection)
  2543. document.selection.empty();
  2544. var tiddler;
  2545. if(this.id.substr(0,7) == "tiddler")
  2546. tiddler = this.id.substr(7);
  2547. if(tiddler)
  2548. displayTiddler(null,tiddler,2,null,null,false,false);
  2549. }
  2550. }
  2551. // Event handler for clicking on toolbar close
  2552. function onClickToolbarClose(e)
  2553. {
  2554. if (!e) var e = window.event;
  2555. clearMessage();
  2556. if(this.parentNode.id)
  2557. closeTiddler(this.parentNode.id.substr(7),e.shiftKey || e.altKey);
  2558. e.cancelBubble = true;
  2559. if (e.stopPropagation) e.stopPropagation();
  2560. return(false);
  2561. }
  2562. // Event handler for clicking on toolbar permalink
  2563. function onClickToolbarPermaLink(e)
  2564. {
  2565. if(this.parentNode.id)
  2566. {
  2567. var title = this.parentNode.id.substr(7);
  2568. var t = encodeURIComponent(String.encodeTiddlyLink(title));
  2569. if(window.location.hash != t)
  2570. window.location.hash = t;
  2571. }
  2572. }
  2573. // Event handler for clicking on toolbar close
  2574. function onClickToolbarDelete(e)
  2575. {
  2576. clearMessage();
  2577. if(this.parentNode.id)
  2578. deleteTiddler(this.parentNode.id.substr(7));
  2579. }
  2580. // Event handler for clicking on the toolbar references button
  2581. function onClickToolbarReferences(e)
  2582. {
  2583. if (!e) var e = window.event;
  2584. var theTarget = resolveTarget(e);
  2585. var popup = createTiddlerPopup(this);
  2586. if(popup && this.parentNode.id)
  2587. {
  2588. var title = this.parentNode.id.substr(7);
  2589. var references = store.getReferringTiddlers(title);
  2590. var c = false;
  2591. for(var r=0; r<references.length; r++)
  2592. if(references[r].title != title)
  2593. {
  2594. createTiddlyLink(popup,references[r].title,true);
  2595. c = true;
  2596. }
  2597. if(!c)
  2598. popup.appendChild(document.createTextNode(config.views.wikified.toolbarReferences.popupNone));
  2599. }
  2600. scrollToTiddlerPopup(popup,false);
  2601. e.cancelBubble = true;
  2602. if (e.stopPropagation) e.stopPropagation();
  2603. return(false);
  2604. }
  2605. // Event handler for clicking on a tiddler tag
  2606. function onClickTag(e)
  2607. {
  2608. if (!e) var e = window.event;
  2609. var theTarget = resolveTarget(e);
  2610. var popup = createTiddlerPopup(this);
  2611. var tag = this.getAttribute("tag");
  2612. var title = this.getAttribute("tiddler");
  2613. if(popup && tag)
  2614. {
  2615. var tagged = store.getTaggedTiddlers(tag);
  2616. var c = false;
  2617. for(var r=0;r<tagged.length;r++)
  2618. if(tagged[r].title != title)
  2619. {
  2620. createTiddlyLink(popup,tagged[r].title,true);
  2621. c = true;
  2622. }
  2623. var lingo = config.views.wikified.tag;
  2624. if(c)
  2625. {
  2626. popup.insertBefore(document.createElement("hr"),popup.firstChild);
  2627. var openAll = createTiddlyButton(null,lingo.openAllText.format([tag]),lingo.openAllTooltip,onClickTagOpenAll);
  2628. openAll.setAttribute("tag",tag);
  2629. popup.insertBefore(openAll,popup.firstChild);
  2630. }
  2631. else
  2632. popup.appendChild(document.createTextNode(lingo.popupNone.format([tag])));
  2633. }
  2634. scrollToTiddlerPopup(popup,false);
  2635. e.cancelBubble = true;
  2636. if (e.stopPropagation) e.stopPropagation();
  2637. return(false);
  2638. }
  2639. // Event handler for 'open all' on a tiddler popup
  2640. function onClickTagOpenAll(e)
  2641. {
  2642. if (!e) var e = window.event;
  2643. var tag = this.getAttribute("tag");
  2644. var tagged = store.getTaggedTiddlers(tag);
  2645. for(var t=tagged.length-1; t>=0; t--)
  2646. displayTiddler(this,tagged[t].title,0,null,null,false,e.shiftKey || e.altKey);
  2647. e.cancelBubble = true;
  2648. if (e.stopPropagation) e.stopPropagation();
  2649. return(false);
  2650. }
  2651. // Event handler for clicking on the 'add tag' button
  2652. function onClickAddTag(e)
  2653. {
  2654. if (!e) var e = window.event;
  2655. var theTarget = resolveTarget(e);
  2656. var popup = createTiddlerPopup(this);
  2657. var tiddler = this.getAttribute("tiddler");
  2658. var tags = store.getTags();
  2659. var lingo = config.views.editor.tagChooser;
  2660. if(tags.length == 0)
  2661. createTiddlyElement(popup,"div",null,null,lingo.popupNone);
  2662. for (t=0; t<tags.length; t++)
  2663. {
  2664. var theTag = createTiddlyButton(popup,tags[t][0],lingo.tagTooltip.format([tags[t][0]]),onClickAddTagPopup);
  2665. theTag.setAttribute("tag",tags[t][0]);
  2666. theTag.setAttribute("tiddler",tiddler);
  2667. }
  2668. scrollToTiddlerPopup(popup,false);
  2669. e.cancelBubble = true;
  2670. if (e.stopPropagation) e.stopPropagation();
  2671. return(false);
  2672. }
  2673. // Event handler for clicking on a tag in the 'add tag' popup
  2674. function onClickAddTagPopup(e)
  2675. {
  2676. if (!e) var e = window.event;
  2677. var theTarget = resolveTarget(e);
  2678. var tiddler = this.getAttribute("tiddler");
  2679. var tag = this.getAttribute("tag");
  2680. var tagsBox = document.getElementById("editorTags" + tiddler);
  2681. if(tagsBox)
  2682. tagsBox.value += " " + String.encodeTiddlyLink(tag);
  2683. e.cancelBubble = true;
  2684. if (e.stopPropagation) e.stopPropagation();
  2685. return(false);
  2686. }
  2687. // Event handler for clicking on toolbar close
  2688. function onClickToolbarEdit(e)
  2689. {
  2690. clearMessage();
  2691. if(this.parentNode.id)
  2692. displayTiddler(null,this.parentNode.id.substr(7),2,null,null,false,false);
  2693. }
  2694. // Event handler for clicking on toolbar save
  2695. function onClickToolbarSave(e)
  2696. {
  2697. if(this.parentNode.id)
  2698. saveTiddler(this.parentNode.id.substr(7));
  2699. }
  2700. // Event handler for clicking on toolbar save
  2701. function onClickToolbarUndo(e)
  2702. {
  2703. if(this.parentNode.id)
  2704. displayTiddler(null,this.parentNode.id.substr(7),1,null,null,false,false);
  2705. }
  2706. // Eek... it's bad that this is done via a function rather than a normal, copy-able href
  2707. function onClickPermaView()
  2708. {
  2709. var tiddlerDisplay = document.getElementById("tiddlerDisplay");
  2710. var links = [];
  2711. for(var t=0;t<tiddlerDisplay.childNodes.length;t++)
  2712. {
  2713. var tiddlerName = tiddlerDisplay.childNodes[t].id.substr(7);
  2714. links.push(String.encodeTiddlyLink(tiddlerName));
  2715. }
  2716. window.location.hash = encodeURIComponent(links.join(" "));
  2717. }
  2718. // ---------------------------------------------------------------------------------
  2719. // Animation engine
  2720. // ---------------------------------------------------------------------------------
  2721. function Animator()
  2722. {
  2723. this.running = 0; // Incremented at start of each animation, decremented afterwards. If zero, the interval timer is disabled
  2724. this.timerID; // ID of the timer used for animating
  2725. this.animations = []; // List of animations in progress
  2726. return this;
  2727. }
  2728. // Start animation engine
  2729. Animator.prototype.startAnimating = function() // Variable number of arguments
  2730. {
  2731. for(var t=0; t<arguments.length; t++)
  2732. this.animations.push(arguments[t]);
  2733. if(this.running == 0)
  2734. {
  2735. var me = this;
  2736. this.timerID = window.setInterval(function() {me.doAnimate(me);},25);
  2737. }
  2738. this.running += arguments.length;
  2739. }
  2740. // Perform an animation engine tick, calling each of the known animation modules
  2741. Animator.prototype.doAnimate = function(me)
  2742. {
  2743. var a = 0;
  2744. while(a<me.animations.length)
  2745. {
  2746. var animation = me.animations[a];
  2747. animation.progress += animation.step;
  2748. if(animation.progress < 0 || animation.progress > 1)
  2749. {
  2750. animation.stop();
  2751. me.animations.splice(a,1);
  2752. if(--me.running == 0)
  2753. window.clearInterval(me.timerID);
  2754. }
  2755. else
  2756. {
  2757. animation.tick();
  2758. a++;
  2759. }
  2760. }
  2761. }
  2762. // Map a 0..1 value to 0..1, but slow down at the start and end
  2763. Animator.slowInSlowOut = function(progress)
  2764. {
  2765. return(1-((Math.cos(progress * Math.PI)+1)/2));
  2766. }
  2767. // ---------------------------------------------------------------------------------
  2768. // Zoomer animation
  2769. // ---------------------------------------------------------------------------------
  2770. function Zoomer(text,startElement,targetElement,slowly)
  2771. {
  2772. this.element = document.createElement("div");
  2773. this.element.appendChild(document.createTextNode(text));
  2774. this.element.className = "zoomer";
  2775. document.body.appendChild(this.element);
  2776. this.startElement = startElement;
  2777. this.startLeft = findPosX(this.startElement);
  2778. this.startTop = findPosY(this.startElement);
  2779. this.startWidth = this.startElement.offsetWidth;
  2780. this.startHeight = this.startElement.offsetHeight;
  2781. this.targetElement = targetElement;
  2782. this.targetLeft = findPosX(this.targetElement);
  2783. this.targetTop = findPosY(this.targetElement);
  2784. this.targetWidth = this.targetElement.offsetWidth;
  2785. this.targetHeight = this.targetElement.offsetHeight;
  2786. this.progress = 0;
  2787. this.step = slowly ? config.animSlow : config.animFast;
  2788. this.targetElement.style.opacity = 0;
  2789. return this;
  2790. }
  2791. Zoomer.prototype.stop = function()
  2792. {
  2793. this.element.parentNode.removeChild(this.element);
  2794. this.targetElement.style.opacity = 1;
  2795. }
  2796. Zoomer.prototype.tick = function()
  2797. {
  2798. var f = Animator.slowInSlowOut(this.progress);
  2799. this.element.style.left = this.startLeft + (this.targetLeft-this.startLeft) * f + "px";
  2800. this.element.style.top = this.startTop + (this.targetTop-this.startTop) * f + "px";
  2801. this.element.style.width = this.startWidth + (this.targetWidth-this.startWidth) * f + "px";
  2802. this.element.style.height = this.startHeight + (this.targetHeight-this.startHeight) * f + "px";
  2803. this.element.style.display = "block";
  2804. this.targetElement.style.opacity = this.progress;
  2805. this.targetElement.style.filter = "alpha(opacity:" + this.progress * 100 + ")";
  2806. }
  2807. // ---------------------------------------------------------------------------------
  2808. // Scroller animation
  2809. // ---------------------------------------------------------------------------------
  2810. function Scroller(targetElement,slowly)
  2811. {
  2812. this.targetElement = targetElement;
  2813. this.startScroll = findScrollY();
  2814. this.targetScroll = ensureVisible(targetElement);
  2815. this.progress = 0;
  2816. this.step = slowly ? config.animSlow : config.animFast;
  2817. return this;
  2818. }
  2819. Scroller.prototype.stop = function()
  2820. {
  2821. window.scrollTo(0,this.targetScroll);
  2822. }
  2823. Scroller.prototype.tick = function()
  2824. {
  2825. var f = Animator.slowInSlowOut(this.progress);
  2826. window.scrollTo(0,this.startScroll + (this.targetScroll-this.startScroll) * f);
  2827. }
  2828. // ---------------------------------------------------------------------------------
  2829. // Slider animation
  2830. // ---------------------------------------------------------------------------------
  2831. // deleteMode - "none", "all" [delete target element and it's children], [only] "children" [but not the target element]
  2832. function Slider(element,opening,slowly,deleteMode)
  2833. {
  2834. this.element = element;
  2835. element.style.display = "block";
  2836. this.deleteMode = deleteMode;
  2837. this.element.style.height = "auto";
  2838. this.realHeight = element.offsetHeight;
  2839. this.opening = opening;
  2840. this.step = slowly ? config.animSlow : config.animFast;
  2841. if(opening)
  2842. {
  2843. this.progress = 0;
  2844. element.style.height = "0px";
  2845. element.style.display = "block";
  2846. }
  2847. else
  2848. {
  2849. this.progress = 1;
  2850. this.step = -this.step;
  2851. }
  2852. element.style.overflow = "hidden";
  2853. return this;
  2854. }
  2855. Slider.prototype.stop = function()
  2856. {
  2857. if(this.opening)
  2858. this.element.style.height = "auto";
  2859. else
  2860. {
  2861. switch(this.deleteMode)
  2862. {
  2863. case "none":
  2864. this.element.style.display = "none";
  2865. break;
  2866. case "all":
  2867. this.element.parentNode.removeChild(this.element);
  2868. break;
  2869. case "children":
  2870. removeChildren(this.element);
  2871. break;
  2872. }
  2873. }
  2874. }
  2875. Slider.prototype.tick = function()
  2876. {
  2877. var f = Animator.slowInSlowOut(this.progress);
  2878. var h = this.realHeight * f;
  2879. this.element.style.height = h + "px";
  2880. this.element.style.opacity = f;
  2881. }
  2882. // ---------------------------------------------------------------------------------
  2883. // Augmented methods for the JavaScript Number(), Array() and String() objects
  2884. // ---------------------------------------------------------------------------------
  2885. // Clamp a number to a range
  2886. Number.prototype.clamp = function(min,max)
  2887. {
  2888. c = this;
  2889. if(c < min)
  2890. c = min;
  2891. if(c > max)
  2892. c = max;
  2893. return c;
  2894. }
  2895. // Find an entry in an array. Returns the array index or null
  2896. Array.prototype.find = function(item)
  2897. {
  2898. var p = null;
  2899. for(var t=0; t<this.length; t++)
  2900. if(this[t] == item)
  2901. {
  2902. p = t;
  2903. break;
  2904. }
  2905. return p;
  2906. }
  2907. // 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
  2908. Array.prototype.pushUnique = function(item,unique)
  2909. {
  2910. if(unique != undefined && unique == false)
  2911. this.push(item);
  2912. else
  2913. {
  2914. if(this.find(item) == null)
  2915. this.push(item);
  2916. }
  2917. }
  2918. // Get characters from the right end of a string
  2919. String.prototype.right = function(n)
  2920. {
  2921. if(n < this.length)
  2922. return this.slice(this.length-n);
  2923. else
  2924. return this;
  2925. }
  2926. // Trim whitespace from both ends of a string
  2927. String.prototype.trim = function()
  2928. {
  2929. var regexpTrim = new RegExp("^\\s*(.*)\\s*$","mg");
  2930. return(this.replace(regexpTrim,"$1"));
  2931. }
  2932. // Substitute substrings from an array into a format string that includes '%1'-type specifiers
  2933. String.prototype.format = function(substrings)
  2934. {
  2935. var subRegExp = new RegExp("(?:%(\\d+))","mg");
  2936. var currPos = 0;
  2937. var r = [];
  2938. do {
  2939. var match = subRegExp.exec(this);
  2940. if(match && match[1])
  2941. {
  2942. if(match.index > currPos)
  2943. r.push(this.substring(currPos,match.index));
  2944. r.push(substrings[parseInt(match[1])]);
  2945. currPos = subRegExp.lastIndex;
  2946. }
  2947. } while(match);
  2948. if(currPos < this.length)
  2949. r.push(this.substring(currPos,this.length));
  2950. return r.join("");
  2951. }
  2952. // Escape any special RegExp characters with that character preceded by a backslash
  2953. String.prototype.escapeRegExp = function()
  2954. {
  2955. return(this.replace(new RegExp("[\\\\\\^\\$\\*\\+\\?\\(\\)\\=\\!\\|\\,\\{\\}\\[\\]\\.]","g"),"\\$&"));
  2956. }
  2957. // Convert & to "&amp;", < to "&lt;", > to "&gt;" and " to "&quot;"
  2958. String.prototype.htmlEncode = function()
  2959. {
  2960. var regexpAmp = new RegExp("&","mg");
  2961. var regexpLessThan = new RegExp("<","mg");
  2962. var regexpGreaterThan = new RegExp(">","mg");
  2963. var regexpQuote = new RegExp("\"","mg");
  2964. return(this.replace(regexpAmp,"&amp;").replace(regexpLessThan,"&lt;").replace(regexpGreaterThan,"&gt;").replace(regexpQuote,"&quot;"));
  2965. }
  2966. // Process a string list of macro parameters into an array. Parameters can be quoted with "", '', [[]] or left unquoted (and therefore space-separated)
  2967. String.prototype.readMacroParams = function()
  2968. {
  2969. var regexpMacroParam = new RegExp("(?:\\s*)(?:(?:\"([^\"]*)\")|(?:'([^']*)')|(?:\\[\\[([^\\]]*)\\]\\])|([^\"'\\s]\\S*))","mg");
  2970. var params = [];
  2971. do {
  2972. var match = regexpMacroParam.exec(this);
  2973. if(match)
  2974. {
  2975. if(match[1]) // Double quoted
  2976. params.push(match[1]);
  2977. else if(match[2]) // Single quoted
  2978. params.push(match[2]);
  2979. else if(match[3]) // Double-square-bracket quoted
  2980. params.push(match[3]);
  2981. else if(match[4]) // Unquoted
  2982. params.push(match[4]);
  2983. }
  2984. } while(match);
  2985. return params;
  2986. }
  2987. // Process a string list of tiddler names into an array. Tiddler names that have spaces in them must be [[bracketed]]
  2988. String.prototype.readBracketedList = function(unique)
  2989. {
  2990. var bracketedPattern = "\\[\\[([^\\]]+)\\]\\]";
  2991. var unbracketedPattern = "[^\\s$]+";
  2992. var pattern = "(?:" + bracketedPattern + ")|(" + unbracketedPattern + ")";
  2993. var re = new RegExp(pattern,"mg");
  2994. var tiddlerNames = [];
  2995. do {
  2996. var match = re.exec(this);
  2997. if(match)
  2998. {
  2999. if(match[1]) // Bracketed
  3000. tiddlerNames.pushUnique(match[1],unique);
  3001. else if(match[2]) // Unbracketed
  3002. tiddlerNames.pushUnique(match[2],unique);
  3003. }
  3004. } while(match);
  3005. return(tiddlerNames);
  3006. }
  3007. // Static method to bracket a string with double square brackets if it contains a space
  3008. String.encodeTiddlyLink = function(title)
  3009. {
  3010. if(title.indexOf(" ") == -1)
  3011. return(title);
  3012. else
  3013. return("[[" + title + "]]");
  3014. }
  3015. // Static method to left-pad a string with 0s to a certain width
  3016. String.zeroPad = function(n,d)
  3017. {
  3018. var s = n.toString();
  3019. if(s.length < d)
  3020. s = "000000000000000000000000000".substr(0,d-s.length) + s;
  3021. return(s);
  3022. }
  3023. // ---------------------------------------------------------------------------------
  3024. // RGB colour object
  3025. // ---------------------------------------------------------------------------------
  3026. // Construct an RGB colour object from a '#rrggbb' or 'rgb(n,n,n)' string or from separate r,g,b values
  3027. function RGB(r,g,b)
  3028. {
  3029. this.r = 0;
  3030. this.g = 0;
  3031. this.b = 0;
  3032. if(typeof r == "string")
  3033. {
  3034. if(r.substr(0,1) == "#")
  3035. {
  3036. this.r = parseInt(r.substr(1,2),16)/255;
  3037. this.g = parseInt(r.substr(3,2),16)/255;
  3038. this.b = parseInt(r.substr(5,2),16)/255;
  3039. }
  3040. else
  3041. {
  3042. var rgbPattern = /rgb\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/ ;
  3043. var c = r.match(rgbPattern);
  3044. if (c)
  3045. {
  3046. this.r = parseInt(c[1],10)/255;
  3047. this.g = parseInt(c[2],10)/255;
  3048. this.b = parseInt(c[3],10)/255;
  3049. }
  3050. }
  3051. }
  3052. else
  3053. {
  3054. this.r = r;
  3055. this.g = g;
  3056. this.b = b;
  3057. }
  3058. return this;
  3059. }
  3060. // Mixes this colour with another in a specified proportion
  3061. // c = other colour to mix
  3062. // f = 0..1 where 0 is this colour and 1 is the new colour
  3063. // Returns an RGB object
  3064. RGB.prototype.mix = function(c,f)
  3065. {
  3066. return new RGB(this.r + (c.r-this.r) * f,this.g + (c.g-this.g) * f,this.b + (c.b-this.b) * f);
  3067. }
  3068. // Return an rgb colour as a #rrggbb format hex string
  3069. RGB.prototype.toString = function()
  3070. {
  3071. var r = this.r.clamp(0,1);
  3072. var g = this.g.clamp(0,1);
  3073. var b = this.b.clamp(0,1);
  3074. return("#" + ("0" + Math.floor(r * 255).toString(16)).right(2) +
  3075. ("0" + Math.floor(g * 255).toString(16)).right(2) +
  3076. ("0" + Math.floor(b * 255).toString(16)).right(2));
  3077. }
  3078. // ---------------------------------------------------------------------------------
  3079. // Augmented methods for the JavaScript Date() object
  3080. // ---------------------------------------------------------------------------------
  3081. // Substitute date components into a string
  3082. Date.prototype.formatString = function(template)
  3083. {
  3084. template = template.replace("YYYY",this.getFullYear());
  3085. template = template.replace("YY",String.zeroPad(this.getFullYear()-2000,2));
  3086. template = template.replace("MMM",config.messages.dates.months[this.getMonth()]);
  3087. template = template.replace("0MM",String.zeroPad(this.getMonth()+1,2));
  3088. template = template.replace("MM",this.getMonth()+1);
  3089. template = template.replace("DDD",config.messages.dates.days[this.getDay()]);
  3090. template = template.replace("0DD",String.zeroPad(this.getDate(),2));
  3091. template = template.replace("DD",this.getDate());
  3092. template = template.replace("hh",this.getHours());
  3093. template = template.replace("mm",this.getMinutes());
  3094. template = template.replace("ss",this.getSeconds());
  3095. return template;
  3096. }
  3097. // Convert a date to UTC YYYYMMDDHHMM string format
  3098. Date.prototype.convertToYYYYMMDDHHMM = function()
  3099. {
  3100. 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));
  3101. }
  3102. // Convert a date to UTC YYYYMMDD.HHMMSSMMM string format
  3103. Date.prototype.convertToYYYYMMDDHHMMSSMMM = function()
  3104. {
  3105. 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) + String.zeroPad(this.getSeconds(),2) + String.zeroPad(this.getMilliseconds(),4));
  3106. }
  3107. // Static method to create a date from a UTC YYYYMMDDHHMM format string
  3108. Date.convertFromYYYYMMDDHHMM = function(d)
  3109. {
  3110. var theDate = new Date(parseInt(d.substr(0,4),10),
  3111. parseInt(d.substr(4,2),10)-1,
  3112. parseInt(d.substr(6,2),10),
  3113. parseInt(d.substr(8,2),10),
  3114. parseInt(d.substr(10,2),10),0,0);
  3115. return(theDate);
  3116. }
  3117. // ---------------------------------------------------------------------------------
  3118. // DOM utilities - many derived from www.quirksmode.org
  3119. // ---------------------------------------------------------------------------------
  3120. function createTiddlyElement(theParent,theElement,theID,theClass,theText)
  3121. {
  3122. var e = document.createElement(theElement);
  3123. if(theClass != null)
  3124. e.className = theClass;
  3125. if(theID != null)
  3126. e.setAttribute("id",theID);
  3127. if(theText != null)
  3128. e.appendChild(document.createTextNode(theText));
  3129. if(theParent != null)
  3130. theParent.appendChild(e);
  3131. return(e);
  3132. }
  3133. function createTiddlyButton(theParent,theText,theTooltip,theAction,theClass,theId)
  3134. {
  3135. var theButton = document.createElement("a");
  3136. theButton.className = "button";
  3137. if(theAction)
  3138. {
  3139. theButton.onclick = theAction;
  3140. theButton.setAttribute("href","JavaScript:;");
  3141. }
  3142. theButton.setAttribute("title",theTooltip);
  3143. if(theText)
  3144. theButton.appendChild(document.createTextNode(theText));
  3145. if(theClass)
  3146. theButton.className = theClass;
  3147. if(theId)
  3148. theButton.id = theId;
  3149. if(theParent)
  3150. theParent.appendChild(theButton);
  3151. return(theButton);
  3152. }
  3153. function createTiddlyLink(place,title,includeText)
  3154. {
  3155. var text = includeText ? title : null;
  3156. var subTitle;
  3157. var tiddler = store.tiddlers[title];
  3158. if(tiddler)
  3159. subTitle = tiddler.getSubtitle();
  3160. else
  3161. subTitle = config.messages.undefinedTiddlerToolTip.format([title]);
  3162. var theClass = tiddler ? "tiddlyLinkExisting tiddlyLink" : "tiddlyLinkNonExisting tiddlyLink";
  3163. var btn = createTiddlyButton(place,text,subTitle,onClickTiddlerLink,theClass);
  3164. btn.setAttribute("tiddlyLink",title);
  3165. return(btn);
  3166. }
  3167. function createExternalLink(place,url)
  3168. {
  3169. var theLink = document.createElement("a");
  3170. theLink.className = "externalLink";
  3171. theLink.href = url;
  3172. theLink.title = config.messages.externalLinkTooltip.format([url]);
  3173. if(config.options.chkOpenInNewWindow)
  3174. theLink.target = "_blank";
  3175. place.appendChild(theLink);
  3176. return(theLink);
  3177. }
  3178. // Find the tiddler instance (if any) containing a specified element
  3179. function findContainingTiddler(e)
  3180. {
  3181. if(e == null)
  3182. return(null);
  3183. do {
  3184. if(e != document)
  3185. {
  3186. if(e.id)
  3187. if(e.id.substr(0,7) == "tiddler")
  3188. return(e);
  3189. }
  3190. e = e.parentNode;
  3191. } while(e != document);
  3192. return(null);
  3193. }
  3194. // Resolve the target object of an event
  3195. function resolveTarget(e)
  3196. {
  3197. var obj;
  3198. if (e.target)
  3199. obj = e.target;
  3200. else if (e.srcElement)
  3201. obj = e.srcElement;
  3202. if (obj.nodeType == 3) // defeat Safari bug
  3203. obj = obj.parentNode;
  3204. return(obj);
  3205. }
  3206. // Return the content of an element as plain text with no formatting
  3207. function getElementText(elementID)
  3208. {
  3209. var e = document.getElementById(elementID);
  3210. var text = "";
  3211. if(e.innerText)
  3212. text = e.innerText;
  3213. else if(e.textContent)
  3214. text = e.textContent;
  3215. return text;
  3216. }
  3217. // Get the scroll position for window.scrollTo necessary to scroll a given element into view
  3218. function ensureVisible(e)
  3219. {
  3220. var posTop = findPosY(e);
  3221. var posBot = posTop + e.offsetHeight;
  3222. var winTop = findScrollY();
  3223. var winHeight = findWindowHeight();
  3224. var winBot = winTop + winHeight;
  3225. if(posTop < winTop)
  3226. return(posTop);
  3227. else if(posBot > winBot)
  3228. {
  3229. if(e.offsetHeight < winHeight)
  3230. return(posTop - (winHeight - e.offsetHeight));
  3231. else
  3232. return(posTop);
  3233. }
  3234. else
  3235. return(winTop);
  3236. }
  3237. // Get the current height of the display window
  3238. function findWindowHeight()
  3239. {
  3240. return(window.innerHeight ? window.innerHeight : document.body.clientHeight);
  3241. }
  3242. // Get the current vertical page scroll position
  3243. function findScrollY()
  3244. {
  3245. return(window.scrollY ? window.scrollY : document.body.scrollTop);
  3246. }
  3247. function findPosX(obj)
  3248. {
  3249. var curleft = 0;
  3250. while (obj.offsetParent)
  3251. {
  3252. curleft += obj.offsetLeft;
  3253. obj = obj.offsetParent;
  3254. }
  3255. return curleft;
  3256. }
  3257. function findPosY(obj)
  3258. {
  3259. var curtop = 0;
  3260. while (obj.offsetParent)
  3261. {
  3262. curtop += obj.offsetTop;
  3263. obj = obj.offsetParent;
  3264. }
  3265. return curtop;
  3266. }
  3267. // Create a non-breaking space
  3268. function insertSpacer(place)
  3269. {
  3270. var e = document.createTextNode(String.fromCharCode(160));
  3271. if(place)
  3272. place.appendChild(e);
  3273. return e;
  3274. }
  3275. // Remove all children of a node
  3276. function removeChildren(e)
  3277. {
  3278. while(e.hasChildNodes())
  3279. e.removeChild(e.firstChild);
  3280. }
  3281. // Add a stylesheet, replacing any previous custom stylesheet
  3282. function setStylesheet(s,id)
  3283. {
  3284. if(!id)
  3285. id = "customStyleSheet";
  3286. var n = document.getElementById(id);
  3287. if(document.createStyleSheet) // Test for IE's non-standard createStyleSheet method
  3288. {
  3289. if(n)
  3290. n.parentNode.removeChild(n);
  3291. // This failed without the &nbsp;
  3292. document.body.insertAdjacentHTML("beforeEnd","&nbsp;<style id='" + id + "'>" + s + "</style>");
  3293. }
  3294. else
  3295. {
  3296. if(n)
  3297. n.replaceChild(document.createTextNode(s),n.firstChild);
  3298. else
  3299. {
  3300. var n = document.createElement("style");
  3301. n.type = "text/css";
  3302. n.id = id;
  3303. n.appendChild(document.createTextNode(s));
  3304. document.getElementsByTagName("head")[0].appendChild(n);
  3305. }
  3306. }
  3307. }
  3308. // ---------------------------------------------------------------------------------
  3309. // End of scripts
  3310. // ---------------------------------------------------------------------------------
  3311. </script>
  3312. <style type="text/css">
  3313. body {
  3314. background-color: #ffffff;
  3315. font-size: 9pt;
  3316. font-family: verdana,arial,helvetica;
  3317. margin: 0em 0em 0em 0em;
  3318. padding: 0em 0em 0em 0em;
  3319. position: relative;
  3320. z-index: 0;
  3321. }
  3322. a:link, a:visited {
  3323. text-decoration: none;
  3324. }
  3325. a:hover, a:active {
  3326. color: white;
  3327. background-color: #482d94;
  3328. text-decoration: none;
  3329. }
  3330. #contentWrapper {
  3331. position: relative;
  3332. }
  3333. #header {
  3334. }
  3335. #titleLine {
  3336. color: #ffffff;
  3337. background-color: #330000;
  3338. padding: 5em 1em 1em 1em;
  3339. }
  3340. #titleLine a {
  3341. color: #CCFF66;
  3342. }
  3343. #siteTitle {
  3344. font-size: 26pt;
  3345. }
  3346. #siteSubtitle {
  3347. padding-left: 1em;
  3348. font-size: 10pt;
  3349. }
  3350. #mainMenu {
  3351. position: absolute;
  3352. left: 0em;
  3353. width: 10em;
  3354. line-height: 166%;
  3355. padding: 1.5em 0.5em 0.5em 0.5em;
  3356. font-size: 10pt;
  3357. color: black;
  3358. text-align: right;
  3359. }
  3360. #mainMenu .tiddlyLink {
  3361. color: #996633;
  3362. }
  3363. #mainMenu .tiddlyLink:hover {
  3364. background-color: #996633;
  3365. color: #ffffff;
  3366. }
  3367. #mainMenu .externalLink {
  3368. color: #996633;
  3369. text-decoration: underline;
  3370. }
  3371. #mainMenu .externalLink:hover {
  3372. background-color: #996633;
  3373. color: #ffffff;
  3374. }
  3375. #mainMenu .button {
  3376. color: #993300;
  3377. }
  3378. #mainMenu .button:hover {
  3379. color: #ccff66;
  3380. background-color: #993300;
  3381. }
  3382. #displayArea {
  3383. margin: 1em 17em 0em 14em;
  3384. }
  3385. #tiddlerDisplay {
  3386. }
  3387. #messageArea {
  3388. background-color: #993300;
  3389. color: #ffffff;
  3390. padding: 0.5em 0.5em 0.5em 0.5em;
  3391. display: none;
  3392. }
  3393. #messageArea a:link, #messageArea a:visited {
  3394. display: inline;
  3395. text-decoration: underline;
  3396. color: #cc9900;
  3397. }
  3398. #messageArea a:hover {
  3399. color: #996633;
  3400. }
  3401. #messageArea a:active {
  3402. color: #ffffff;
  3403. }
  3404. #popup {
  3405. display: none;
  3406. position: absolute;
  3407. line-height: 110%;
  3408. font-size: 8pt;
  3409. color: #ccff66;
  3410. background-color: #993300;
  3411. padding: 0.25em 0.25em 0.25em 0.25em;
  3412. border-right: 1px solid #330000;
  3413. border-bottom: 1px solid #330000;
  3414. z-index: 10;
  3415. }
  3416. #popup a {
  3417. display: block;
  3418. color: #ccff66;
  3419. padding: 1px 1px 1px 1px;
  3420. }
  3421. #popup a:hover {
  3422. background-color: #ccff66;
  3423. color: #330000;
  3424. }
  3425. #popup hr {
  3426. border-top: solid 1px #ccff66;
  3427. border-left: none;
  3428. border-right: none;
  3429. border-bottom: none;
  3430. height: 1px;
  3431. width: 5em;
  3432. left: 0em;
  3433. color: #ccff66;
  3434. }
  3435. .tabset {
  3436. padding: 1em 0em 0em 0.5em;
  3437. }
  3438. .tab {
  3439. margin: 0em 0em 0em 0.25em;
  3440. padding: 2px 2px 2px 2px;
  3441. }
  3442. .tabSelected {
  3443. background-color: #eeeeaa;
  3444. }
  3445. .tabUnselected {
  3446. background-color: #cc9900;
  3447. }
  3448. .tab:hover {
  3449. }
  3450. .tab:active {
  3451. }
  3452. .tabContents {
  3453. padding: 0.5em 0.5em 0.5em 0.5em;
  3454. background-color: #eeeeaa;
  3455. }
  3456. .tabContents ul, .tabContents ol {
  3457. margin: 0;
  3458. padding: 0;
  3459. }
  3460. .tabContents li {
  3461. list-style: none;
  3462. }
  3463. .tabContents li.listLink {
  3464. margin-left: .75em;
  3465. }
  3466. .tiddler {
  3467. padding: 0em 0em 0em 0em;
  3468. }
  3469. .selectedTiddler {
  3470. padding: 1em 1em 0em 1em;
  3471. font-size: 9pt;
  3472. }
  3473. .unselectedTiddler {
  3474. padding: 1em 1em 0em 1em;
  3475. font-size: 9pt;
  3476. }
  3477. .tiddler a.tiddlyLinkExisting {
  3478. font-weight: bold;
  3479. }
  3480. .tiddler a.tiddlyLinkNonExisting {
  3481. font-style: italic;
  3482. }
  3483. .tiddler a.externalLink {
  3484. text-decoration: underline;
  3485. }
  3486. .tiddler .button {
  3487. padding: 0.2em 0.4em 0.2em 0.4em;
  3488. color: #993300;
  3489. }
  3490. .tiddler .button:hover {
  3491. text-decoration: none;
  3492. color: #ccff66;
  3493. background-color: #993300;
  3494. }
  3495. .tiddler .button:active {
  3496. color: #ffffff;
  3497. background-color: #cc9900;
  3498. }
  3499. .title {
  3500. font-size: 10pt;
  3501. font-weight: bold;
  3502. }
  3503. .selectedTiddler .title {
  3504. }
  3505. .toolbar {
  3506. text-align: right;
  3507. font-weight: normal;
  3508. font-size: 8pt;
  3509. padding: 0em 0em 0em 2em;
  3510. color: #aaaaaa;
  3511. visibility: hidden;
  3512. }
  3513. .toolbar #popup {
  3514. text-align: left;
  3515. }
  3516. .selectedTiddler .toolbar {
  3517. visibility: visible;
  3518. }
  3519. .footer {
  3520. font-weight: normal;
  3521. font-size: 8pt;
  3522. margin: 0em 0em 0em 0em;
  3523. padding: 0em 0em 0em 0em;
  3524. color: #dddddd;
  3525. }
  3526. .selectedTiddler .footer {
  3527. color: #888888;
  3528. }
  3529. .body {
  3530. padding-top: 0.5em;
  3531. }
  3532. .viewer {
  3533. color: #000000;
  3534. line-height: 140%;
  3535. }
  3536. .viewer a:link, .body a:visited {
  3537. color: #996633;
  3538. }
  3539. .viewer a:hover {
  3540. color: #ffffff;
  3541. background-color: #996633;
  3542. }
  3543. .viewer .button {
  3544. margin: 0em 0.25em 0em 0.25em;
  3545. padding: 0em 0.25em 0em 0.25em;
  3546. background-color: #cc9900;
  3547. color: #330000;
  3548. border-right: 1px solid #33000;
  3549. border-bottom: 1px solid #33000;
  3550. }
  3551. .viewer .button:hover {
  3552. background-color: #eeeeaa;
  3553. color: #cc9900;
  3554. }
  3555. .viewer blockquote {
  3556. background-color: #ccbb55;
  3557. font-size: 8pt;
  3558. line-height: 150%;
  3559. border-left: 3px solid #666666;
  3560. padding-left: 0.8em;
  3561. margin-left: 2.5em;
  3562. }
  3563. .viewer ul {
  3564. margin-left: 0.5em;
  3565. padding-left: 1.5em;
  3566. }
  3567. .viewer ol {
  3568. margin-left: 0.5em;
  3569. padding-left: 1.5em;
  3570. }
  3571. .viewer h1,.viewer h2,.viewer h3,.viewer h4,.viewer h5 {
  3572. font-weight: bold;
  3573. text-decoration: none;
  3574. background-color: #cccc99;
  3575. padding-left: 0.4em;
  3576. }
  3577. .viewer h1 {
  3578. font-size: 12pt;
  3579. }
  3580. .viewer h2 {
  3581. font-size: 11pt;
  3582. }
  3583. .viewer h3 {
  3584. font-size: 10pt;
  3585. }
  3586. .viewer h4 {
  3587. font-size: 9pt;
  3588. }
  3589. .viewer h5 {
  3590. font-size: 8pt;
  3591. }
  3592. .viewer table {
  3593. border-collapse: collapse;
  3594. border: 2px solid #303030;
  3595. margin-left: 1.0em;
  3596. margin-right: 1.0em;
  3597. margin-top: 0.8em;
  3598. margin-bottom: 0.8em;
  3599. font-size: 100%;
  3600. }
  3601. .viewer th {
  3602. background-color: #999966;
  3603. border: 1px solid #606060;
  3604. color: #ffffff;
  3605. padding: 3px;
  3606. }
  3607. .viewer td, .viewer tr {
  3608. border: 1px solid #606060;
  3609. padding: 3px;
  3610. }
  3611. .viewer caption {
  3612. padding: 3px;
  3613. }
  3614. .viewer pre {
  3615. padding: 0.5em 0.5em 0.5em 0.5em;
  3616. margin-left: 0.5em;
  3617. font-size: 100%;
  3618. line-height: 1.4em;
  3619. color: #000000;
  3620. border: 1px solid #996633;
  3621. background-color: #eeeeaa;
  3622. overflow: auto;
  3623. }
  3624. .viewer code {
  3625. font-size: 100%;
  3626. line-height: 1.4em;
  3627. color: #663300;
  3628. }
  3629. .viewer hr {
  3630. border-top: dashed 1px #606060;
  3631. border-left: none;
  3632. border-right: none;
  3633. border-bottom: none;
  3634. height: 1px;
  3635. color: #666666;
  3636. }
  3637. .highlight, .marked {
  3638. color: #000000;
  3639. background-color: #ffe72f;
  3640. }
  3641. .editor {
  3642. font-size: 8pt;
  3643. color: #402C74;
  3644. font-weight: normal;
  3645. }
  3646. .editor input {
  3647. display: block;
  3648. border: 1px solid black;
  3649. width: 100%;
  3650. }
  3651. .editor textarea {
  3652. display: block;
  3653. font: inherit;
  3654. border: 1px solid black;
  3655. width: 100%;
  3656. }
  3657. .editorFooter {
  3658. padding: 0.25em 0em 0.25em 0em;
  3659. font-size: 8pt;
  3660. color: #aaaaaa;
  3661. }
  3662. .editorFooter A {
  3663. padding: 0.2em 0.4em 0.2em 0.4em;
  3664. color: #993300;
  3665. }
  3666. .editorFooter A:hover {
  3667. text-decoration: none;
  3668. color: #ccff66;
  3669. background-color: #993300;
  3670. }
  3671. .editorFooter A:active {
  3672. color: #ffffff;
  3673. background-color: #cc9900;
  3674. }
  3675. #sidebar {
  3676. position: absolute;
  3677. right: 0em;
  3678. width: 16em;
  3679. color: #000000;
  3680. font-size: 8pt;
  3681. }
  3682. #sidebarOptions {
  3683. padding-top: 0.5em;
  3684. background-color: #cc9900;
  3685. }
  3686. #sidebarOptions .button {
  3687. color: #993300;
  3688. padding: 0.3em 0.2em 0.3em 1em;
  3689. display: block;
  3690. }
  3691. #sidebarOptions .button:hover {
  3692. color: #ccff66;
  3693. background-color: #993300;
  3694. }
  3695. #sidebarOptions .button:active {
  3696. color: #993300;
  3697. background-color: #ccff66;
  3698. }
  3699. #sidebarOptions input {
  3700. margin: 0.4em 0em 0.3em 1em;
  3701. }
  3702. #sidebarOptions .sliderPanel {
  3703. padding: 0.5em 0.5em 0.5em 0.5em;
  3704. font-size: 7pt;
  3705. background-color: #eeeeaa;
  3706. }
  3707. #sidebarOptions .sliderPanel A {
  3708. color: #993300;
  3709. font-weight: bold;
  3710. }
  3711. #sidebarOptions .sliderPanel A:hover {
  3712. color: #ccff66;
  3713. background-color: #993300;
  3714. }
  3715. #sidebarOptions .sliderPanel A:active {
  3716. color: #993300;
  3717. background-color: #ccff66;
  3718. }
  3719. #sidebarOptions .sliderPanel input {
  3720. margin: 0em 0em 0.3em 0em;
  3721. }
  3722. .sidebarSubHeading {
  3723. font-size: 7pt;
  3724. color: #330000;
  3725. }
  3726. #sidebarTabs {
  3727. background-color: #cc9900;
  3728. }
  3729. #sidebarTabs .tabSelected {
  3730. color: #ccff66;
  3731. background-color: #996633;
  3732. position: relative;
  3733. top: -2px;
  3734. }
  3735. #sidebarTabs .tabUnselected {
  3736. color: #ccff66;
  3737. background-color: #993300;
  3738. }
  3739. #sidebarTabs .tabContents {
  3740. background-color: #996633;
  3741. }
  3742. #sidebarTabs .txtMoreTab .tabSelected {
  3743. background-color: #993300;
  3744. }
  3745. #sidebarTabs .txtMoreTab .tabUnselected {
  3746. background-color: #330000;
  3747. }
  3748. #sidebarTabs .txtMoreTab .tabContents {
  3749. background-color: #993300;
  3750. }
  3751. #sidebarTabs .tabContents .tiddlyLink {
  3752. color: #ccff66;
  3753. }
  3754. #sidebarTabs .tabContents .tiddlyLink:hover {
  3755. background-color: #ccff66;
  3756. color: #330000;
  3757. }
  3758. #sidebarTabs .tabContents .button {
  3759. color: #ccff66;
  3760. padding: 0em 0em 0em 0em;
  3761. display: inline;
  3762. }
  3763. #sidebarTabs .tabContents .button:hover {
  3764. color: #330000;
  3765. background-color: #ccff66;
  3766. }
  3767. #licensePanel {
  3768. padding: 0.5em 0em 0.5em 0em;
  3769. }
  3770. #licensePanel A {
  3771. display: block;
  3772. padding: 0.2em 0.2em 0.2em 0.2em;
  3773. color: #993300;
  3774. }
  3775. #licensePanel A:hover {
  3776. text-decoration: none;
  3777. color: #ccff66;
  3778. background-color: #993300;
  3779. }
  3780. #licensePanel A:active {
  3781. color: #993300;
  3782. background-color: #ccff66;
  3783. }
  3784. #storeArea, #copyright {
  3785. display: none;
  3786. }
  3787. .sparkline {
  3788. background-color: #eeeeaa;
  3789. border: none;
  3790. line-height: 100%;
  3791. }
  3792. .sparktick {
  3793. background-color: #993300;
  3794. outline: 0;
  3795. }
  3796. .errorNoSuchMacro {
  3797. color: #ffff00;
  3798. background-color: #ff0000;
  3799. }
  3800. .zoomer {
  3801. font-size: 10pt;
  3802. display: none;
  3803. color: #996633;
  3804. position: absolute;
  3805. padding: 1em 1em 1em 1em;
  3806. border: 1px solid #996633;
  3807. }
  3808. #saveTest {
  3809. display: none;
  3810. }
  3811. @media print {
  3812. #mainMenu, #sidebar, #messageArea {
  3813. display: none ! important;
  3814. }
  3815. #displayArea {
  3816. margin: 1em 1em 0em 1em;
  3817. }
  3818. }
  3819. </style>
  3820. <noscript>
  3821. <style type="text/css">
  3822. #contentWrapper {
  3823. display: none;
  3824. }
  3825. #storeArea {
  3826. display: block;
  3827. margin: 4em 17em 3em 17em;
  3828. }
  3829. #storeArea div {
  3830. padding: 0.5em; 0.5em; 0.5em; 0.5em;
  3831. margin: 1em 0em 0em 0em;
  3832. border-color: #f0f0f0 #606060 #404040 #d0d0d0;
  3833. border-style: solid;
  3834. border-width: 2px;
  3835. height: 7em;
  3836. overflow: auto;
  3837. }
  3838. #javascriptWarning {
  3839. width: 100%;
  3840. text-align: center;
  3841. font-weight: bold;
  3842. background-color: #dd1100;
  3843. color: #ffffff;
  3844. padding:1em 0em 1em 0em;
  3845. }
  3846. </style>
  3847. </noscript>
  3848. </head>
  3849. <body onload="main();" onunload="checkUnsavedChanges();">
  3850. <script>
  3851. if (detectPlugin("TiddlyWiki Saver"))
  3852. {
  3853. document.write('<embed style="display: none" name="tiddlyWikiSafariSaver" width="0" height="0" type="application/x-webkit-tiddlywiki-saver"></embed>');
  3854. saveUsingSafari = true;
  3855. }
  3856. </script>
  3857. <div id="copyright">
  3858. Welcome to TiddlyWiki by Jeremy Ruston, Copyright &copy; 2005 Osmosoft Limited
  3859. </div>
  3860. <noscript>
  3861. <div id="javascriptWarning">This page requires JavaScript to function properly</div>
  3862. </noscript>
  3863. <div id="saveTest"></div>
  3864. <div id="contentWrapper">
  3865. <div id="header">
  3866. <div id="titleLine">
  3867. <span id="siteTitle"></span>
  3868. <span id="siteSubtitle"></span>
  3869. </div>
  3870. </div>
  3871. <div id="sidebar">
  3872. <div id="sidebarOptions"></div>
  3873. <div id="sidebarTabs"></div>
  3874. </div>
  3875. <div id="mainMenu"></div>
  3876. <div id="displayArea">
  3877. <div id="messageArea"></div>
  3878. <div id="tiddlerDisplay"></div>
  3879. </div>
  3880. </div>
  3881. <div id="storeArea"><div tiddler="ManualConvertUnicodeToUTF8" modified="200509211330" modifier="YourName" tags="systemConfig">window.manualConvertUnicodeToUTF8 = function (s)\n{\n var re=/[^\su0000-\su007F]/g ;\n return( s.replace( re, function( $0 ) { return( &quot;&amp;#&quot; +\n$0.charCodeAt(0).toString() + &quot;;&quot; ) ; }));\n\n}</div>
  3882. <div tiddler="DefaultTiddlers" modified="200509211635" modifier="YourName" tags="systemTiddlers">[[Introduction]]</div>
  3883. <div tiddler="_CloseOthers" modified="200509211952" modifier="UlfWiger" tags="systemConfig">\nfunction onClickToolbarCloseOthers(e) {\nif (!e) var e = window.event;\noldVal = config.options.chkAnimate;\nconfig.options.chkAnimate = false;\ncloseAllTiddlers();\nif(this.parentNode.id) {\nvar title = this.parentNode.id.substr(7);\ndisplayTiddler(e.target,title,0);\n}\nconfig.options.chkAnimate = oldVal;\n}\n\nfunction addCloseOthersButton(ignored, args) {\nvar theToolbar = document.getElementById(&quot;toolbar&quot; + args[0]);\nif(theToolbar) {\nif(!args[1]) {\ncreateTiddlyButton(theToolbar, &quot;close others&quot;, &quot;close others&quot;, onClickToolbarCloseOthers);\ninsertSpacer(theToolbar);\n}\n}\n\nreturn args;\n}\n\nAspects.addAfter(this, &quot;createTiddlerToolbar&quot;, addCloseOthersButton);</div>
  3884. <div tiddler="_.FunctionDecorator" modified="200509211956" modifier="UlfWiger" tags="systemConfig">//Thanks to Roman Porotnikov\n//http:www.jroller.com/page/deep/20030701\n\n//NB this systemConfig needs to be evaluated before other ones \n//that use the Aspects so the name starts with &quot;.&quot;\n//since they're loaded alphabetically\n//should really put it into the main source code, but\n//wanted to keep everything upgrade-proof\n\nAspects = new Object();\n\nAspects.addBefore = function(obj, fname, before) {\nvar oldFunc = obj[fname];\nobj[fname] = function() {\nreturn oldFunc.apply(this, before(arguments, oldFunc, this));\n};\n};\n\nAspects.addAfter = function(obj, fname, after) {\nvar oldFunc = obj[fname];\nobj[fname] = function() {\nreturn after(oldFunc.apply(this, arguments), arguments, oldFunc, this);\n};\n};\n\nAspects.addAround = function(obj, fname, around) {\nvar oldFunc = obj[fname];\nobj[fname] = function() {\nreturn around(arguments, oldFunc, this);\n};\n};</div>
  3885. <div tiddler="_BackupDirectory" modified="200509212143" modifier="UlfWiger" tags="systemConfig">///////////////////////////////////////////////////////////////\n// Requires http://www.digitaldimsum.co.uk/#_.FunctionDecorator\n///////////////////////////////////////////////////////////////\nconfig.options.txtBackupDir = &quot;backup&quot;;\nconfig.shadowTiddlers.AdvancedOptions += &quot;\sn\sn Backup Subdirectory: &lt;&lt;option txtBackupDir&gt;&gt;\sn &quot;;\n\nfunction alterBackupDir(args) {\n var path = args[0];\n var re = new RegExp(&quot;\s.[0-9]+\s.html&quot;);\n if (re.test(path)) {\n var backSlash = true;\n var dirPathPos = path.lastIndexOf(&quot;\s\s&quot;);\n if (dirPathPos == -1) {\n dirPathPos = path.lastIndexOf(&quot;/&quot;);\n backSlash = false;\n }\n var backupPath = path.substr(0,dirPathPos) + (backSlash ? &quot;\s\s&quot; : &quot;/&quot;);\n backupPath += config.options.txtBackupDir + path.substr(dirPathPos, path.length - dirPathPos);\n \n args[0] = backupPath;\n }\n return args;\n}\n\nAspects.addBefore(this,&quot;saveFile&quot;,alterBackupDir);\n</div>
  3886. <div tiddler="TiddlyWiki" modified="200509220854" modifier="UlfWiger" tags="">See [[TiddlyWiki.com|http://www.tiddlywiki.com]] for descriptions, examples, and tutorials on TiddlyWiki.\n\nIf you want to create your own TiddlyWiki page, you can start by saving a copy of [[this file|http://avweb.etxb.ericsson.se/~etxuwig/empty_tiddlywiki.html]].\n</div>
  3887. <div tiddler="SiteTitle" modified="200601262130" modifier="UlfWiger" tags="systemTiddlers">RDBMS</div>
  3888. <div tiddler="SiteSubtitle" modified="200601262131" modifier="UlfWiger" tags="systemTiddlers">A relational database layer on top of Mnesia</div>
  3889. <div tiddler="SiteUrl" modified="200601262131" modifier="UlfWiger" tags="systemTiddlers"></div>
  3890. <div tiddler="What is rdbms?" modified="200602101117" modifier="UlfWiger" tags="">'Rdbms' is mainly a mnesia activity module for Mnesia that adds support for constraint checking.\n\nMnesia is a robust distributed DBMS, but it performs very little validation on the actual data. Basically, the only things that it does validate (apart from ensuring ACID properties, which is no small thing) is that the record tag and number of attributes are correct. There is no support for verifying the type of data, or indeed to check relations between different tables.\n\n'Rdbms' does this, in a way that is almost totally transparent to the Mnesia user. It supports definition and validation of type, bounds, access rights, and relational constraints.</div>
  3891. <div tiddler="Introduction" modified="200602101120" modifier="UlfWiger" tags="">This document describes the 'rdbms' add-on to Mnesia. I've decided to arrange it as a TiddlyWiki in order to attempt a [[User Guide]], [[Reference Manual]], [[design log]], and [[tutorials]] in one single document. We'll se how it goes.\n\nA good starting point is [[What is rdbms?]]\n</div>
  3892. <div tiddler="update_on_read" modified="200602141700" modifier="UlfWiger" tags="todo">in ''rdbms_index''. For example, read-only file system tables would be difficult to index. If one were to allow &quot;partially complete&quot; indexes, one could allow ''update_on_read'' indexes. Basically, one would [[rebuild_index]] once, and then refresh a portion of the index every time an object is read. This will not guarantee that the index is up to date, but should keep it mostly accurate, as long as updates are reasonably rare.\n</div>
  3893. <div tiddler="rebuild_indexes" modified="200602141703" modifier="UlfWiger" tags="todo">In [[module rdbms_index_load]], there is code for creating an index table on the fly. We should perhaps have a function, ''rebuild_indexes'', that does the same thing on demand for existing indexes.</div>
  3894. <div tiddler="module rdbms_index_load" modified="200602141707" modifier="UlfWiger" tags="reference rdbms_index_load.erl">This module is a callback for the mnesia_schema:do_restore/1 function, which is used by [[module rdbms_index]] in order to (re-)build an index at runtime. The reason for doing it this way, is that normal writes are not allowed within a schema transaction (which, for various reasons, is probably a good thing.)</div>
  3895. <div tiddler="Reference Manual" modified="200602211005" modifier="UlfWiger" tags="reference">[[Data model]]\n\nThe following API functions exist in rdbms:\n- [[module rdbms]]\n- [[module rdbms_index]]\n- [[module rdbms_props]]\n- [[module rdbms_mailmerge]]\n- [[module rdbms_wsearch]]</div>
  3896. <div tiddler="Data model" modified="200602221641" modifier="UlfWiger" tags="reference">''Rdbms'' metadata can be specified while creating tables, using the following functions in [[module rdbms]]:\n&lt;&lt;&lt;\nrdbms:create_table(Name, Options) % where Options = [{K,V}], rdbms metadata given as {rdbms, Properties}\nrdbms:do_create_table(Name, Options) \n&lt;&lt;&lt;\n\nand for existing tables using the following functions in [[module rdbms_props]]:\n\n&lt;&lt;&lt;\nset_property(Tab, Key, Value)\ndo_set_property(Tab, Key, Value)\nset_global_type(Class, Name, Value)\ndo_set_global_type(Class, Name, Value)\n&lt;&lt;&lt;\n\nThe following types of table metadata can be specified:\n- [[references]]\n- [[acl]]\n- [[verify]]\n- [[{typedef, Name}]]\n- [[{attr, Attr, Prop}]]\n</div>
  3897. <div tiddler="{attr, Attr, Prop}" modified="200602222041" modifier="UlfWiger" tags="reference type">This defines an attribute property for the given table (or a global attribute property).\n\nFor a table property, ''Attr'' must refer to an existing attribute in the table, or to a &quot;compound_attribute&quot; (see below).\n\nThe following properties can be specified for table attributes:\n\n- ''type'' - see [[attribute types]]\n- ''required'' - ''true | false''\n- ''key_type'' - ''primary | secondary | none''\n- ''default'' - ''{'value', term()} | {'auto', {M, F}}'')</div>
  3898. <div tiddler="Ongoing work" modified="200602222054" modifier="UlfWiger" tags="todo">- [[rebuild_indexes]], [[update_on_read]]\n- [[optimize type checks]]\n- [[rdbms_rofs]]</div>
  3899. <div tiddler="optimize type checks" modified="200602222059" modifier="UlfWiger" tags="">There is some code in [[module rdbms_props]] that normalizes type definitions. Part of the purpose is to expand all typedefs, but another reason is to try to simplify the type expressions. Obviously, e.g. {list, {'or', [..., any]}} can be simplified to {list, any}, which means that the list elements do not 't have to be inspected. This code lacks sophistication (to say the least), and could be made much better.\n\nBTW, should {list, any} be read as &quot;a non-empty list&quot;, or &quot;any list, including nil&quot;? I think I prefer the former, since we have no other good expression for non-empty list.</div>
  3900. <div tiddler="complex type" modified="200602261838" modifier="YourName" tags="reference type">&lt;&lt;&lt;\n''type'' = builtinType | complexType | altType\n''logicalType'' = {logicalOp, [type]} | {'not', type} | 'true' | 'false'\n''logicalOp'' = 'and' | 'or' | 'andalso' | 'orelse'\n''comType'' = {compOp, value}\n''compOp'' = '==' | '=/=' | '&gt;' | '&lt;' | '=&lt;' | '&gt;='\n''builtinType'' = 'atom' | 'string' | 'binary' | 'number' | 'integer' | 'float' | 'oid' | 'any' |\n 'list' | 'nil' | 'tuple' | 'function' | 'pid' | 'port' | 'reference'\n''complexType'' = tupleType | listType | enumType\n''tupleType'' = {'tuple', [type]}\n''listType'' = {'list', type}\n''enumType'' = {'enum', [value]}\n&lt;&lt;&lt;\n\nSpecifically, ''{'and', []}'' and ''{'or',[]}'' are the same as ''no_type'', and \n- '''nil''' means the empty list\n- ''{'list', 'false'}'' is the same as '''nil'''\n- ''{'list', 'any'} and ''{'list', 'undefined'}'' both mean a non-empty list</div>
  3901. <div tiddler="attribute types" modified="200602261840" modifier="YourName" tags="reference type">The following types are supported by ''rdbms'':\n\n- ''undefined'' - specifically matches the value 'undefined'\n- ''no_type'' - is an internal representation for &quot;no type has been defined&quot;. It cannot be set.\n- ''any'' - means specifically that any value is allowed.\n- ''oid'' - built-in type for use as globally unique identifier. See [[oid]]\n- Simple builtin types (''atom, string, binary, number, integer, float, oid, any, pid, port, reference, list, nil, tuple, function, boolean'')\n- The distinct types 'true' and 'false'\n- [[global type]] (''{type, typeRef}'', see TypeDefs)\n- [[complex type]] (tuples or lists)\n</div>
  3902. <div tiddler="User Guide" modified="200602261857" modifier="YourName" tags="guide">This user guide covers the following topics\n- [[activating rdbms]]\n- [[specifying types]]\n- [[referential integrity]]\n- [[parameterized indexes]]\n- [[fragmented tables]]</div>
  3903. <div tiddler="MainMenu" modified="200603011143" modifier="UlfWiger" tags="systemTiddlers">[[Introduction]]\n[[What is rdbms?]]\n[[Reference Manual]]\n[[User Guide]]\n[[Ongoing work]]\nTiddlyWiki\n[[TiddlyWiki Tutorial|http://www.blogjones.com/TiddlyWikiTutorial.html]]\n\n\n&lt;&lt;newTiddler&gt;&gt;\n&lt;&lt;newJournal &quot;DD MMM YYYY&quot;&gt;&gt;</div>
  3904. <div tiddler="module rdbms_index" modified="200603011200" modifier="UlfWiger" tags="">The 'rdbms' library supports parameterized indexes. These indexes are regular mnesia tables, and index values are determined by a user-supplied callback function, which operates either on a single attribute, or on the whole object. This is the main difference between 'rdbms' indexes and mnesia indexes, for which the index value is always just the value of the identified attribute.\n\nThe 'rdbms' indexes can also be ''fragmented'', since they are regular mnesia tables. This should lead to better scalability in cluster databases.\n\nThe following types of index are supported:\n* ''bag'' - unordered indexes of the sort mnesia supports today (except for the differences mentioned above.)\n* ''ordered'' - ordered_set indexes where the key is {{{{~IndexValue, ~ObjectKey}}}}\n* ''set'' - like ''bag'', but with the restriction that the index value must be present in only one object.\n* ''weighted'' - also ordered_sets. The index function is expected to return {{{[{~IndexValue, Weight}]}}}, where {{{Weight}}} can be any term. The key in the index table becomes {{{{~IndexValue, Weight, ~ObjectKey}}}}</div>
  3905. <div tiddler="module rdbms" modified="200603011234" modifier="UlfWiger" tags="rdbms.erl reference">The rdbms module implements the actual activity module for mnesia. To enable rdbms, add the environment variable {{{{access_module, rdbms}}}} to Mnesia. It is also possible to select (or override) rdbms for each transaction through the function {{{mnesia:activity(Type, Fun, Args, Module)}}}.\n\nRdbms uses the naming convention that {{{do_xxxxx(...)}}} signifies a function that must be run within a schema transaction. Normally, there is one function that starts a schema transaction implicitly (e.g. {{{add_properties/1}}}, and a corresponding function that assumes an existing scheme transaction (e.g. {{{do_add_properties/1}}}). The idea is to be able to perform complex data initialization (possibly creating the entire database) within one transaction. Note, however, that Mnesia does not allow insertion of normal data within a schema transaction.\n\nFunctions:\n* [[rdbms:activity/1]]\n* [[rdbms:drop_references/1]], [[rdbms:do_drop_references/1]]\n* [[rdbms:load_schema/1]], [[rdbms:load_schema/2]]\n* [[rdbms:create_table/2]], [[rdbms:do_create_table/2]]\n* [[rdbms:delete_table/1]], [[rdbms:do_delete_table/1]]\n* [[rdbms:make_simple_form/1]]\n* [[rdbms:null_value/0]]</div>
  3906. <div tiddler="rdbms:null_value/0" modified="200603011239" modifier="UlfWiger" tags="rdbms.erl reference">{{{rdbms:null_value() -&gt; undefined}}}\n\nErlang doesn't have a proper {{{null}}} value, but the convention is to use the atom {{{undefined}}}.</div>
  3907. <div tiddler="rdbms:activity/1" modified="200603011240" modifier="UlfWiger" tags="rdbms.erl reference">{{{rdbms:activity(Fun)}}}\n\nEquvalent to {{{mnesia:activity(transaction, Fun, [], rdbms)}}}.\n\nStarts a mnesia transaction, using the 'rdbms' activity module. If mnesia has been started with the environment varliable {{{{access_module, rdbms}}}}, the function {{{mnesia:activity(transaction, Fun)}}} will also have the same effect. If another activity module is the default, {{{rdbms:activity(Fun)}}} will override the setting, and use 'rdbms' instead.</div>
  3908. <div tiddler="rdbms:create_table/2" modified="200603011241" modifier="UlfWiger" tags="">{{{rdbms:create_table(Tab, Options) -&gt; {atomic, ok} | {aborted, Reason}}}}\n\nWorks like {{{mnesia:create_table(Tab, Options)}}}, except that it accepts the additional option {{{{rdbms, ~RdbmsOptions}}}}.\n</div>
  3909. <div tiddler="module rdbms_props" modified="200603011258" modifier="UlfWiger" tags="rdbms_props.erl reference">The rdbms_props module contains functions for manipulating and accessing meta-data.\n\n* Table Properties\n** [[references]]\n** [[indexes]]\n** [[acl]]\n** [[read_filter]]\n** [[write_filter]]\n*Attribute Properties\n** [[{attr,Attr,type}]]\n** [[{attr,Attr,default}]]\n</div>
  3910. <div tiddler="activating rdbms" modified="200603011512" modifier="UlfWiger" tags="guide">''rdbms'' is activated whenever the {{{mnesia:activity()}}} function is called with ''rdbms'' as callback module. For example:\n\n&lt;&lt;&lt;\n{{{mnesia:activity(transaction, F, [], rdbms)}}}\n&lt;&lt;&lt;\n\nwhere F is the function to execute within the transaction.\n\n&lt;&lt;&lt;\nNote that the old {{{mnesia:transaction(F)}}} function _never_ uses any callback module other than the default (mnesia).\n&lt;&lt;&lt;\n\nAnother way to activate ''rdbms'' is to specify the mnesia environment variable {{{{access_module,rdbms}}}}. This can be done in three different ways.\n\n- from the command line: {{{erl ... -mnesia access_module rdbms}}}\n- from the sys.config file: {{{{mnesia, [{access_module, rdbms}]}}}}\n- by starting mnesia with {{{mnesia:start([{access_module,rdbms}])}}}\n\nWhen ''rdbms'' has been activated as the default access module, it can still be overridden using the mnesia:activity/4 function.</div>
  3911. </div>
  3912. </body>
  3913. </html>