Annotation of embedaddon/axTLS/www/index.html, revision 1.1.1.1

1.1       misho       1: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
                      2: <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
                      3: <head>
                      4: <script type="text/javascript">
                      5: //<![CDATA[
                      6: var version = {major: 2, minor: 1, revision: 3, date: new Date("Nov 3, 2006"), extensions: {}};
                      7: //]]>
                      8: </script>
                      9: <!--
                     10: TiddlyWiki 2.1.3 by Jeremy Ruston, (jeremy [at] osmosoft [dot] com)
                     11: 
                     12: Copyright (c) Osmosoft Limited 2004-2006
                     13: 
                     14: Redistribution and use in source and binary forms, with or without modification,
                     15: are permitted provided that the following conditions are met:
                     16: 
                     17: Redistributions of source code must retain the above copyright notice, this
                     18: list of conditions and the following disclaimer.
                     19: 
                     20: Redistributions in binary form must reproduce the above copyright notice, this
                     21: list of conditions and the following disclaimer in the documentation and/or other
                     22: materials provided with the distribution.
                     23: 
                     24: Neither the name of the Osmosoft Limited nor the names of its contributors may be
                     25: used to endorse or promote products derived from this software without specific
                     26: prior written permission.
                     27: 
                     28: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
                     29: EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
                     30: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
                     31: SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
                     32: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
                     33: TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
                     34: BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
                     35: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
                     36: ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
                     37: DAMAGE.
                     38: -->
                     39: <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
                     40: <!--PRE-HEAD-START-->
                     41: <!--{{{-->
                     42: <link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>
                     43: <!--}}}-->
                     44: <!--PRE-HEAD-END-->
                     45: <title> axTLS Embedded SSL - changes, notes and errata </title>
                     46: <script type="text/javascript">
                     47: //<![CDATA[
                     48: // ---------------------------------------------------------------------------------
                     49: // Configuration repository
                     50: // ---------------------------------------------------------------------------------
                     51: 
                     52: // Miscellaneous options
                     53: var config = {
                     54:        numRssItems: 20, // Number of items in the RSS feed
                     55:        animFast: 0.12, // Speed for animations (lower == slower)
                     56:        animSlow: 0.01, // Speed for EasterEgg animations
                     57:        cascadeFast: 20, // Speed for cascade animations (higher == slower)
                     58:        cascadeSlow: 60, // Speed for EasterEgg cascade animations
                     59:        cascadeDepth: 5, // Depth of cascade animation
                     60:        displayStartupTime: false // Whether to display startup time
                     61:        };
                     62: 
                     63: // Messages
                     64: config.messages = {
                     65:        messageClose: {},
                     66:        dates: {}
                     67: };
                     68: 
                     69: // Options that can be set in the options panel and/or cookies
                     70: config.options = {
                     71:        chkRegExpSearch: false,
                     72:        chkCaseSensitiveSearch: false,
                     73:        chkAnimate: true,
                     74:        chkSaveBackups: true,
                     75:        chkAutoSave: false,
                     76:        chkGenerateAnRssFeed: false,
                     77:        chkSaveEmptyTemplate: false,
                     78:        chkOpenInNewWindow: true,
                     79:        chkToggleLinks: false,
                     80:        chkHttpReadOnly: true,
                     81:        chkForceMinorUpdate: false,
                     82:        chkConfirmDelete: true,
                     83:        chkInsertTabs: false,
                     84:        txtBackupFolder: "",
                     85:        txtMainTab: "tabTimeline",
                     86:        txtMoreTab: "moreTabAll",
                     87:        txtMaxEditRows: "30"
                     88:        };
                     89:        
                     90: // List of notification functions to be called when certain tiddlers are changed or deleted
                     91: config.notifyTiddlers = [
                     92:        {name: "StyleSheetLayout", notify: refreshStyles},
                     93:        {name: "StyleSheetColors", notify: refreshStyles},
                     94:        {name: "StyleSheet", notify: refreshStyles},
                     95:        {name: "StyleSheetPrint", notify: refreshStyles},
                     96:        {name: "PageTemplate", notify: refreshPageTemplate},
                     97:        {name: "SiteTitle", notify: refreshPageTitle},
                     98:        {name: "SiteSubtitle", notify: refreshPageTitle},
                     99:        {name: "ColorPalette", notify: refreshColorPalette},
                    100:        {name: null, notify: refreshDisplay}
                    101:        ];
                    102: 
                    103: // Default tiddler templates
                    104: var DEFAULT_VIEW_TEMPLATE = 1;
                    105: var DEFAULT_EDIT_TEMPLATE = 2;
                    106: config.tiddlerTemplates = {
                    107:        1: "ViewTemplate",
                    108:        2: "EditTemplate"
                    109:        };
                    110: 
                    111: // More messages (rather a legacy layout that shouldn't really be like this)
                    112: config.views = {
                    113:        wikified: {
                    114:                tag: {}
                    115:                },
                    116:        editor: {
                    117:                tagChooser: {}
                    118:                }
                    119:        };
                    120: 
                    121: // Macros; each has a 'handler' member that is inserted later
                    122: config.macros = {
                    123:        today: {},
                    124:        version: {},
                    125:        search: {sizeTextbox: 15},
                    126:        tiddler: {},
                    127:        tag: {},
                    128:        tags: {},
                    129:        tagging: {},
                    130:        timeline: {},
                    131:        allTags: {},
                    132:        list: {
                    133:                all: {},
                    134:                missing: {},
                    135:                orphans: {},
                    136:                shadowed: {}
                    137:                },
                    138:        closeAll: {},
                    139:        permaview: {},
                    140:        saveChanges: {},
                    141:        slider: {},
                    142:        option: {},
                    143:        newTiddler: {},
                    144:        newJournal: {},
                    145:        sparkline: {},
                    146:        tabs: {},
                    147:        gradient: {},
                    148:        message: {},
                    149:        view: {},
                    150:        edit: {},
                    151:        tagChooser: {},
                    152:        toolbar: {},
                    153:        br: {},
                    154:        plugins: {},
                    155:        refreshDisplay: {},
                    156:        importTiddlers: {}
                    157:        };
                    158: 
                    159: // Commands supported by the toolbar macro
                    160: config.commands = {
                    161:        closeTiddler: {},
                    162:        closeOthers: {},
                    163:        editTiddler: {},
                    164:        saveTiddler: {hideReadOnly: true},
                    165:        cancelTiddler: {},
                    166:        deleteTiddler: {hideReadOnly: true},
                    167:        permalink: {},
                    168:        references: {},
                    169:        jump: {}
                    170:        };
                    171: 
                    172: // Browser detection... In a very few places, there's nothing else for it but to
                    173: // know what browser we're using.
                    174: config.userAgent = navigator.userAgent.toLowerCase();
                    175: config.browser = {
                    176:        isIE: config.userAgent.indexOf("msie") != -1 && config.userAgent.indexOf("opera") == -1,
                    177:        ieVersion: /MSIE (\d.\d)/i.exec(config.userAgent), // config.browser.ieVersion[1], if it exists, will be the IE version string, eg "6.0"
                    178:        isSafari: config.userAgent.indexOf("applewebkit") != -1,
                    179:        isBadSafari: !((new RegExp("[\u0150\u0170]","g")).test("\u0150")),
                    180:        firefoxDate: /Gecko\/(\d{8})/i.exec(config.userAgent), // config.browser.firefoxDate[1], if it exists, will be Firefox release date as "YYYYMMDD"
                    181:        isOpera: config.userAgent.indexOf("opera") != -1,
                    182:        isLinux: config.userAgent.indexOf("linux") != -1,
                    183:        isUnix: config.userAgent.indexOf("x11") != -1,
                    184:        isMac: config.userAgent.indexOf("mac") != -1,
                    185:        isWindows: config.userAgent.indexOf("win") != -1
                    186:        };
                    187: 
                    188: // Basic regular expressions
                    189: config.textPrimitives = {
                    190:        upperLetter: "[A-Z\u00c0-\u00de\u0150\u0170]",
                    191:        lowerLetter: "[a-z0-9_\\-\u00df-\u00ff\u0151\u0171]",
                    192:        anyLetter:   "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]",
                    193:        anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]"
                    194:        };
                    195: if(config.browser.isBadSafari)
                    196:        config.textPrimitives = {
                    197:                upperLetter: "[A-Z\u00c0-\u00de]",
                    198:                lowerLetter: "[a-z0-9_\\-\u00df-\u00ff]",
                    199:                anyLetter:   "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff]",
                    200:                anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff]"
                    201:                }
                    202: config.textPrimitives.sliceSeparator = "::";
                    203: config.textPrimitives.urlPattern = "[a-z]{3,8}:[^\\s:'\"][^\\s'\"]*(?:/|\\b)";
                    204: config.textPrimitives.unWikiLink = "~";
                    205: config.textPrimitives.wikiLink = "(?:(?:" + config.textPrimitives.upperLetter + "+" +
                    206:                                                                                                config.textPrimitives.lowerLetter + "+" +
                    207:                                                                                                config.textPrimitives.upperLetter +
                    208:                                                                                                config.textPrimitives.anyLetter + "*)|(?:" +
                    209:                                                                                                config.textPrimitives.upperLetter + "{2,}" +
                    210:                                                                                                config.textPrimitives.lowerLetter + "+))";
                    211: 
                    212: config.textPrimitives.cssLookahead = "(?:(" + config.textPrimitives.anyLetter + "+)\\(([^\\)\\|\\n]+)(?:\\):))|(?:(" + config.textPrimitives.anyLetter + "+):([^;\\|\\n]+);)";
                    213: config.textPrimitives.cssLookaheadRegExp = new RegExp(config.textPrimitives.cssLookahead,"mg");
                    214: 
                    215: config.textPrimitives.brackettedLink = "\\[\\[([^\\]]+)\\]\\]";
                    216: config.textPrimitives.titledBrackettedLink = "\\[\\[([^\\[\\]\\|]+)\\|([^\\[\\]\\|]+)\\]\\]";
                    217: config.textPrimitives.tiddlerForcedLinkRegExp = new RegExp("(?:" + config.textPrimitives.titledBrackettedLink + ")|(?:" +
                    218:        config.textPrimitives.brackettedLink + ")|(?:" + 
                    219:        config.textPrimitives.urlPattern + ")","mg");
                    220: config.textPrimitives.tiddlerAnyLinkRegExp = new RegExp("("+ config.textPrimitives.wikiLink + ")|(?:" +
                    221:        config.textPrimitives.titledBrackettedLink + ")|(?:" +
                    222:        config.textPrimitives.brackettedLink + ")|(?:" +
                    223:        config.textPrimitives.urlPattern + ")","mg");
                    224: 
                    225: // ---------------------------------------------------------------------------------
                    226: // Shadow tiddlers
                    227: // ---------------------------------------------------------------------------------
                    228: 
                    229: config.shadowTiddlers = {
                    230:        ColorPalette: "Background: #fff\n" + 
                    231:                                  "Foreground: #000\n" +
                    232:                                  "PrimaryPale: #8cf\n" +
                    233:                                  "PrimaryLight: #18f\n" +
                    234:                                  "PrimaryMid: #04b\n" +
                    235:                                  "PrimaryDark: #014\n" +
                    236:                                  "SecondaryPale: #ffc\n" +
                    237:                                  "SecondaryLight: #fe8\n" +
                    238:                                  "SecondaryMid: #db4\n" +
                    239:                                  "SecondaryDark: #841\n" +
                    240:                                  "TertiaryPale: #eee\n" +
                    241:                                  "TertiaryLight: #ccc\n" +
                    242:                                  "TertiaryMid: #999\n" +
                    243:                                  "TertiaryDark: #666\n" +
                    244:                                  "Error: #f88\n",
                    245:        StyleSheet: "",
                    246:        StyleSheetColors: "/*{{{*/\nbody {\n    background: [[ColorPalette::Background]];\n     color: [[ColorPalette::Foreground]];\n}\n\na{\n color: [[ColorPalette::PrimaryMid]];\n}\n\na:hover{\n   background: [[ColorPalette::PrimaryMid]];\n     color: [[ColorPalette::Background]];\n}\n\na img{\n     border: 0;\n}\n\nh1,h2,h3,h4,h5 {\n     color: [[ColorPalette::SecondaryDark]];\n       background: [[ColorPalette::PrimaryPale]];\n}\n\n.button {\n    color: [[ColorPalette::PrimaryDark]];\n border: 1px solid [[ColorPalette::Background]];\n}\n\n.button:hover {\n color: [[ColorPalette::PrimaryDark]];\n background: [[ColorPalette::SecondaryLight]];\n border-color: [[ColorPalette::SecondaryMid]];\n}\n\n.button:active {\n  color: [[ColorPalette::Background]];\n  background: [[ColorPalette::SecondaryMid]];\n   border: 1px solid [[ColorPalette::SecondaryDark]];\n}\n\n.header {\n    background: [[ColorPalette::PrimaryMid]];\n}\n\n.headerShadow {\n       color: [[ColorPalette::Foreground]];\n}\n\n.headerShadow a {\n  font-weight: normal;\n  color: [[ColorPalette::Foreground]];\n}\n\n.headerForeground {\n        color: [[ColorPalette::Background]];\n}\n\n.headerForeground a {\n      font-weight: normal;\n  color: [[ColorPalette::PrimaryPale]];\n}\n\n.tabSelected{\n     color: [[ColorPalette::PrimaryDark]];\n background: [[ColorPalette::TertiaryPale]];\n   border-left: 1px solid [[ColorPalette::TertiaryLight]];\n       border-top: 1px solid [[ColorPalette::TertiaryLight]];\n        border-right: 1px solid [[ColorPalette::TertiaryLight]];\n}\n\n.tabUnselected {\n       color: [[ColorPalette::Background]];\n  background: [[ColorPalette::TertiaryMid]];\n}\n\n.tabContents {\n       color: [[ColorPalette::PrimaryDark]];\n background: [[ColorPalette::TertiaryPale]];\n   border: 1px solid [[ColorPalette::TertiaryLight]];\n}\n\n.tabContents .button {\n        border: 0;}\n\n#sidebar {\n}\n\n#sidebarOptions input {\n      border: 1px solid [[ColorPalette::PrimaryMid]];\n}\n\n#sidebarOptions .sliderPanel {\n  background: [[ColorPalette::PrimaryPale]];\n}\n\n#sidebarOptions .sliderPanel a {\n     border: none;\n color: [[ColorPalette::PrimaryMid]];\n}\n\n#sidebarOptions .sliderPanel a:hover {\n     color: [[ColorPalette::Background]];\n  background: [[ColorPalette::PrimaryMid]];\n}\n\n#sidebarOptions .sliderPanel a:active {\n       color: [[ColorPalette::PrimaryMid]];\n  background: [[ColorPalette::Background]];\n}\n\n.wizard {\n     background: [[ColorPalette::SecondaryLight]];\n border-top: 1px solid [[ColorPalette::SecondaryMid]];\n border-left: 1px solid [[ColorPalette::SecondaryMid]];\n}\n\n.wizard h1 {\n     color: [[ColorPalette::SecondaryDark]];\n}\n\n.wizard h2 {\n    color: [[ColorPalette::Foreground]];\n}\n\n.wizardStep {\n      background: [[ColorPalette::Background]];\n     border-top: 1px solid [[ColorPalette::SecondaryMid]];\n border-bottom: 1px solid [[ColorPalette::SecondaryMid]];\n      border-left: 1px solid [[ColorPalette::SecondaryMid]];\n}\n\n.wizard .button {\n        color: [[ColorPalette::Background]];\n  background: [[ColorPalette::PrimaryMid]];\n     border-top: 1px solid [[ColorPalette::PrimaryLight]];\n border-right: 1px solid [[ColorPalette::PrimaryDark]];\n        border-bottom: 1px solid [[ColorPalette::PrimaryDark]];\n       border-left: 1px solid [[ColorPalette::PrimaryLight]];\n}\n\n.wizard .button:hover {\n  color: [[ColorPalette::PrimaryLight]];\n        background: [[ColorPalette::PrimaryDark]];\n    border-color: [[ColorPalette::PrimaryLight]];\n}\n\n.wizard .button:active {\n  color: [[ColorPalette::Background]];\n  background: [[ColorPalette::PrimaryMid]];\n     border-top: 1px solid [[ColorPalette::PrimaryLight]];\n border-right: 1px solid [[ColorPalette::PrimaryDark]];\n        border-bottom: 1px solid [[ColorPalette::PrimaryDark]];\n       border-left: 1px solid [[ColorPalette::PrimaryLight]];\n}\n\n#messageArea {\n   border: 1px solid [[ColorPalette::SecondaryDark]];\n    background: [[ColorPalette::SecondaryMid]];\n   color: [[ColorPalette::PrimaryDark]];\n}\n\n#messageArea .button {\n    padding: 0.2em 0.2em 0.2em 0.2em;\n     color: [[ColorPalette::PrimaryDark]];\n background: [[ColorPalette::Background]];\n}\n\n.popup {\n      background: [[ColorPalette::PrimaryLight]];\n   border: 1px solid [[ColorPalette::PrimaryMid]];\n}\n\n.popup hr {\n     color: [[ColorPalette::PrimaryDark]];\n background: [[ColorPalette::PrimaryDark]];\n    border-bottom: 1px;\n}\n\n.listBreak div{\n     border-bottom: 1px solid [[ColorPalette::PrimaryDark]];\n}\n\n.popup li.disabled {\n    color: [[ColorPalette::PrimaryMid]];\n}\n\n.popup li a, .popup li a:visited {\n color: [[ColorPalette::TertiaryPale]];\n        border: none;\n}\n\n.popup li a:hover {\n       background: [[ColorPalette::PrimaryDark]];\n    color: [[ColorPalette::Background]];\n  border: none;\n}\n\n.tiddler .defaultCommand {\n font-weight: bold;\n}\n\n.shadow .title {\n    color: [[ColorPalette::TertiaryDark]];\n}\n\n.title {\n color: [[ColorPalette::SecondaryDark]];\n}\n\n.subtitle {\n     color: [[ColorPalette::TertiaryDark]];\n}\n\n.toolbar {\n       color: [[ColorPalette::PrimaryMid]];\n}\n\n.tagging, .tagged {\n        border: 1px solid [[ColorPalette::TertiaryPale]];\n     background-color: [[ColorPalette::TertiaryPale]];\n}\n\n.selected .tagging, .selected .tagged {\n       background-color: [[ColorPalette::TertiaryLight]];\n    border: 1px solid [[ColorPalette::TertiaryMid]];\n}\n\n.tagging .listTitle, .tagged .listTitle {\n      color: [[ColorPalette::PrimaryDark]];\n}\n\n.tagging .button, .tagged .button {\n               border: none;\n}\n\n.footer {\n color: [[ColorPalette::TertiaryLight]];\n}\n\n.selected .footer {\n     color: [[ColorPalette::TertiaryMid]];\n}\n\n.sparkline {\n      background: [[ColorPalette::PrimaryPale]];\n    border: 0;\n}\n\n.sparktick {\n background: [[ColorPalette::PrimaryDark]];\n}\n\n.error, .errorButton {\n       color: [[ColorPalette::Foreground]];\n  background: [[ColorPalette::Error]];\n}\n\n.warning {\n color: [[ColorPalette::Foreground]];\n  background: [[ColorPalette::SecondaryPale]];\n}\n\n.cascade {\n background: [[ColorPalette::TertiaryPale]];\n   color: [[ColorPalette::TertiaryMid]];\n border: 1px solid [[ColorPalette::TertiaryMid]];\n}\n\n.imageLink, #displayArea .imageLink {\n  background: transparent;\n}\n\n.viewer .listTitle {list-style-type: none; margin-left: -2em;}\n\n.viewer .button {\n    border: 1px solid [[ColorPalette::SecondaryMid]];\n}\n\n.viewer blockquote {\n  border-left: 3px solid [[ColorPalette::TertiaryDark]];\n}\n\n.viewer table {\n  border: 2px solid [[ColorPalette::TertiaryDark]];\n}\n\n.viewer th, thead td {\n        background: [[ColorPalette::SecondaryMid]];\n   border: 1px solid [[ColorPalette::TertiaryDark]];\n     color: [[ColorPalette::Background]];\n}\n\n.viewer td, .viewer tr {\n   border: 1px solid [[ColorPalette::TertiaryDark]];\n}\n\n.viewer pre {\n border: 1px solid [[ColorPalette::SecondaryLight]];\n   background: [[ColorPalette::SecondaryPale]];\n}\n\n.viewer code {\n     color: [[ColorPalette::SecondaryDark]];\n}\n\n.viewer hr {\n    border: 0;\n    border-top: dashed 1px [[ColorPalette::TertiaryDark]];\n        color: [[ColorPalette::TertiaryDark]];\n}\n\n.highlight, .marked {\n    background: [[ColorPalette::SecondaryLight]];\n}\n\n.editor input {\n   border: 1px solid [[ColorPalette::PrimaryMid]];\n}\n\n.editor textarea {\n      border: 1px solid [[ColorPalette::PrimaryMid]];\n       width: 100%;\n}\n\n.editorFooter {\n    color: [[ColorPalette::TertiaryMid]];\n}\n\n/*}}}*/",
                    247:        StyleSheetLayout: "/*{{{*/\n* html .tiddler {\n    height: 1%;\n}\n\nbody {\n   font-size: .75em;\n     font-family: arial,helvetica;\n margin: 0;\n    padding: 0;\n}\n\nh1,h2,h3,h4,h5 {\n    font-weight: bold;\n    text-decoration: none;\n        padding-left: 0.4em;\n}\n\nh1 {font-size: 1.35em;}\nh2 {font-size: 1.25em;}\nh3 {font-size: 1.1em;}\nh4 {font-size: 1em;}\nh5 {font-size: .9em;}\n\nhr {\n      height: 1px;\n}\n\na{\n text-decoration: none;\n}\n\ndt {font-weight: bold;}\n\nol { list-style-type: decimal }\nol ol { list-style-type: lower-alpha }\nol ol ol { list-style-type: lower-roman }\nol ol ol ol { list-style-type: decimal }\nol ol ol ol ol { list-style-type: lower-alpha }\nol ol ol ol ol ol { list-style-type: lower-roman }\nol ol ol ol ol ol ol { list-style-type: decimal }\n\n.txtOptionInput {\n     width: 11em;\n}\n\n#contentWrapper .chkOptionInput {\n  border: 0;\n}\n\n.externalLink {\n      text-decoration: underline;\n}\n\n.indent {margin-left:3em;}\n.outdent {margin-left:3em; text-indent:-3em;}\ncode.escaped {white-space:nowrap;}\n\n.tiddlyLinkExisting {\n      font-weight: bold;\n}\n\n.tiddlyLinkNonExisting {\n     font-style: italic;\n}\n\n/* the 'a' is required for IE, otherwise it renders the whole tiddler a bold */\na.tiddlyLinkNonExisting.shadow {\n   font-weight: bold;\n}\n\n#mainMenu .tiddlyLinkExisting, \n#mainMenu .tiddlyLinkNonExisting,\n#sidebarTabs .tiddlyLinkNonExisting{\n font-weight: normal;\n font-style: normal;\n}\n\n#sidebarTabs .tiddlyLinkExisting {\n font-weight: bold;\n font-style: normal;\n}\n\n.header {\n            position: relative;\n}\n\n.header a:hover {\n   background: transparent;\n}\n\n.headerShadow {\n        position: relative;\n   padding: 4.5em 0em 1em 1em;\n   left: -1px;\n   top: -1px;\n}\n\n.headerForeground {\n  position: absolute;\n   padding: 4.5em 0em 1em 1em;\n   left: 0px;\n    top: 0px;\n}\n\n.siteTitle {\n  font-size: 3em;\n}\n\n.siteSubtitle {\n font-size: 1.2em;\n}\n\n#mainMenu {\n   position: absolute;\n   left: 0;\n      width: 10em;\n  text-align: right;\n    line-height: 1.6em;\n   padding: 1.5em 0.5em 0.5em 0.5em;\n     font-size: 1.1em;\n}\n\n#sidebar {\n    position: absolute;\n   right: 3px;\n   width: 16em;\n  font-size: .9em;\n}\n\n#sidebarOptions {\n      padding-top: 0.3em;\n}\n\n#sidebarOptions a {\n margin: 0em 0.2em;\n    padding: 0.2em 0.3em;\n display: block;\n}\n\n#sidebarOptions input {\n margin: 0.4em 0.5em;\n}\n\n#sidebarOptions .sliderPanel {\n     margin-left: 1em;\n     padding: 0.5em;\n       font-size: .85em;\n}\n\n#sidebarOptions .sliderPanel a {\n      font-weight: bold;\n    display: inline;\n      padding: 0;\n}\n\n#sidebarOptions .sliderPanel input {\n        margin: 0 0 .3em 0;\n}\n\n#sidebarTabs .tabContents {\n width: 15em;\n  overflow: hidden;\n}\n\n.wizard {\n     padding: 0.1em 0em 0em 2em;\n}\n\n.wizard h1 {\n        font-size: 2em;\n       font-weight: bold;\n    background: none;\n     padding: 0em 0em 0em 0em;\n     margin: 0.4em 0em 0.2em 0em;\n}\n\n.wizard h2 {\n       font-size: 1.2em;\n     font-weight: bold;\n    background: none;\n     padding: 0em 0em 0em 0em;\n     margin: 0.2em 0em 0.2em 0em;\n}\n\n.wizardStep {\n      padding: 1em 1em 1em 1em;\n}\n\n.wizard .button {\n     margin: 0.5em 0em 0em 0em;\n    font-size: 1.2em;\n}\n\n#messageArea {\nposition:absolute; top:0; right:0; margin: 0.5em; padding: 0.5em;\n}\n\n*[id='messageArea'] {\nposition:fixed !important; z-index:99;}\n\n.messageToolbar {\ndisplay: block;\ntext-align: right;\n}\n\n#messageArea a{\n        text-decoration: underline;\n}\n\n.popup {\n    font-size: .9em;\n      padding: 0.2em;\n       list-style: none;\n     margin: 0;\n}\n\n.popup hr {\n  display: block;\n       height: 1px;\n  width: auto;\n  padding: 0;\n   margin: 0.2em 0em;\n}\n\n.listBreak {\n font-size: 1px;\n       line-height: 1px;\n}\n\n.listBreak div {\n      margin: 2px 0;\n}\n\n.popup li.disabled {\n     padding: 0.2em;\n}\n\n.popup li a{\n    display: block;\n       padding: 0.2em;\n}\n\n.tabset {\n       padding: 1em 0em 0em 0.5em;\n}\n\n.tab {\n      margin: 0em 0em 0em 0.25em;\n   padding: 2px;\n}\n\n.tabContents {\n    padding: 0.5em;\n}\n\n.tabContents ul, .tabContents ol {\n      margin: 0;\n    padding: 0;\n}\n\n.txtMainTab .tabContents li {\n       list-style: none;\n}\n\n.tabContents li.listLink {\n     margin-left: .75em;\n}\n\n#displayArea {\n     margin: 1em 17em 0em 14em;\n}\n\n\n.toolbar {\n text-align: right;\n    font-size: .9em;\n      visibility: hidden;\n}\n\n.selected .toolbar {\n        visibility: visible;\n}\n\n.tiddler {\n padding: 1em 1em 0em 1em;\n}\n\n.missing .viewer,.missing .title {\n    font-style: italic;\n}\n\n.title {\n    font-size: 1.6em;\n     font-weight: bold;\n}\n\n.missing .subtitle {\n display: none;\n}\n\n.subtitle {\n      font-size: 1.1em;\n}\n\n.tiddler .button {\n    padding: 0.2em 0.4em;\n}\n\n.tagging {\nmargin: 0.5em 0.5em 0.5em 0;\nfloat: left;\ndisplay: none;\n}\n\n.isTag .tagging {\ndisplay: block;\n}\n\n.tagged {\nmargin: 0.5em;\nfloat: right;\n}\n\n.tagging, .tagged {\nfont-size: 0.9em;\npadding: 0.25em;\n}\n\n.tagging ul, .tagged ul {\nlist-style: none;margin: 0.25em;\npadding: 0;\n}\n\n.tagClear {\nclear: both;\n}\n\n.footer {\n      font-size: .9em;\n}\n\n.footer li {\ndisplay: inline;\n}\n\n* html .viewer pre {\n      width: 99%;\n   padding: 0 0 1em 0;\n}\n\n.viewer {\n   line-height: 1.4em;\n   padding-top: 0.5em;\n}\n\n.viewer .button {\n   margin: 0em 0.25em;\n   padding: 0em 0.25em;\n}\n\n.viewer blockquote {\n       line-height: 1.5em;\n   padding-left: 0.8em;\n  margin-left: 2.5em;\n}\n\n.viewer ul, .viewer ol{\n     margin-left: 0.5em;\n   padding-left: 1.5em;\n}\n\n.viewer table {\n    border-collapse: collapse;\n    margin: 0.8em 1.0em;\n}\n\n.viewer th, .viewer td, .viewer tr,.viewer caption{\n        padding: 3px;\n}\n\n.viewer table.listView {\n  font-size: 0.85em;\n    margin: 0.8em 1.0em;\n}\n\n.viewer table.listView th, .viewer table.listView td, .viewer table.listView tr {\n  padding: 0px 3px 0px 3px;\n}\n\n.viewer pre {\n padding: 0.5em;\n       margin-left: 0.5em;\n   font-size: 1.2em;\n     line-height: 1.4em;\n   overflow: auto;\n}\n\n.viewer code {\n  font-size: 1.2em;\n     line-height: 1.4em;\n}\n\n.editor {\nfont-size: 1.1em;\n}\n\n.editor input, .editor textarea {\n        display: block;\n       width: 100%;\n  font: inherit;\n}\n\n.editorFooter {\n  padding: 0.25em 0em;\n  font-size: .9em;\n}\n\n.editorFooter .button {\npadding-top: 0px; padding-bottom: 0px;}\n\n.fieldsetFix {border: 0;\npadding: 0;\nmargin: 1px 0px 1px 0px;\n}\n\n.sparkline {\n line-height: 1em;\n}\n\n.sparktick {\n  outline: 0;\n}\n\n.zoomer {\n   font-size: 1.1em;\n     position: absolute;\n   padding: 1em;\n}\n\n.cascade {\n        font-size: 1.1em;\n     position: absolute;\n   overflow: hidden;\n}\n/*}}}*/",
                    248:        StyleSheetPrint: "/*{{{*/\n@media print {\n#mainMenu, #sidebar, #messageArea, .toolbar {display: none ! important;}\n#displayArea {margin: 1em 1em 0em 1em;}\n/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */\nnoscript {display:none;}\n}\n/*}}}*/",
                    249:        PageTemplate: "<!--{{{-->\n<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>\n<div class='headerShadow'>\n<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;\n<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>\n</div>\n<div class='headerForeground'>\n<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;\n<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>\n</div>\n</div>\n<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>\n<div id='sidebar'>\n<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>\n<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>\n</div>\n<div id='displayArea'>\n<div id='messageArea'></div>\n<div id='tiddlerDisplay'></div>\n</div>\n<!--}}}-->",
                    250:        ViewTemplate: "<!--{{{-->\n<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler permalink references jump'></div>\n<div class='title' macro='view title'></div>\n<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date [[DD MMM YYYY]]'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date [[DD MMM YYYY]]'></span>)</div>\n<div class='tagging' macro='tagging'></div>\n<div class='tagged' macro='tags'></div>\n<div class='viewer' macro='view text wikified'></div>\n<div class='tagClear'></div>\n<!--}}}-->",
                    251:        EditTemplate: "<!--{{{-->\n<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>\n<div class='title' macro='view title'></div>\n<div class='editor' macro='edit title'></div>\n<div class='editor' macro='edit text'></div>\n<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>\n<!--}}}-->",
                    252:        MarkupPreHead: "<!--{{{-->\n<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>\n<!--}}}-->",
                    253:        MarkupPostHead: "",
                    254:        MarkupPreBody: "",
                    255:        MarkupPostBody: ""
                    256:        };
                    257: 
                    258: // ---------------------------------------------------------------------------------
                    259: // Translateable strings
                    260: // ---------------------------------------------------------------------------------
                    261: 
                    262: // Strings in "double quotes" should be translated; strings in 'single quotes' should be left alone
                    263: 
                    264: merge(config.options,{
                    265:        txtUserName: "YourName"});
                    266: 
                    267: merge(config.messages,{
                    268:        customConfigError: "Problems were encountered loading plugins. See PluginManager for details",
                    269:        pluginError: "Error: %0",
                    270:        pluginDisabled: "Not executed because disabled via 'systemConfigDisable' tag",
                    271:        pluginForced: "Executed because forced via 'systemConfigForce' tag",
                    272:        pluginVersionError: "Not executed because this plugin needs a newer version of TiddlyWiki",
                    273:        nothingSelected: "Nothing is selected. You must select one or more items first",
                    274:        savedSnapshotError: "It appears that this TiddlyWiki has been incorrectly saved. Please see http://www.tiddlywiki.com/#DownloadSoftware for details",
                    275:        subtitleUnknown: "(unknown)",
                    276:        undefinedTiddlerToolTip: "The tiddler '%0' doesn't yet exist",
                    277:        shadowedTiddlerToolTip: "The tiddler '%0' doesn't yet exist, but has a pre-defined shadow value",
                    278:        tiddlerLinkTooltip: "%0 - %1, %2",
                    279:        externalLinkTooltip: "External link to %0",
                    280:        noTags: "There are no tagged tiddlers",
                    281:        notFileUrlError: "You need to save this TiddlyWiki to a file before you can save changes",
                    282:        cantSaveError: "It's not possible to save changes. This could be because your browser doesn't support saving (instead, use FireFox if you can), or because the pathname to your TiddlyWiki file contains illegal characters",
                    283:        invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
                    284:        backupSaved: "Backup saved",
                    285:        backupFailed: "Failed to save backup file",
                    286:        rssSaved: "RSS feed saved",
                    287:        rssFailed: "Failed to save RSS feed file",
                    288:        emptySaved: "Empty template saved",
                    289:        emptyFailed: "Failed to save empty template file",
                    290:        mainSaved: "Main TiddlyWiki file saved",
                    291:        mainFailed: "Failed to save main TiddlyWiki file. Your changes have not been saved",
                    292:        macroError: "Error in macro <<%0>>",
                    293:        macroErrorDetails: "Error while executing macro <<%0>>:\n%1",
                    294:        missingMacro: "No such macro",
                    295:        overwriteWarning: "A tiddler named '%0' already exists. Choose OK to overwrite it",
                    296:        unsavedChangesWarning: "WARNING! There are unsaved changes in TiddlyWiki\n\nChoose OK to save\nChoose CANCEL to discard",
                    297:        confirmExit: "--------------------------------\n\nThere are unsaved changes in TiddlyWiki. If you continue you will lose those changes\n\n--------------------------------",
                    298:        saveInstructions: "SaveChanges",
                    299:        unsupportedTWFormat: "Unsupported TiddlyWiki format '%0'",
                    300:        tiddlerSaveError: "Error when saving tiddler '%0'",
                    301:        tiddlerLoadError: "Error when loading tiddler '%0'",
                    302:        wrongSaveFormat: "Cannot save with storage format '%0'. Using standard format for save.",
                    303:        invalidFieldName: "Invalid field name %0",
                    304:        fieldCannotBeChanged: "Field '%0' cannot be changed"});
                    305: 
                    306: merge(config.messages.messageClose,{
                    307:        text: "close",
                    308:        tooltip: "close this message area"});
                    309: 
                    310: config.messages.dates.months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November","December"];
                    311: config.messages.dates.days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
                    312: config.messages.dates.shortMonths = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
                    313: config.messages.dates.shortDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
                    314: 
                    315: merge(config.views.wikified.tag,{
                    316:        labelNoTags: "no tags",
                    317:        labelTags: "tags: ",
                    318:        openTag: "Open tag '%0'",
                    319:        tooltip: "Show tiddlers tagged with '%0'",
                    320:        openAllText: "Open all",
                    321:        openAllTooltip: "Open all of these tiddlers",
                    322:        popupNone: "No other tiddlers tagged with '%0'"});
                    323: 
                    324: merge(config.views.wikified,{
                    325:        defaultText: "The tiddler '%0' doesn't yet exist. Double-click to create it",
                    326:        defaultModifier: "(missing)",
                    327:        shadowModifier: "(built-in shadow tiddler)",
                    328:        createdPrompt: "created"});
                    329: 
                    330: merge(config.views.editor,{
                    331:        tagPrompt: "Type tags separated with spaces, [[use double square brackets]] if necessary, or add existing",
                    332:        defaultText: "Type the text for '%0'"});
                    333: 
                    334: merge(config.views.editor.tagChooser,{
                    335:        text: "tags",
                    336:        tooltip: "Choose existing tags to add to this tiddler",
                    337:        popupNone: "There are no tags defined",
                    338:        tagTooltip: "Add the tag '%0'"});
                    339: 
                    340: merge(config.macros.search,{
                    341:        label: "search",
                    342:        prompt: "Search this TiddlyWiki",
                    343:        accessKey: "F",
                    344:        successMsg: "%0 tiddlers found matching %1",
                    345:        failureMsg: "No tiddlers found matching %0"});
                    346: 
                    347: merge(config.macros.tagging,{
                    348:        label: "tagging: ",
                    349:        labelNotTag: "not tagging",
                    350:        tooltip: "List of tiddlers tagged with '%0'"});
                    351: 
                    352: merge(config.macros.timeline,{
                    353:        dateFormat: "DD MMM YYYY"});
                    354: 
                    355: merge(config.macros.allTags,{
                    356:        tooltip: "Show tiddlers tagged with '%0'",
                    357:        noTags: "There are no tagged tiddlers"});
                    358: 
                    359: config.macros.list.all.prompt = "All tiddlers in alphabetical order";
                    360: config.macros.list.missing.prompt = "Tiddlers that have links to them but are not defined";
                    361: config.macros.list.orphans.prompt = "Tiddlers that are not linked to from any other tiddlers";
                    362: config.macros.list.shadowed.prompt = "Tiddlers shadowed with default contents";
                    363: 
                    364: merge(config.macros.closeAll,{
                    365:        label: "close all",
                    366:        prompt: "Close all displayed tiddlers (except any that are being edited)"});
                    367: 
                    368: merge(config.macros.permaview,{
                    369:        label: "permaview",
                    370:        prompt: "Link to an URL that retrieves all the currently displayed tiddlers"});
                    371: 
                    372: merge(config.macros.saveChanges,{
                    373:        label: "save changes",
                    374:        prompt: "Save all tiddlers to create a new TiddlyWiki",
                    375:        accessKey: "S"});
                    376: 
                    377: merge(config.macros.newTiddler,{
                    378:        label: "new tiddler",
                    379:        prompt: "Create a new tiddler",
                    380:        title: "New Tiddler",
                    381:        accessKey: "N"});
                    382: 
                    383: merge(config.macros.newJournal,{
                    384:        label: "new journal",
                    385:        prompt: "Create a new tiddler from the current date and time",
                    386:        accessKey: "J"});
                    387: 
                    388: merge(config.macros.plugins,{
                    389:        skippedText: "(This plugin has not been executed because it was added since startup)",
                    390:        noPluginText: "There are no plugins installed",
                    391:        confirmDeleteText: "Are you sure you want to delete these tiddlers:\n\n%0",
                    392:        listViewTemplate : {
                    393:                columns: [
                    394:                        {name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
                    395:                        {name: 'Title', field: 'title', tiddlerLink: 'title', title: "Title", type: 'TiddlerLink'},
                    396:                        {name: 'Forced', field: 'forced', title: "Forced", tag: 'systemConfigForce', type: 'TagCheckbox'},
                    397:                        {name: 'Disabled', field: 'disabled', title: "Disabled", tag: 'systemConfigDisable', type: 'TagCheckbox'},
                    398:                        {name: 'Executed', field: 'executed', title: "Loaded", type: 'Boolean', trueText: "Yes", falseText: "No"},
                    399:                        {name: 'Error', field: 'error', title: "Status", type: 'Boolean', trueText: "Error", falseText: "OK"},
                    400:                        {name: 'Log', field: 'log', title: "Log", type: 'StringList'}
                    401:                        ],
                    402:                rowClasses: [
                    403:                        {className: 'error', field: 'error'},
                    404:                        {className: 'warning', field: 'warning'}
                    405:                        ],
                    406:                actions: [
                    407:                        {caption: "More actions...", name: ''},
                    408:                        {caption: "Remove systemConfig tag", name: 'remove'},
                    409:                        {caption: "Delete these tiddlers forever", name: 'delete'}
                    410:                        ]}
                    411:        });
                    412: 
                    413: merge(config.macros.refreshDisplay,{
                    414:        label: "refresh",
                    415:        prompt: "Redraw the entire TiddlyWiki display"
                    416:        });
                    417: 
                    418: merge(config.macros.importTiddlers,{
                    419:        readOnlyWarning: "You cannot import tiddlers into a read-only TiddlyWiki. Try opening the TiddlyWiki file from a file:// URL",
                    420:        defaultPath: "http://www.tiddlywiki.com/index.html",
                    421:        fetchLabel: "fetch",
                    422:        fetchPrompt: "Fetch the tiddlywiki file",
                    423:        fetchError: "There were problems fetching the tiddlywiki file",
                    424:        confirmOverwriteText: "Are you sure you want to overwrite these tiddlers:\n\n%0",
                    425:        wizardTitle: "Import tiddlers from another TiddlyWiki file",
                    426:        step1: "Step 1: Locate the TiddlyWiki file",
                    427:        step1prompt: "Enter the URL or pathname here: ",
                    428:        step1promptFile: "...or browse for a file: ",
                    429:        step1promptFeeds: "...or select a pre-defined feed: ",
                    430:        step1feedPrompt: "Choose...",
                    431:        step2: "Step 2: Loading TiddlyWiki file",
                    432:        step2Text: "Please wait while the file is loaded from: %0",
                    433:        step3: "Step 3: Choose the tiddlers to import",
                    434:        step4: "%0 tiddler(s) imported",
                    435:        step5: "Done",
                    436:        listViewTemplate: {
                    437:                columns: [
                    438:                        {name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
                    439:                        {name: 'Title', field: 'title', title: "Title", type: 'String'},
                    440:                        {name: 'Snippet', field: 'text', title: "Snippet", type: 'String'},
                    441:                        {name: 'Tags', field: 'tags', title: "Tags", type: 'Tags'}
                    442:                        ],
                    443:                rowClasses: [
                    444:                        ],
                    445:                actions: [
                    446:                        {caption: "More actions...", name: ''},
                    447:                        {caption: "Import these tiddlers", name: 'import'}
                    448:                        ]}
                    449:        });
                    450: 
                    451: merge(config.commands.closeTiddler,{
                    452:        text: "close",
                    453:        tooltip: "Close this tiddler"});
                    454: 
                    455: merge(config.commands.closeOthers,{
                    456:        text: "close others",
                    457:        tooltip: "Close all other tiddlers"});
                    458: 
                    459: merge(config.commands.editTiddler,{
                    460:        text: "edit",
                    461:        tooltip: "Edit this tiddler",
                    462:        readOnlyText: "view",
                    463:        readOnlyTooltip: "View the source of this tiddler"});
                    464: 
                    465: merge(config.commands.saveTiddler,{
                    466:        text: "done",
                    467:        tooltip: "Save changes to this tiddler"});
                    468: 
                    469: merge(config.commands.cancelTiddler,{
                    470:        text: "cancel",
                    471:        tooltip: "Undo changes to this tiddler",
                    472:        warning: "Are you sure you want to abandon your changes to '%0'?",
                    473:        readOnlyText: "done",
                    474:        readOnlyTooltip: "View this tiddler normally"});
                    475: 
                    476: merge(config.commands.deleteTiddler,{
                    477:        text: "delete",
                    478:        tooltip: "Delete this tiddler",
                    479:        warning: "Are you sure you want to delete '%0'?"});
                    480: 
                    481: merge(config.commands.permalink,{
                    482:        text: "permalink",
                    483:        tooltip: "Permalink for this tiddler"});
                    484: 
                    485: merge(config.commands.references,{
                    486:        text: "references",
                    487:        tooltip: "Show tiddlers that link to this one",
                    488:        popupNone: "No references"});
                    489: 
                    490: merge(config.commands.jump,{
                    491:        text: "jump",
                    492:        tooltip: "Jump to another open tiddler"});
                    493: 
                    494: merge(config.shadowTiddlers,{
                    495:        DefaultTiddlers: "GettingStarted",
                    496:        MainMenu: "GettingStarted",
                    497:        SiteTitle: "My TiddlyWiki",
                    498:        SiteSubtitle: "a reusable non-linear personal web notebook",
                    499:        SiteUrl: "http://www.tiddlywiki.com/",
                    500:        GettingStarted: "To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:\n* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)\n* MainMenu: The menu (usually on the left)\n* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened\nYou'll also need to enter your username for signing your edits: <<option txtUserName>>",
                    501:        SideBarOptions: "<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal 'DD MMM YYYY'>><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel 'options »' 'Change TiddlyWiki advanced options'>>",
                    502:        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 chkRegExpSearch>> RegExpSearch\n<<option chkCaseSensitiveSearch>> CaseSensitiveSearch\n<<option chkAnimate>> EnableAnimations\n\n----\nAdvancedOptions\nPluginManager\nImportTiddlers",
                    503:        AdvancedOptions: "<<option chkGenerateAnRssFeed>> GenerateAnRssFeed\n<<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\n<<option chkForceMinorUpdate>> Treat edits as MinorChanges by preserving date and time\n^^(override with Shift key when clicking 'done' or by pressing Ctrl-Shift-Enter^^\n<<option chkConfirmDelete>> ConfirmBeforeDeleting\nMaximum number of lines in a tiddler edit box: <<option txtMaxEditRows>>\nFolder name for backup files: <<option txtBackupFolder>>\n<<option chkInsertTabs>> Use tab key to insert tab characters instead of jumping to next field",
                    504:        SideBarTabs: "<<tabs txtMainTab Timeline Timeline TabTimeline All 'All tiddlers' TabAll Tags 'All tags' TabTags More 'More lists' TabMore>>",
                    505:        TabTimeline: "<<timeline>>",
                    506:        TabAll: "<<list all>>",
                    507:        TabTags: "<<allTags>>",
                    508:        TabMore: "<<tabs txtMoreTab Missing 'Missing tiddlers' TabMoreMissing Orphans 'Orphaned tiddlers' TabMoreOrphans Shadowed 'Shadowed tiddlers' TabMoreShadowed>>",
                    509:        TabMoreMissing: "<<list missing>>",
                    510:        TabMoreOrphans: "<<list orphans>>",
                    511:        TabMoreShadowed: "<<list shadowed>>",
                    512:        PluginManager: "<<plugins>>",
                    513:        ImportTiddlers: "<<importTiddlers>>"});
                    514: 
                    515: // ---------------------------------------------------------------------------------
                    516: // Main
                    517: // ---------------------------------------------------------------------------------
                    518: 
                    519: var params = null; // Command line parameters
                    520: var store = null; // TiddlyWiki storage
                    521: var story = null; // Main story
                    522: var formatter = null; // Default formatters for the wikifier
                    523: config.parsers = {}; // Hashmap of alternative parsers for the wikifier
                    524: var anim = new Animator(); // Animation engine
                    525: var readOnly = false; // Whether we're in readonly mode
                    526: var highlightHack = null; // Embarrassing hack department...
                    527: var hadConfirmExit = false; // Don't warn more than once
                    528: var safeMode = false; // Disable all plugins and cookies
                    529: var installedPlugins = []; // Information filled in when plugins are executed
                    530: var startingUp = false; // Whether we're in the process of starting up
                    531: var pluginInfo,tiddler; // Used to pass information to plugins in loadPlugins()
                    532: 
                    533: // Whether to use the JavaSaver applet
                    534: var useJavaSaver = config.browser.isSafari || config.browser.isOpera;
                    535: 
                    536: // Starting up
                    537: function main()
                    538: {
                    539:        var now, then = new Date();
                    540:        startingUp = true;
                    541:        window.onbeforeunload = function(e) {if(window.confirmExit) return confirmExit();};
                    542:        params = getParameters();
                    543:        if(params)
                    544:                params = params.parseParams("open",null,false);
                    545:        store = new TiddlyWiki();
                    546:        invokeParamifier(params,"oninit");
                    547:        story = new Story("tiddlerDisplay","tiddler");
                    548:        addEvent(document,"click",Popup.onDocumentClick);
                    549:        saveTest();
                    550:        loadOptionsCookie();
                    551:        for(var s=0; s<config.notifyTiddlers.length; s++)
                    552:                store.addNotification(config.notifyTiddlers[s].name,config.notifyTiddlers[s].notify);
                    553:        store.loadFromDiv("storeArea","store",true);
                    554:        invokeParamifier(params,"onload");
                    555:        var pluginProblem = loadPlugins();
                    556:        formatter = new Formatter(config.formatters);
                    557:        readOnly = (window.location.protocol == "file:") ? false : config.options.chkHttpReadOnly;
                    558:        invokeParamifier(params,"onconfig");
                    559:        store.notifyAll();
                    560:        restart();
                    561:        if(pluginProblem)
                    562:                {
                    563:                story.displayTiddler(null,"PluginManager");
                    564:                displayMessage(config.messages.customConfigError);
                    565:                }
                    566:        now = new Date();
                    567:        if(config.displayStartupTime)
                    568:                displayMessage("TiddlyWiki startup in " + (now-then)/1000 + " seconds");
                    569:        startingUp = false;
                    570: }
                    571: 
                    572: // Restarting
                    573: function restart()
                    574: {
                    575:        invokeParamifier(params,"onstart");
                    576:        if(story.isEmpty())
                    577:                {
                    578:                var defaultParams = store.getTiddlerText("DefaultTiddlers").parseParams("open",null,false);
                    579:                invokeParamifier(defaultParams,"onstart");
                    580:                }
                    581:        window.scrollTo(0,0);
                    582: }
                    583: 
                    584: function saveTest()
                    585: {
                    586:        var saveTest = document.getElementById("saveTest");
                    587:        if(saveTest.hasChildNodes())
                    588:                alert(config.messages.savedSnapshotError);
                    589:        saveTest.appendChild(document.createTextNode("savetest"));
                    590: }
                    591: 
                    592: function loadPlugins()
                    593: {
                    594:        if(safeMode)
                    595:                return false;
                    596:        var configTiddlers = store.getTaggedTiddlers("systemConfig");
                    597:        installedPlugins = [];
                    598:        var hadProblem = false;
                    599:        for(var t=0; t<configTiddlers.length; t++)
                    600:                {
                    601:                tiddler = configTiddlers[t];
                    602:                pluginInfo = getPluginInfo(tiddler);
                    603:                if(isPluginExecutable(pluginInfo))
                    604:                        {
                    605:                        pluginInfo.executed = true;
                    606:                        pluginInfo.error = false;
                    607:                        try
                    608:                                {
                    609:                                if(tiddler.text && tiddler.text != "")
                    610:                                        window.eval(tiddler.text);
                    611:                                }
                    612:                        catch(e)
                    613:                                {
                    614:                                pluginInfo.log.push(config.messages.pluginError.format([exceptionText(e)]));
                    615:                                pluginInfo.error = true;
                    616:                                hadProblem = true;
                    617:                                }
                    618:                        }
                    619:                else
                    620:                        pluginInfo.warning = true;
                    621:                installedPlugins.push(pluginInfo);
                    622:                }
                    623:        return hadProblem;
                    624: }
                    625: 
                    626: function getPluginInfo(tiddler)
                    627: {
                    628:        var p = store.getTiddlerSlices(tiddler.title,["Name","Description","Version","CoreVersion","Date","Source","Author","License","Browsers"]);
                    629:        p.tiddler = tiddler;
                    630:        p.title = tiddler.title;
                    631:        p.log = [];
                    632:        return p;
                    633: }
                    634: 
                    635: // Check that a particular plugin is valid for execution
                    636: function isPluginExecutable(plugin)
                    637: {
                    638:        if(plugin.tiddler.isTagged("systemConfigDisable"))
                    639:                return verifyTail(plugin,false,config.messages.pluginDisabled);
                    640:        if(plugin.tiddler.isTagged("systemConfigForce"))
                    641:                return verifyTail(plugin,true,config.messages.pluginForced);
                    642:        if(plugin["CoreVersion"])
                    643:                {
                    644:                var coreVersion = plugin["CoreVersion"].split(".");
                    645:                var w = parseInt(coreVersion[0]) - version.major;
                    646:                if(w == 0 && coreVersion[1])
                    647:                        w = parseInt(coreVersion[1]) - version.minor;
                    648:                if(w == 0 && coreVersion[2])
                    649:                        w = parseInt(coreVersion[2]) - version.revision;
                    650:                if(w > 0)
                    651:                        return verifyTail(plugin,false,config.messages.pluginVersionError);
                    652:                }
                    653:        return true;
                    654: }
                    655: 
                    656: function verifyTail(plugin,result,message)
                    657: {
                    658:        plugin.log.push(message);
                    659:        return result;
                    660: }
                    661: 
                    662: function invokeMacro(place,macro,params,wikifier,tiddler)
                    663: {
                    664:        try
                    665:                {
                    666:                var m = config.macros[macro];
                    667:                if(m && m.handler)
                    668:                        m.handler(place,macro,params.readMacroParams(),wikifier,params,tiddler);
                    669:                else
                    670:                        createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,config.messages.missingMacro]));
                    671:                }
                    672:        catch(ex)
                    673:                {
                    674:                createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,ex.toString()]));
                    675:                }
                    676: }
                    677: 
                    678: // ---------------------------------------------------------------------------------
                    679: // Paramifiers
                    680: // ---------------------------------------------------------------------------------
                    681: 
                    682: function getParameters()
                    683: {
                    684:        var p = null;
                    685:        if(window.location.hash)
                    686:                {
                    687:                p = decodeURI(window.location.hash.substr(1));
                    688:                if(config.browser.firefoxDate != null && config.browser.firefoxDate[1] < "20051111")
                    689:                        p = convertUTF8ToUnicode(p);
                    690:                }
                    691:        return p;
                    692: }
                    693: 
                    694: function invokeParamifier(params,handler)
                    695: {
                    696:        if(!params || params.length == undefined || params.length <= 1)
                    697:                return;
                    698:        for(var t=1; t<params.length; t++)
                    699:                {
                    700:                var p = config.paramifiers[params[t].name];
                    701:                if(p && p[handler] instanceof Function)
                    702:                        p[handler](params[t].value);
                    703:                }
                    704: }
                    705: 
                    706: config.paramifiers = {};
                    707: 
                    708: config.paramifiers.start = {
                    709:        oninit: function(v) {
                    710:                safeMode = v.toLowerCase() == "safe";
                    711:                }
                    712: };
                    713: 
                    714: config.paramifiers.open = {
                    715:        onstart: function(v) {
                    716:                story.displayTiddler("bottom",v,null,false,false);
                    717:                }
                    718: };
                    719: 
                    720: config.paramifiers.story = {
                    721:        onstart: function(v) {
                    722:                var list = store.getTiddlerText(v,"").parseParams("open",null,false);
                    723:                invokeParamifier(list,"onstart");
                    724:                }
                    725: };
                    726: 
                    727: config.paramifiers.search = {
                    728:        onstart: function(v) {
                    729:                story.search(v,false,false);
                    730:                }
                    731: };
                    732: 
                    733: config.paramifiers.searchRegExp = {
                    734:        onstart: function(v) {
                    735:                story.prototype.search(v,false,true);
                    736:                }
                    737: };
                    738: 
                    739: config.paramifiers.tag = {
                    740:        onstart: function(v) {
                    741:                var tagged = store.getTaggedTiddlers(v,"title");
                    742:                for(var t=0; t<tagged.length; t++)
                    743:                        story.displayTiddler("bottom",tagged[t].title,null,false,false);
                    744:                }
                    745: };
                    746: 
                    747: config.paramifiers.newTiddler = {
                    748:        onstart: function(v) {
                    749:                if(!readOnly)
                    750:                        {
                    751:                        story.displayTiddler(null,v,DEFAULT_EDIT_TEMPLATE);
                    752:                        story.focusTiddler(v,"text");
                    753:                        }
                    754:                }
                    755: };
                    756: 
                    757: config.paramifiers.newJournal = {
                    758:        onstart: function(v) {
                    759:                if(!readOnly)
                    760:                        {
                    761:                        var now = new Date();
                    762:                        var title = now.formatString(v.trim());
                    763:                        story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
                    764:                        story.focusTiddler(title,"text");
                    765:                        }
                    766:                }
                    767: };
                    768: 
                    769: // ---------------------------------------------------------------------------------
                    770: // Formatter helpers
                    771: // ---------------------------------------------------------------------------------
                    772: 
                    773: function Formatter(formatters)
                    774: {
                    775:        this.formatters = [];
                    776:        var pattern = [];
                    777:        for(var n=0; n<formatters.length; n++)
                    778:                {
                    779:                pattern.push("(" + formatters[n].match + ")");
                    780:                this.formatters.push(formatters[n]);
                    781:                }
                    782:        this.formatterRegExp = new RegExp(pattern.join("|"),"mg");
                    783: }
                    784: 
                    785: config.formatterHelpers = {
                    786: 
                    787:        createElementAndWikify: function(w)
                    788:        {
                    789:                w.subWikifyTerm(createTiddlyElement(w.output,this.element),this.termRegExp);
                    790:        },
                    791:        
                    792:        inlineCssHelper: function(w)
                    793:        {
                    794:                var styles = [];
                    795:                config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
                    796:                var lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
                    797:                while(lookaheadMatch && lookaheadMatch.index == w.nextMatch)
                    798:                        {
                    799:                        var s,v;
                    800:                        if(lookaheadMatch[1])
                    801:                                {
                    802:                                s = lookaheadMatch[1].unDash();
                    803:                                v = lookaheadMatch[2];
                    804:                                }
                    805:                        else
                    806:                                {
                    807:                                s = lookaheadMatch[3].unDash();
                    808:                                v = lookaheadMatch[4];
                    809:                                }
                    810:                        if (s=="bgcolor")
                    811:                                s = "backgroundColor";
                    812:                        styles.push({style: s, value: v});
                    813:                        w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
                    814:                        config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
                    815:                        lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
                    816:                        }
                    817:                return styles;
                    818:        },
                    819: 
                    820:        applyCssHelper: function(e,styles)
                    821:        {
                    822:                for(var t=0; t< styles.length; t++)
                    823:                        {
                    824:                        try
                    825:                                {
                    826:                                e.style[styles[t].style] = styles[t].value;
                    827:                                }
                    828:                        catch (ex)
                    829:                                {
                    830:                                }
                    831:                        }
                    832:        },
                    833: 
                    834:        enclosedTextHelper: function(w)
                    835:        {
                    836:                this.lookaheadRegExp.lastIndex = w.matchStart;
                    837:                var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
                    838:                if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
                    839:                        {
                    840:                        var text = lookaheadMatch[1];
                    841:                        if(config.browser.isIE)
                    842:                                text = text.replace(/\n/g,"\r");
                    843:                        createTiddlyElement(w.output,this.element,null,null,text);
                    844:                        w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
                    845:                        }
                    846:        },
                    847: 
                    848:        isExternalLink: function(link)
                    849:        {
                    850:                if(store.tiddlerExists(link) || store.isShadowTiddler(link))
                    851:                        {
                    852:                        //# Definitely not an external link
                    853:                        return false;
                    854:                        }
                    855:                var urlRegExp = new RegExp(config.textPrimitives.urlPattern,"mg");
                    856:                if(urlRegExp.exec(link))
                    857:                        {
                    858:                        // Definitely an external link
                    859:                        return true;
                    860:                        }
                    861:                if (link.indexOf(".")!=-1 || link.indexOf("\\")!=-1 || link.indexOf("/")!=-1)
                    862:                        {
                    863:                        //# Link contains . / or \ so is probably an external link
                    864:                        return true;
                    865:                        }
                    866:                //# Otherwise assume it is not an external link
                    867:                return false;
                    868:        }
                    869: 
                    870: };
                    871: 
                    872: // ---------------------------------------------------------------------------------
                    873: // Standard formatters
                    874: // ---------------------------------------------------------------------------------
                    875: 
                    876: config.formatters = [
                    877: {
                    878:        name: "table",
                    879:        match: "^\\|(?:[^\\n]*)\\|(?:[fhck]?)$",
                    880:        lookaheadRegExp: /^\|([^\n]*)\|([fhck]?)$/mg,
                    881:        rowTermRegExp: /(\|(?:[fhck]?)$\n?)/mg,
                    882:        cellRegExp: /(?:\|([^\n\|]*)\|)|(\|[fhck]?$\n?)/mg,
                    883:        cellTermRegExp: /((?:\x20*)\|)/mg,
                    884:        rowTypes: {"c":"caption", "h":"thead", "":"tbody", "f":"tfoot"},
                    885: 
                    886:        handler: function(w)
                    887:        {
                    888:                var table = createTiddlyElement(w.output,"table");
                    889:                var prevColumns = [];
                    890:                var currRowType = null;
                    891:                var rowContainer;
                    892:                var rowCount = 0;
                    893:                w.nextMatch = w.matchStart;
                    894:                this.lookaheadRegExp.lastIndex = w.nextMatch;
                    895:                var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
                    896:                while(lookaheadMatch && lookaheadMatch.index == w.nextMatch)
                    897:                        {
                    898:                        var nextRowType = lookaheadMatch[2];
                    899:                        if(nextRowType == "k")
                    900:                                {
                    901:                                table.className = lookaheadMatch[1];
                    902:                                w.nextMatch += lookaheadMatch[0].length+1;
                    903:                                }
                    904:                        else
                    905:                                {
                    906:                                if(nextRowType != currRowType)
                    907:                                        {
                    908:                                        rowContainer = createTiddlyElement(table,this.rowTypes[nextRowType]);
                    909:                                        currRowType = nextRowType;
                    910:                                        }
                    911:                                if(currRowType == "c")
                    912:                                        {
                    913:                                        // Caption
                    914:                                        w.nextMatch++;
                    915:                                        if(rowContainer != table.firstChild)
                    916:                                                table.insertBefore(rowContainer,table.firstChild);
                    917:                                        rowContainer.setAttribute("align",rowCount == 0?"top":"bottom");
                    918:                                        w.subWikifyTerm(rowContainer,this.rowTermRegExp);
                    919:                                        }
                    920:                                else
                    921:                                        {
                    922:                                        this.rowHandler(w,createTiddlyElement(rowContainer,"tr",null,(rowCount&1)?"oddRow":"evenRow"),prevColumns);
                    923:                                        rowCount++;
                    924:                                        }
                    925:                                }
                    926:                        this.lookaheadRegExp.lastIndex = w.nextMatch;
                    927:                        lookaheadMatch = this.lookaheadRegExp.exec(w.source);
                    928:                        }
                    929:        },
                    930:        rowHandler: function(w,e,prevColumns)
                    931:        {
                    932:                var col = 0;
                    933:                var colSpanCount = 1;
                    934:                var prevCell = null;
                    935:                this.cellRegExp.lastIndex = w.nextMatch;
                    936:                var cellMatch = this.cellRegExp.exec(w.source);
                    937:                while(cellMatch && cellMatch.index == w.nextMatch)
                    938:                        {
                    939:                        if(cellMatch[1] == "~")
                    940:                                {
                    941:                                // Rowspan
                    942:                                var last = prevColumns[col];
                    943:                                if(last)
                    944:                                        {
                    945:                                        last.rowSpanCount++;
                    946:                                        last.element.setAttribute("rowspan",last.rowSpanCount);
                    947:                                        last.element.setAttribute("rowSpan",last.rowSpanCount); // Needed for IE
                    948:                                        last.element.valign = "center";
                    949:                                        }
                    950:                                w.nextMatch = this.cellRegExp.lastIndex-1;
                    951:                                }
                    952:                        else if(cellMatch[1] == ">")
                    953:                                {
                    954:                                // Colspan
                    955:                                colSpanCount++;
                    956:                                w.nextMatch = this.cellRegExp.lastIndex-1;
                    957:                                }
                    958:                        else if(cellMatch[2])
                    959:                                {
                    960:                                // End of row
                    961:                                if(prevCell && colSpanCount > 1)
                    962:                                        {
                    963:                                        prevCell.setAttribute("colspan",colSpanCount);
                    964:                                        prevCell.setAttribute("colSpan",colSpanCount); // Needed for IE
                    965:                                        }
                    966:                                w.nextMatch = this.cellRegExp.lastIndex;
                    967:                                break;
                    968:                                }
                    969:                        else
                    970:                                {
                    971:                                // Cell
                    972:                                w.nextMatch++;
                    973:                                var styles = config.formatterHelpers.inlineCssHelper(w);
                    974:                                var spaceLeft = false;
                    975:                                var chr = w.source.substr(w.nextMatch,1);
                    976:                                while(chr == " ")
                    977:                                        {
                    978:                                        spaceLeft = true;
                    979:                                        w.nextMatch++;
                    980:                                        chr = w.source.substr(w.nextMatch,1);
                    981:                                        }
                    982:                                var cell;
                    983:                                if(chr == "!")
                    984:                                        {
                    985:                                        cell = createTiddlyElement(e,"th");
                    986:                                        w.nextMatch++;
                    987:                                        }
                    988:                                else
                    989:                                        cell = createTiddlyElement(e,"td");
                    990:                                prevCell = cell;
                    991:                                prevColumns[col] = {rowSpanCount:1, element:cell};
                    992:                                if(colSpanCount > 1)
                    993:                                        {
                    994:                                        cell.setAttribute("colspan",colSpanCount);
                    995:                                        cell.setAttribute("colSpan",colSpanCount); // Needed for IE
                    996:                                        colSpanCount = 1;
                    997:                                        }
                    998:                                config.formatterHelpers.applyCssHelper(cell,styles);
                    999:                                w.subWikifyTerm(cell,this.cellTermRegExp);
                   1000:                                if(w.matchText.substr(w.matchText.length-2,1) == " ") // spaceRight
                   1001:                                        cell.align = spaceLeft ? "center" : "left";
                   1002:                                else if(spaceLeft)
                   1003:                                        cell.align = "right";
                   1004:                                w.nextMatch--;
                   1005:                                }
                   1006:                        col++;
                   1007:                        this.cellRegExp.lastIndex = w.nextMatch;
                   1008:                        cellMatch = this.cellRegExp.exec(w.source);
                   1009:                        }
                   1010:        }
                   1011: },
                   1012: 
                   1013: {
                   1014:        name: "heading",
                   1015:        match: "^!{1,5}",
                   1016:        termRegExp: /(\n)/mg,
                   1017:        handler: function(w)
                   1018:        {
                   1019:                w.subWikifyTerm(createTiddlyElement(w.output,"h" + w.matchLength),this.termRegExp);
                   1020:        }
                   1021: },
                   1022: 
                   1023: {
                   1024:        name: "list",
                   1025:        match: "^(?:(?:(?:\\*)|(?:#)|(?:;)|(?::))+)",
                   1026:        lookaheadRegExp: /^(?:(?:(\*)|(#)|(;)|(:))+)/mg,
                   1027:        termRegExp: /(\n)/mg,
                   1028:        handler: function(w)
                   1029:        {
                   1030:                var placeStack = [w.output];
                   1031:                var currLevel = 0, currType = null;
                   1032:                var listLevel, listType, itemType;
                   1033:                w.nextMatch = w.matchStart;
                   1034:                this.lookaheadRegExp.lastIndex = w.nextMatch;
                   1035:                var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
                   1036:                while(lookaheadMatch && lookaheadMatch.index == w.nextMatch)
                   1037:                        {
                   1038:                        if(lookaheadMatch[1])
                   1039:                                {
                   1040:                                listType = "ul";
                   1041:                                itemType = "li";
                   1042:                                }
                   1043:                        else if(lookaheadMatch[2])
                   1044:                                {
                   1045:                                listType = "ol";
                   1046:                                itemType = "li";
                   1047:                                }
                   1048:                        else if(lookaheadMatch[3])
                   1049:                                {
                   1050:                                listType = "dl";
                   1051:                                itemType = "dt";
                   1052:                                }
                   1053:                        else if(lookaheadMatch[4])
                   1054:                                {
                   1055:                                listType = "dl";
                   1056:                                itemType = "dd";
                   1057:                                }
                   1058:                        listLevel = lookaheadMatch[0].length;
                   1059:                        w.nextMatch += lookaheadMatch[0].length;
                   1060:                        if(listLevel > currLevel)
                   1061:                                {
                   1062:                                for(var t=currLevel; t<listLevel; t++)
                   1063:                                        placeStack.push(createTiddlyElement(placeStack[placeStack.length-1],listType));
                   1064:                                }
                   1065:                        else if(listLevel < currLevel)
                   1066:                                {
                   1067:                                for(var t=currLevel; t>listLevel; t--)
                   1068:                                        placeStack.pop();
                   1069:                                }
                   1070:                        else if(listLevel == currLevel && listType != currType)
                   1071:                                {
                   1072:                                placeStack.pop();
                   1073:                                placeStack.push(createTiddlyElement(placeStack[placeStack.length-1],listType));
                   1074:                                }
                   1075:                        currLevel = listLevel;
                   1076:                        currType = listType;
                   1077:                        var e = createTiddlyElement(placeStack[placeStack.length-1],itemType);
                   1078:                        w.subWikifyTerm(e,this.termRegExp);
                   1079:                        this.lookaheadRegExp.lastIndex = w.nextMatch;
                   1080:                        lookaheadMatch = this.lookaheadRegExp.exec(w.source);
                   1081:                }
                   1082:        }
                   1083: },
                   1084: 
                   1085: {
                   1086:        name: "quoteByBlock",
                   1087:        match: "^<<<\\n",
                   1088:        termRegExp: /(^<<<(\n|$))/mg,
                   1089:        element: "blockquote",
                   1090:        handler: config.formatterHelpers.createElementAndWikify
                   1091: },
                   1092: 
                   1093: {
                   1094:        name: "quoteByLine",
                   1095:        match: "^>+",
                   1096:        lookaheadRegExp: /^>+/mg,
                   1097:        termRegExp: /(\n)/mg,
                   1098:        element: "blockquote",
                   1099:        handler: function(w)
                   1100:        {
                   1101:                var placeStack = [w.output];
                   1102:                var currLevel = 0;
                   1103:                var newLevel = w.matchLength;
                   1104:                var t;
                   1105:                do {
                   1106:                        if(newLevel > currLevel)
                   1107:                                {
                   1108:                                for(t=currLevel; t<newLevel; t++)
                   1109:                                        placeStack.push(createTiddlyElement(placeStack[placeStack.length-1],this.element));
                   1110:                                }
                   1111:                        else if(newLevel < currLevel)
                   1112:                                {
                   1113:                                for(t=currLevel; t>newLevel; t--)
                   1114:                                        placeStack.pop();
                   1115:                                }
                   1116:                        currLevel = newLevel;
                   1117:                        w.subWikifyTerm(placeStack[placeStack.length-1],this.termRegExp);
                   1118:                        createTiddlyElement(placeStack[placeStack.length-1],"br");
                   1119:                        this.lookaheadRegExp.lastIndex = w.nextMatch;
                   1120:                        var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
                   1121:                        var matched = lookaheadMatch && lookaheadMatch.index == w.nextMatch;
                   1122:                        if(matched)
                   1123:                                {
                   1124:                                newLevel = lookaheadMatch[0].length;
                   1125:                                w.nextMatch += lookaheadMatch[0].length;
                   1126:                                }
                   1127:                } while(matched);
                   1128:        }
                   1129: },
                   1130: 
                   1131: {
                   1132:        name: "rule",
                   1133:        match: "^----+$\\n?",
                   1134:        handler: function(w)
                   1135:        {
                   1136:                createTiddlyElement(w.output,"hr");
                   1137:        }
                   1138: },
                   1139: 
                   1140: {
                   1141:        name: "monospacedByLine",
                   1142:        match: "^\\{\\{\\{\\n",
                   1143:        lookaheadRegExp: /^\{\{\{\n((?:^[^\n]*\n)+?)(^\}\}\}$\n?)/mg,
                   1144:        element: "pre",
                   1145:        handler: config.formatterHelpers.enclosedTextHelper
                   1146: },
                   1147: 
                   1148: {
                   1149:        name: "monospacedByLineForCSS",
                   1150:        match: "^/\\*[\\{]{3}\\*/\\n",
                   1151:        lookaheadRegExp: /\/\*[\{]{3}\*\/\n*((?:^[^\n]*\n)+?)(\n*^\/\*[\}]{3}\*\/$\n?)/mg,
                   1152:        element: "pre",
                   1153:        handler: config.formatterHelpers.enclosedTextHelper
                   1154: },
                   1155: 
                   1156: {
                   1157:        name: "monospacedByLineForPlugin",
                   1158:        match: "^//\\{\\{\\{\\n",
                   1159:        lookaheadRegExp: /^\/\/\{\{\{\n\n*((?:^[^\n]*\n)+?)(\n*^\/\/\}\}\}$\n?)/mg,
                   1160:        element: "pre",
                   1161:        handler: config.formatterHelpers.enclosedTextHelper
                   1162: },
                   1163: 
                   1164: {
                   1165:        name: "monospacedByLineForTemplate",
                   1166:        match: "^<!--[\\{]{3}-->\\n",
                   1167:        lookaheadRegExp: /<!--[\{]{3}-->\n*((?:^[^\n]*\n)+?)(\n*^<!--[\}]{3}-->$\n?)/mg, 
                   1168:        element: "pre",
                   1169:        handler: config.formatterHelpers.enclosedTextHelper
                   1170: },
                   1171: 
                   1172: {
                   1173:        name: "wikifyCommentForPlugin", 
                   1174:        match: "^/\\*\\*\\*\\n",
                   1175:        termRegExp: /(^\*\*\*\/\n)/mg,
                   1176:        handler: function(w)
                   1177:        {
                   1178:                w.subWikifyTerm(w.output,this.termRegExp);
                   1179:        }
                   1180: },
                   1181: 
                   1182: {
                   1183:        name: "wikifyCommentForTemplate", 
                   1184:        match: "^<!---\\n",
                   1185:        termRegExp: /(^--->\n)/mg,
                   1186:        handler: function(w) 
                   1187:        {
                   1188:                w.subWikifyTerm(w.output,this.termRegExp);
                   1189:        }
                   1190: },
                   1191: 
                   1192: {
                   1193:        name: "macro",
                   1194:        match: "<<",
                   1195:        lookaheadRegExp: /<<([^>\s]+)(?:\s*)((?:[^>]|(?:>(?!>)))*)>>/mg,
                   1196:        handler: function(w)
                   1197:        {
                   1198:                this.lookaheadRegExp.lastIndex = w.matchStart;
                   1199:                var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
                   1200:                if(lookaheadMatch && lookaheadMatch.index == w.matchStart && lookaheadMatch[1])
                   1201:                        {
                   1202:                        w.nextMatch = this.lookaheadRegExp.lastIndex;
                   1203:                        invokeMacro(w.output,lookaheadMatch[1],lookaheadMatch[2],w,w.tiddler);
                   1204:                        }
                   1205:        }
                   1206: },
                   1207: 
                   1208: {
                   1209:        name: "prettyLink",
                   1210:        match: "\\[\\[",
                   1211:        lookaheadRegExp: /\[\[(.*?)(?:\|(~)?(.*?))?\]\]/mg,
                   1212:        handler: function(w)
                   1213:        {
                   1214:                this.lookaheadRegExp.lastIndex = w.matchStart;
                   1215:                var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
                   1216:                if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
                   1217:                        {
                   1218:                        var e;
                   1219:                        var text = lookaheadMatch[1];
                   1220:                        if(lookaheadMatch[3])
                   1221:                                {
                   1222:                                // Pretty bracketted link
                   1223:                                var link = lookaheadMatch[3];
                   1224:                                e = (!lookaheadMatch[2] && config.formatterHelpers.isExternalLink(link))
                   1225:                                                ? createExternalLink(w.output,link)
                   1226:                                                : createTiddlyLink(w.output,link,false,null,w.isStatic);
                   1227:                                }
                   1228:                        else
                   1229:                                {
                   1230:                                // Simple bracketted link
                   1231:                                e = createTiddlyLink(w.output,text,false,null,w.isStatic);
                   1232:                                }
                   1233:                        createTiddlyText(e,text);
                   1234:                        w.nextMatch = this.lookaheadRegExp.lastIndex;
                   1235:                        }
                   1236:        }
                   1237: },
                   1238: 
                   1239: {
                   1240:        name: "unWikiLink",
                   1241:        match: config.textPrimitives.unWikiLink+config.textPrimitives.wikiLink,
                   1242:        handler: function(w)
                   1243:        {
                   1244:                w.outputText(w.output,w.matchStart+1,w.nextMatch);
                   1245:        }
                   1246: },
                   1247: 
                   1248: {
                   1249:        name: "wikiLink",
                   1250:        match: config.textPrimitives.wikiLink,
                   1251:        handler: function(w)
                   1252:        {
                   1253:                if(w.matchStart > 0)
                   1254:                        {
                   1255:                        var preRegExp = new RegExp(config.textPrimitives.anyLetterStrict,"mg");
                   1256:                        preRegExp.lastIndex = w.matchStart-1;
                   1257:                        var preMatch = preRegExp.exec(w.source);
                   1258:                        if(preMatch.index == w.matchStart-1)
                   1259:                                {
                   1260:                                w.outputText(w.output,w.matchStart,w.nextMatch);
                   1261:                                return;
                   1262:                                }
                   1263:                        }
                   1264:                if(w.autoLinkWikiWords == true || store.isShadowTiddler(w.matchText))
                   1265:                        {
                   1266:                        var link = createTiddlyLink(w.output,w.matchText,false,null,w.isStatic);
                   1267:                        w.outputText(link,w.matchStart,w.nextMatch);
                   1268:                        }
                   1269:                else
                   1270:                        {
                   1271:                        w.outputText(w.output,w.matchStart,w.nextMatch);
                   1272:                        }
                   1273:        }
                   1274: },
                   1275: 
                   1276: {
                   1277:        name: "urlLink",
                   1278:        match: config.textPrimitives.urlPattern,
                   1279:        handler: function(w)
                   1280:        {
                   1281:                w.outputText(createExternalLink(w.output,w.matchText),w.matchStart,w.nextMatch);
                   1282:        }
                   1283: },
                   1284: 
                   1285: {
                   1286:        name: "image",
                   1287:        match: "\\[[<>]?[Ii][Mm][Gg]\\[",
                   1288:        lookaheadRegExp: /\[(<?)(>?)[Ii][Mm][Gg]\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg,
                   1289:        handler: function(w)
                   1290:        {
                   1291:                this.lookaheadRegExp.lastIndex = w.matchStart;
                   1292:                var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
                   1293:                if(lookaheadMatch && lookaheadMatch.index == w.matchStart) // Simple bracketted link
                   1294:                        {
                   1295:                        var e = w.output;
                   1296:                        if(lookaheadMatch[5])
                   1297:                                {
                   1298:                                var link = lookaheadMatch[5];
                   1299:                                e = config.formatterHelpers.isExternalLink(link) ? createExternalLink(w.output,link) : createTiddlyLink(w.output,link,false,null,w.isStatic);
                   1300:                                addClass(e,"imageLink");
                   1301:                                }
                   1302:                        var img = createTiddlyElement(e,"img");
                   1303:                        if(lookaheadMatch[1])
                   1304:                                img.align = "left";
                   1305:                        else if(lookaheadMatch[2])
                   1306:                                img.align = "right";
                   1307:                        if(lookaheadMatch[3])
                   1308:                                img.title = lookaheadMatch[3];
                   1309:                        img.src = lookaheadMatch[4];
                   1310:                        w.nextMatch = this.lookaheadRegExp.lastIndex;
                   1311:                        }
                   1312:        }
                   1313: },
                   1314: 
                   1315: {
                   1316:        name: "html",
                   1317:        match: "<[Hh][Tt][Mm][Ll]>",
                   1318:        lookaheadRegExp: /<[Hh][Tt][Mm][Ll]>((?:.|\n)*?)<\/[Hh][Tt][Mm][Ll]>/mg,
                   1319:        handler: function(w)
                   1320:        {
                   1321:                this.lookaheadRegExp.lastIndex = w.matchStart;
                   1322:                var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
                   1323:                if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
                   1324:                        {
                   1325:                        createTiddlyElement(w.output,"span").innerHTML = lookaheadMatch[1];
                   1326:                        w.nextMatch = this.lookaheadRegExp.lastIndex;
                   1327:                        }
                   1328:        }
                   1329: },
                   1330: 
                   1331: {
                   1332:        name: "commentByBlock",
                   1333:        match: "/%",
                   1334:        lookaheadRegExp: /\/%((?:.|\n)*?)%\//mg,
                   1335:        handler: function(w)
                   1336:        {
                   1337:                this.lookaheadRegExp.lastIndex = w.matchStart;
                   1338:                var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
                   1339:                if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
                   1340:                        w.nextMatch = this.lookaheadRegExp.lastIndex;
                   1341:        }
                   1342: },
                   1343: 
                   1344: {
                   1345:        name: "boldByChar",
                   1346:        match: "''",
                   1347:        termRegExp: /('')/mg,
                   1348:        element: "strong",
                   1349:        handler: config.formatterHelpers.createElementAndWikify
                   1350: },
                   1351: 
                   1352: {
                   1353:        name: "italicByChar",
                   1354:        match: "//",
                   1355:        termRegExp: /(\/\/)/mg,
                   1356:        element: "em",
                   1357:        handler: config.formatterHelpers.createElementAndWikify
                   1358: },
                   1359: 
                   1360: {
                   1361:        name: "underlineByChar",
                   1362:        match: "__",
                   1363:        termRegExp: /(__)/mg,
                   1364:        element: "u",
                   1365:        handler: config.formatterHelpers.createElementAndWikify
                   1366: },
                   1367: 
                   1368: {
                   1369:        name: "strikeByChar",
                   1370:        match: "--(?!\\s|$)",
                   1371:        termRegExp: /((?!\s)--|(?=\n\n))/mg,
                   1372:        element: "strike",
                   1373:        handler: config.formatterHelpers.createElementAndWikify
                   1374: },
                   1375: 
                   1376: {
                   1377:        name: "superscriptByChar",
                   1378:        match: "\\^\\^",
                   1379:        termRegExp: /(\^\^)/mg,
                   1380:        element: "sup",
                   1381:        handler: config.formatterHelpers.createElementAndWikify
                   1382: },
                   1383: 
                   1384: {
                   1385:        name: "subscriptByChar",
                   1386:        match: "~~",
                   1387:        termRegExp: /(~~)/mg,
                   1388:        element: "sub",
                   1389:        handler: config.formatterHelpers.createElementAndWikify
                   1390: },
                   1391: 
                   1392: {
                   1393:        name: "monospacedByChar",
                   1394:        match: "\\{\\{\\{",
                   1395:        lookaheadRegExp: /\{\{\{((?:.|\n)*?)\}\}\}/mg,
                   1396:        handler: function(w)
                   1397:        {
                   1398:                this.lookaheadRegExp.lastIndex = w.matchStart;
                   1399:                var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
                   1400:                if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
                   1401:                        {
                   1402:                        createTiddlyElement(w.output,"code",null,null,lookaheadMatch[1]);
                   1403:                        w.nextMatch = this.lookaheadRegExp.lastIndex;
                   1404:                        }
                   1405:        }
                   1406: },
                   1407: 
                   1408: {
                   1409:        name: "styleByChar",
                   1410:        match: "@@",
                   1411:        termRegExp: /(@@)/mg,
                   1412:        handler:  function(w)
                   1413:        {
                   1414:                var e = createTiddlyElement(w.output,"span");
                   1415:                var styles = config.formatterHelpers.inlineCssHelper(w);
                   1416:                if(styles.length == 0)
                   1417:                        e.className = "marked";
                   1418:                else
                   1419:                        config.formatterHelpers.applyCssHelper(e,styles);
                   1420:                w.subWikifyTerm(e,this.termRegExp);
                   1421:        }
                   1422: },
                   1423: 
                   1424: {
                   1425:        name: "lineBreak",
                   1426:        match: "\\n|<br ?/?>",
                   1427:        handler: function(w)
                   1428:        {
                   1429:                createTiddlyElement(w.output,"br");
                   1430:        }
                   1431: },
                   1432: 
                   1433: {
                   1434:        name: "rawText",
                   1435:        match: "\\\"{3}|<nowiki>",
                   1436:        lookaheadRegExp: /(?:\"{3}|<nowiki>)((?:.|\n)*?)(?:\"{3}|<\/nowiki>)/mg,
                   1437:        handler: function(w)
                   1438:        {
                   1439:                this.lookaheadRegExp.lastIndex = w.matchStart;
                   1440:                var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
                   1441:                if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
                   1442:                        {
                   1443:                        createTiddlyElement(w.output,"span",null,null,lookaheadMatch[1]);
                   1444:                        w.nextMatch = this.lookaheadRegExp.lastIndex;
                   1445:                        }
                   1446:        }
                   1447: },
                   1448: 
                   1449: {
                   1450:        name: "mdash",
                   1451:        match: "--",
                   1452:        handler: function(w)
                   1453:                {
                   1454:                createTiddlyElement(w.output,"span").innerHTML = "&mdash;";
                   1455:                }
                   1456: },
                   1457: 
                   1458: {
                   1459:        name: "htmlEntitiesEncoding",
                   1460:        match: "(?:(?:&#?[a-zA-Z0-9]{2,8};|.)(?:&#?(?:x0*(?:3[0-6][0-9a-fA-F]|1D[c-fC-F][0-9a-fA-F]|20[d-fD-F][0-9a-fA-F]|FE2[0-9a-fA-F])|0*(?:76[89]|7[7-9][0-9]|8[0-7][0-9]|761[6-9]|76[2-7][0-9]|84[0-3][0-9]|844[0-7]|6505[6-9]|6506[0-9]|6507[0-1]));)+|&#?[a-zA-Z0-9]{2,8};)",
                   1461:        handler: function(w)
                   1462:                {
                   1463:                createTiddlyElement(w.output,"span").innerHTML = w.matchText;
                   1464:                }
                   1465: },
                   1466: 
                   1467: {
                   1468:        name: "customClasses",
                   1469:        match: "\\{\\{",
                   1470:        termRegExp: /(\}\}\})/mg,
                   1471:        lookaheadRegExp: /\{\{[\s]*([\w]+[\s\w]*)[\s]*\{(\n?)/mg,
                   1472:        handler: function(w)
                   1473:        {
                   1474:                this.lookaheadRegExp.lastIndex = w.matchStart;
                   1475:                var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
                   1476:                if(lookaheadMatch)
                   1477:                        {
                   1478:                        var e = createTiddlyElement(w.output,lookaheadMatch[2] == "\n" ? "div" : "span",null,lookaheadMatch[1]);
                   1479:                        w.nextMatch = this.lookaheadRegExp.lastIndex;
                   1480:                        w.subWikifyTerm(e,this.termRegExp);
                   1481:                        }
                   1482:        }
                   1483: }
                   1484: 
                   1485: ];
                   1486: 
                   1487: // ---------------------------------------------------------------------------------
                   1488: // Wikifier
                   1489: // ---------------------------------------------------------------------------------
                   1490: 
                   1491: function getParser(tiddler)
                   1492: {
                   1493:        var f = formatter;
                   1494:        if(tiddler!=null)
                   1495:                {
                   1496:                for(var i in config.parsers)
                   1497:                        {
                   1498:                        if(tiddler.isTagged(config.parsers[i].formatTag))
                   1499:                                {
                   1500:                                f = config.parsers[i];
                   1501:                                break;
                   1502:                                }
                   1503:                        }
                   1504:                }
                   1505:        return f;
                   1506: }
                   1507: 
                   1508: function wikify(source,output,highlightRegExp,tiddler)
                   1509: {
                   1510:        if(source && source != "")
                   1511:                {
                   1512:                var wikifier = new Wikifier(source,getParser(tiddler),highlightRegExp,tiddler);
                   1513:                wikifier.subWikifyUnterm(output);
                   1514:                }
                   1515: }
                   1516: 
                   1517: function wikifyStatic(source,highlightRegExp,tiddler)
                   1518: {
                   1519:        var e = createTiddlyElement(document.body,"div");
                   1520:        e.style.display = "none";
                   1521:        var html = "";
                   1522:        if(source && source != "")
                   1523:                {
                   1524:                var wikifier = new Wikifier(source,getParser(tiddler),highlightRegExp,tiddler);
                   1525:                wikifier.isStatic = true;
                   1526:                wikifier.subWikifyUnterm(e);
                   1527:                html = e.innerHTML;
                   1528:                e.parentNode.removeChild(e);
                   1529:                }
                   1530:        return html;
                   1531: }
                   1532: 
                   1533: // Wikify a named tiddler to plain text
                   1534: function wikifyPlain(title)
                   1535: {
                   1536:        if(store.tiddlerExists(title) || store.isShadowTiddler(title))
                   1537:                {
                   1538:                var wikifier = new Wikifier(store.getTiddlerText(title),formatter,null,store.getTiddler(title));
                   1539:                return wikifier.wikifyPlain();
                   1540:                }
                   1541:        else
                   1542:                return "";
                   1543: }
                   1544: 
                   1545: // Highlight plain text into an element
                   1546: function highlightify(source,output,highlightRegExp)
                   1547: {
                   1548:        if(source && source != "")
                   1549:                {
                   1550:                var wikifier = new Wikifier(source,formatter,highlightRegExp);
                   1551:                wikifier.outputText(output,0,source.length);
                   1552:                }
                   1553: }
                   1554: 
                   1555: // Construct a wikifier object
                   1556: // source - source string that's going to be wikified
                   1557: // formatter - Formatter() object containing the list of formatters to be used
                   1558: // highlightRegExp - regular expression of the text string to highlight
                   1559: // tiddler - reference to the tiddler that's taken to be the container for this wikification
                   1560: function Wikifier(source,formatter,highlightRegExp,tiddler)
                   1561: {
                   1562:        this.source = source;
                   1563:        this.output = null;
                   1564:        this.formatter = formatter;
                   1565:        this.nextMatch = 0;
                   1566:        this.autoLinkWikiWords = tiddler && tiddler.autoLinkWikiWords() == false ? false : true;
                   1567:        this.highlightRegExp = highlightRegExp;
                   1568:        this.highlightMatch = null;
                   1569:        this.isStatic = false;
                   1570:        if(highlightRegExp)
                   1571:                {
                   1572:                highlightRegExp.lastIndex = 0;
                   1573:                this.highlightMatch = highlightRegExp.exec(source);
                   1574:                }
                   1575:        this.tiddler = tiddler;
                   1576: }
                   1577: 
                   1578: Wikifier.prototype.wikifyPlain = function()
                   1579: {
                   1580:        var e = createTiddlyElement(document.body,"div");
                   1581:        e.style.display = "none";
                   1582:        this.subWikify(e);
                   1583:        var text = getPlainText(e);
                   1584:        e.parentNode.removeChild(e);
                   1585:        return text;
                   1586: }
                   1587: 
                   1588: Wikifier.prototype.subWikify = function(output,terminator)
                   1589: {
                   1590:        // Handle the terminated and unterminated cases separately
                   1591:        if (terminator)
                   1592:                this.subWikifyTerm(output,new RegExp("(" + terminator + ")","mg"));
                   1593:        else
                   1594:                this.subWikifyUnterm(output);
                   1595: }
                   1596: 
                   1597: Wikifier.prototype.subWikifyUnterm = function(output)
                   1598: {
                   1599:        // subWikify() can be indirectly recursive, so we need to save the old output pointer
                   1600:        var oldOutput = this.output;
                   1601:        this.output = output;
                   1602:        // Get the first match
                   1603:        this.formatter.formatterRegExp.lastIndex = this.nextMatch;
                   1604:        var formatterMatch = this.formatter.formatterRegExp.exec(this.source);
                   1605:        while(formatterMatch)
                   1606:                {
                   1607:                // Output any text before the match
                   1608:                if(formatterMatch.index > this.nextMatch)
                   1609:                        this.outputText(this.output,this.nextMatch,formatterMatch.index);
                   1610:                // Set the match parameters for the handler
                   1611:                this.matchStart = formatterMatch.index;
                   1612:                this.matchLength = formatterMatch[0].length;
                   1613:                this.matchText = formatterMatch[0];
                   1614:                this.nextMatch = this.formatter.formatterRegExp.lastIndex;
                   1615:                // Figure out which formatter matched and call its handler
                   1616:                for(var t=1; t<formatterMatch.length; t++)
                   1617:                        {
                   1618:                        if(formatterMatch[t])
                   1619:                                {
                   1620:                                this.formatter.formatters[t-1].handler(this);
                   1621:                                this.formatter.formatterRegExp.lastIndex = this.nextMatch;
                   1622:                                break;
                   1623:                                }
                   1624:                        }
                   1625:                // Get the next match
                   1626:                formatterMatch = this.formatter.formatterRegExp.exec(this.source);
                   1627:                }
                   1628:        // Output any text after the last match
                   1629:        if(this.nextMatch < this.source.length)
                   1630:                {
                   1631:                this.outputText(this.output,this.nextMatch,this.source.length);
                   1632:                this.nextMatch = this.source.length;
                   1633:                }
                   1634:        // Restore the output pointer
                   1635:        this.output = oldOutput;
                   1636: }
                   1637: 
                   1638: Wikifier.prototype.subWikifyTerm = function(output,terminatorRegExp)
                   1639: {
                   1640:        // subWikify() can be indirectly recursive, so we need to save the old output pointer
                   1641:        var oldOutput = this.output;
                   1642:        this.output = output;
                   1643:        // Get the first matches for the formatter and terminator RegExps
                   1644:        terminatorRegExp.lastIndex = this.nextMatch;
                   1645:        var terminatorMatch = terminatorRegExp.exec(this.source);
                   1646:        this.formatter.formatterRegExp.lastIndex = this.nextMatch;
                   1647:        var formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
                   1648:        while(terminatorMatch || formatterMatch)
                   1649:                {
                   1650:                // Check for a terminator match  before the next formatter match
                   1651:                if(terminatorMatch && (!formatterMatch || terminatorMatch.index <= formatterMatch.index))
                   1652:                        {
                   1653:                        // Output any text before the match
                   1654:                        if(terminatorMatch.index > this.nextMatch)
                   1655:                                this.outputText(this.output,this.nextMatch,terminatorMatch.index);
                   1656:                        // Set the match parameters
                   1657:                        this.matchText = terminatorMatch[1];
                   1658:                        this.matchLength = terminatorMatch[1].length;
                   1659:                        this.matchStart = terminatorMatch.index;
                   1660:                        this.nextMatch = this.matchStart + this.matchLength;
                   1661:                        // Restore the output pointer
                   1662:                        this.output = oldOutput;
                   1663:                        return;
                   1664:                        }
                   1665:                // It must be a formatter match; output any text before the match
                   1666:                if(formatterMatch.index > this.nextMatch)
                   1667:                        this.outputText(this.output,this.nextMatch,formatterMatch.index);
                   1668:                // Set the match parameters
                   1669:                this.matchStart = formatterMatch.index;
                   1670:                this.matchLength = formatterMatch[0].length;
                   1671:                this.matchText = formatterMatch[0];
                   1672:                this.nextMatch = this.formatter.formatterRegExp.lastIndex;
                   1673:                // Figure out which formatter matched and call its handler
                   1674:                for(var t=1; t<formatterMatch.length; t++)
                   1675:                        {
                   1676:                        if(formatterMatch[t])
                   1677:                                {
                   1678:                                this.formatter.formatters[t-1].handler(this);
                   1679:                                this.formatter.formatterRegExp.lastIndex = this.nextMatch;
                   1680:                                break;
                   1681:                                }
                   1682:                        }
                   1683:                // Get the next match
                   1684:                terminatorRegExp.lastIndex = this.nextMatch;
                   1685:                terminatorMatch = terminatorRegExp.exec(this.source);
                   1686:                formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
                   1687:                }
                   1688:        // Output any text after the last match
                   1689:        if(this.nextMatch < this.source.length)
                   1690:                {
                   1691:                this.outputText(this.output,this.nextMatch,this.source.length);
                   1692:                this.nextMatch = this.source.length;
                   1693:                }
                   1694:        // Restore the output pointer
                   1695:        this.output = oldOutput;
                   1696: }
                   1697: 
                   1698: Wikifier.prototype.outputText = function(place,startPos,endPos)
                   1699: {
                   1700:        // Check for highlights
                   1701:        while(this.highlightMatch && (this.highlightRegExp.lastIndex > startPos) && (this.highlightMatch.index < endPos) && (startPos < endPos))
                   1702:                {
                   1703:                // Deal with any plain text before the highlight
                   1704:                if(this.highlightMatch.index > startPos)
                   1705:                        {
                   1706:                        createTiddlyText(place,this.source.substring(startPos,this.highlightMatch.index));
                   1707:                        startPos = this.highlightMatch.index;
                   1708:                        }
                   1709:                // Deal with the highlight
                   1710:                var highlightEnd = Math.min(this.highlightRegExp.lastIndex,endPos);
                   1711:                var theHighlight = createTiddlyElement(place,"span",null,"highlight",this.source.substring(startPos,highlightEnd));
                   1712:                startPos = highlightEnd;
                   1713:                // Nudge along to the next highlight if we're done with this one
                   1714:                if(startPos >= this.highlightRegExp.lastIndex)
                   1715:                        this.highlightMatch = this.highlightRegExp.exec(this.source);
                   1716:                }
                   1717:        // Do the unhighlighted text left over
                   1718:        if(startPos < endPos)
                   1719:                {
                   1720:                createTiddlyText(place,this.source.substring(startPos,endPos));
                   1721:                }
                   1722: }
                   1723: 
                   1724: // ---------------------------------------------------------------------------------
                   1725: // Macro definitions
                   1726: // ---------------------------------------------------------------------------------
                   1727: 
                   1728: config.macros.today.handler = function(place,macroName,params)
                   1729: {
                   1730:        var now = new Date();
                   1731:        var text;
                   1732:        if(params[0])
                   1733:                text = now.formatString(params[0].trim());
                   1734:        else
                   1735:                text = now.toLocaleString();
                   1736:        createTiddlyElement(place,"span",null,null,text);
                   1737: }
                   1738: 
                   1739: config.macros.version.handler = function(place)
                   1740: {
                   1741:        createTiddlyElement(place,"span",null,null,version.major + "." + version.minor + "." + version.revision + (version.beta ? " (beta " + version.beta + ")" : ""));
                   1742: }
                   1743: 
                   1744: config.macros.list.handler = function(place,macroName,params)
                   1745: {
                   1746:        var type = params[0] ? params[0] : "all";
                   1747:        var theList = document.createElement("ul");
                   1748:        place.appendChild(theList);
                   1749:        if(this[type].prompt)
                   1750:                createTiddlyElement(theList,"li",null,"listTitle",this[type].prompt);
                   1751:        var results;
                   1752:        if(this[type].handler)
                   1753:                results = this[type].handler(params);
                   1754:        for(var t = 0; t < results.length; t++)
                   1755:                {
                   1756:                var theListItem = document.createElement("li")
                   1757:                theList.appendChild(theListItem);
                   1758:                if(typeof results[t] == "string")
                   1759:                        createTiddlyLink(theListItem,results[t],true);
                   1760:                else
                   1761:                        createTiddlyLink(theListItem,results[t].title,true);
                   1762:                }
                   1763: }
                   1764: 
                   1765: config.macros.list.all.handler = function(params)
                   1766: {
                   1767:        return store.reverseLookup("tags","excludeLists",false,"title");
                   1768: }
                   1769: 
                   1770: config.macros.list.missing.handler = function(params)
                   1771: {
                   1772:        return store.getMissingLinks();
                   1773: }
                   1774: 
                   1775: config.macros.list.orphans.handler = function(params)
                   1776: {
                   1777:        return store.getOrphans();
                   1778: }
                   1779: 
                   1780: config.macros.list.shadowed.handler = function(params)
                   1781: {
                   1782:        return store.getShadowed();
                   1783: }
                   1784: 
                   1785: config.macros.allTags.handler = function(place,macroName,params)
                   1786: {
                   1787:        var tags = store.getTags();
                   1788:        var theDateList = createTiddlyElement(place,"ul");
                   1789:        if(tags.length == 0)
                   1790:                createTiddlyElement(theDateList,"li",null,"listTitle",this.noTags);
                   1791:        for(var t=0; t<tags.length; t++)
                   1792:                {
                   1793:                var theListItem =createTiddlyElement(theDateList,"li");
                   1794:                var theTag = createTiddlyButton(theListItem,tags[t][0] + " (" + tags[t][1] + ")",this.tooltip.format([tags[t][0]]),onClickTag);
                   1795:                theTag.setAttribute("tag",tags[t][0]);
                   1796:                }
                   1797: }
                   1798: 
                   1799: config.macros.timeline.handler = function(place,macroName,params)
                   1800: {
                   1801:        var field = params[0] ? params[0] : "modified";
                   1802:        var tiddlers = store.reverseLookup("tags","excludeLists",false,field);
                   1803:        var lastDay = "";
                   1804:        var last = params[1] ? tiddlers.length-Math.min(tiddlers.length,parseInt(params[1])) : 0;
                   1805:        for(var t=tiddlers.length-1; t>=last; t--)
                   1806:                {
                   1807:                var tiddler = tiddlers[t];
                   1808:                var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
                   1809:                if(theDay != lastDay)
                   1810:                        {
                   1811:                        var theDateList = document.createElement("ul");
                   1812:                        place.appendChild(theDateList);
                   1813:                        createTiddlyElement(theDateList,"li",null,"listTitle",tiddler[field].formatString(this.dateFormat));
                   1814:                        lastDay = theDay;
                   1815:                        }
                   1816:                var theDateListItem = createTiddlyElement(theDateList,"li",null,"listLink");
                   1817:                theDateListItem.appendChild(createTiddlyLink(place,tiddler.title,true));
                   1818:                }
                   1819: }
                   1820: 
                   1821: config.macros.search.handler = function(place,macroName,params)
                   1822: {
                   1823:        var searchTimeout = null;
                   1824:        var btn = createTiddlyButton(place,this.label,this.prompt,this.onClick);
                   1825:        var txt = createTiddlyElement(place,"input",null,"txtOptionInput");
                   1826:        if(params[0])
                   1827:                txt.value = params[0];
                   1828:        txt.onkeyup = this.onKeyPress;
                   1829:        txt.onfocus = this.onFocus;
                   1830:        txt.setAttribute("size",this.sizeTextbox);
                   1831:        txt.setAttribute("accessKey",this.accessKey);
                   1832:        txt.setAttribute("autocomplete","off");
                   1833:        txt.setAttribute("lastSearchText","");
                   1834:        if(config.browser.isSafari)
                   1835:                {
                   1836:                txt.setAttribute("type","search");
                   1837:                txt.setAttribute("results","5");
                   1838:                }
                   1839:        else
                   1840:                txt.setAttribute("type","text");
                   1841: }
                   1842: 
                   1843: // Global because there's only ever one outstanding incremental search timer
                   1844: config.macros.search.timeout = null;
                   1845: 
                   1846: config.macros.search.doSearch = function(txt)
                   1847: {
                   1848:        if(txt.value.length > 0)
                   1849:                {
                   1850:                story.search(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch);
                   1851:                txt.setAttribute("lastSearchText",txt.value);
                   1852:                }
                   1853: }
                   1854: 
                   1855: config.macros.search.onClick = function(e)
                   1856: {
                   1857:        config.macros.search.doSearch(this.nextSibling);
                   1858:        return false;
                   1859: }
                   1860: 
                   1861: config.macros.search.onKeyPress = function(e)
                   1862: {
                   1863:        if(!e) var e = window.event;
                   1864:        switch(e.keyCode)
                   1865:                {
                   1866:                case 13: // Ctrl-Enter
                   1867:                case 10: // Ctrl-Enter on IE PC
                   1868:                        config.macros.search.doSearch(this);
                   1869:                        break;
                   1870:                case 27: // Escape
                   1871:                        this.value = "";
                   1872:                        clearMessage();
                   1873:                        break;
                   1874:                }
                   1875:        if(this.value.length > 2)
                   1876:                {
                   1877:                if(this.value != this.getAttribute("lastSearchText"))
                   1878:                        {
                   1879:                        if(config.macros.search.timeout)
                   1880:                                clearTimeout(config.macros.search.timeout);
                   1881:                        var txt = this;
                   1882:                        config.macros.search.timeout = setTimeout(function() {config.macros.search.doSearch(txt);},500);
                   1883:                        }
                   1884:                }
                   1885:        else
                   1886:                {
                   1887:                if(config.macros.search.timeout)
                   1888:                        clearTimeout(config.macros.search.timeout);
                   1889:                }
                   1890: }
                   1891: 
                   1892: config.macros.search.onFocus = function(e)
                   1893: {
                   1894:        this.select();
                   1895: }
                   1896: 
                   1897: config.macros.tiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
                   1898: {
                   1899:        params = paramString.parseParams("name",null,true,false,true);
                   1900:        var names = params[0]["name"];
                   1901:        var tiddlerName = names[0];
                   1902:        var className = names[1] ? names[1] : null;
                   1903:        var args = params[0]["with"];
                   1904:        var wrapper = createTiddlyElement(place,"span",null,className);
                   1905:        if(!args)
                   1906:                {
                   1907:                wrapper.setAttribute("refresh","content");
                   1908:                wrapper.setAttribute("tiddler",tiddlerName);
                   1909:                }
                   1910:        var text = store.getTiddlerText(tiddlerName);
                   1911:        if(text)
                   1912:                {
                   1913:                var stack = config.macros.tiddler.tiddlerStack;
                   1914:                if(stack.indexOf(tiddlerName) !== -1)
                   1915:                        return;
                   1916:                stack.push(tiddlerName);
                   1917:                try
                   1918:                        {
                   1919:                        var n = args ? Math.min(args.length,9) : 0;
                   1920:                        for(var i=0; i<n; i++) 
                   1921:                                {
                   1922:                                var placeholderRE = new RegExp("\\$" + (i + 1),"mg");
                   1923:                                text = text.replace(placeholderRE,args[i]);
                   1924:                                }
                   1925:                        config.macros.tiddler.renderText(wrapper,text,tiddlerName,params);
                   1926:                        }
                   1927:                finally
                   1928:                        {
                   1929:                        stack.pop();
                   1930:                        }
                   1931:                }
                   1932: }
                   1933: 
                   1934: config.macros.tiddler.renderText = function(place,text,tiddlerName,params) 
                   1935: {
                   1936:        wikify(text,place,null,store.getTiddler(tiddlerName));
                   1937: }
                   1938: 
                   1939: config.macros.tiddler.tiddlerStack = [];
                   1940: 
                   1941: config.macros.tag.handler = function(place,macroName,params)
                   1942: {
                   1943:        createTagButton(place,params[0]);
                   1944: }
                   1945: 
                   1946: config.macros.tags.handler = function(place,macroName,params,wikifier,paramString,tiddler)
                   1947: {
                   1948:        params = paramString.parseParams("anon",null,true,false,false);
                   1949:        var theList = createTiddlyElement(place,"ul");
                   1950:        var title = getParam(params,"anon","");
                   1951:        if(title && store.tiddlerExists(title))
                   1952:                tiddler = store.getTiddler(title);
                   1953:        var sep = getParam(params,"sep"," ");
                   1954:        var lingo = config.views.wikified.tag;
                   1955:        var prompt = tiddler.tags.length == 0 ? lingo.labelNoTags : lingo.labelTags;
                   1956:        createTiddlyElement(theList,"li",null,"listTitle",prompt.format([tiddler.title]));
                   1957:        for(var t=0; t<tiddler.tags.length; t++)
                   1958:                {
                   1959:                createTagButton(createTiddlyElement(theList,"li"),tiddler.tags[t],tiddler.title);
                   1960:                if(t<tiddler.tags.length-1)
                   1961:                        createTiddlyText(theList,sep);
                   1962:                }
                   1963: }
                   1964: 
                   1965: config.macros.tagging.handler = function(place,macroName,params,wikifier,paramString,tiddler)
                   1966: {
                   1967:        params = paramString.parseParams("anon",null,true,false,false);
                   1968:        var theList = createTiddlyElement(place,"ul");
                   1969:        var title = getParam(params,"anon","");
                   1970:        if(title == "" && tiddler instanceof Tiddler)
                   1971:                title = tiddler.title;
                   1972:        var sep = getParam(params,"sep"," ");
                   1973:        theList.setAttribute("title",this.tooltip.format([title]));
                   1974:        var tagged = store.getTaggedTiddlers(title);
                   1975:        var prompt = tagged.length == 0 ? this.labelNotTag : this.label;
                   1976:        createTiddlyElement(theList,"li",null,"listTitle",prompt.format([title,tagged.length]));
                   1977:        for(var t=0; t<tagged.length; t++)
                   1978:                {
                   1979:                createTiddlyLink(createTiddlyElement(theList,"li"),tagged[t].title,true);
                   1980:                if(t<tagged.length-1)
                   1981:                        createTiddlyText(theList,sep);
                   1982:                }
                   1983: }
                   1984: 
                   1985: config.macros.closeAll.handler = function(place)
                   1986: {
                   1987:        createTiddlyButton(place,this.label,this.prompt,this.onClick);
                   1988: }
                   1989: 
                   1990: config.macros.closeAll.onClick = function(e)
                   1991: {
                   1992:        story.closeAllTiddlers();
                   1993:        return false;
                   1994: }
                   1995: 
                   1996: config.macros.permaview.handler = function(place)
                   1997: {
                   1998:        createTiddlyButton(place,this.label,this.prompt,this.onClick);
                   1999: }
                   2000: 
                   2001: config.macros.permaview.onClick = function(e)
                   2002: {
                   2003:        story.permaView();
                   2004:        return false;
                   2005: }
                   2006: 
                   2007: config.macros.saveChanges.handler = function(place)
                   2008: {
                   2009:        if(!readOnly)
                   2010:                createTiddlyButton(place,this.label,this.prompt,this.onClick,null,null,this.accessKey);
                   2011: }
                   2012: 
                   2013: config.macros.saveChanges.onClick = function(e)
                   2014: {
                   2015:        saveChanges();
                   2016:        return false;
                   2017: }
                   2018: 
                   2019: config.macros.slider.onClickSlider = function(e)
                   2020: {
                   2021:        if(!e) var e = window.event;
                   2022:        var n = this.nextSibling;
                   2023:        var cookie = n.getAttribute("cookie");
                   2024:        var isOpen = n.style.display != "none";
                   2025:        if(anim && config.options.chkAnimate)
                   2026:                anim.startAnimating(new Slider(n,!isOpen,e.shiftKey || e.altKey,"none"));
                   2027:        else
                   2028:                n.style.display = isOpen ? "none" : "block";
                   2029:        config.options[cookie] = !isOpen;
                   2030:        saveOptionCookie(cookie);
                   2031:        return false;
                   2032: }
                   2033: 
                   2034: config.macros.slider.createSlider = function(place,cookie,title,tooltip)
                   2035: {
                   2036:        var cookie = cookie ? cookie : "";
                   2037:        var btn = createTiddlyButton(place,title,tooltip,this.onClickSlider);
                   2038:        var panel = createTiddlyElement(null,"div",null,"sliderPanel");
                   2039:        panel.setAttribute("cookie",cookie);
                   2040:        panel.style.display = config.options[cookie] ? "block" : "none";
                   2041:        place.appendChild(panel);
                   2042:        return panel;
                   2043: }
                   2044: 
                   2045: config.macros.slider.handler = function(place,macroName,params)
                   2046: {
                   2047:        var panel = this.createSlider(place,params[0],params[2],params[3]);
                   2048:        var text = store.getTiddlerText(params[1]);
                   2049:        panel.setAttribute("refresh", "content");
                   2050:        panel.setAttribute("tiddler", params[1]);
                   2051:        if(text)
                   2052:                wikify(text,panel,null,store.getTiddler(params[1]));
                   2053: }
                   2054: 
                   2055: config.macros.option.onChangeOption = function(e)
                   2056: {
                   2057:        var opt = this.getAttribute("option");
                   2058:        var elementType,valueField;
                   2059:        if(opt)
                   2060:                {
                   2061:                switch(opt.substr(0,3))
                   2062:                        {
                   2063:                        case "txt":
                   2064:                                elementType = "input";
                   2065:                                valueField = "value";
                   2066:                                break;
                   2067:                        case "chk":
                   2068:                                elementType = "input";
                   2069:                                valueField = "checked";
                   2070:                                break;
                   2071:                        }
                   2072:                config.options[opt] = this[valueField];
                   2073:                saveOptionCookie(opt);
                   2074:                var nodes = document.getElementsByTagName(elementType);
                   2075:                for(var t=0; t<nodes.length; t++)
                   2076:                        {
                   2077:                        var optNode = nodes[t].getAttribute("option");
                   2078:                        if(opt == optNode)
                   2079:                                nodes[t][valueField] = this[valueField];
                   2080:                        }
                   2081:                }
                   2082:        return(true);
                   2083: }
                   2084: 
                   2085: config.macros.option.handler = function(place,macroName,params)
                   2086: {
                   2087:        var opt = params[0];
                   2088:        if(config.options[opt] == undefined)
                   2089:                return;
                   2090:        var c;
                   2091:        switch(opt.substr(0,3))
                   2092:                {
                   2093:                case "txt":
                   2094:                        c = document.createElement("input");
                   2095:                        c.onkeyup = this.onChangeOption;
                   2096:                        c.setAttribute("option",opt);
                   2097:                        c.className = "txtOptionInput";
                   2098:                        place.appendChild(c);
                   2099:                        c.value = config.options[opt];
                   2100:                        break;
                   2101:                case "chk":
                   2102:                        c = document.createElement("input");
                   2103:                        c.setAttribute("type","checkbox");
                   2104:                        c.onclick = this.onChangeOption;
                   2105:                        c.setAttribute("option",opt);
                   2106:                        c.className = "chkOptionInput";
                   2107:                        place.appendChild(c);
                   2108:                        c.checked = config.options[opt];
                   2109:                        break;
                   2110:                }
                   2111: }
                   2112: 
                   2113: 
                   2114: 
                   2115: config.macros.newTiddler.createNewTiddlerButton = function(place,title,params,label,prompt,accessKey,newFocus,isJournal)
                   2116: {
                   2117:        var tags = [];
                   2118:        for(var t=1; t<params.length; t++)
                   2119:                if((params[t].name == "anon" && t != 1) || (params[t].name == "tag"))
                   2120:                        tags.push(params[t].value);
                   2121:        label = getParam(params,"label",label);
                   2122:        prompt = getParam(params,"prompt",prompt);
                   2123:        accessKey = getParam(params,"accessKey",accessKey);
                   2124:        newFocus = getParam(params,"focus",newFocus);
                   2125:        var btn = createTiddlyButton(place,label,prompt,this.onClickNewTiddler,null,null,accessKey);
                   2126:        btn.setAttribute("newTitle",title);
                   2127:        btn.setAttribute("isJournal",isJournal);
                   2128:        btn.setAttribute("params",tags.join("|"));
                   2129:        btn.setAttribute("newFocus",newFocus);
                   2130:        btn.setAttribute("newTemplate",getParam(params,"template",DEFAULT_EDIT_TEMPLATE));
                   2131:        var text = getParam(params,"text");
                   2132:        if(text !== undefined) 
                   2133:                btn.setAttribute("newText",text);
                   2134:        return btn;
                   2135: }
                   2136: 
                   2137: config.macros.newTiddler.onClickNewTiddler = function()
                   2138: {
                   2139:        var title = this.getAttribute("newTitle");
                   2140:        if(this.getAttribute("isJournal"))
                   2141:                {
                   2142:                var now = new Date();
                   2143:                title = now.formatString(title.trim());
                   2144:                }
                   2145:        var params = this.getAttribute("params").split("|");
                   2146:        var focus = this.getAttribute("newFocus");
                   2147:        var template = this.getAttribute("newTemplate");
                   2148:        story.displayTiddler(null,title,template);
                   2149:        var text = this.getAttribute("newText");
                   2150:        if(typeof text == "string")
                   2151:                story.getTiddlerField(title,"text").value = text.format([title]);
                   2152:        for(var t=0;t<params.length;t++)
                   2153:                story.setTiddlerTag(title,params[t],+1);
                   2154:        story.focusTiddler(title,focus);
                   2155:        return false;
                   2156: }
                   2157: 
                   2158: config.macros.newTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
                   2159: {
                   2160:        if(!readOnly)
                   2161:                {
                   2162:                params = paramString.parseParams("anon",null,true,false,false);
                   2163:                var title = params[1] && params[1].name == "anon" ? params[1].value : this.title;
                   2164:                title = getParam(params,"title",title);
                   2165:                this.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"title",false);
                   2166:                }
                   2167: }
                   2168: 
                   2169: config.macros.newJournal.handler = function(place,macroName,params,wikifier,paramString,tiddler)
                   2170: {
                   2171:        if(!readOnly)
                   2172:                {
                   2173:                params = paramString.parseParams("anon",null,true,false,false);
                   2174:                var title = params[1] && params[1].name == "anon" ? params[1].value : "";
                   2175:                title = getParam(params,"title",title);
                   2176:                config.macros.newTiddler.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"text",true);
                   2177:                }
                   2178: }
                   2179: 
                   2180: config.macros.sparkline.handler = function(place,macroName,params)
                   2181: {
                   2182:        var data = [];
                   2183:        var min = 0;
                   2184:        var max = 0;
                   2185:        for(var t=0; t<params.length; t++)
                   2186:                {
                   2187:                var v = parseInt(params[t]);
                   2188:                if(v < min)
                   2189:                        min = v;
                   2190:                if(v > max)
                   2191:                        max = v;
                   2192:                data.push(v);
                   2193:                }
                   2194:        if(data.length < 1)
                   2195:                return;
                   2196:        var box = createTiddlyElement(place,"span",null,"sparkline",String.fromCharCode(160));
                   2197:        box.title = data.join(",");
                   2198:        var w = box.offsetWidth;
                   2199:        var h = box.offsetHeight;
                   2200:        box.style.paddingRight = (data.length * 2 - w) + "px";
                   2201:        box.style.position = "relative";
                   2202:        for(var d=0; d<data.length; d++)
                   2203:                {
                   2204:                var tick = document.createElement("img");
                   2205:                tick.border = 0;
                   2206:                tick.className = "sparktick";
                   2207:                tick.style.position = "absolute";
                   2208:                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";
                   2209:                tick.style.left = d*2 + "px";
                   2210:                tick.style.width = "2px";
                   2211:                var v = Math.floor(((data[d] - min)/(max-min)) * h);
                   2212:                tick.style.top = (h-v) + "px";
                   2213:                tick.style.height = v + "px";
                   2214:                box.appendChild(tick);
                   2215:                }
                   2216: }
                   2217: 
                   2218: config.macros.tabs.handler = function(place,macroName,params)
                   2219: {
                   2220:        var cookie = params[0];
                   2221:        var numTabs = (params.length-1)/3;
                   2222:        var wrapper = createTiddlyElement(null,"div",null,cookie);
                   2223:        var tabset = createTiddlyElement(wrapper,"div",null,"tabset");
                   2224:        tabset.setAttribute("cookie",cookie);
                   2225:        var validTab = false;
                   2226:        for(var t=0; t<numTabs; t++)
                   2227:                {
                   2228:                var label = params[t*3+1];
                   2229:                var prompt = params[t*3+2];
                   2230:                var content = params[t*3+3];
                   2231:                var tab = createTiddlyButton(tabset,label,prompt,this.onClickTab,"tab tabUnselected");
                   2232:                tab.setAttribute("tab",label);
                   2233:                tab.setAttribute("content",content);
                   2234:                tab.title = prompt;
                   2235:                if(config.options[cookie] == label)
                   2236:                        validTab = true;
                   2237:                }
                   2238:        if(!validTab)
                   2239:                config.options[cookie] = params[1];
                   2240:        place.appendChild(wrapper);
                   2241:        this.switchTab(tabset,config.options[cookie]);
                   2242: }
                   2243: 
                   2244: config.macros.tabs.onClickTab = function(e)
                   2245: {
                   2246:        config.macros.tabs.switchTab(this.parentNode,this.getAttribute("tab"));
                   2247:        return false;
                   2248: }
                   2249: 
                   2250: config.macros.tabs.switchTab = function(tabset,tab)
                   2251: {
                   2252:        var cookie = tabset.getAttribute("cookie");
                   2253:        var theTab = null
                   2254:        var nodes = tabset.childNodes;
                   2255:        for(var t=0; t<nodes.length; t++)
                   2256:                if(nodes[t].getAttribute && nodes[t].getAttribute("tab") == tab)
                   2257:                        {
                   2258:                        theTab = nodes[t];
                   2259:                        theTab.className = "tab tabSelected";
                   2260:                        }
                   2261:                else
                   2262:                        nodes[t].className = "tab tabUnselected"
                   2263:        if(theTab)
                   2264:                {
                   2265:                if(tabset.nextSibling && tabset.nextSibling.className == "tabContents")
                   2266:                        tabset.parentNode.removeChild(tabset.nextSibling);
                   2267:                var tabContent = createTiddlyElement(null,"div",null,"tabContents");
                   2268:                tabset.parentNode.insertBefore(tabContent,tabset.nextSibling);
                   2269:                var contentTitle = theTab.getAttribute("content");
                   2270:                wikify(store.getTiddlerText(contentTitle),tabContent,null,store.getTiddler(contentTitle));
                   2271:                if(cookie)
                   2272:                        {
                   2273:                        config.options[cookie] = tab;
                   2274:                        saveOptionCookie(cookie);
                   2275:                        }
                   2276:                }
                   2277: }
                   2278: 
                   2279: // <<gradient [[tiddler name]] vert|horiz rgb rgb rgb rgb... >>
                   2280: config.macros.gradient.handler = function(place,macroName,params,wikifier)
                   2281: {
                   2282:        var terminator = ">>";
                   2283:        var panel;
                   2284:        if(wikifier)
                   2285:                panel = createTiddlyElement(place,"div",null,"gradient");
                   2286:        else
                   2287:                panel = place;
                   2288:        panel.style.position = "relative";
                   2289:        panel.style.overflow = "hidden";
                   2290:        panel.style.zIndex = "0";
                   2291:        var t;
                   2292:        if(wikifier)
                   2293:                {
                   2294:                var styles = config.formatterHelpers.inlineCssHelper(wikifier);
                   2295:                config.formatterHelpers.applyCssHelper(panel,styles);
                   2296:                }
                   2297:        var colours = [];
                   2298:        for(t=1; t<params.length; t++)
                   2299:                {
                   2300:                var c = new RGB(params[t]);
                   2301:                if(c)
                   2302:                        colours.push(c);
                   2303:                }
                   2304:        drawGradient(panel,params[0] != "vert",colours);
                   2305:        if(wikifier)
                   2306:                wikifier.subWikify(panel,terminator);
                   2307:        if(document.all)
                   2308:                {
                   2309:                panel.style.height = "100%";
                   2310:                panel.style.width = "100%";
                   2311:                }
                   2312: }
                   2313: 
                   2314: config.macros.message.handler = function(place,macroName,params)
                   2315: {
                   2316:        if(params[0])
                   2317:                {
                   2318:                var m = config;
                   2319:                var p = params[0].split(".");
                   2320:                for(var t=0; t<p.length; t++)
                   2321:                        {
                   2322:                        if(p[t] in m)
                   2323:                                m = m[p[t]];
                   2324:                        else
                   2325:                                break;
                   2326:                        }
                   2327:                createTiddlyText(place,m.toString().format(params.splice(1)));
                   2328:                }
                   2329: }
                   2330: 
                   2331: config.macros.view.handler = function(place,macroName,params,wikifier,paramString,tiddler)
                   2332: {
                   2333:        if((tiddler instanceof Tiddler) && params[0])
                   2334:                {
                   2335:                var value = store.getValue(tiddler,params[0]);
                   2336:                if(value != undefined)
                   2337:                        switch(params[1])
                   2338:                                {
                   2339:                                case undefined:
                   2340:                                        highlightify(value,place,highlightHack);
                   2341:                                        break;
                   2342:                                case "link":
                   2343:                                        createTiddlyLink(place,value,true);
                   2344:                                        break;
                   2345:                                case "wikified":
                   2346:                                        wikify(value,place,highlightHack,tiddler);
                   2347:                                        break;
                   2348:                                case "date":
                   2349:                                        value = Date.convertFromYYYYMMDDHHMM(value);
                   2350:                                        if(params[2])
                   2351:                                                createTiddlyText(place,value.formatString(params[2]));
                   2352:                                        else
                   2353:                                                createTiddlyText(place,value);
                   2354:                                        break;
                   2355:                                }
                   2356:                }
                   2357: }
                   2358: 
                   2359: config.macros.edit.handler = function(place,macroName,params,wikifier,paramString,tiddler)
                   2360: {
                   2361:        var field = params[0];
                   2362:        if((tiddler instanceof Tiddler) && field)
                   2363:                {
                   2364:                story.setDirty(tiddler.title,true);
                   2365:                if(field != "text")
                   2366:                        {
                   2367:                                var e = createTiddlyElement(null,"input");
                   2368:                                if(tiddler.isReadOnly())
                   2369:                                        e.setAttribute("readOnly","readOnly");
                   2370:                                e.setAttribute("edit",field);
                   2371:                                e.setAttribute("type","text");
                   2372:                                var v = store.getValue(tiddler,field);
                   2373:                                if(!v) 
                   2374:                                        v = "";
                   2375:                                e.value = v;
                   2376:                                e.setAttribute("size","40");
                   2377:                                e.setAttribute("autocomplete","off");
                   2378:                                place.appendChild(e);
                   2379:                        }
                   2380:                else
                   2381:                        {
                   2382:                                var wrapper1 = createTiddlyElement(null,"fieldset",null,"fieldsetFix");
                   2383:                                var wrapper2 = createTiddlyElement(wrapper1,"div");
                   2384:                                var e = createTiddlyElement(wrapper2,"textarea");
                   2385:                                if(tiddler.isReadOnly())
                   2386:                                        e.setAttribute("readOnly","readOnly");
                   2387:                                var v = store.getValue(tiddler,field);
                   2388:                                if(!v) 
                   2389:                                        v = "";
                   2390:                                e.value = v;
                   2391:                                var rows = 10;
                   2392:                                var lines = v.match(/\n/mg);
                   2393:                                var maxLines = Math.max(parseInt(config.options.txtMaxEditRows),5);
                   2394:                                if(lines != null && lines.length > rows)
                   2395:                                        rows = lines.length + 5;
                   2396:                                rows = Math.min(rows,maxLines);
                   2397:                                e.setAttribute("rows",rows);
                   2398:                                e.setAttribute("edit",field);
                   2399:                                place.appendChild(wrapper1);
                   2400:                        }
                   2401:                }
                   2402: }
                   2403: 
                   2404: config.macros.tagChooser.onClick = function(e)
                   2405: {
                   2406:        if(!e) var e = window.event;
                   2407:        var lingo = config.views.editor.tagChooser;
                   2408:        var popup = Popup.create(this);
                   2409:        var tags = store.getTags();
                   2410:        if(tags.length == 0)
                   2411:                createTiddlyText(createTiddlyElement(popup,"li"),lingo.popupNone);
                   2412:        for(var t=0; t<tags.length; t++)
                   2413:                {
                   2414:                var theTag = createTiddlyButton(createTiddlyElement(popup,"li"),tags[t][0],lingo.tagTooltip.format([tags[t][0]]),config.macros.tagChooser.onTagClick);
                   2415:                theTag.setAttribute("tag",tags[t][0]);
                   2416:                theTag.setAttribute("tiddler", this.getAttribute("tiddler"));
                   2417:                }
                   2418:        Popup.show(popup,false);
                   2419:        e.cancelBubble = true;
                   2420:        if(e.stopPropagation) e.stopPropagation();
                   2421:        return(false);
                   2422: }
                   2423: 
                   2424: config.macros.tagChooser.onTagClick = function(e)
                   2425: {
                   2426:        if(!e) var e = window.event;
                   2427:        var tag = this.getAttribute("tag");
                   2428:        var title = this.getAttribute("tiddler");
                   2429:        if(!readOnly)
                   2430:                story.setTiddlerTag(title,tag,0);
                   2431:        return(false);
                   2432: }
                   2433: 
                   2434: config.macros.tagChooser.handler = function(place,macroName,params,wikifier,paramString,tiddler)
                   2435: {
                   2436:        if(tiddler instanceof Tiddler)
                   2437:                {
                   2438:                var title = tiddler.title;
                   2439:                var lingo = config.views.editor.tagChooser;
                   2440:                var btn = createTiddlyButton(place,lingo.text,lingo.tooltip,this.onClick);
                   2441:                btn.setAttribute("tiddler", title);
                   2442:                }
                   2443: }
                   2444: 
                   2445: // Create a toolbar command button
                   2446: // place - parent DOM element
                   2447: // command - reference to config.commands[] member -or- name of member
                   2448: // tiddler - reference to tiddler that toolbar applies to
                   2449: // theClass - the class to give the button
                   2450: config.macros.toolbar.createCommand = function(place,commandName,tiddler,theClass)
                   2451: {
                   2452:        if(typeof commandName != "string")
                   2453:                {
                   2454:                var c = null;
                   2455:                for(var t in config.commands)
                   2456:                        if(config.commands[t] == commandName)
                   2457:                                c = t;
                   2458:                commandName = c;
                   2459:                }
                   2460:        if((tiddler instanceof Tiddler) && (typeof commandName == "string"))
                   2461:                {
                   2462:                var title = tiddler.title;
                   2463:                var command = config.commands[commandName];
                   2464:                var ro = tiddler.isReadOnly();
                   2465:                var shadow = store.isShadowTiddler(title) && !store.tiddlerExists(title);
                   2466:                var text = ro && command.readOnlyText ? command.readOnlyText : command.text;
                   2467:                var tooltip = ro && command.readOnlyTooltip ? command.readOnlyTooltip : command.tooltip;
                   2468:                if((!ro || (ro && !command.hideReadOnly)) && !(shadow && command.hideShadow))
                   2469: 
                   2470:                        {
                   2471:                        var btn = createTiddlyButton(null,text,tooltip,this.onClickCommand);
                   2472:                        btn.setAttribute("commandName", commandName);
                   2473:                        btn.setAttribute("tiddler", title);
                   2474:                        if(theClass)
                   2475:                                addClass(btn,theClass);
                   2476:                        place.appendChild(btn);
                   2477:                        }
                   2478:                }
                   2479: }
                   2480: 
                   2481: config.macros.toolbar.onClickCommand = function(e)
                   2482: {
                   2483:        if(!e) var e = window.event;
                   2484:        var command = config.commands[this.getAttribute("commandName")];
                   2485:        return command.handler(e,this,this.getAttribute("tiddler"));
                   2486: }
                   2487: 
                   2488: // Invoke the first command encountered from a given place that is tagged with a specified class
                   2489: config.macros.toolbar.invokeCommand = function(place,theClass,event)
                   2490: {
                   2491:        var children = place.getElementsByTagName("a")
                   2492:        for(var t=0; t<children.length; t++)
                   2493:                {
                   2494:                var c = children[t];
                   2495:                if(hasClass(c,theClass) && c.getAttribute && c.getAttribute("commandName"))
                   2496:                        {
                   2497:                        if(c.onclick instanceof Function)
                   2498:                                c.onclick.call(c,event);
                   2499:                        break;
                   2500:                        }
                   2501:                }
                   2502: }
                   2503: 
                   2504: config.macros.toolbar.handler = function(place,macroName,params,wikifier,paramString,tiddler)
                   2505: {
                   2506:        for(var t=0; t<params.length; t++)
                   2507:                {
                   2508:                var c = params[t];
                   2509:                var theClass = "";
                   2510:                switch(c.substr(0,1))
                   2511:                        {
                   2512:                        case "+":
                   2513:                                theClass = "defaultCommand";
                   2514:                                c = c.substr(1);
                   2515:                                break;
                   2516:                        case "-":
                   2517:                                theClass = "cancelCommand";
                   2518:                                c = c.substr(1);
                   2519:                                break;
                   2520:                        }
                   2521:                if(c in config.commands)
                   2522:                        this.createCommand(place,c,tiddler,theClass);
                   2523:                }
                   2524: }
                   2525: 
                   2526: config.macros.plugins.handler = function(place,macroName,params,wikifier,paramString,tiddler)
                   2527: {
                   2528:        var e = createTiddlyElement(place,"div");
                   2529:        e.setAttribute("refresh","macro");
                   2530:        e.setAttribute("macroName","plugins");
                   2531:        e.setAttribute("params",paramString);
                   2532:        this.refresh(e,paramString);
                   2533: }
                   2534: 
                   2535: config.macros.plugins.refresh = function(place,params)
                   2536: {
                   2537:        var selectedRows = [];
                   2538:        ListView.forEachSelector(place,function(e,rowName) {
                   2539:                        if(e.checked)
                   2540:                                selectedRows.push(e.getAttribute("rowName"));
                   2541:                });
                   2542:        removeChildren(place);
                   2543:        params = params.parseParams("anon");
                   2544:        var plugins = installedPlugins.slice(0);
                   2545:        var t,tiddler,p;
                   2546:        var configTiddlers = store.getTaggedTiddlers("systemConfig");
                   2547:        for(t=0; t<configTiddlers.length; t++)
                   2548:                {
                   2549:                tiddler = configTiddlers[t];
                   2550:                if(plugins.findByField("title",tiddler.title) == null)
                   2551:                        {
                   2552:                        p = getPluginInfo(tiddler);
                   2553:                        p.executed = false;
                   2554:                        p.log.splice(0,0,this.skippedText);
                   2555:                        plugins.push(p);
                   2556:                        }
                   2557:                }
                   2558:        for(t=0; t<plugins.length; t++)
                   2559:                {
                   2560:                var p = plugins[t];
                   2561:                p.forced = p.tiddler.isTagged("systemConfigForce");
                   2562:                p.disabled = p.tiddler.isTagged("systemConfigDisable");
                   2563:                p.Selected = selectedRows.indexOf(plugins[t].title) != -1;
                   2564:                }
                   2565:        if(plugins.length == 0)
                   2566:                createTiddlyElement(place,"em",null,null,this.noPluginText);
                   2567:        else
                   2568:                ListView.create(place,plugins,this.listViewTemplate,this.onSelectCommand);
                   2569: }
                   2570: 
                   2571: config.macros.plugins.onSelectCommand = function(command,rowNames)
                   2572: {
                   2573:        var t;
                   2574:        switch(command)
                   2575:                {
                   2576:                case "remove":
                   2577:                        for(t=0; t<rowNames.length; t++)
                   2578:                                store.setTiddlerTag(rowNames[t],false,"systemConfig");
                   2579:                        break;
                   2580:                case "delete":
                   2581:                        if(rowNames.length > 0 && confirm(config.macros.plugins.confirmDeleteText.format([rowNames.join(", ")])))
                   2582:                                {
                   2583:                                for(t=0; t<rowNames.length; t++)
                   2584:                                        {
                   2585:                                        store.removeTiddler(rowNames[t]);
                   2586:                                        story.closeTiddler(rowNames[t],true,false);
                   2587:                                        }
                   2588:                                }
                   2589:                        break;
                   2590:                }
                   2591:        if(config.options.chkAutoSave)
                   2592:                saveChanges(true);
                   2593: }
                   2594: 
                   2595: config.macros.refreshDisplay.handler = function(place)
                   2596: {
                   2597:        createTiddlyButton(place,this.label,this.prompt,this.onClick);
                   2598: }
                   2599: 
                   2600: config.macros.refreshDisplay.onClick = function(e)
                   2601: {
                   2602:        refreshAll();
                   2603:        return false;
                   2604: }
                   2605: 
                   2606: config.macros.importTiddlers.handler = function(place,macroName,params,wikifier,paramString,tiddler)
                   2607: {
                   2608:        if(readOnly)
                   2609:                {
                   2610:                createTiddlyElement(place,"div",null,"marked",this.readOnlyWarning);
                   2611:                return;
                   2612:                }
                   2613:        var importer = createTiddlyElement(null,"div",null,"importTiddler wizard");
                   2614:        createTiddlyElement(importer,"h1",null,null,this.wizardTitle);
                   2615:        createTiddlyElement(importer,"h2",null,"step1",this.step1);
                   2616:        var step = createTiddlyElement(importer,"div",null,"wizardStep");
                   2617:        createTiddlyText(step,this.step1prompt);
                   2618:        var input = createTiddlyElement(null,"input",null,"txtOptionInput");
                   2619:        input.type = "text";
                   2620:        input.size = 50;
                   2621:        step.appendChild(input);
                   2622:        importer.inputBox = input;
                   2623:        createTiddlyElement(step,"br");
                   2624:        createTiddlyText(step,this.step1promptFile);
                   2625:        var fileInput = createTiddlyElement(null,"input",null,"txtOptionInput");
                   2626:        fileInput.type = "file";
                   2627:        fileInput.size = 50;
                   2628:        fileInput.onchange = this.onBrowseChange;
                   2629:        fileInput.onkeyup = this.onBrowseChange;
                   2630:        step.appendChild(fileInput);
                   2631:        createTiddlyElement(step,"br");
                   2632:        createTiddlyText(step,this.step1promptFeeds);
                   2633:        var feeds = this.getFeeds([{caption: this.step1feedPrompt, name: ""}]);
                   2634:        createTiddlyDropDown(step,this.onFeedChange,feeds);
                   2635:        createTiddlyElement(step,"br");
                   2636:        createTiddlyButton(step,this.fetchLabel,this.fetchPrompt,this.onFetch,null,null,null);
                   2637:         place.appendChild(importer);
                   2638: }
                   2639: 
                   2640: config.macros.importTiddlers.getFeeds = function(feeds)
                   2641: {
                   2642:        var tagged = store.getTaggedTiddlers("contentPublisher","title");
                   2643:        for(var t=0; t<tagged.length; t++)
                   2644:                feeds.push({caption: tagged[t].title, name: store.getTiddlerSlice(tagged[t].title,"URL")});
                   2645:        return feeds;
                   2646: }
                   2647: 
                   2648: config.macros.importTiddlers.onFeedChange = function(e)
                   2649: {
                   2650:        var importer = findRelated(this,"importTiddler","className","parentNode");
                   2651:        importer.inputBox.value = this.value;
                   2652:        this.selectedIndex = 0;
                   2653: }
                   2654: 
                   2655: config.macros.importTiddlers.onBrowseChange = function(e)
                   2656: {
                   2657:        var importer = findRelated(this,"importTiddler","className","parentNode");
                   2658:        importer.inputBox.value = "file://" + this.value;
                   2659: }
                   2660: 
                   2661: config.macros.importTiddlers.onFetch = function(e)
                   2662: {
                   2663:        var importer = findRelated(this,"importTiddler","className","parentNode");
                   2664:        var url = importer.inputBox.value;
                   2665:        var cutoff = findRelated(importer.firstChild,"step2","className","nextSibling");
                   2666:        while(cutoff)
                   2667:                {
                   2668:                var temp = cutoff.nextSibling;
                   2669:                cutoff.parentNode.removeChild(cutoff);
                   2670:                cutoff = temp;
                   2671:                }
                   2672:        createTiddlyElement(importer,"h2",null,"step2",config.macros.importTiddlers.step2);
                   2673:        var step = createTiddlyElement(importer,"div",null,"wizardStep",config.macros.importTiddlers.step2Text.format([url]));
                   2674:        loadRemoteFile(url,config.macros.importTiddlers.onLoad,importer);
                   2675: }
                   2676: 
                   2677: config.macros.importTiddlers.onLoad = function(status,params,responseText,url,xhr)
                   2678: {
                   2679:        if(!status)
                   2680:                {
                   2681:                displayMessage(this.fetchError);
                   2682:                return;
                   2683:                }
                   2684:        var importer = params;
                   2685:        // Check that the tiddler we're in hasn't been closed - doesn't work on IE
                   2686: //     var p = importer;
                   2687: //     while(p.parentNode)
                   2688: //             p = p.parentNode;
                   2689: //     if(!(p instanceof HTMLDocument))
                   2690: //             return;
                   2691:        // Crack out the content - (should be refactored)
                   2692:        var posOpeningDiv = responseText.indexOf(startSaveArea);
                   2693:        var limitClosingDiv = responseText.indexOf("<!--POST-BODY-START--"+">");
                   2694:        var posClosingDiv = responseText.lastIndexOf(endSaveArea,limitClosingDiv == -1 ? responseText.length : limitClosingDiv);
                   2695:        if((posOpeningDiv == -1) || (posClosingDiv == -1))
                   2696:                {
                   2697:                alert(config.messages.invalidFileError.format([url]));
                   2698:                return;
                   2699:                }
                   2700:        var content = "<html><body>" + responseText.substring(posOpeningDiv,posClosingDiv + endSaveArea.length) + "</body></html>";
                   2701:        // Create the iframe
                   2702:        var iframe = document.createElement("iframe");
                   2703:        iframe.style.display = "none";
                   2704:        importer.insertBefore(iframe,importer.firstChild);
                   2705:        var doc = iframe.document;
                   2706:        if(iframe.contentDocument)
                   2707:                doc = iframe.contentDocument; // For NS6
                   2708:        else if(iframe.contentWindow)
                   2709:                doc = iframe.contentWindow.document; // For IE5.5 and IE6
                   2710:        // Put the content in the iframe
                   2711:        doc.open();
                   2712:        doc.writeln(content);
                   2713:        doc.close();
                   2714:        // Load the content into a TiddlyWiki() object
                   2715:        var storeArea = doc.getElementById("storeArea");
                   2716:        var importStore = new TiddlyWiki();
                   2717:        importStore.loadFromDiv(storeArea,"store");
                   2718:        // Get rid of the iframe
                   2719:        iframe.parentNode.removeChild(iframe);
                   2720:        // Extract data for the listview
                   2721:        var tiddlers = [];
                   2722:        importStore.forEachTiddler(function(title,tiddler)
                   2723:                {
                   2724:                var t = {};
                   2725:                t.title = title;
                   2726:                t.modified = tiddler.modified;
                   2727:                t.modifier = tiddler.modifier;
                   2728:                t.text = tiddler.text.substr(0,50);
                   2729:                t.tags = tiddler.tags;
                   2730:                tiddlers.push(t);
                   2731:                });
                   2732:        // Display the listview
                   2733:        createTiddlyElement(importer,"h2",null,"step3",config.macros.importTiddlers.step3);
                   2734:        var step = createTiddlyElement(importer,"div",null,"wizardStep");
                   2735:        ListView.create(step,tiddlers,config.macros.importTiddlers.listViewTemplate,config.macros.importTiddlers.onSelectCommand);
                   2736:        // Save the importer
                   2737:        importer.store = importStore;
                   2738: }
                   2739: 
                   2740: config.macros.importTiddlers.onSelectCommand = function(listView,command,rowNames)
                   2741: {
                   2742:        var importer = findRelated(listView,"importTiddler","className","parentNode");
                   2743:        switch(command)
                   2744:                {
                   2745:                case "import":
                   2746:                        config.macros.importTiddlers.doImport(importer,rowNames);
                   2747:                        break;
                   2748:                }
                   2749:        if(config.options.chkAutoSave)
                   2750:                saveChanges(true);
                   2751: }
                   2752: 
                   2753: config.macros.importTiddlers.doImport = function(importer,rowNames)
                   2754: {
                   2755:        var theStore = importer.store;
                   2756:        var overwrite = new Array();
                   2757:        var t;
                   2758:        for(t=0; t<rowNames.length; t++)
                   2759:                {
                   2760:                if(store.tiddlerExists(rowNames[t]))
                   2761:                        overwrite.push(rowNames[t]);
                   2762:        }
                   2763:        if(overwrite.length > 0)
                   2764:                if(!confirm(this.confirmOverwriteText.format([overwrite.join(", ")])))
                   2765:                        return;
                   2766:        for(t=0; t<rowNames.length; t++)
                   2767:                {
                   2768:                var inbound = theStore.fetchTiddler(rowNames[t]);
                   2769:                store.saveTiddler(inbound.title, inbound.title, inbound.text, inbound.modifier, inbound.modified, inbound.tags);
                   2770:                store.fetchTiddler(inbound.title).created = inbound.created;
                   2771:                store.notify(rowNames[t],false);
                   2772:                }
                   2773:        store.notifyAll();
                   2774:        store.setDirty(true);
                   2775:        createTiddlyElement(importer,"h2",null,"step4",this.step4.format([rowNames.length]));
                   2776:        var step = createTiddlyElement(importer,"div",null,"wizardStep");
                   2777:        for(t=0; t<rowNames.length; t++)
                   2778:                {
                   2779:                createTiddlyLink(step,rowNames[t],true);
                   2780:                createTiddlyElement(step,"br");
                   2781:                }
                   2782:        createTiddlyElement(importer,"h2",null,"step5",this.step5);
                   2783: }
                   2784: // ---------------------------------------------------------------------------------
                   2785: // Menu and toolbar commands
                   2786: // ---------------------------------------------------------------------------------
                   2787: 
                   2788: config.commands.closeTiddler.handler = function(event,src,title)
                   2789: {
                   2790:        story.closeTiddler(title,true,event.shiftKey || event.altKey);
                   2791:        return false;
                   2792: }
                   2793: 
                   2794: config.commands.closeOthers.handler = function(event,src,title)
                   2795: {
                   2796:        story.closeAllTiddlers(title);
                   2797:        return false;
                   2798: }
                   2799: 
                   2800: config.commands.editTiddler.handler = function(event,src,title)
                   2801: {
                   2802:        clearMessage();
                   2803:        story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
                   2804:        story.focusTiddler(title,"text");
                   2805:        return false;
                   2806: }
                   2807: 
                   2808: config.commands.saveTiddler.handler = function(event,src,title)
                   2809: {
                   2810:        var newTitle = story.saveTiddler(title,event.shiftKey);
                   2811:        if(newTitle)
                   2812:           story.displayTiddler(null,newTitle);
                   2813:        return false;
                   2814: }
                   2815: 
                   2816: config.commands.cancelTiddler.handler = function(event,src,title)
                   2817: {
                   2818:        if(story.hasChanges(title) && !readOnly)
                   2819:                if(!confirm(this.warning.format([title])))
                   2820:                        return false;
                   2821:        story.setDirty(title,false);
                   2822:        story.displayTiddler(null,title);
                   2823:        return false;
                   2824: }
                   2825: 
                   2826: config.commands.deleteTiddler.handler = function(event,src,title)
                   2827: {
                   2828:        var deleteIt = true;
                   2829:        if (config.options.chkConfirmDelete)
                   2830:                deleteIt = confirm(this.warning.format([title]));
                   2831:        if (deleteIt)
                   2832:                {
                   2833:                store.removeTiddler(title);
                   2834:                story.closeTiddler(title,true,event.shiftKey || event.altKey);
                   2835:                if(config.options.chkAutoSave)
                   2836:                        saveChanges();
                   2837:                }
                   2838:        return false;
                   2839: }
                   2840: 
                   2841: config.commands.permalink.handler = function(event,src,title)
                   2842: {
                   2843:        var t = encodeURIComponent(String.encodeTiddlyLink(title));
                   2844:        if(window.location.hash != t)
                   2845:                window.location.hash = t;
                   2846:        return false;
                   2847: }
                   2848: 
                   2849: config.commands.references.handler = function(event,src,title)
                   2850: {
                   2851:        var popup = Popup.create(src);
                   2852:        if(popup)
                   2853:                {
                   2854:                var references = store.getReferringTiddlers(title);
                   2855:                var c = false;
                   2856:                for(var r=0; r<references.length; r++)
                   2857:                        if(references[r].title != title && !references[r].isTagged("excludeLists"))
                   2858:                                {
                   2859:                                createTiddlyLink(createTiddlyElement(popup,"li"),references[r].title,true);
                   2860:                                c = true;
                   2861:                                }
                   2862:                if(!c)
                   2863:                        createTiddlyText(createTiddlyElement(popup,"li",null,"disabled"),this.popupNone);
                   2864:                }
                   2865:        Popup.show(popup,false);
                   2866:        event.cancelBubble = true;
                   2867:        if (event.stopPropagation) event.stopPropagation();
                   2868:        return false;
                   2869: }
                   2870: 
                   2871: config.commands.jump.handler = function(event,src,title)
                   2872: {
                   2873:        var popup = Popup.create(src);
                   2874:        if(popup)
                   2875:                {
                   2876:                story.forEachTiddler(function(title,element) {
                   2877:                        createTiddlyLink(createTiddlyElement(popup,"li"),title,true);
                   2878:                        });
                   2879:                }
                   2880:        Popup.show(popup,false);
                   2881:        event.cancelBubble = true;
                   2882:        if (event.stopPropagation) event.stopPropagation();
                   2883:        return false;
                   2884: }
                   2885: 
                   2886: // ---------------------------------------------------------------------------------
                   2887: // Tiddler() object
                   2888: // ---------------------------------------------------------------------------------
                   2889: 
                   2890: function Tiddler()
                   2891: {
                   2892:        this.title = null;
                   2893:        this.text = null;
                   2894:        this.modifier = null;
                   2895:        this.modified = new Date();
                   2896:        this.created = new Date();
                   2897:        this.links = [];
                   2898:        this.linksUpdated = false;
                   2899:        this.tags = [];
                   2900:        return this;
                   2901: }
                   2902: 
                   2903: Tiddler.prototype.getLinks = function()
                   2904: {
                   2905:        if(this.linksUpdated==false)
                   2906:                this.changed();
                   2907:        return this.links;
                   2908: }
                   2909: 
                   2910: // Format the text for storage in an RSS item
                   2911: Tiddler.prototype.saveToRss = function(url)
                   2912: {
                   2913:        var s = [];
                   2914:        s.push("<item>");
                   2915:        s.push("<title" + ">" + this.title.htmlEncode() + "</title" + ">");
                   2916:        s.push("<description>" + wikifyStatic(this.text,null,this).htmlEncode() + "</description>");
                   2917:        for(var t=0; t<this.tags.length; t++)
                   2918:                s.push("<category>" + this.tags[t] + "</category>");
                   2919:        s.push("<link>" + url + "#" + encodeURIComponent(String.encodeTiddlyLink(this.title)) + "</link>");
                   2920:        s.push("<pubDate>" + this.modified.toGMTString() + "</pubDate>");
                   2921:        s.push("</item>");
                   2922:        return(s.join("\n"));
                   2923: }
                   2924: 
                   2925: // Change the text and other attributes of a tiddler
                   2926: Tiddler.prototype.set = function(title,text,modifier,modified,tags,created,fields)
                   2927: {
                   2928:        this.assign(title,text,modifier,modified,tags,created,fields);
                   2929:        this.changed();
                   2930:        return this;
                   2931: }
                   2932: 
                   2933: // Change the text and other attributes of a tiddler without triggered a tiddler.changed() call
                   2934: Tiddler.prototype.assign = function(title,text,modifier,modified,tags,created,fields)
                   2935: {
                   2936:        if(title != undefined)
                   2937:                this.title = title;
                   2938:        if(text != undefined)
                   2939:                this.text = text;
                   2940:        if(modifier != undefined)
                   2941:                this.modifier = modifier;
                   2942:        if(modified != undefined)
                   2943:                this.modified = modified;
                   2944:        if(created != undefined)
                   2945:                this.created = created;
                   2946:        if(fields != undefined)
                   2947:                this.fields = fields;
                   2948:        if(tags != undefined)
                   2949:                this.tags = (typeof tags == "string") ? tags.readBracketedList() : tags;
                   2950:        else if(this.tags == undefined)
                   2951:                this.tags = [];
                   2952:        return this;
                   2953: }
                   2954: 
                   2955: // Get the tags for a tiddler as a string (space delimited, using [[brackets]] for tags containing spaces)
                   2956: Tiddler.prototype.getTags = function()
                   2957: {
                   2958:        return String.encodeTiddlyLinkList(this.tags);
                   2959: }
                   2960: 
                   2961: // Test if a tiddler carries a tag
                   2962: Tiddler.prototype.isTagged = function(tag)
                   2963: {
                   2964:        return this.tags.indexOf(tag) != -1;
                   2965: }
                   2966: 
                   2967: // Static method to convert "\n" to newlines, "\s" to "\"
                   2968: Tiddler.unescapeLineBreaks = function(text)
                   2969: {
                   2970:        return text ? text.unescapeLineBreaks() : "";
                   2971: }
                   2972: 
                   2973: // Convert newlines to "\n", "\" to "\s"
                   2974: Tiddler.prototype.escapeLineBreaks = function()
                   2975: {
                   2976:        return this.text.escapeLineBreaks();
                   2977: }
                   2978: 
                   2979: // Updates the secondary information (like links[] array) after a change to a tiddler
                   2980: Tiddler.prototype.changed = function()
                   2981: {
                   2982:        this.links = [];
                   2983:        var t = this.autoLinkWikiWords() ? 0 : 1;
                   2984:        var tiddlerLinkRegExp = t==0 ? config.textPrimitives.tiddlerAnyLinkRegExp : config.textPrimitives.tiddlerForcedLinkRegExp;
                   2985:        tiddlerLinkRegExp.lastIndex = 0;
                   2986:        var formatMatch = tiddlerLinkRegExp.exec(this.text);
                   2987:        while(formatMatch)
                   2988:                {
                   2989:                if(t==0 && formatMatch[1] && formatMatch[1] != this.title) // wikiWordLink
                   2990:                        {
                   2991:                        if(formatMatch.index > 0)
                   2992:                                {
                   2993:                                var preRegExp = new RegExp(config.textPrimitives.unWikiLink+"|"+config.textPrimitives.anyLetter,"mg");
                   2994:                                preRegExp.lastIndex = formatMatch.index-1;
                   2995:                                var preMatch = preRegExp.exec(this.text);
                   2996:                                if(preMatch.index != formatMatch.index-1)
                   2997:                                        this.links.pushUnique(formatMatch[1]);
                   2998:                                }
                   2999:                        else
                   3000:                                this.links.pushUnique(formatMatch[1]);
                   3001:                        }
                   3002:                else if(formatMatch[2-t] && (store.tiddlerExists(formatMatch[3-t]) || store.isShadowTiddler(formatMatch[3-t]))) // titledBrackettedLink
                   3003:                        this.links.pushUnique(formatMatch[3-t]);
                   3004:                else if(formatMatch[4-t] && formatMatch[4-t] != this.title) // brackettedLink
                   3005:                        this.links.pushUnique(formatMatch[4-t]);
                   3006:                // Do not add link if match urlPattern (formatMatch[5-t])
                   3007:                formatMatch = tiddlerLinkRegExp.exec(this.text);
                   3008:                }
                   3009:        this.linksUpdated = true;
                   3010:        return;
                   3011: }
                   3012: 
                   3013: Tiddler.prototype.getSubtitle = function()
                   3014: {
                   3015:        var theModifier = this.modifier;
                   3016:        if(!theModifier)
                   3017:                theModifier = config.messages.subtitleUnknown;
                   3018:        var theModified = this.modified;
                   3019:        if(theModified)
                   3020:                theModified = theModified.toLocaleString();
                   3021:        else
                   3022:                theModified = config.messages.subtitleUnknown;
                   3023:        return(config.messages.tiddlerLinkTooltip.format([this.title,theModifier,theModified]));
                   3024: }
                   3025: 
                   3026: Tiddler.prototype.isReadOnly = function()
                   3027: {
                   3028:        return readOnly;
                   3029: }
                   3030: 
                   3031: Tiddler.prototype.autoLinkWikiWords = function()
                   3032: {
                   3033:        return !(this.isTagged("systemConfig") || this.isTagged("excludeMissing"));
                   3034: }
                   3035: 
                   3036: Tiddler.prototype.generateFingerprint = function()
                   3037: {
                   3038:        return "0x" + Crypto.hexSha1Str(this.text);
                   3039: }
                   3040: 
                   3041: // ---------------------------------------------------------------------------------
                   3042: // TiddlyWiki() object contains Tiddler()s
                   3043: // ---------------------------------------------------------------------------------
                   3044: 
                   3045: function TiddlyWiki()
                   3046: {
                   3047:        var tiddlers = {}; // Hashmap by name of tiddlers
                   3048:        this.tiddlersUpdated = false;
                   3049:        this.namedNotifications = []; // Array of {name:,notify:} of notification functions
                   3050:        this.notificationLevel = 0;
                   3051:        this.slices = {}; // map tiddlerName->(map sliceName->sliceValue). Lazy.
                   3052:        this.clear = function() {
                   3053:                tiddlers = {};
                   3054:                this.setDirty(false);
                   3055:                };
                   3056:        this.fetchTiddler = function(title) {
                   3057:                return tiddlers[title];
                   3058:                };
                   3059:        this.deleteTiddler = function(title) {
                   3060:                delete this.slices[title];
                   3061:                delete tiddlers[title];
                   3062:                };
                   3063:        this.addTiddler = function(tiddler) {
                   3064:                delete this.slices[tiddler.title];
                   3065:                tiddlers[tiddler.title] = tiddler;
                   3066:                };
                   3067:        this.forEachTiddler = function(callback) {
                   3068:                for(var t in tiddlers)
                   3069:                        {
                   3070:                        var tiddler = tiddlers[t];
                   3071:                        if(tiddler instanceof Tiddler)
                   3072:                                callback.call(this,t,tiddler);
                   3073:                        }
                   3074:                };
                   3075: }
                   3076: 
                   3077: // Set the dirty flag
                   3078: TiddlyWiki.prototype.setDirty = function(dirty)
                   3079: {
                   3080:        this.dirty = dirty;
                   3081: }
                   3082: 
                   3083: TiddlyWiki.prototype.isDirty = function()
                   3084: {
                   3085:        return this.dirty;
                   3086: }
                   3087: 
                   3088: TiddlyWiki.prototype.suspendNotifications = function()
                   3089: {
                   3090:        this.notificationLevel--;
                   3091: }
                   3092: 
                   3093: TiddlyWiki.prototype.resumeNotifications = function()
                   3094: {
                   3095:        this.notificationLevel++;
                   3096: }
                   3097: 
                   3098: // Invoke the notification handlers for a particular tiddler
                   3099: TiddlyWiki.prototype.notify = function(title,doBlanket)
                   3100: {
                   3101:        if(!this.notificationLevel)
                   3102:                for(var t=0; t<this.namedNotifications.length; t++)
                   3103:                        {
                   3104:                        var n = this.namedNotifications[t];
                   3105:                        if((n.name == null && doBlanket) || (n.name == title))
                   3106:                                n.notify(title);
                   3107:                        }
                   3108: }
                   3109: 
                   3110: // Invoke the notification handlers for all tiddlers
                   3111: TiddlyWiki.prototype.notifyAll = function()
                   3112: {
                   3113:        if(!this.notificationLevel)
                   3114:                for(var t=0; t<this.namedNotifications.length; t++)
                   3115:                        {
                   3116:                        var n = this.namedNotifications[t];
                   3117:                        if(n.name)
                   3118:                                n.notify(n.name);
                   3119:                        }
                   3120: }
                   3121: 
                   3122: // Add a notification handler to a tiddler
                   3123: TiddlyWiki.prototype.addNotification = function(title,fn)
                   3124: {
                   3125:        for (var i=0; i<this.namedNotifications.length; i++)
                   3126:                if((this.namedNotifications[i].name == title) && (this.namedNotifications[i].notify == fn))
                   3127:                        return this;
                   3128:        this.namedNotifications.push({name: title, notify: fn});
                   3129:        return this;
                   3130: }
                   3131: 
                   3132: TiddlyWiki.prototype.removeTiddler = function(title)
                   3133: {
                   3134:        var tiddler = this.fetchTiddler(title);
                   3135:        if(tiddler)
                   3136:                {
                   3137:                this.deleteTiddler(title);
                   3138:                this.notify(title,true);
                   3139:                this.setDirty(true);
                   3140:                }
                   3141: }
                   3142: 
                   3143: TiddlyWiki.prototype.tiddlerExists = function(title)
                   3144: {
                   3145:        var t = this.fetchTiddler(title);
                   3146:        return (t != undefined);
                   3147: }
                   3148: 
                   3149: TiddlyWiki.prototype.isShadowTiddler = function(title)
                   3150: {
                   3151:        return typeof config.shadowTiddlers[title] == "string";
                   3152: }
                   3153: 
                   3154: TiddlyWiki.prototype.getTiddler = function(title)
                   3155: {
                   3156:        var t = this.fetchTiddler(title);
                   3157:        if(t != undefined)
                   3158:                return t;
                   3159:        else
                   3160:                return null;
                   3161: }
                   3162: 
                   3163: TiddlyWiki.prototype.getTiddlerText = function(title,defaultText)
                   3164: {
                   3165:        var tiddler = this.fetchTiddler(title);
                   3166:        if(tiddler)
                   3167:                return tiddler.text;
                   3168:        if(!title)
                   3169:                return defaultText;
                   3170:        var pos = title.indexOf(config.textPrimitives.sliceSeparator);
                   3171:        if(pos != -1)
                   3172:                {
                   3173:                var slice = this.getTiddlerSlice(title.substr(0,pos),title.substr(pos + config.textPrimitives.sliceSeparator.length));
                   3174:                if(slice)
                   3175:                        return slice;
                   3176:                }
                   3177:        if(this.isShadowTiddler(title))
                   3178:                return config.shadowTiddlers[title];
                   3179:        if(defaultText != undefined)
                   3180:                return defaultText;
                   3181:        return null;
                   3182: }
                   3183: 
                   3184: TiddlyWiki.prototype.slicesRE = /(?:[\'\/]*~?(\w+)[\'\/]*\:[\'\/]*\s*(.*?)\s*$)|(?:\|[\'\/]*~?(\w+)\:?[\'\/]*\|\s*(.*?)\s*\|)/gm
                   3185: 
                   3186: // @internal
                   3187: TiddlyWiki.prototype.calcAllSlices = function(title) 
                   3188: {
                   3189:        var slices = {};
                   3190:        var text = this.getTiddlerText(title,"");
                   3191:        this.slicesRE.lastIndex = 0;
                   3192:        do 
                   3193:                {
                   3194:                        var m = this.slicesRE.exec(text);
                   3195:                        if (m) 
                   3196:                                {
                   3197:                                        if (m[1])
                   3198:                                                slices[m[1]] = m[2];
                   3199:                                        else
                   3200:                                                slices[m[3]] = m[4];
                   3201:                                }
                   3202:                }
                   3203:        while(m);
                   3204:        return slices;
                   3205: }
                   3206: 
                   3207: // Returns the slice of text of the given name
                   3208: //#
                   3209: //# A text slice is a substring in the tiddler's text that is defined
                   3210: //# either like this
                   3211: //#    aName:  textSlice
                   3212: //# or
                   3213: //#    |aName:| textSlice |
                   3214: //# or
                   3215: //#    |aName| textSlice |
                   3216: //#
                   3217: //# In the text the name (or name:) may be decorated with '' or //. I.e.
                   3218: //# this would also a possible text slice:
                   3219: //#
                   3220: //#    |''aName:''| textSlice |
                   3221: //#
                   3222: //# @param name should only contain "word characters" (i.e. "a-ZA-Z_0-9")
                   3223: //# @return [may be undefined] the (trimmed) text of the specified slice.
                   3224: TiddlyWiki.prototype.getTiddlerSlice = function(title,sliceName)
                   3225: {
                   3226:        var slices = this.slices[title];
                   3227:        if (!slices) {
                   3228:                slices = this.calcAllSlices(title);
                   3229:                this.slices[title] = slices;
                   3230:        }
                   3231:        return slices[sliceName];
                   3232: }
                   3233: 
                   3234: // Build an hashmap of the specified named slices of a tiddler
                   3235: TiddlyWiki.prototype.getTiddlerSlices = function(title,sliceNames)
                   3236: {
                   3237:        var r = {};
                   3238:        for(var t=0; t<sliceNames.length; t++)
                   3239:                {
                   3240:                var slice = this.getTiddlerSlice(title,sliceNames[t]);
                   3241:                if(slice)
                   3242:                        r[sliceNames[t]] = slice;
                   3243:                }
                   3244:        return r;
                   3245: }
                   3246: 
                   3247: TiddlyWiki.prototype.getRecursiveTiddlerText = function(title,defaultText,depth)
                   3248: {
                   3249:        var bracketRegExp = new RegExp("(?:\\[\\[([^\\]]+)\\]\\])","mg");
                   3250:        var text = this.getTiddlerText(title,null);
                   3251:        if(text == null)
                   3252:                return defaultText;
                   3253:        var textOut = [];
                   3254:        var lastPos = 0;
                   3255:        do {
                   3256:                var match = bracketRegExp.exec(text);
                   3257:                if(match)
                   3258:                        {
                   3259:                        textOut.push(text.substr(lastPos,match.index-lastPos));
                   3260:                        if(match[1])
                   3261:                                {
                   3262:                                if(depth <= 0)
                   3263:                                        textOut.push(match[1]);
                   3264:                                else
                   3265:                                        textOut.push(this.getRecursiveTiddlerText(match[1],"[[" + match[1] + "]]",depth-1));
                   3266:                                }
                   3267:                        lastPos = match.index + match[0].length;
                   3268:                        }
                   3269:                else
                   3270:                        textOut.push(text.substr(lastPos));
                   3271:        } while(match);
                   3272:        return(textOut.join(""));
                   3273: }
                   3274: 
                   3275: TiddlyWiki.prototype.setTiddlerTag = function(title,status,tag)
                   3276: {
                   3277:        var tiddler = this.fetchTiddler(title);
                   3278:        if(tiddler)
                   3279:                {
                   3280:                var t = tiddler.tags.indexOf(tag);
                   3281:                if(t != -1)
                   3282:                        tiddler.tags.splice(t,1);
                   3283:                if(status)
                   3284:                        tiddler.tags.push(tag);
                   3285:                tiddler.changed();
                   3286:                this.notify(title,true);
                   3287:                this.setDirty(true);
                   3288:                }
                   3289: }
                   3290: 
                   3291: TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags,fields)
                   3292: {
                   3293:        var tiddler = this.fetchTiddler(title);
                   3294:        var created;
                   3295:        if(tiddler)
                   3296:                {
                   3297:                created = tiddler.created; // Preserve created date
                   3298:                this.deleteTiddler(title);
                   3299:                }
                   3300:        else
                   3301:                {
                   3302:                tiddler = new Tiddler();
                   3303:                created = modified;
                   3304:                }
                   3305:        tiddler.set(newTitle,newBody,modifier,modified,tags,created,fields);
                   3306:        this.addTiddler(tiddler);
                   3307:        if(title != newTitle)
                   3308:                this.notify(title,true);
                   3309:        this.notify(newTitle,true);
                   3310:        this.setDirty(true);
                   3311:        return tiddler;
                   3312: }
                   3313: 
                   3314: TiddlyWiki.prototype.createTiddler = function(title)
                   3315: {
                   3316:        var tiddler = this.fetchTiddler(title);
                   3317:        if(!tiddler)
                   3318:                {
                   3319:                tiddler = new Tiddler();
                   3320:                tiddler.title = title;
                   3321:                this.addTiddler(tiddler);
                   3322:                this.setDirty(true);
                   3323:                }
                   3324:        return tiddler;
                   3325: }
                   3326: 
                   3327: // Load contents of a tiddlywiki from an HTML DIV
                   3328: TiddlyWiki.prototype.loadFromDiv = function(src,idPrefix,noUpdate)
                   3329: {
                   3330:        this.idPrefix = idPrefix;
                   3331:        var storeElem = (typeof src == "string") ? document.getElementById(src) : src;
                   3332:        var tiddlers = this.getLoader().loadTiddlers(this,storeElem.childNodes);
                   3333:        this.setDirty(false);
                   3334:        if(!noUpdate)
                   3335:                {
                   3336:                for(var i = 0;i<tiddlers.length; i++)
                   3337:                        tiddlers[i].changed();
                   3338:                }
                   3339: }
                   3340: 
                   3341: TiddlyWiki.prototype.updateTiddlers = function()
                   3342: {
                   3343:        this.tiddlersUpdated = true;
                   3344:        this.forEachTiddler(function(title,tiddler) {
                   3345:                tiddler.changed();
                   3346:                });
                   3347: }
                   3348: 
                   3349: // Return all tiddlers formatted as an HTML string
                   3350: TiddlyWiki.prototype.allTiddlersAsHtml = function()
                   3351: {
                   3352:        return store.getSaver().externalize(store);
                   3353: }
                   3354: 
                   3355: // Return an array of tiddlers matching a search regular expression
                   3356: TiddlyWiki.prototype.search = function(searchRegExp,sortField,excludeTag)
                   3357: {
                   3358:        var candidates = this.reverseLookup("tags",excludeTag,false);
                   3359:        var results = [];
                   3360:        for(var t=0; t<candidates.length; t++)
                   3361:                {
                   3362:                if((candidates[t].title.search(searchRegExp) != -1) || (candidates[t].text.search(searchRegExp) != -1))
                   3363:                        results.push(candidates[t]);
                   3364:                }
                   3365:        if(!sortField)
                   3366:                sortField = "title";
                   3367:        results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
                   3368:        return results;
                   3369: }
                   3370: 
                   3371: // 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
                   3372: TiddlyWiki.prototype.getTags = function()
                   3373: {
                   3374:        var results = [];
                   3375:        this.forEachTiddler(function(title,tiddler) {
                   3376:                for(var g=0; g<tiddler.tags.length; g++)
                   3377:                        {
                   3378:                        var tag = tiddler.tags[g];
                   3379:                        var f = false;
                   3380:                        for(var c=0; c<results.length; c++)
                   3381:                                if(results[c][0] == tag)
                   3382:                                        {
                   3383:                                        f = true;
                   3384:                                        results[c][1]++;
                   3385:                                        }
                   3386:                        if(!f)
                   3387:                                results.push([tag,1]);
                   3388:                        }
                   3389:                });
                   3390:        results.sort(function(a,b) {return a[0].toLowerCase() < b[0].toLowerCase() ? -1 : (a[0].toLowerCase() == b[0].toLowerCase() ? 0 : +1);});
                   3391:        return results;
                   3392: }
                   3393: 
                   3394: // Return an array of the tiddlers that are tagged with a given tag
                   3395: TiddlyWiki.prototype.getTaggedTiddlers = function(tag,sortField)
                   3396: {
                   3397:        return this.reverseLookup("tags",tag,true,sortField);
                   3398: }
                   3399: 
                   3400: // Return an array of the tiddlers that link to a given tiddler
                   3401: TiddlyWiki.prototype.getReferringTiddlers = function(title,unusedParameter,sortField)
                   3402: {
                   3403:        if(!this.tiddlersUpdated)
                   3404:                this.updateTiddlers();
                   3405:        return this.reverseLookup("links",title,true,sortField);
                   3406: }
                   3407: 
                   3408: // Return an array of the tiddlers that do or do not have a specified entry in the specified storage array (ie, "links" or "tags")
                   3409: // lookupMatch == true to match tiddlers, false to exclude tiddlers
                   3410: TiddlyWiki.prototype.reverseLookup = function(lookupField,lookupValue,lookupMatch,sortField)
                   3411: {
                   3412:        var results = [];
                   3413:        this.forEachTiddler(function(title,tiddler) {
                   3414:                var f = !lookupMatch;
                   3415:                for(var lookup=0; lookup<tiddler[lookupField].length; lookup++)
                   3416:                        if(tiddler[lookupField][lookup] == lookupValue)
                   3417:                                f = lookupMatch;
                   3418:                if(f)
                   3419:                        results.push(tiddler);
                   3420:                });
                   3421:        if(!sortField)
                   3422:                sortField = "title";
                   3423:        results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
                   3424:        return results;
                   3425: }
                   3426: 
                   3427: // Return the tiddlers as a sorted array
                   3428: TiddlyWiki.prototype.getTiddlers = function(field,excludeTag)
                   3429: {
                   3430:        var results = [];
                   3431:        this.forEachTiddler(function(title,tiddler) {
                   3432:                if(excludeTag == undefined || !tiddler.isTagged(excludeTag))
                   3433:                        results.push(tiddler);
                   3434:                });
                   3435:        if(field)
                   3436:                results.sort(function(a,b) {return a[field] < b[field] ? -1 : (a[field] == b[field] ? 0 : +1);});
                   3437:        return results;
                   3438: }
                   3439: 
                   3440: // Return array of names of tiddlers that are referred to but not defined
                   3441: TiddlyWiki.prototype.getMissingLinks = function(sortField)
                   3442: {
                   3443:        if(!this.tiddlersUpdated)
                   3444:                this.updateTiddlers();
                   3445:        var results = [];
                   3446:        this.forEachTiddler(function (title,tiddler) {
                   3447:                for(var n=0; n<tiddler.links.length;n++)
                   3448:                        {
                   3449:                        var link = tiddler.links[n];
                   3450:                        if(this.fetchTiddler(link) == null && !this.isShadowTiddler(link))
                   3451:                                results.pushUnique(link);
                   3452:                        }
                   3453:                });
                   3454:        results.sort();
                   3455:        return results;
                   3456: }
                   3457: 
                   3458: // Return an array of names of tiddlers that are defined but not referred to
                   3459: TiddlyWiki.prototype.getOrphans = function()
                   3460: {
                   3461:        var results = [];
                   3462:        this.forEachTiddler(function (title,tiddler) {
                   3463:                if(this.getReferringTiddlers(title).length == 0 && !tiddler.isTagged("excludeLists"))
                   3464:                        results.push(title);
                   3465:                });
                   3466:        results.sort();
                   3467:        return results;
                   3468: }
                   3469: 
                   3470: // Return an array of names of all the shadow tiddlers
                   3471: TiddlyWiki.prototype.getShadowed = function()
                   3472: {
                   3473:        var results = [];
                   3474:        for(var t in config.shadowTiddlers)
                   3475:                if(typeof config.shadowTiddlers[t] == "string")
                   3476:                        results.push(t);
                   3477:        results.sort();
                   3478:        return results;
                   3479: }
                   3480: 
                   3481: // Resolves a Tiddler reference or tiddler title into a Tiddler object, or null if it doesn't exist
                   3482: TiddlyWiki.prototype.resolveTiddler = function(tiddler) 
                   3483: {
                   3484:        var t = (typeof tiddler == 'string') ? this.getTiddler(tiddler) : tiddler;
                   3485:        return t instanceof Tiddler ? t : null;
                   3486: }
                   3487: 
                   3488: TiddlyWiki.prototype.getLoader = function() 
                   3489: {
                   3490:        if (!this.loader) 
                   3491:                this.loader = new TW21Loader();
                   3492:        return this.loader;
                   3493: }
                   3494:  
                   3495: TiddlyWiki.prototype.getSaver = function() 
                   3496: {
                   3497:        if (!this.saver) 
                   3498:                this.saver = new TW21Saver();
                   3499:        return this.saver;
                   3500: }
                   3501: 
                   3502: // Returns true if path is a valid field name (path),
                   3503: // i.e. a sequence of identifiers, separated by '.'
                   3504: TiddlyWiki.isValidFieldName = function (name) {
                   3505:        var match = /[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*/.exec(name);
                   3506:        return match && (match[0] == name);
                   3507: }
                   3508: 
                   3509: // Throws an exception when name is not a valid field name.
                   3510: TiddlyWiki.checkFieldName = function(name) {
                   3511:        if (!TiddlyWiki.isValidFieldName(name))
                   3512:                throw config.messages.invalidFieldName.format([name]);
                   3513: }
                   3514: 
                   3515: function StringFieldAccess(n, readOnly) {
                   3516:        this.set = readOnly 
                   3517:                ? function(t,v) {if (v != t[n]) throw config.messages.fieldCannotBeChanged.format([n]);}
                   3518:                : function(t,v) {if (v != t[n]) {t[n] = v; return true;}};
                   3519:        this.get = function(t) {return t[n];};
                   3520: }
                   3521: 
                   3522: function DateFieldAccess(n) {
                   3523:        this.set = function(t,v) {
                   3524:                        var d = v instanceof Date ? v : Date.convertFromYYYYMMDDHHMM(v); 
                   3525:                        if (d != t[n]) {
                   3526:                                t[n] = d; return true;
                   3527:                        }
                   3528:                };
                   3529:        this.get = function(t)   {return t[n].convertToYYYYMMDDHHMM();}
                   3530: }
                   3531: 
                   3532: function LinksFieldAccess(n) {
                   3533:        this.set = function(t,v) {
                   3534:                        var s = (typeof v == "string") ? v.readBracketedList() : v; 
                   3535:                        if (s.toString() != t[n].toString()) {
                   3536:                                t[n] = s; return true;
                   3537:                        }
                   3538:                };
                   3539:        this.get = function(t)   {return String.encodeTiddlyLinkList(t[n]);}
                   3540: }
                   3541: 
                   3542: TiddlyWiki.standardFieldAccess = {
                   3543:        // The set functions return true when setting the data has changed the value.
                   3544:        
                   3545:        "title":    new StringFieldAccess("title", true),
                   3546:        // Handle the "tiddler" field name as the title
                   3547:        "tiddler":  new StringFieldAccess("title", true),
                   3548:        
                   3549:        "text":     new StringFieldAccess("text"),
                   3550:        "modifier": new StringFieldAccess("modifier"),
                   3551:        "modified": new DateFieldAccess("modified"),
                   3552:        "created":  new DateFieldAccess("created"),
                   3553:        "tags":     new LinksFieldAccess("tags")
                   3554: };
                   3555: 
                   3556: TiddlyWiki.isStandardField = function(name) {
                   3557:        return TiddlyWiki.standardFieldAccess[name] != undefined;
                   3558: }
                   3559: 
                   3560: // Sets the value of the given field of the tiddler to the value. 
                   3561: // Setting an ExtendedField's value to null or undefined removes the field. 
                   3562: // Setting a namespace to undefined removes all fields of that namespace.
                   3563: // The fieldName is case-insensitive.
                   3564: // All values will be converted to a string value.
                   3565: TiddlyWiki.prototype.setValue = function(tiddler, fieldName, value) {
                   3566:        TiddlyWiki.checkFieldName(fieldName);
                   3567:        var t = this.resolveTiddler(tiddler);
                   3568:        if (!t)
                   3569:                return;
                   3570:                
                   3571:        fieldName = fieldName.toLowerCase();
                   3572: 
                   3573:        var isRemove = (value === undefined) || (value === null);
                   3574: 
                   3575:        if (!t.fields) 
                   3576:                t.fields = {};
                   3577:                
                   3578:        var accessor = TiddlyWiki.standardFieldAccess[fieldName];
                   3579:        if (accessor) {
                   3580:                if (isRemove)
                   3581:                        // don't remove StandardFields
                   3582:                        return;
                   3583:                var h = TiddlyWiki.standardFieldAccess[fieldName];
                   3584:                if (!h.set(t, value))
                   3585:                        return;
                   3586: 
                   3587:        } else {
                   3588:                var oldValue = t.fields[fieldName];
                   3589:                
                   3590:                if (isRemove) {
                   3591:                        if (oldValue !== undefined) {
                   3592:                                // deletes a single field
                   3593:                                delete t.fields[fieldName];
                   3594:                        } else {
                   3595:                                // no concrete value is defined for the fieldName
                   3596:                                // so we guess this is a namespace path.
                   3597:                                
                   3598:                                // delete all fields in a namespace
                   3599:                                var re = new RegExp('^'+fieldName+'\\.');
                   3600:                                var dirty = false;
                   3601:                                for (var n in t.fields) {
                   3602:                                        if (n.match(re)) {
                   3603:                                                delete t.fields[n];
                   3604:                                                dirty = true;
                   3605:                                        }
                   3606:                                }
                   3607:                                if (!dirty)
                   3608:                                        return
                   3609:                        }
                   3610:                                
                   3611:                } else {
                   3612:                        // the "normal" set case. value is defined (not null/undefined)
                   3613:                        // For convenience provide a nicer conversion Date->String
                   3614:                        value = value instanceof Date 
                   3615:                                ? value.convertToYYYYMMDDHHMMSSMMM() 
                   3616:                                : String(value);
                   3617:                        if (oldValue == value) 
                   3618:                                return;
                   3619:                        t.fields[fieldName] = value;
                   3620:                }
                   3621:        }
                   3622:        
                   3623:        // When we are here the tiddler/store really was changed.
                   3624:        this.notify(t.title,true);
                   3625:        if (!fieldName.match(/^temp\./))
                   3626:                this.setDirty(true);
                   3627: }
                   3628: 
                   3629: // Returns the value of the given field of the tiddler. 
                   3630: // The fieldName is case-insensitive.
                   3631: // Will only return String values (or undefined).
                   3632: TiddlyWiki.prototype.getValue = function(tiddler, fieldName) {
                   3633:        var t = this.resolveTiddler(tiddler);
                   3634:        if (!t)
                   3635:                return undefined;
                   3636: 
                   3637:        fieldName = fieldName.toLowerCase();
                   3638: 
                   3639:        var accessor = TiddlyWiki.standardFieldAccess[fieldName];
                   3640:        if (accessor) {
                   3641:                return accessor.get(t);
                   3642:        }
                   3643:        
                   3644:        return t.fields ? t.fields[fieldName] : undefined;
                   3645: }
                   3646: 
                   3647: // Calls the callback function for every field in the tiddler.
                   3648: //
                   3649: // When callback function returns a non-false value the iteration stops 
                   3650: // and that value is returned. 
                   3651: //
                   3652: // The order of the fields is not defined.
                   3653: // 
                   3654: // @param callback a function(tiddler, fieldName, value). 
                   3655: // 
                   3656: TiddlyWiki.prototype.forEachField = function(tiddler, callback, onlyExtendedFields) {
                   3657:        var t = this.resolveTiddler(tiddler);
                   3658:        if (!t)
                   3659:                return undefined;
                   3660:        
                   3661:        if (t.fields) {
                   3662:                for (var n in t.fields) {
                   3663:                        var result = callback(t, n, t.fields[n]);
                   3664:                        if (result)
                   3665:                                return result;
                   3666:                }
                   3667:        }
                   3668:        
                   3669:        if (onlyExtendedFields)
                   3670:                return undefined;
                   3671: 
                   3672:        for (var n in TiddlyWiki.standardFieldAccess) {
                   3673:                if (n == "tiddler")
                   3674:                        // even though the "title" field can also be referenced through the name "tiddler"
                   3675:                        // we only visit this field once.
                   3676:                        continue;
                   3677:                        
                   3678:                var result = callback(t, n, TiddlyWiki.standardFieldAccess[n].get(t));
                   3679:                if (result)
                   3680:                        return result;
                   3681:        }
                   3682: 
                   3683:        return undefined;
                   3684: };
                   3685: // ---------------------------------------------------------------------------------
                   3686: // Story functions
                   3687: // ---------------------------------------------------------------------------------
                   3688: 
                   3689: // A story is a HTML div containing a sequence of tiddlers that can be manipulated
                   3690: // container - id of containing element
                   3691: // idPrefix - string prefix prepended to title to make ids for tiddlers in this story
                   3692: function Story(container,idPrefix)
                   3693: {
                   3694:        this.container = container;
                   3695:        this.idPrefix = idPrefix;
                   3696:        this.highlightRegExp = null;
                   3697: }
                   3698: 
                   3699: // Iterate through all the tiddlers in a story
                   3700: // fn - callback function to be called for each tiddler. Arguments are:
                   3701: //             tiddler - reference to Tiddler object
                   3702: //             element - reference to tiddler display element
                   3703: Story.prototype.forEachTiddler = function(fn)
                   3704: {
                   3705:        var place = document.getElementById(this.container);
                   3706:        if(!place)
                   3707:                return;
                   3708:        var e = place.firstChild;
                   3709:        while(e)
                   3710:                {
                   3711:                var n = e.nextSibling;
                   3712:                var title = e.getAttribute("tiddler");
                   3713:                fn.call(this,title,e);
                   3714:                e = n;
                   3715:                }
                   3716: }
                   3717: 
                   3718: // Display several tiddlers given their titles in an array. Parameters same as displayTiddler(), except:
                   3719: // titles - array of string titles
                   3720: Story.prototype.displayTiddlers = function(srcElement,titles,template,animate,slowly)
                   3721: {
                   3722:        for(var t = titles.length-1;t>=0;t--)
                   3723:                this.displayTiddler(srcElement,titles[t],template,animate,slowly);
                   3724: }
                   3725: 
                   3726: // Display a given tiddler with a given template. If the tiddler is already displayed but with a different
                   3727: // template, it is switched to the specified template
                   3728: // srcElement - reference to element from which this one is being opened -or-
                   3729: //              special positions "top", "bottom"
                   3730: // title - title of tiddler to display
                   3731: // template - the name of the tiddler containing the template -or-
                   3732: //                       one of the constants DEFAULT_VIEW_TEMPLATE and DEFAULT_EDIT_TEMPLATE -or-
                   3733: //                       null or undefined to indicate the current template if there is one, DEFAULT_VIEW_TEMPLATE if not
                   3734: // animate - whether to perform animations
                   3735: // slowly - whether to perform animations in slomo
                   3736: Story.prototype.displayTiddler = function(srcElement,title,template,animate,slowly)
                   3737: {
                   3738:        var place = document.getElementById(this.container);
                   3739:        var tiddlerElem = document.getElementById(this.idPrefix + title);
                   3740:        if(tiddlerElem)
                   3741:                this.refreshTiddler(title,template);
                   3742:        else
                   3743:                {
                   3744:                var before = this.positionTiddler(srcElement);
                   3745:                tiddlerElem = this.createTiddler(place,before,title,template);
                   3746:                }
                   3747:        if(srcElement && typeof srcElement !== "string")
                   3748:                {
                   3749:                if(anim && config.options.chkAnimate && (animate == undefined || animate == true))
                   3750:                        anim.startAnimating(new Cascade(title,srcElement,tiddlerElem,slowly),new Scroller(tiddlerElem,slowly));
                   3751:                else
                   3752:                        window.scrollTo(0,ensureVisible(tiddlerElem));
                   3753:                }
                   3754: }
                   3755: 
                   3756: // Figure out the appropriate position for a newly opened tiddler
                   3757: // srcElement - reference to the element containing the link to the tiddler -or-
                   3758: //              special positions "top", "bottom"
                   3759: // returns - reference to the tiddler that the new one should appear before (null for the bottom of the story)
                   3760: Story.prototype.positionTiddler = function(srcElement)
                   3761: {
                   3762:        var place = document.getElementById(this.container);
                   3763:        var before;
                   3764:        if(typeof srcElement == "string")
                   3765:                {
                   3766:                switch(srcElement)
                   3767:                        {
                   3768:                        case "top":
                   3769:                                before = place.firstChild;
                   3770:                                break;
                   3771:                        case "bottom":
                   3772:                                before = null;
                   3773:                                break;
                   3774:                        }
                   3775:                }
                   3776:        else
                   3777:                {
                   3778:                var after = this.findContainingTiddler(srcElement);
                   3779:                if(after == null)
                   3780:                        before = place.firstChild;
                   3781:                else if(after.nextSibling)
                   3782:                        before = after.nextSibling;
                   3783:                else
                   3784:                        before = null;
                   3785:                }
                   3786:        return before;
                   3787: }
                   3788: 
                   3789: // Create a tiddler frame at the appropriate place in a story column
                   3790: // place - reference to parent element
                   3791: // before - null, or reference to element before which to insert new tiddler
                   3792: // title - title of new tiddler
                   3793: // template - the name of the tiddler containing the template or one of the constants DEFAULT_VIEW_TEMPLATE and DEFAULT_EDIT_TEMPLATE
                   3794: Story.prototype.createTiddler = function(place,before,title,template)
                   3795: {
                   3796:        var tiddlerElem = createTiddlyElement(null,"div",this.idPrefix + title,"tiddler");
                   3797:        tiddlerElem.setAttribute("refresh","tiddler");
                   3798:        place.insertBefore(tiddlerElem,before);
                   3799:        this.refreshTiddler(title,template);
                   3800:        return tiddlerElem;
                   3801: }
                   3802: 
                   3803: // Overridable for choosing the name of the template to apply for a tiddler
                   3804: Story.prototype.chooseTemplateForTiddler = function(title,template)
                   3805: {
                   3806:        if(!template)
                   3807:                template = DEFAULT_VIEW_TEMPLATE;
                   3808:        if(template == DEFAULT_VIEW_TEMPLATE || template == DEFAULT_EDIT_TEMPLATE)
                   3809:                template = config.tiddlerTemplates[template];
                   3810:        return template;
                   3811: }
                   3812: 
                   3813: // Overridable for extracting the text of a template from a tiddler
                   3814: Story.prototype.getTemplateForTiddler = function(title,template,tiddler)
                   3815: {
                   3816:        return store.getRecursiveTiddlerText(template,null,10);
                   3817: }
                   3818: 
                   3819: // Apply a template to an existing tiddler if it is not already displayed using that template
                   3820: // title - title of tiddler to update
                   3821: // template - the name of the tiddler containing the template or one of the constants DEFAULT_VIEW_TEMPLATE and DEFAULT_EDIT_TEMPLATE
                   3822: // force - if true, forces the refresh even if the template hasn't changedd
                   3823: Story.prototype.refreshTiddler = function(title,template,force)
                   3824: {
                   3825:        var tiddlerElem = document.getElementById(this.idPrefix + title);
                   3826:        if(tiddlerElem)
                   3827:                {
                   3828:                if(tiddlerElem.getAttribute("dirty") == "true" && !force)
                   3829:                        return tiddlerElem;
                   3830:                template = this.chooseTemplateForTiddler(title,template);
                   3831:                var currTemplate = tiddlerElem.getAttribute("template");
                   3832:                if((template != currTemplate) || force)
                   3833:                        {
                   3834:                        var tiddler = store.getTiddler(title);
                   3835:                        if(!tiddler)
                   3836:                                {
                   3837:                                tiddler = new Tiddler();
                   3838:                                if(store.isShadowTiddler(title))
                   3839:                                        tiddler.set(title,store.getTiddlerText(title),config.views.wikified.shadowModifier,version.date,[],version.date);
                   3840:                                else
                   3841:                                        {
                   3842:                                        var text = template=="EditTemplate"
                   3843:                                                                ? config.views.editor.defaultText.format([title])
                   3844:                                                                : config.views.wikified.defaultText.format([title]);
                   3845:                                        tiddler.set(title,text,config.views.wikified.defaultModifier,version.date,[],version.date);
                   3846:                                        }
                   3847:                                }
                   3848:                        tiddlerElem.setAttribute("tags",tiddler.tags.join(" "));
                   3849:                        tiddlerElem.setAttribute("tiddler",title);
                   3850:                        tiddlerElem.setAttribute("template",template);
                   3851:                        var me = this;
                   3852:                        tiddlerElem.onmouseover = this.onTiddlerMouseOver;
                   3853:                        tiddlerElem.onmouseout = this.onTiddlerMouseOut;
                   3854:                        tiddlerElem.ondblclick = this.onTiddlerDblClick;
                   3855:                        tiddlerElem[window.event?"onkeydown":"onkeypress"] = this.onTiddlerKeyPress;
                   3856:                        var html = this.getTemplateForTiddler(title,template,tiddler);
                   3857:                        tiddlerElem.innerHTML = html;
                   3858:                        applyHtmlMacros(tiddlerElem,tiddler);
                   3859:                        if(store.getTaggedTiddlers(title).length > 0)
                   3860:                                addClass(tiddlerElem,"isTag");
                   3861:                        else
                   3862:                                removeClass(tiddlerElem,"isTag");
                   3863:                        if(!store.tiddlerExists(title))
                   3864:                                {
                   3865:                                if(store.isShadowTiddler(title))
                   3866:                                        addClass(tiddlerElem,"shadow");
                   3867:                                else
                   3868:                                        addClass(tiddlerElem,"missing");
                   3869:                                }
                   3870:                        else
                   3871:                                {
                   3872:                                removeClass(tiddlerElem,"shadow");
                   3873:                                removeClass(tiddlerElem,"missing");
                   3874:                                }
                   3875:                        }
                   3876:                }
                   3877:        return tiddlerElem;
                   3878: }
                   3879: 
                   3880: // Refresh all tiddlers in the Story
                   3881: Story.prototype.refreshAllTiddlers = function() 
                   3882: {
                   3883:        var place = document.getElementById(this.container);
                   3884:        var e = place.firstChild;
                   3885:        if(!e)
                   3886:                return;
                   3887:        this.refreshTiddler(e.getAttribute("tiddler"),e.getAttribute("template"),true);
                   3888:        while((e = e.nextSibling) != null) 
                   3889:                this.refreshTiddler(e.getAttribute("tiddler"),e.getAttribute("template"),true);
                   3890: }
                   3891: 
                   3892: // Default tiddler onmouseover/out event handlers
                   3893: Story.prototype.onTiddlerMouseOver = function(e)
                   3894: {
                   3895:        if(window.addClass instanceof Function)
                   3896:                addClass(this,"selected");
                   3897: }
                   3898: 
                   3899: Story.prototype.onTiddlerMouseOut = function(e)
                   3900: {
                   3901:        if(window.removeClass instanceof Function)
                   3902:                removeClass(this,"selected");
                   3903: }
                   3904: 
                   3905: // Default tiddler ondblclick event handler
                   3906: Story.prototype.onTiddlerDblClick = function(e)
                   3907: {
                   3908:        if(!e) var e = window.event;
                   3909:        var theTarget = resolveTarget(e);
                   3910:        if(theTarget && theTarget.nodeName.toLowerCase() != "input" && theTarget.nodeName.toLowerCase() != "textarea")
                   3911:                {
                   3912:                if(document.selection && document.selection.empty)
                   3913:                        document.selection.empty();
                   3914:                config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
                   3915:                e.cancelBubble = true;
                   3916:                if (e.stopPropagation) e.stopPropagation();
                   3917:                return true;
                   3918:                }
                   3919:        else
                   3920:                return false;
                   3921: }
                   3922: 
                   3923: Story.prototype.onTiddlerKeyPress = function(e)
                   3924: {
                   3925:        if(!e) var e = window.event;
                   3926:        clearMessage();
                   3927:        var consume = false; 
                   3928:        var title = this.getAttribute("tiddler");
                   3929:        var target = resolveTarget(e);
                   3930:        switch(e.keyCode)
                   3931:                {
                   3932:                case 9: // Tab
                   3933:                        if(config.options.chkInsertTabs && target.tagName.toLowerCase() == "textarea")
                   3934:                                {
                   3935:                                replaceSelection(target,String.fromCharCode(9));
                   3936:                                consume = true; 
                   3937:                                }
                   3938:                        if(config.isOpera)
                   3939:                                {
                   3940:                                target.onblur = function()
                   3941:                                        {
                   3942:                                        this.focus();
                   3943:                                        this.onblur = null;
                   3944:                                        }
                   3945:                                }
                   3946:                        break;
                   3947:                case 13: // Ctrl-Enter
                   3948:                case 10: // Ctrl-Enter on IE PC
                   3949:                case 77: // Ctrl-Enter is "M" on some platforms
                   3950:                        if(e.ctrlKey)
                   3951:                                {
                   3952:                                blurElement(this);
                   3953:                                config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
                   3954:                                consume = true;
                   3955:                                }
                   3956:                        break; 
                   3957:                case 27: // Escape
                   3958:                        blurElement(this);
                   3959:                        config.macros.toolbar.invokeCommand(this,"cancelCommand",e);
                   3960:                        consume = true;
                   3961:                        break;
                   3962:                }
                   3963:        e.cancelBubble = consume;
                   3964:        if(consume)
                   3965:                {
                   3966:                if(e.stopPropagation) e.stopPropagation(); // Stop Propagation
                   3967:                e.returnValue = true; // Cancel The Event in IE
                   3968:                if(e.preventDefault ) e.preventDefault(); // Cancel The Event in Moz
                   3969:                }
                   3970:        return(!consume);
                   3971: };
                   3972: 
                   3973: // Returns the specified field (input or textarea element) in a tiddler, otherwise the first edit field it finds
                   3974: // or null if it found no edit field at all
                   3975: Story.prototype.getTiddlerField = function(title,field)
                   3976: {
                   3977:        var tiddlerElem = document.getElementById(this.idPrefix + title);
                   3978:        var e = null;
                   3979:        if(tiddlerElem != null)
                   3980:                {
                   3981:                var children = tiddlerElem.getElementsByTagName("*");
                   3982:                for (var t=0; t<children.length; t++)
                   3983:                        {
                   3984:                        var c = children[t];
                   3985:                        if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
                   3986:                                {
                   3987:                                if(!e)
                   3988:                                        e = c;
                   3989:                                if(c.getAttribute("edit") == field)
                   3990:                                        e = c;
                   3991:                                }
                   3992:                        }
                   3993:                }
                   3994:        return e;
                   3995: }
                   3996: 
                   3997: // Focus a specified tiddler. Attempts to focus the specified field, otherwise the first edit field it finds
                   3998: Story.prototype.focusTiddler = function(title,field)
                   3999: {
                   4000:        var e = this.getTiddlerField(title,field);
                   4001:        if(e)
                   4002:                {
                   4003:                e.focus();
                   4004:                e.select();
                   4005:                }
                   4006: }
                   4007: 
                   4008: // Ensures that a specified tiddler does not have the focus
                   4009: Story.prototype.blurTiddler = function(title)
                   4010: {
                   4011:        var tiddlerElem = document.getElementById(this.idPrefix + title);
                   4012:        if(tiddlerElem != null && tiddlerElem.focus && tiddlerElem.blur)
                   4013:                {
                   4014:                tiddlerElem.focus();
                   4015:                tiddlerElem.blur();
                   4016:                }
                   4017: }
                   4018: 
                   4019: // Adds a specified value to the edit controls (if any) of a particular
                   4020: // array-formatted field of a particular tiddler (eg "tags")
                   4021: //  title - name of tiddler
                   4022: //  tag - value of field, without any [[brackets]]
                   4023: //  mode - +1 to add the tag, -1 to remove it, 0 to toggle it
                   4024: //  field - name of field (eg "tags")
                   4025: Story.prototype.setTiddlerField = function(title,tag,mode,field)
                   4026: {
                   4027:        var c = story.getTiddlerField(title,field);
                   4028: 
                   4029:        var tags = c.value.readBracketedList();
                   4030:        tags.setItem(tag,mode);
                   4031:        c.value = String.encodeTiddlyLinkList(tags);
                   4032: }
                   4033: 
                   4034: // The same as setTiddlerField but preset to the "tags" field
                   4035: Story.prototype.setTiddlerTag = function(title,tag,mode)
                   4036: {
                   4037:        Story.prototype.setTiddlerField(title,tag,mode,"tags");
                   4038: }
                   4039: 
                   4040: // Close a specified tiddler
                   4041: // title - name of tiddler to close
                   4042: // animate - whether to perform animations
                   4043: // slowly - whether to perform animations in slomo
                   4044: Story.prototype.closeTiddler = function(title,animate,slowly)
                   4045: {
                   4046:        var tiddlerElem = document.getElementById(this.idPrefix + title);
                   4047:        if(tiddlerElem != null)
                   4048:                {
                   4049:                clearMessage();
                   4050:                this.scrubTiddler(tiddlerElem);
                   4051:                if(anim && config.options.chkAnimate && animate)
                   4052:                        anim.startAnimating(new Slider(tiddlerElem,false,slowly,"all"));
                   4053:                else
                   4054:                        tiddlerElem.parentNode.removeChild(tiddlerElem);
                   4055:                }
                   4056: }
                   4057: 
                   4058: // Scrub IDs from a tiddler. This is so that the 'ghost' of a tiddler while it is being closed
                   4059: // does not interfere with things
                   4060: // tiddler - reference to the tiddler element
                   4061: Story.prototype.scrubTiddler = function(tiddlerElem)
                   4062: {
                   4063:        tiddlerElem.id = null;
                   4064: }
                   4065: 
                   4066: // Set the 'dirty' flag of a tiddler
                   4067: // title - title of tiddler to change
                   4068: // dirty - new boolean status of flag
                   4069: Story.prototype.setDirty = function(title,dirty)
                   4070: {
                   4071:        var tiddlerElem = document.getElementById(this.idPrefix + title);
                   4072:        if(tiddlerElem != null)
                   4073:                tiddlerElem.setAttribute("dirty",dirty ? "true" : "false");
                   4074: }
                   4075: 
                   4076: // Is a particular tiddler dirty (with unsaved changes)?
                   4077: Story.prototype.isDirty = function(title)
                   4078: {
                   4079:        var tiddlerElem = document.getElementById(this.idPrefix + title);
                   4080:        if(tiddlerElem != null)
                   4081:                return tiddlerElem.getAttribute("dirty") == "true";
                   4082:        return null;
                   4083: }
                   4084: 
                   4085: // Determine whether any open tiddler are dirty
                   4086: Story.prototype.areAnyDirty = function()
                   4087: {
                   4088:        var r = false;
                   4089:        this.forEachTiddler(function(title,element) {
                   4090:                if(this.isDirty(title))
                   4091:                        r = true;
                   4092:                });
                   4093:        return r;
                   4094: }
                   4095: 
                   4096: // Close all tiddlers in the story
                   4097: Story.prototype.closeAllTiddlers = function(exclude)
                   4098: {
                   4099:        clearMessage();
                   4100:        this.forEachTiddler(function(title,element) {
                   4101:                if((title != exclude) && element.getAttribute("dirty") != "true")
                   4102:                        this.closeTiddler(title);
                   4103:                });
                   4104:        window.scrollTo(0,0);
                   4105: }
                   4106: 
                   4107: // Check if there are any tiddlers in the story
                   4108: Story.prototype.isEmpty = function()
                   4109: {
                   4110:        var place = document.getElementById(this.container);
                   4111:        return(place && place.firstChild == null);
                   4112: }
                   4113: 
                   4114: // Perform a search and display the result
                   4115: // text - text to search for
                   4116: // useCaseSensitive - true for case sensitive matching
                   4117: // useRegExp - true to interpret text as a RegExp
                   4118: Story.prototype.search = function(text,useCaseSensitive,useRegExp)
                   4119: {
                   4120:        this.closeAllTiddlers();
                   4121:        highlightHack = new RegExp(useRegExp ?   text : text.escapeRegExp(),useCaseSensitive ? "mg" : "img");
                   4122:        var matches = store.search(highlightHack,"title","excludeSearch");
                   4123:        var titles = [];
                   4124:        for(var t=matches.length-1; t>=0; t--)
                   4125:                titles.push(matches[t].title);
                   4126:        this.displayTiddlers(null,titles);
                   4127:        highlightHack = null;
                   4128:        var q = useRegExp ? "/" : "'";
                   4129:        if(matches.length > 0)
                   4130:                displayMessage(config.macros.search.successMsg.format([titles.length.toString(),q + text + q]));
                   4131:        else
                   4132:                displayMessage(config.macros.search.failureMsg.format([q + text + q]));
                   4133: }
                   4134: 
                   4135: // Determine if the specified element is within a tiddler in this story
                   4136: // e - reference to an element
                   4137: // returns: reference to a tiddler element or null if none
                   4138: Story.prototype.findContainingTiddler = function(e)
                   4139: {
                   4140:        while(e && !hasClass(e,"tiddler"))
                   4141:                e = e.parentNode;
                   4142:        return(e);
                   4143: }
                   4144: 
                   4145: // Gather any saveable fields from a tiddler element
                   4146: // e - reference to an element to scan recursively
                   4147: // fields - object to contain gathered field values
                   4148: Story.prototype.gatherSaveFields = function(e,fields)
                   4149: {
                   4150:        if(e && e.getAttribute)
                   4151:                {
                   4152:                var f = e.getAttribute("edit");
                   4153:                if(f)
                   4154:                        fields[f] = e.value.replace(/\r/mg,"");;
                   4155:                if(e.hasChildNodes())
                   4156:                        {
                   4157:                        var c = e.childNodes;
                   4158:                        for(var t=0; t<c.length; t++)
                   4159:                                this.gatherSaveFields(c[t],fields)
                   4160:                        }
                   4161:                }
                   4162: }
                   4163: 
                   4164: // Determine whether a tiddler has any edit fields, and if so if their values have been changed
                   4165: // title - name of tiddler
                   4166: Story.prototype.hasChanges = function(title)
                   4167: {
                   4168:        var e = document.getElementById(this.idPrefix + title);
                   4169:        if(e != null)
                   4170:                {
                   4171:                var fields = {};
                   4172:                this.gatherSaveFields(e,fields);
                   4173:                var tiddler = store.fetchTiddler(title);
                   4174:                if (!tiddler)
                   4175:                        return false;
                   4176:                for(var n in fields)
                   4177:                        if (store.getValue(title,n) != fields[n])
                   4178:                                return true;
                   4179:                }
                   4180:        return false;
                   4181: }
                   4182: 
                   4183: // Save any open edit fields of a tiddler and updates the display as necessary
                   4184: // title - name of tiddler
                   4185: // minorUpdate - true if the modified date shouldn't be updated
                   4186: // returns: title of saved tiddler, or null if not saved
                   4187: Story.prototype.saveTiddler = function(title,minorUpdate)
                   4188: {
                   4189:        var tiddlerElem = document.getElementById(this.idPrefix + title);
                   4190:        if(tiddlerElem != null)
                   4191:                {
                   4192:                var fields = {};
                   4193:                this.gatherSaveFields(tiddlerElem,fields);
                   4194:                var newTitle = fields.title ? fields.title : title;
                   4195:                if(store.tiddlerExists(newTitle) && newTitle != title)
                   4196:                        {
                   4197:                        if(confirm(config.messages.overwriteWarning.format([newTitle.toString()])))
                   4198:                                this.closeTiddler(newTitle,false,false);
                   4199:                        else
                   4200:                                return null;
                   4201:                        }
                   4202:                tiddlerElem.id = this.idPrefix + newTitle;
                   4203:                tiddlerElem.setAttribute("tiddler",newTitle);
                   4204:                tiddlerElem.setAttribute("template",DEFAULT_VIEW_TEMPLATE);
                   4205:                tiddlerElem.setAttribute("dirty","false");
                   4206:                if(config.options.chkForceMinorUpdate)
                   4207:                        minorUpdate = !minorUpdate;
                   4208:                var newDate = new Date();
                   4209:                store.saveTiddler(title,newTitle,fields.text,config.options.txtUserName,minorUpdate ? undefined : newDate,fields.tags);
                   4210:                for (var n in fields) 
                   4211:                        if (!TiddlyWiki.isStandardField(n))
                   4212:                                store.setValue(newTitle,n,fields[n]);
                   4213:                if(config.options.chkAutoSave)
                   4214:                        saveChanges();
                   4215:                return newTitle;
                   4216:                }
                   4217:        return null;
                   4218: }
                   4219: 
                   4220: Story.prototype.permaView = function()
                   4221: {
                   4222:        var links = [];
                   4223:        this.forEachTiddler(function(title,element) {
                   4224:                links.push(String.encodeTiddlyLink(title));
                   4225:                });
                   4226:        var t = encodeURIComponent(links.join(" "));
                   4227:        if(t == "")
                   4228:                t = "#";
                   4229:        if(window.location.hash != t)
                   4230:                window.location.hash = t;
                   4231: }
                   4232: 
                   4233: // ---------------------------------------------------------------------------------
                   4234: // Message area
                   4235: // ---------------------------------------------------------------------------------
                   4236: 
                   4237: function getMessageDiv()
                   4238: {
                   4239:        var msgArea = document.getElementById("messageArea");
                   4240:        if(!msgArea)
                   4241:                return null;
                   4242:        if(!msgArea.hasChildNodes())
                   4243:                createTiddlyButton(createTiddlyElement(msgArea,"div",null,"messageToolbar"),
                   4244:                        config.messages.messageClose.text,
                   4245:                        config.messages.messageClose.tooltip,
                   4246:                        clearMessage);
                   4247:        msgArea.style.display = "block";
                   4248:        return createTiddlyElement(msgArea,"div");
                   4249: }
                   4250: 
                   4251: function displayMessage(text,linkText)
                   4252: {
                   4253:        var e = getMessageDiv();
                   4254:        if(!e)
                   4255:                {
                   4256:                alert(text);
                   4257:                return;
                   4258:                }
                   4259:        if(linkText)
                   4260:                {
                   4261:                var link = createTiddlyElement(e,"a",null,null,text);
                   4262:                link.href = linkText;
                   4263:                link.target = "_blank";
                   4264:                }
                   4265:        else
                   4266:                e.appendChild(document.createTextNode(text));
                   4267: }
                   4268: 
                   4269: function clearMessage()
                   4270: {
                   4271:        var msgArea = document.getElementById("messageArea");
                   4272:        if(msgArea)
                   4273:                {
                   4274:                removeChildren(msgArea);
                   4275:                msgArea.style.display = "none";
                   4276:                }
                   4277:        return false;
                   4278: }
                   4279: 
                   4280: // ---------------------------------------------------------------------------------
                   4281: // Refresh mechanism
                   4282: // ---------------------------------------------------------------------------------
                   4283: 
                   4284: config.refreshers = {
                   4285:        link: function(e,changeList)
                   4286:                {
                   4287:                var title = e.getAttribute("tiddlyLink");
                   4288:                refreshTiddlyLink(e,title);
                   4289:                return true;
                   4290:                },
                   4291:        
                   4292:        tiddler: function(e,changeList)
                   4293:                {
                   4294:                var title = e.getAttribute("tiddler");
                   4295:                var template = e.getAttribute("template");
                   4296:                if(changeList && changeList.indexOf(title) != -1 && !story.isDirty(title))
                   4297:                        story.refreshTiddler(title,template,true);
                   4298:                else
                   4299:                        refreshElements(e,changeList);
                   4300:                return true;
                   4301:                },
                   4302: 
                   4303:        content: function(e,changeList)
                   4304:                {
                   4305:                var title = e.getAttribute("tiddler");
                   4306:                var force = e.getAttribute("force");
                   4307:                if(force != null || changeList == null || changeList.indexOf(title) != -1)
                   4308:                        {
                   4309:                        removeChildren(e);
                   4310:                        wikify(store.getTiddlerText(title,title),e);
                   4311:                        return true;
                   4312:                        }
                   4313:                else
                   4314:                        return false;
                   4315:                },
                   4316: 
                   4317:        macro: function(e,changeList)
                   4318:                {
                   4319:                var macro = e.getAttribute("macroName");
                   4320:                var params = e.getAttribute("params");
                   4321:                if(macro)
                   4322:                        macro = config.macros[macro];
                   4323:                if(macro && macro.refresh)
                   4324:                        macro.refresh(e,params);
                   4325:                return true;
                   4326:                }
                   4327: };
                   4328: 
                   4329: function refreshElements(root,changeList)
                   4330: {
                   4331:        var nodes = root.childNodes;
                   4332:        for(var c=0; c<nodes.length; c++)
                   4333:                {
                   4334:                var e = nodes[c],type;
                   4335:                if(e.getAttribute)
                   4336:                        type = e.getAttribute("refresh");
                   4337:                else
                   4338:                        type = null;
                   4339:                var refresher = config.refreshers[type];
                   4340:                var refreshed = false;
                   4341:                if(refresher != undefined)
                   4342:                        refreshed = refresher(e,changeList);
                   4343:                if(e.hasChildNodes() && !refreshed)
                   4344:                        refreshElements(e,changeList);
                   4345:                }
                   4346: }
                   4347: 
                   4348: function applyHtmlMacros(root,tiddler)
                   4349: {
                   4350:        var e = root.firstChild;
                   4351:        while(e)
                   4352:                {
                   4353:                var nextChild = e.nextSibling;
                   4354:                if(e.getAttribute)
                   4355:                        {
                   4356:                        var macro = e.getAttribute("macro");
                   4357:                        if(macro)
                   4358:                                {
                   4359:                                var params = "";
                   4360:                                var p = macro.indexOf(" ");
                   4361:                                if(p != -1)
                   4362:                                        {
                   4363:                                        params = macro.substr(p+1);
                   4364:                                        macro = macro.substr(0,p);
                   4365:                                        }
                   4366:                                invokeMacro(e,macro,params,null,tiddler);
                   4367:                                }
                   4368:                        }
                   4369:                if(e.hasChildNodes())
                   4370:                        applyHtmlMacros(e,tiddler);
                   4371:                e = nextChild;
                   4372:                }
                   4373: }
                   4374: 
                   4375: function refreshPageTemplate(title)
                   4376: {
                   4377:        var stash = createTiddlyElement(document.body,"div");
                   4378:        stash.style.display = "none";
                   4379:        var display = document.getElementById("tiddlerDisplay");
                   4380:        var nodes,t;
                   4381:        if(display)
                   4382:                {
                   4383:                nodes = display.childNodes;
                   4384:                for(t=nodes.length-1; t>=0; t--)
                   4385:                        stash.appendChild(nodes[t]);
                   4386:                }
                   4387:        var wrapper = document.getElementById("contentWrapper");
                   4388:        if(!title)
                   4389:                title = "PageTemplate";
                   4390:        var html = store.getRecursiveTiddlerText(title,null,10);
                   4391:        wrapper.innerHTML = html;
                   4392:        applyHtmlMacros(wrapper);
                   4393:        refreshElements(wrapper);
                   4394:        display = document.getElementById("tiddlerDisplay");
                   4395:        removeChildren(display);
                   4396:        if(!display)
                   4397:                display = createTiddlyElement(wrapper,"div","tiddlerDisplay");
                   4398:        nodes = stash.childNodes;
                   4399:        for(t=nodes.length-1; t>=0; t--)
                   4400:                display.appendChild(nodes[t]);
                   4401:        stash.parentNode.removeChild(stash);
                   4402: }
                   4403: 
                   4404: function refreshDisplay(hint)
                   4405: {
                   4406:        var e = document.getElementById("contentWrapper");
                   4407:        if(typeof hint == "string")
                   4408:                hint = [hint];
                   4409:        refreshElements(e,hint);
                   4410: }
                   4411: 
                   4412: function refreshPageTitle()
                   4413: {
                   4414:        document.title = wikifyPlain("SiteTitle") + " - " + wikifyPlain("SiteSubtitle");
                   4415: }
                   4416: 
                   4417: function refreshStyles(title)
                   4418: {
                   4419:        setStylesheet(title == null ? "" : store.getRecursiveTiddlerText(title,"",10),title);
                   4420: }
                   4421: 
                   4422: function refreshColorPalette(title)
                   4423: {
                   4424:        if(!startingUp)
                   4425:                refreshAll();
                   4426: }
                   4427: 
                   4428: function refreshAll()
                   4429: {
                   4430:        refreshPageTemplate();
                   4431:        refreshDisplay();
                   4432:        refreshStyles("StyleSheetLayout");
                   4433:        refreshStyles("StyleSheetColors");
                   4434:        refreshStyles("StyleSheet");
                   4435:        refreshStyles("StyleSheetPrint");
                   4436: }
                   4437: 
                   4438: // ---------------------------------------------------------------------------------
                   4439: // Options cookie stuff
                   4440: // ---------------------------------------------------------------------------------
                   4441: 
                   4442: function loadOptionsCookie()
                   4443: {
                   4444:        if(safeMode)
                   4445:                return;
                   4446:        var cookies = document.cookie.split(";");
                   4447:        for(var c=0; c<cookies.length; c++)
                   4448:                {
                   4449:                var p = cookies[c].indexOf("=");
                   4450:                if(p != -1)
                   4451:                        {
                   4452:                        var name = cookies[c].substr(0,p).trim();
                   4453:                        var value = cookies[c].substr(p+1).trim();
                   4454:                        switch(name.substr(0,3))
                   4455:                                {
                   4456:                                case "txt":
                   4457:                                        config.options[name] = unescape(value);
                   4458:                                        break;
                   4459:                                case "chk":
                   4460:                                        config.options[name] = value == "true";
                   4461:                                        break;
                   4462:                                }
                   4463:                        }
                   4464:                }
                   4465: }
                   4466: 
                   4467: function saveOptionCookie(name)
                   4468: {
                   4469:        if(safeMode)
                   4470:                return;
                   4471:        var c = name + "=";
                   4472:        switch(name.substr(0,3))
                   4473:                {
                   4474:                case "txt":
                   4475:                        c += escape(config.options[name].toString());
                   4476:                        break;
                   4477:                case "chk":
                   4478:                        c += config.options[name] ? "true" : "false";
                   4479:                        break;
                   4480:                }
                   4481:        c += "; expires=Fri, 1 Jan 2038 12:00:00 UTC; path=/";
                   4482:        document.cookie = c;
                   4483: }
                   4484: 
                   4485: // ---------------------------------------------------------------------------------
                   4486: // Saving
                   4487: // ---------------------------------------------------------------------------------
                   4488: 
                   4489: var saveUsingSafari = false;
                   4490: 
                   4491: var startSaveArea = '<div id="' + 'storeArea">'; // Split up into two so that indexOf() of this source doesn't find it
                   4492: var endSaveArea = '</d' + 'iv>';
                   4493: 
                   4494: // If there are unsaved changes, force the user to confirm before exitting
                   4495: function confirmExit()
                   4496: {
                   4497:        hadConfirmExit = true;
                   4498:        if((store && store.isDirty && store.isDirty()) || (story && story.areAnyDirty && story.areAnyDirty()))
                   4499:                return config.messages.confirmExit;
                   4500: }
                   4501: 
                   4502: // Give the user a chance to save changes before exitting
                   4503: function checkUnsavedChanges()
                   4504: {
                   4505:        if(store && store.isDirty && store.isDirty() && window.hadConfirmExit === false)
                   4506:                {
                   4507:                if(confirm(config.messages.unsavedChangesWarning))
                   4508:                        saveChanges();
                   4509:                }
                   4510: }
                   4511: 
                   4512: function updateMarkupBlock(s,blockName,tiddlerName)
                   4513: {
                   4514:        return s.replaceChunk(
                   4515:                        "<!--%0-START-->".format([blockName]),
                   4516:                        "<!--%0-END-->".format([blockName]),
                   4517:                        "\n" + store.getRecursiveTiddlerText(tiddlerName,"") + "\n");
                   4518: }
                   4519: 
                   4520: // Save this tiddlywiki with the pending changes
                   4521: function saveChanges(onlyIfDirty)
                   4522: {
                   4523:        if(onlyIfDirty && !store.isDirty())
                   4524:                return;
                   4525:        clearMessage();
                   4526:        // Get the URL of the document
                   4527:        var originalPath = document.location.toString();
                   4528:        // Check we were loaded from a file URL
                   4529:        if(originalPath.substr(0,5) != "file:")
                   4530:                {
                   4531:                alert(config.messages.notFileUrlError);
                   4532:                if(store.tiddlerExists(config.messages.saveInstructions))
                   4533:                        story.displayTiddler(null,config.messages.saveInstructions);
                   4534:                return;
                   4535:                }
                   4536:        var localPath = getLocalPath(originalPath);
                   4537:        // Load the original file
                   4538:        var original = loadFile(localPath);
                   4539:        if(original == null)
                   4540:                {
                   4541:                alert(config.messages.cantSaveError);
                   4542:                if(store.tiddlerExists(config.messages.saveInstructions))
                   4543:                        story.displayTiddler(null,config.messages.saveInstructions);
                   4544:                return;
                   4545:                }
                   4546:        // Locate the storeArea div's
                   4547:        var posOpeningDiv = original.indexOf(startSaveArea);
                   4548:        var limitClosingDiv = original.indexOf("<!--POST-BODY-START--"+">");
                   4549:        var posClosingDiv = original.lastIndexOf(endSaveArea,limitClosingDiv == -1 ? original.length : limitClosingDiv);
                   4550:        if((posOpeningDiv == -1) || (posClosingDiv == -1))
                   4551:                {
                   4552:                alert(config.messages.invalidFileError.format([localPath]));
                   4553:                return;
                   4554:                }
                   4555:        // Save the backup
                   4556:        if(config.options.chkSaveBackups)
                   4557:                {
                   4558:                var backupPath = getBackupPath(localPath);
                   4559:                var backup = saveFile(backupPath,original);
                   4560:                if(backup)
                   4561:                        displayMessage(config.messages.backupSaved,"file://" + backupPath);
                   4562:                else
                   4563:                        alert(config.messages.backupFailed);
                   4564:                }
                   4565:        // Save Rss
                   4566:        if(config.options.chkGenerateAnRssFeed)
                   4567:                {
                   4568:                var rssPath = localPath.substr(0,localPath.lastIndexOf(".")) + ".xml";
                   4569:                var rssSave = saveFile(rssPath,convertUnicodeToUTF8(generateRss()));
                   4570:                if(rssSave)
                   4571:                        displayMessage(config.messages.rssSaved,"file://" + rssPath);
                   4572:                else
                   4573:                        alert(config.messages.rssFailed);
                   4574:                }
                   4575:        // Save empty template
                   4576:        if(config.options.chkSaveEmptyTemplate)
                   4577:                {
                   4578:                var emptyPath,p;
                   4579:                if((p = localPath.lastIndexOf("/")) != -1)
                   4580:                        emptyPath = localPath.substr(0,p) + "/empty.html";
                   4581:                else if((p = localPath.lastIndexOf("\\")) != -1)
                   4582:                        emptyPath = localPath.substr(0,p) + "\\empty.html";
                   4583:                else
                   4584:                        emptyPath = localPath + ".empty.html";
                   4585:                var empty = original.substr(0,posOpeningDiv + startSaveArea.length) + original.substr(posClosingDiv);
                   4586:                var emptySave = saveFile(emptyPath,empty);
                   4587:                if(emptySave)
                   4588:                        displayMessage(config.messages.emptySaved,"file://" + emptyPath);
                   4589:                else
                   4590:                        alert(config.messages.emptyFailed);
                   4591:                }
                   4592:        var save;
                   4593:        try 
                   4594:                {
                   4595:                // Save new file
                   4596:                var revised = original.substr(0,posOpeningDiv + startSaveArea.length) + "\n" +
                   4597:                                        convertUnicodeToUTF8(store.allTiddlersAsHtml()) + "\n" +
                   4598:                                        original.substr(posClosingDiv);
                   4599:                var newSiteTitle = convertUnicodeToUTF8((wikifyPlain("SiteTitle") + " - " + wikifyPlain("SiteSubtitle")).htmlEncode());
                   4600:                revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
                   4601:                revised = updateMarkupBlock(revised,"PRE-HEAD","MarkupPreHead");
                   4602:                revised = updateMarkupBlock(revised,"POST-HEAD","MarkupPostHead");
                   4603:                revised = updateMarkupBlock(revised,"PRE-BODY","MarkupPreBody");
                   4604:                revised = updateMarkupBlock(revised,"POST-BODY","MarkupPostBody");
                   4605:                save = saveFile(localPath,revised);
                   4606:                }
                   4607:        catch (e) 
                   4608:                {
                   4609:                showException(e);
                   4610:                }
                   4611:        if(save)
                   4612:                {
                   4613:                displayMessage(config.messages.mainSaved,"file://" + localPath);
                   4614:                store.setDirty(false);
                   4615:                }
                   4616:        else
                   4617:                alert(config.messages.mainFailed);
                   4618: }
                   4619: 
                   4620: function getLocalPath(originalPath)
                   4621: {
                   4622:        // Remove any location or query part of the URL
                   4623:        var argPos = originalPath.indexOf("?");
                   4624:        if(argPos != -1)
                   4625:                originalPath = originalPath.substr(0,argPos);
                   4626:        var hashPos = originalPath.indexOf("#");
                   4627:        if(hashPos != -1)
                   4628:                originalPath = originalPath.substr(0,hashPos);
                   4629:        // Convert file://localhost/ to file:///
                   4630:        if(originalPath.indexOf("file://localhost/") == 0)
                   4631:                originalPath = "file://" + originalPath.substr(16);
                   4632:        // Convert to a native file format assuming
                   4633:        // "file:///x:/path/path/path..." - pc local file --> "x:\path\path\path..."
                   4634:        // "file://///server/share/path/path/path..." - FireFox pc network file --> "\\server\share\path\path\path..."
                   4635:        // "file:///path/path/path..." - mac/unix local file --> "/path/path/path..."
                   4636:        // "file://server/share/path/path/path..." - pc network file --> "\\server\share\path\path\path..."
                   4637:        var localPath;
                   4638:        if(originalPath.charAt(9) == ":") // pc local file
                   4639:                localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\");
                   4640:        else if(originalPath.indexOf("file://///") == 0) // FireFox pc network file
                   4641:                localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\");
                   4642:        else if(originalPath.indexOf("file:///") == 0) // mac/unix local file
                   4643:                localPath = unescape(originalPath.substr(7));
                   4644:        else if(originalPath.indexOf("file:/") == 0) // mac/unix local file
                   4645:                localPath = unescape(originalPath.substr(5));
                   4646:        else // pc network file
                   4647:                localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\");
                   4648:        return localPath;
                   4649: }
                   4650: 
                   4651: function getBackupPath(localPath)
                   4652: {
                   4653:        var backSlash = true;
                   4654:        var dirPathPos = localPath.lastIndexOf("\\");
                   4655:        if(dirPathPos == -1)
                   4656:                {
                   4657:                dirPathPos = localPath.lastIndexOf("/");
                   4658:                backSlash = false;
                   4659:                }
                   4660:        var backupFolder = config.options.txtBackupFolder;
                   4661:        if(!backupFolder || backupFolder == "")
                   4662:                backupFolder = ".";
                   4663:        var backupPath = localPath.substr(0,dirPathPos) + (backSlash ? "\\" : "/") + backupFolder + localPath.substr(dirPathPos);
                   4664:        backupPath = backupPath.substr(0,backupPath.lastIndexOf(".")) + "." + (new Date()).convertToYYYYMMDDHHMMSSMMM() + ".html";
                   4665:        return backupPath;
                   4666: }
                   4667: 
                   4668: function generateRss()
                   4669: {
                   4670:        var s = [];
                   4671:        var d = new Date();
                   4672:        var u = store.getTiddlerText("SiteUrl");
                   4673:        // Assemble the header
                   4674:        s.push("<" + "?xml version=\"1.0\"?" + ">");
                   4675:        s.push("<rss version=\"2.0\">");
                   4676:        s.push("<channel>");
                   4677:        s.push("<title" + ">" + wikifyPlain("SiteTitle").htmlEncode() + "</title" + ">");
                   4678:        if(u)
                   4679:                s.push("<link>" + u.htmlEncode() + "</link>");
                   4680:        s.push("<description>" + wikifyPlain("SiteSubtitle").htmlEncode() + "</description>");
                   4681:        s.push("<language>en-us</language>");
                   4682:        s.push("<copyright>Copyright " + d.getFullYear() + " " + config.options.txtUserName.htmlEncode() + "</copyright>");
                   4683:        s.push("<pubDate>" + d.toGMTString() + "</pubDate>");
                   4684:        s.push("<lastBuildDate>" + d.toGMTString() + "</lastBuildDate>");
                   4685:        s.push("<docs>http://blogs.law.harvard.edu/tech/rss</docs>");
                   4686:        s.push("<generator>TiddlyWiki " + version.major + "." + version.minor + "." + version.revision + "</generator>");
                   4687:        // The body
                   4688:        var tiddlers = store.getTiddlers("modified","excludeLists");
                   4689:        var n = config.numRssItems > tiddlers.length ? 0 : tiddlers.length-config.numRssItems;
                   4690:        for (var t=tiddlers.length-1; t>=n; t--)
                   4691:                s.push(tiddlers[t].saveToRss(u));
                   4692:        // And footer
                   4693:        s.push("</channel>");
                   4694:        s.push("</rss>");
                   4695:        // Save it all
                   4696:        return s.join("\n");
                   4697: }
                   4698: 
                   4699: 
                   4700: // UTF-8 encoding rules:
                   4701: // 0x0000 - 0x007F:    0xxxxxxx
                   4702: // 0x0080 - 0x07FF:    110xxxxx 10xxxxxx
                   4703: // 0x0800 - 0xFFFF:    1110xxxx 10xxxxxx 10xxxxxx
                   4704: 
                   4705: function convertUTF8ToUnicode(u)
                   4706: {
                   4707:        if(window.netscape == undefined)
                   4708:                return manualConvertUTF8ToUnicode(u);
                   4709:        else
                   4710:                return mozConvertUTF8ToUnicode(u);
                   4711: }
                   4712: 
                   4713: function manualConvertUTF8ToUnicode(utf)
                   4714: {
                   4715:        var uni = utf;
                   4716:        var src = 0;
                   4717:        var dst = 0;
                   4718:        var b1, b2, b3;
                   4719:        var c;
                   4720:        while(src < utf.length)
                   4721:                {
                   4722:                b1 = utf.charCodeAt(src++);
                   4723:                if(b1 < 0x80)
                   4724:                        dst++;
                   4725:                else if(b1 < 0xE0)
                   4726:                        {
                   4727:                        b2 = utf.charCodeAt(src++);
                   4728:                        c = String.fromCharCode(((b1 & 0x1F) << 6) | (b2 & 0x3F));
                   4729:                        uni = uni.substring(0,dst++).concat(c,utf.substr(src));
                   4730:                        }
                   4731:                else
                   4732:                        {
                   4733:                        b2 = utf.charCodeAt(src++);
                   4734:                        b3 = utf.charCodeAt(src++);
                   4735:                        c = String.fromCharCode(((b1 & 0xF) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F));
                   4736:                        uni = uni.substring(0,dst++).concat(c,utf.substr(src));
                   4737:                        }
                   4738:        }
                   4739:        return(uni);
                   4740: }
                   4741: 
                   4742: function mozConvertUTF8ToUnicode(u)
                   4743: {
                   4744:        try
                   4745:                {
                   4746:                netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
                   4747:                var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
                   4748:                converter.charset = "UTF-8";
                   4749:                }
                   4750:        catch(e)
                   4751:                {
                   4752:                return manualConvertUTF8ToUnicode(u);
                   4753:                } // fallback
                   4754:        var s = converter.ConvertToUnicode(u);
                   4755:        var fin = converter.Finish();
                   4756:        return (fin.length > 0) ? s+fin : s;
                   4757: }
                   4758: 
                   4759: function convertUnicodeToUTF8(s)
                   4760: {
                   4761:        if(window.netscape == undefined)
                   4762:                return manualConvertUnicodeToUTF8(s);
                   4763:        else
                   4764:                return mozConvertUnicodeToUTF8(s);
                   4765: }
                   4766: 
                   4767: function manualConvertUnicodeToUTF8(s)
                   4768: {
                   4769:        var re = /[^\u0000-\u007F]/g ;
                   4770:        return s.replace(re, function($0) {return("&#" + $0.charCodeAt(0).toString() + ";");})
                   4771: }
                   4772: 
                   4773: function mozConvertUnicodeToUTF8(s)
                   4774: {
                   4775:        try
                   4776:                {
                   4777:                netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
                   4778:                var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
                   4779:                converter.charset = "UTF-8";
                   4780:                }
                   4781:        catch(e)
                   4782:                {
                   4783:                return manualConvertUnicodeToUTF8(s);
                   4784:                } // fallback
                   4785:        var u = converter.ConvertFromUnicode(s);
                   4786:        var fin = converter.Finish();
                   4787:        if(fin.length > 0)
                   4788:                return u + fin;
                   4789:        else
                   4790:                return u;
                   4791: }
                   4792: 
                   4793: function saveFile(fileUrl, content)
                   4794: {
                   4795:        var r = null;
                   4796:        if((r == null) || (r == false))
                   4797:                r = mozillaSaveFile(fileUrl, content);
                   4798:        if((r == null) || (r == false))
                   4799:                r = ieSaveFile(fileUrl, content);
                   4800:        if((r == null) || (r == false))
                   4801:                r = javaSaveFile(fileUrl, content);
                   4802:        return(r);
                   4803: }
                   4804: 
                   4805: function loadFile(fileUrl)
                   4806: {
                   4807:        var r = null;
                   4808:        if((r == null) || (r == false))
                   4809:                r = mozillaLoadFile(fileUrl);
                   4810:        if((r == null) || (r == false))
                   4811:                r = ieLoadFile(fileUrl);
                   4812:        if((r == null) || (r == false))
                   4813:                r = javaLoadFile(fileUrl);
                   4814:        return(r);
                   4815: }
                   4816: 
                   4817: // Returns null if it can't do it, false if there's an error, true if it saved OK
                   4818: function ieSaveFile(filePath, content)
                   4819: {
                   4820:        try
                   4821:                {
                   4822:                var fso = new ActiveXObject("Scripting.FileSystemObject");
                   4823:                }
                   4824:        catch(e)
                   4825:                {
                   4826:                //alert("Exception while attempting to save\n\n" + e.toString());
                   4827:                return(null);
                   4828:                }
                   4829:        var file = fso.OpenTextFile(filePath,2,-1,0);
                   4830:        file.Write(content);
                   4831:        file.Close();
                   4832:        return(true);
                   4833: }
                   4834: 
                   4835: // Returns null if it can't do it, false if there's an error, or a string of the content if successful
                   4836: function ieLoadFile(filePath)
                   4837: {
                   4838:        try
                   4839:                {
                   4840:                var fso = new ActiveXObject("Scripting.FileSystemObject");
                   4841:                var file = fso.OpenTextFile(filePath,1);
                   4842:                var content = file.ReadAll();
                   4843:                file.Close();
                   4844:                }
                   4845:        catch(e)
                   4846:                {
                   4847:                //alert("Exception while attempting to load\n\n" + e.toString());
                   4848:                return(null);
                   4849:                }
                   4850:        return(content);
                   4851: }
                   4852: 
                   4853: // Returns null if it can't do it, false if there's an error, true if it saved OK
                   4854: function mozillaSaveFile(filePath, content)
                   4855: {
                   4856:        if(window.Components)
                   4857:                try
                   4858:                        {
                   4859:                        netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
                   4860:                        var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
                   4861:                        file.initWithPath(filePath);
                   4862:                        if (!file.exists())
                   4863:                                file.create(0, 0664);
                   4864:                        var out = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
                   4865:                        out.init(file, 0x20 | 0x02, 00004,null);
                   4866:                        out.write(content, content.length);
                   4867:                        out.flush();
                   4868:                        out.close();
                   4869:                        return(true);
                   4870:                        }
                   4871:                catch(e)
                   4872:                        {
                   4873:                        //alert("Exception while attempting to save\n\n" + e);
                   4874:                        return(false);
                   4875:                        }
                   4876:        return(null);
                   4877: }
                   4878: 
                   4879: // Returns null if it can't do it, false if there's an error, or a string of the content if successful
                   4880: function mozillaLoadFile(filePath)
                   4881: {
                   4882:        if(window.Components)
                   4883:                try
                   4884:                        {
                   4885:                        netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
                   4886:                        var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
                   4887:                        file.initWithPath(filePath);
                   4888:                        if (!file.exists())
                   4889:                                return(null);
                   4890:                        var inputStream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
                   4891:                        inputStream.init(file, 0x01, 00004, null);
                   4892:                        var sInputStream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
                   4893:                        sInputStream.init(inputStream);
                   4894:                        return(sInputStream.read(sInputStream.available()));
                   4895:                        }
                   4896:                catch(e)
                   4897:                        {
                   4898:                        //alert("Exception while attempting to load\n\n" + e);
                   4899:                        return(false);
                   4900:                        }
                   4901:        return(null);
                   4902: }
                   4903: 
                   4904: function javaUrlToFilename(url)
                   4905: {
                   4906:        var f = "//localhost";
                   4907:        if(url.indexOf(f) == 0)
                   4908:                return url.substring(f.length);
                   4909:        var i = url.indexOf(":");
                   4910:        if(i > 0)
                   4911:                return url.substring(i-1);
                   4912:        return url;
                   4913: }
                   4914: 
                   4915: function javaSaveFile(filePath, content)
                   4916: {
                   4917:        try
                   4918:                {
                   4919:                if(document.applets["TiddlySaver"])
                   4920:                        return document.applets["TiddlySaver"].saveFile(javaUrlToFilename(filePath),"UTF-8",content);
                   4921:                }
                   4922:        catch(e)
                   4923:                {
                   4924:                }
                   4925:        try
                   4926:                {
                   4927:                var s = new java.io.PrintStream(new java.io.FileOutputStream(javaUrlToFilename(filePath)));
                   4928:                s.print(content);
                   4929:                s.close();
                   4930:                }
                   4931:        catch(e)
                   4932:                {
                   4933:                return null;
                   4934:                }
                   4935:        return true;
                   4936: }
                   4937: 
                   4938: function javaLoadFile(filePath)
                   4939: {
                   4940:        try
                   4941:                {
                   4942:        if(document.applets["TiddlySaver"])
                   4943:                return String(document.applets["TiddlySaver"].loadFile(javaUrlToFilename(filePath),"UTF-8"));
                   4944:                }
                   4945:        catch(e)
                   4946:                {
                   4947:                }
                   4948:        var content = [];
                   4949:        try
                   4950:                {
                   4951:                var r = new java.io.BufferedReader(new java.io.FileReader(javaUrlToFilename(filePath)));
                   4952:                var line;
                   4953:                while ((line = r.readLine()) != null)
                   4954:                        content.push(new String(line));
                   4955:                r.close();
                   4956:                }
                   4957:        catch(e)
                   4958:                {
                   4959:                return null;
                   4960:                }
                   4961:        return content.join("\n");
                   4962: }
                   4963: 
                   4964: 
                   4965: // ---------------------------------------------------------------------------------
                   4966: // Remote HTTP requests
                   4967: // ---------------------------------------------------------------------------------
                   4968: 
                   4969: // Load a file over http
                   4970: //   url - the source url
                   4971: //   callback - function to call when there's a response
                   4972: //   params - parameter object that gets passed to the callback for storing it's state
                   4973: // Return value is the underlying XMLHttpRequest object, or 'null' if there was an error
                   4974: // Callback function is called like this:
                   4975: //   callback(status,params,responseText,xhr)
                   4976: //     status - true if OK, false if error
                   4977: //     params - the parameter object provided to loadRemoteFile()
                   4978: //     responseText - the text of the file
                   4979: //     xhr - the underlying XMLHttpRequest object
                   4980: function loadRemoteFile(url,callback,params)
                   4981: {
                   4982:        // Get an xhr object
                   4983:        var x;
                   4984:        try
                   4985:                {
                   4986:                x = new XMLHttpRequest(); // Modern
                   4987:                }
                   4988:        catch(e)
                   4989:                {
                   4990:                try
                   4991:                        {
                   4992:                        x = new ActiveXObject("Msxml2.XMLHTTP"); // IE 6
                   4993:                        }
                   4994:                catch (e)
                   4995:                        {
                   4996:                        return null;
                   4997:                        }
                   4998:                }
                   4999:        // Install callback
                   5000:        x.onreadystatechange = function()
                   5001:                {
                   5002:                if (x.readyState == 4)
                   5003:                        {
                   5004:                        if ((x.status == 0 || x.status == 200) && callback)
                   5005:                                {
                   5006:                                callback(true,params,x.responseText,url,x);
                   5007:                        }
                   5008:                        else
                   5009:                                callback(false,params,null,url,x);
                   5010:                        }
                   5011:                }
                   5012:        // Send request
                   5013:        if(window.netscape && window.netscape.security && document.location.protocol.indexOf("http") == -1)
                   5014:                window.netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
                   5015:        try
                   5016:                {
                   5017:                url = url + (url.indexOf("?") < 0 ? "?" : "&") + "nocache=" + Math.random();
                   5018:                x.open("GET",url,true);
                   5019:                if (x.overrideMimeType)
                   5020:                        x.overrideMimeType("text/html");
                   5021:                x.send(null);
                   5022:                }
                   5023:        catch (e)
                   5024:                {
                   5025:                alert("Error in send " + e);
                   5026:                return null;
                   5027:                }
                   5028:        return x;
                   5029: }
                   5030: // ---------------------------------------------------------------------------------
                   5031: // TiddlyWiki-specific utility functions
                   5032: // ---------------------------------------------------------------------------------
                   5033: 
                   5034: function createTiddlyButton(theParent,theText,theTooltip,theAction,theClass,theId,theAccessKey)
                   5035: {
                   5036:        var theButton = document.createElement("a");
                   5037:        if(theAction)
                   5038:                {
                   5039:                theButton.onclick = theAction;
                   5040:                theButton.setAttribute("href","javascript:;");
                   5041:                }
                   5042:        if(theTooltip)
                   5043:                theButton.setAttribute("title",theTooltip);
                   5044:        if(theText)
                   5045:                theButton.appendChild(document.createTextNode(theText));
                   5046:        if(theClass)
                   5047:                theButton.className = theClass;
                   5048:        else
                   5049:                theButton.className = "button";
                   5050:        if(theId)
                   5051:                theButton.id = theId;
                   5052:        if(theParent)
                   5053:                theParent.appendChild(theButton);
                   5054:        if(theAccessKey)
                   5055:                theButton.setAttribute("accessKey",theAccessKey);
                   5056:        return(theButton);
                   5057: }
                   5058: 
                   5059: function createTiddlyLink(place,title,includeText,theClass,isStatic)
                   5060: {
                   5061:        var text = includeText ? title : null;
                   5062:        var i = getTiddlyLinkInfo(title,theClass)
                   5063:        var btn;
                   5064:        if(isStatic)
                   5065:                btn = createExternalLink(place,"#" + title);
                   5066:        else
                   5067:                btn = createTiddlyButton(place,text,i.subTitle,onClickTiddlerLink,i.classes);
                   5068:        btn.setAttribute("refresh","link");
                   5069:        btn.setAttribute("tiddlyLink",title);
                   5070:        return(btn);
                   5071: }
                   5072: 
                   5073: function refreshTiddlyLink(e,title)
                   5074: {
                   5075:        var i = getTiddlyLinkInfo(title,e.className);
                   5076:        e.className = i.classes;
                   5077:        e.title = i.subTitle;
                   5078: }
                   5079: 
                   5080: function getTiddlyLinkInfo(title,currClasses)
                   5081: {
                   5082:        var classes = currClasses ? currClasses.split(" ") : [];
                   5083:        classes.pushUnique("tiddlyLink");
                   5084:        var tiddler = store.fetchTiddler(title);
                   5085:        var subTitle;
                   5086:        if(tiddler)
                   5087:                {
                   5088:                subTitle = tiddler.getSubtitle();
                   5089:                classes.pushUnique("tiddlyLinkExisting");
                   5090:                classes.remove("tiddlyLinkNonExisting");
                   5091:                classes.remove("shadow");
                   5092:                }
                   5093:        else
                   5094:                {
                   5095:                classes.remove("tiddlyLinkExisting");
                   5096:                classes.pushUnique("tiddlyLinkNonExisting");
                   5097:                if(store.isShadowTiddler(title))
                   5098:                        {
                   5099:                        subTitle = config.messages.shadowedTiddlerToolTip.format([title]);
                   5100:                        classes.pushUnique("shadow");
                   5101:                        }
                   5102:                else
                   5103:                        {
                   5104:                        subTitle = config.messages.undefinedTiddlerToolTip.format([title]);
                   5105:                        classes.remove("shadow");
                   5106:                        }
                   5107:                }
                   5108:        return {classes: classes.join(" "), subTitle: subTitle};
                   5109: }
                   5110: 
                   5111: function createExternalLink(place,url)
                   5112: {
                   5113:        var theLink = document.createElement("a");
                   5114:        theLink.className = "externalLink";
                   5115:        theLink.href = url;
                   5116:        theLink.title = config.messages.externalLinkTooltip.format([url]);
                   5117:        if(config.options.chkOpenInNewWindow)
                   5118:                theLink.target = "_blank";
                   5119:        place.appendChild(theLink);
                   5120:        return(theLink);
                   5121: }
                   5122: 
                   5123: // Event handler for clicking on a tiddly link
                   5124: function onClickTiddlerLink(e)
                   5125: {
                   5126:        if (!e) var e = window.event;
                   5127:        var theTarget = resolveTarget(e);
                   5128:        var theLink = theTarget;
                   5129:        var title = null;
                   5130:        do {
                   5131:                title = theLink.getAttribute("tiddlyLink");
                   5132:                theLink = theLink.parentNode;
                   5133:        } while(title == null && theLink != null);
                   5134:        if(title)
                   5135:                {
                   5136:                var toggling = e.metaKey || e.ctrlKey;
                   5137:                if(config.options.chkToggleLinks)
                   5138:                        toggling = !toggling;
                   5139:                var opening;
                   5140:                if(toggling && document.getElementById("tiddler" + title))
                   5141:                        story.closeTiddler(title,true,e.shiftKey || e.altKey);
                   5142:                else
                   5143:                        story.displayTiddler(theTarget,title,null,true,e.shiftKey || e.altKey);
                   5144:                }
                   5145:        clearMessage();
                   5146:        return(false);
                   5147: }
                   5148: 
                   5149: // Create a button for a tag with a popup listing all the tiddlers that it tags
                   5150: function createTagButton(place,tag,excludeTiddler)
                   5151: {
                   5152:        var theTag = createTiddlyButton(place,tag,config.views.wikified.tag.tooltip.format([tag]),onClickTag);
                   5153:        theTag.setAttribute("tag",tag);
                   5154:        if(excludeTiddler)
                   5155:                theTag.setAttribute("tiddler",excludeTiddler);
                   5156:        return(theTag);
                   5157: }
                   5158: 
                   5159: // Event handler for clicking on a tiddler tag
                   5160: function onClickTag(e)
                   5161: {
                   5162:        if (!e) var e = window.event;
                   5163:        var theTarget = resolveTarget(e);
                   5164:        var popup = Popup.create(this);
                   5165:        var tag = this.getAttribute("tag");
                   5166:        var title = this.getAttribute("tiddler");
                   5167:        if(popup && tag)
                   5168:                {
                   5169:                var tagged = store.getTaggedTiddlers(tag);
                   5170:                var titles = [];
                   5171:                var li,r;
                   5172:                for(r=0;r<tagged.length;r++)
                   5173:                        if(tagged[r].title != title)
                   5174:                                titles.push(tagged[r].title);
                   5175:                var lingo = config.views.wikified.tag;
                   5176:                if(titles.length > 0)
                   5177:                        {
                   5178:                        var openAll = createTiddlyButton(createTiddlyElement(popup,"li"),lingo.openAllText.format([tag]),lingo.openAllTooltip,onClickTagOpenAll);
                   5179:                        openAll.setAttribute("tag",tag);
                   5180:                        createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
                   5181:                        for(r=0; r<titles.length; r++)
                   5182:                                {
                   5183:                                createTiddlyLink(createTiddlyElement(popup,"li"),titles[r],true);
                   5184:                                }
                   5185:                        }
                   5186:                else
                   5187:                        createTiddlyText(createTiddlyElement(popup,"li",null,"disabled"),lingo.popupNone.format([tag]));
                   5188:                createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
                   5189:                var h = createTiddlyLink(createTiddlyElement(popup,"li"),tag,false);
                   5190:                createTiddlyText(h,lingo.openTag.format([tag]));
                   5191:                }
                   5192:        Popup.show(popup,false);
                   5193:        e.cancelBubble = true;
                   5194:        if (e.stopPropagation) e.stopPropagation();
                   5195:        return(false);
                   5196: }
                   5197: 
                   5198: // Event handler for 'open all' on a tiddler popup
                   5199: function onClickTagOpenAll(e)
                   5200: {
                   5201:        if (!e) var e = window.event;
                   5202:        var tag = this.getAttribute("tag");
                   5203:        var tagged = store.getTaggedTiddlers(tag);
                   5204:        var titles = [];
                   5205:        for(var t=0; t<tagged.length; t++)
                   5206:                titles.push(tagged[t].title);
                   5207:        story.displayTiddlers(this,titles);
                   5208:        return(false);
                   5209: }
                   5210: 
                   5211: function onClickError(e)
                   5212: {
                   5213:        if (!e) var e = window.event;
                   5214:        var popup = Popup.create(this);
                   5215:        var lines = this.getAttribute("errorText").split("\n");
                   5216:        for(var t=0; t<lines.length; t++)
                   5217:                createTiddlyElement(popup,"li",null,null,lines[t]);
                   5218:        Popup.show(popup,false);
                   5219:        e.cancelBubble = true;
                   5220:        if (e.stopPropagation) e.stopPropagation();
                   5221:        return false;
                   5222: }
                   5223: 
                   5224: function createTiddlyDropDown(place,onchange,options)
                   5225: {
                   5226:        var sel = createTiddlyElement(place,"select");
                   5227:        sel.onchange = onchange;
                   5228:        for(var t=0; t<options.length; t++)
                   5229:                {
                   5230:                var e = createTiddlyElement(sel,"option",null,null,options[t].caption);
                   5231:                e.value = options[t].name;
                   5232:                }
                   5233: }
                   5234: 
                   5235: function createTiddlyError(place,title,text)
                   5236: {
                   5237:        var btn = createTiddlyButton(place,title,null,onClickError,"errorButton");
                   5238:        if (text) btn.setAttribute("errorText",text);
                   5239: }
                   5240: 
                   5241: function merge(dst,src,preserveExisting)
                   5242: {
                   5243:        for (p in src)
                   5244:                if (!preserveExisting || dst[p] === undefined)
                   5245:                        dst[p] = src[p];
                   5246:        return dst;
                   5247: }
                   5248: 
                   5249: // Returns a string containing the description of an exception, optionally prepended by a message
                   5250: function exceptionText(e, message)
                   5251: {
                   5252:        var s = e.description ? e.description : e.toString();
                   5253:        return message ? "%0:\n%1".format([message, s]) : s;
                   5254: }
                   5255: 
                   5256: // Displays an alert of an exception description with optional message
                   5257: function showException(e, message)
                   5258: {
                   5259:        alert(exceptionText(e, message));
                   5260: }
                   5261: 
                   5262: // ---------------------------------------------------------------------------------
                   5263: // Animation engine
                   5264: // ---------------------------------------------------------------------------------
                   5265: 
                   5266: function Animator()
                   5267: {
                   5268:        this.running = 0; // Incremented at start of each animation, decremented afterwards. If zero, the interval timer is disabled
                   5269:        this.timerID = 0; // ID of the timer used for animating
                   5270:        this.animations = []; // List of animations in progress
                   5271:        return this;
                   5272: }
                   5273: 
                   5274: // Start animation engine
                   5275: Animator.prototype.startAnimating = function() // Variable number of arguments
                   5276: {
                   5277:        for(var t=0; t<arguments.length; t++)
                   5278:                this.animations.push(arguments[t]);
                   5279:        if(this.running == 0)
                   5280:                {
                   5281:                var me = this;
                   5282:                this.timerID = window.setInterval(function() {me.doAnimate(me);},5);
                   5283:                }
                   5284:        this.running += arguments.length;
                   5285: }
                   5286: 
                   5287: // Perform an animation engine tick, calling each of the known animation modules
                   5288: Animator.prototype.doAnimate = function(me)
                   5289: {
                   5290:        var a = 0;
                   5291:        while(a < me.animations.length)
                   5292:                {
                   5293:                var animation = me.animations[a];
                   5294:                if(animation.tick())
                   5295:                        a++;
                   5296:                else
                   5297:                        {
                   5298:                        me.animations.splice(a,1);
                   5299:                        if(--me.running == 0)
                   5300:                                window.clearInterval(me.timerID);
                   5301:                        }
                   5302:                }
                   5303: }
                   5304: 
                   5305: // Map a 0..1 value to 0..1, but slow down at the start and end
                   5306: Animator.slowInSlowOut = function(progress)
                   5307: {
                   5308:        return(1-((Math.cos(progress * Math.PI)+1)/2));
                   5309: }
                   5310: 
                   5311: // ---------------------------------------------------------------------------------
                   5312: // Cascade animation
                   5313: // ---------------------------------------------------------------------------------
                   5314: 
                   5315: function Cascade(text,startElement,targetElement,slowly)
                   5316: {
                   5317:        var winWidth = findWindowWidth();
                   5318:        var winHeight = findWindowHeight();
                   5319:        this.elements = [];
                   5320:        this.startElement = startElement;
                   5321:        this.startLeft = findPosX(this.startElement);
                   5322:        this.startTop = findPosY(this.startElement);
                   5323:        this.startWidth = Math.min(this.startElement.offsetWidth,winWidth);
                   5324:        this.startHeight = Math.min(this.startElement.offsetHeight,winHeight);
                   5325:        this.targetElement = targetElement;
                   5326:        targetElement.style.position = "relative";
                   5327:        targetElement.style.zIndex = 2;
                   5328:        this.targetLeft = findPosX(this.targetElement);
                   5329:        this.targetTop = findPosY(this.targetElement);
                   5330:        this.targetWidth = Math.min(this.targetElement.offsetWidth,winWidth);
                   5331:        this.targetHeight = Math.min(this.targetElement.offsetHeight,winHeight);
                   5332:        this.progress = -1;
                   5333:        this.steps = slowly ? config.cascadeSlow : config.cascadeFast;
                   5334:        this.text = text;
                   5335:        this.tick();
                   5336:        return this;
                   5337: }
                   5338: 
                   5339: Cascade.prototype.tick = function()
                   5340: {
                   5341:        this.progress++;
                   5342:        if(this.progress >= this.steps)
                   5343:                {
                   5344:                while(this.elements.length > 0)
                   5345:                        this.removeTail();
                   5346:                this.targetElement.style.position = "static";
                   5347:                this.targetElement.style.zIndex = "";
                   5348:                return false;
                   5349:                }
                   5350:        else
                   5351:                {
                   5352:                if(this.elements.length > 0 && this.progress > config.cascadeDepth)
                   5353:                        this.removeTail();
                   5354:                if(this.progress < (this.steps - config.cascadeDepth))
                   5355:                        {
                   5356:                        var f = Animator.slowInSlowOut(this.progress/(this.steps - config.cascadeDepth - 1));
                   5357:                        var e = createTiddlyElement(document.body,"div",null,"cascade",this.text);
                   5358:                        e.style.zIndex = 1;
                   5359:                        e.style.left = this.startLeft + (this.targetLeft-this.startLeft) * f + "px";
                   5360:                        e.style.top = this.startTop + (this.targetTop-this.startTop) * f + "px";
                   5361:                        e.style.width = this.startWidth + (this.targetWidth-this.startWidth) * f + "px";
                   5362:                        e.style.height = this.startHeight + (this.targetHeight-this.startHeight) * f + "px";
                   5363:                        e.style.display = "block";
                   5364:                        this.elements.push(e);
                   5365:                        }
                   5366:                return true;
                   5367:                }
                   5368: }
                   5369: 
                   5370: Cascade.prototype.removeTail = function()
                   5371: {
                   5372:        var e = this.elements[0];
                   5373:        e.parentNode.removeChild(e);
                   5374:        this.elements.shift();
                   5375: }
                   5376: 
                   5377: // ---------------------------------------------------------------------------------
                   5378: // Scroller animation
                   5379: // ---------------------------------------------------------------------------------
                   5380: 
                   5381: function Scroller(targetElement,slowly)
                   5382: {
                   5383:        this.targetElement = targetElement;
                   5384:        this.startScroll = findScrollY();
                   5385:        this.targetScroll = ensureVisible(targetElement);
                   5386:        this.progress = 0;
                   5387:        this.step = slowly ? config.animSlow : config.animFast;
                   5388:        return this;
                   5389: }
                   5390: 
                   5391: Scroller.prototype.tick = function()
                   5392: {
                   5393:        this.progress += this.step;
                   5394:        if(this.progress > 1)
                   5395:                {
                   5396:                window.scrollTo(0,this.targetScroll);
                   5397:                return false;
                   5398:                }
                   5399:        else
                   5400:                {
                   5401:                var f = Animator.slowInSlowOut(this.progress);
                   5402:                window.scrollTo(0,this.startScroll + (this.targetScroll-this.startScroll) * f);
                   5403:                return true;
                   5404:                }
                   5405: }
                   5406: 
                   5407: // ---------------------------------------------------------------------------------
                   5408: // Slider animation
                   5409: // ---------------------------------------------------------------------------------
                   5410: 
                   5411: // deleteMode - "none", "all" [delete target element and it's children], [only] "children" [but not the target element]
                   5412: function Slider(element,opening,slowly,deleteMode)
                   5413: {
                   5414:        this.element = element;
                   5415:        element.style.display = "block";
                   5416:        this.deleteMode = deleteMode;
                   5417:        this.element.style.height = "auto";
                   5418:        this.realHeight = element.offsetHeight;
                   5419:        this.opening = opening;
                   5420:        this.step = slowly ? config.animSlow : config.animFast;
                   5421:        if(opening)
                   5422:                {
                   5423:                this.progress = 0;
                   5424:                element.style.height = "0px";
                   5425:                element.style.display = "block";
                   5426:                }
                   5427:        else
                   5428:                {
                   5429:                this.progress = 1;
                   5430:                this.step = -this.step;
                   5431:                }
                   5432:        element.style.overflow = "hidden";
                   5433:        return this;
                   5434: }
                   5435: 
                   5436: Slider.prototype.stop = function()
                   5437: {
                   5438:        if(this.opening)
                   5439:                {
                   5440:                this.element.style.height = "auto";
                   5441:                this.element.style.opacity = 1;
                   5442:                this.element.style.filter = "alpha(opacity:100)";
                   5443:                }
                   5444:        else
                   5445:                {
                   5446:                switch(this.deleteMode)
                   5447:                        {
                   5448:                        case "none":
                   5449:                                this.element.style.display = "none";
                   5450:                                break;
                   5451:                        case "all":
                   5452:                                this.element.parentNode.removeChild(this.element);
                   5453:                                break;
                   5454:                        case "children":
                   5455:                                removeChildren(this.element);
                   5456:                                break;
                   5457:                        }
                   5458:                }
                   5459: }
                   5460: 
                   5461: Slider.prototype.tick = function()
                   5462: {
                   5463:        this.progress += this.step;
                   5464:        if(this.progress < 0 || this.progress > 1)
                   5465:                {
                   5466:                this.stop();
                   5467:                return false;
                   5468:                }
                   5469:        else
                   5470:                {
                   5471:                var f = Animator.slowInSlowOut(this.progress);
                   5472:                var h = this.realHeight * f;
                   5473:                this.element.style.height = h + "px";
                   5474:                this.element.style.opacity = f;
                   5475:                this.element.style.filter = "alpha(opacity:" + f * 100 +")";
                   5476:                return true;
                   5477:                }
                   5478: }
                   5479: 
                   5480: // ---------------------------------------------------------------------------------
                   5481: // Popup menu
                   5482: // ---------------------------------------------------------------------------------
                   5483: 
                   5484: var Popup = {
                   5485:        stack: [] // Array of objects with members root: and popup:
                   5486:        };
                   5487: 
                   5488: Popup.create = function(root)
                   5489: {
                   5490:        Popup.remove();
                   5491:        var popup = createTiddlyElement(document.body,"ol","popup","popup");
                   5492:        Popup.stack.push({root: root, popup: popup});
                   5493:        return popup;
                   5494: }
                   5495: 
                   5496: Popup.onDocumentClick = function(e)
                   5497: {
                   5498:        if (!e) var e = window.event;
                   5499:        var target = resolveTarget(e);
                   5500:        if(e.eventPhase == undefined)
                   5501:                Popup.remove();
                   5502:        else if(e.eventPhase == Event.BUBBLING_PHASE || e.eventPhase == Event.AT_TARGET)
                   5503:                Popup.remove();
                   5504:        return true;
                   5505: }
                   5506: 
                   5507: Popup.show = function(unused,slowly)
                   5508: {
                   5509:        var curr = Popup.stack[Popup.stack.length-1];
                   5510:        var rootLeft = findPosX(curr.root);
                   5511:        var rootTop = findPosY(curr.root);
                   5512:        var rootHeight = curr.root.offsetHeight;
                   5513:        var popupLeft = rootLeft;
                   5514:        var popupTop = rootTop + rootHeight;
                   5515:        var popupWidth = curr.popup.offsetWidth;
                   5516:        var winWidth = findWindowWidth();
                   5517:        if(popupLeft + popupWidth > winWidth)
                   5518:                popupLeft = winWidth - popupWidth;
                   5519:        curr.popup.style.left = popupLeft + "px";
                   5520:        curr.popup.style.top = popupTop + "px";
                   5521:        curr.popup.style.display = "block";
                   5522:        addClass(curr.root,"highlight");
                   5523:        if(anim && config.options.chkAnimate)
                   5524:                anim.startAnimating(new Scroller(curr.popup,slowly));
                   5525:        else
                   5526:                window.scrollTo(0,ensureVisible(curr.popup));
                   5527: }
                   5528: 
                   5529: Popup.remove = function()
                   5530: {
                   5531:        if(Popup.stack.length > 0)
                   5532:                {
                   5533:                Popup.removeFrom(0);
                   5534:                }
                   5535: }
                   5536: 
                   5537: Popup.removeFrom = function(from)
                   5538: {
                   5539:        for(var t=Popup.stack.length-1; t>=from; t--)
                   5540:                {
                   5541:                var p = Popup.stack[t];
                   5542:                removeClass(p.root,"highlight");
                   5543:                p.popup.parentNode.removeChild(p.popup);
                   5544:                }
                   5545:        Popup.stack = Popup.stack.slice(0,from);
                   5546: }
                   5547: 
                   5548: // ---------------------------------------------------------------------------------
                   5549: // ListView gadget
                   5550: // ---------------------------------------------------------------------------------
                   5551: 
                   5552: var ListView = {};
                   5553: 
                   5554: // Create a listview
                   5555: //   place - where in the DOM tree to insert the listview
                   5556: //   listObject - array of objects to be included in the listview
                   5557: //   listTemplate - template for the listview
                   5558: //   callback - callback for a command being selected
                   5559: //   className - optional classname for the <table> element
                   5560: ListView.create = function(place,listObject,listTemplate,callback,className)
                   5561: {
                   5562:        var table = createTiddlyElement(place,"table",null,className ? className : "listView");
                   5563:        var thead = createTiddlyElement(table,"thead");
                   5564:        var r = createTiddlyElement(thead,"tr");
                   5565:        for(var t=0; t<listTemplate.columns.length; t++)
                   5566:                {
                   5567:                var columnTemplate = listTemplate.columns[t];
                   5568:                var c = createTiddlyElement(r,"th");
                   5569:                var colType = ListView.columnTypes[columnTemplate.type];
                   5570:                if(colType && colType.createHeader)
                   5571:                        colType.createHeader(c,columnTemplate,t);
                   5572:                }
                   5573:        var tbody = createTiddlyElement(table,"tbody");
                   5574:        for(var rc=0; rc<listObject.length; rc++)
                   5575:                {
                   5576:                rowObject = listObject[rc];
                   5577:                r = createTiddlyElement(tbody,"tr");
                   5578:                for(var c=0; c<listTemplate.rowClasses.length; c++)
                   5579:                        {
                   5580:                        if(rowObject[listTemplate.rowClasses[c].field])
                   5581:                                addClass(r,listTemplate.rowClasses[c].className);
                   5582:                        }
                   5583:                rowObject.rowElement = rowObject;
                   5584:                rowObject.colElements = {};
                   5585:                for(var cc=0; cc<listTemplate.columns.length; cc++)
                   5586:                        {
                   5587:                        var c = createTiddlyElement(r,"td");
                   5588:                        var columnTemplate = listTemplate.columns[cc];
                   5589:                        var field = columnTemplate.field;
                   5590:                        var colType = ListView.columnTypes[columnTemplate.type];
                   5591:                        if(colType && colType.createItem)
                   5592:                                colType.createItem(c,rowObject,field,columnTemplate,cc,rc);
                   5593:                        rowObject.colElements[field] = c;
                   5594:                        }
                   5595:                }
                   5596:        if(callback && listTemplate.actions)
                   5597:                createTiddlyDropDown(place,ListView.getCommandHandler(callback),listTemplate.actions);
                   5598:        if(callback && listTemplate.buttons)
                   5599:                {
                   5600:                for(t=0; t<listTemplate.buttons.length; t++)
                   5601:                        {
                   5602:                        var a = listTemplate.buttons[t];
                   5603:                        if(a && a.name != "")
                   5604:                                createTiddlyButton(place,a.caption,null,ListView.getCommandHandler(callback,a.name,a.allowEmptySelection));
                   5605:                        }
                   5606:                }
                   5607:        return table;
                   5608: }
                   5609: 
                   5610: ListView.getCommandHandler = function(callback,name,allowEmptySelection)
                   5611: {
                   5612:        return function(e)
                   5613:                {
                   5614:                var view = findRelated(this,"TABLE",null,"previousSibling");
                   5615:                var tiddlers = [];
                   5616:                ListView.forEachSelector(view,function(e,rowName) {
                   5617:                                        if(e.checked)
                   5618:                                                tiddlers.push(rowName);
                   5619:                                        });
                   5620:                if(tiddlers.length == 0 && !allowEmptySelection)
                   5621:                        alert(config.messages.nothingSelected);
                   5622:                else
                   5623:                        {
                   5624:                        if(this.nodeName.toLowerCase() == "select")
                   5625:                                {
                   5626:                                callback(view,this.value,tiddlers);
                   5627:                                this.selectedIndex = 0;
                   5628:                                }
                   5629:                        else
                   5630:                                callback(view,name,tiddlers);
                   5631:                        }
                   5632:                };
                   5633: }
                   5634: 
                   5635: // Invoke a callback for each selector checkbox in the listview
                   5636: //   view - <table> element of listView
                   5637: //   callback(checkboxElement,rowName)
                   5638: //     where
                   5639: //       checkboxElement - DOM element of checkbox
                   5640: //       rowName - name of this row as assigned by the column template
                   5641: //   result: true if at least one selector was checked
                   5642: ListView.forEachSelector = function(view,callback)
                   5643: {
                   5644:        var checkboxes = view.getElementsByTagName("input");
                   5645:        var hadOne = false;
                   5646:        for(var t=0; t<checkboxes.length; t++)
                   5647:                {
                   5648:                var cb = checkboxes[t];
                   5649:                if(cb.getAttribute("type") == "checkbox")
                   5650:                        {
                   5651:                        var rn = cb.getAttribute("rowName");
                   5652:                        if(rn)
                   5653:                                {
                   5654:                                callback(cb,rn);
                   5655:                                hadOne = true;
                   5656:                                }
                   5657:                        }
                   5658:                }
                   5659:        return hadOne;
                   5660: }
                   5661: 
                   5662: ListView.columnTypes = {};
                   5663: 
                   5664: ListView.columnTypes.String = {
                   5665:        createHeader: function(place,columnTemplate,col)
                   5666:                {
                   5667:                        createTiddlyText(place,columnTemplate.title);
                   5668:                },
                   5669:        createItem: function(place,listObject,field,columnTemplate,col,row)
                   5670:                {
                   5671:                        var v = listObject[field];
                   5672:                        if(v != undefined)
                   5673:                                createTiddlyText(place,v);
                   5674:                }
                   5675: };
                   5676: 
                   5677: ListView.columnTypes.Date = {
                   5678:        createHeader: ListView.columnTypes.String.createHeader,
                   5679:        createItem: function(place,listObject,field,columnTemplate,col,row)
                   5680:                {
                   5681:                        var v = listObject[field];
                   5682:                        if(v != undefined)
                   5683:                                createTiddlyText(place,v.formatString(columnTemplate.dateFormat));
                   5684:                }
                   5685: };
                   5686: 
                   5687: ListView.columnTypes.StringList = {
                   5688:        createHeader: ListView.columnTypes.String.createHeader,
                   5689:        createItem: function(place,listObject,field,columnTemplate,col,row)
                   5690:                {
                   5691:                        var v = listObject[field];
                   5692:                        if(v != undefined)
                   5693:                                {
                   5694:                                for(var t=0; t<v.length; t++)
                   5695:                                        {
                   5696:                                        createTiddlyText(place,v[t]);
                   5697:                                        createTiddlyElement(place,"br");
                   5698:                                        }
                   5699:                                }
                   5700:                }
                   5701: };
                   5702: 
                   5703: ListView.columnTypes.Selector = {
                   5704:        createHeader: function(place,columnTemplate,col)
                   5705:                {
                   5706:                        createTiddlyCheckbox(place,null,false,this.onHeaderChange);
                   5707:                },
                   5708:        createItem: function(place,listObject,field,columnTemplate,col,row)
                   5709:                {
                   5710:                        var e = createTiddlyCheckbox(place,null,listObject[field],null);
                   5711:                        e.setAttribute("rowName",listObject[columnTemplate.rowName]);
                   5712:                },
                   5713:        onHeaderChange: function(e)
                   5714:                {
                   5715:                        var state = this.checked;
                   5716:                        var view = findRelated(this,"TABLE");
                   5717:                        if(!view)
                   5718:                                return;
                   5719:                        ListView.forEachSelector(view,function(e,rowName) {
                   5720:                                                                e.checked = state;
                   5721:                                                        });
                   5722:                }
                   5723: };
                   5724: 
                   5725: ListView.columnTypes.Tags = {
                   5726:        createHeader: ListView.columnTypes.String.createHeader,
                   5727:        createItem: function(place,listObject,field,columnTemplate,col,row)
                   5728:                {
                   5729:                        var tags = listObject[field];
                   5730:                        createTiddlyText(place,String.encodeTiddlyLinkList(tags));
                   5731:                }
                   5732: };
                   5733: 
                   5734: ListView.columnTypes.Boolean = {
                   5735:        createHeader: ListView.columnTypes.String.createHeader,
                   5736:        createItem: function(place,listObject,field,columnTemplate,col,row)
                   5737:                {
                   5738:                        if(listObject[field] == true)
                   5739:                                createTiddlyText(place,columnTemplate.trueText);
                   5740:                        if(listObject[field] == false)
                   5741:                                createTiddlyText(place,columnTemplate.falseText);
                   5742:                }
                   5743: };
                   5744: 
                   5745: ListView.columnTypes.TagCheckbox = {
                   5746:        createHeader: ListView.columnTypes.String.createHeader,
                   5747:        createItem: function(place,listObject,field,columnTemplate,col,row)
                   5748:                {
                   5749:                        var e = createTiddlyCheckbox(place,null,listObject[field],this.onChange);
                   5750:                        e.setAttribute("tiddler",listObject.title);
                   5751:                        e.setAttribute("tag",columnTemplate.tag);
                   5752:                },
                   5753:        onChange : function(e)
                   5754:                {
                   5755:                        var tag = this.getAttribute("tag");
                   5756:                        var tiddler = this.getAttribute("tiddler");
                   5757:                        store.setTiddlerTag(tiddler,this.checked,tag);
                   5758:                }
                   5759: };
                   5760: 
                   5761: ListView.columnTypes.TiddlerLink = {
                   5762:        createHeader: ListView.columnTypes.String.createHeader,
                   5763:        createItem: function(place,listObject,field,columnTemplate,col,row)
                   5764:                {
                   5765:                        var v = listObject[field];
                   5766:                        if(v != undefined)
                   5767:                                {
                   5768:                                var link = createTiddlyLink(place,listObject[columnTemplate.tiddlerLink],false,null);
                   5769:                                createTiddlyText(link,listObject[field]);
                   5770:                                }
                   5771:                }
                   5772: };
                   5773: // ---------------------------------------------------------------------------------
                   5774: // Augmented methods for the JavaScript Number(), Array(), String() and Date() objects
                   5775: // ---------------------------------------------------------------------------------
                   5776: 
                   5777: // Clamp a number to a range
                   5778: Number.prototype.clamp = function(min,max)
                   5779: {
                   5780:        var c = this;
                   5781:        if(c < min)
                   5782:                c = min;
                   5783:        if(c > max)
                   5784:                c = max;
                   5785:        return c;
                   5786: }
                   5787: 
                   5788: // Add indexOf function if browser does not support it
                   5789: if(!Array.indexOf) {
                   5790: Array.prototype.indexOf = function(item,from)
                   5791: {
                   5792:        if(!from)
                   5793:                from = 0;
                   5794:        for(var i=from; i<this.length; i++)
                   5795:                if(this[i] === item)
                   5796:                        return i;
                   5797:        return -1;
                   5798: }}
                   5799: 
                   5800: // Find an entry in a given field of the members of an array
                   5801: Array.prototype.findByField = function(field,value)
                   5802: {
                   5803:        for(var t=0; t<this.length; t++)
                   5804:                if(this[t][field] == value)
                   5805:                        return t;
                   5806:        return null;
                   5807: }
                   5808: 
                   5809: // Return whether an entry exists in an array
                   5810: Array.prototype.contains = function(item)
                   5811: {
                   5812:        return this.indexOf(item) != -1;
                   5813: };
                   5814: 
                   5815: // Adds, removes or toggles a particular value within an array
                   5816: //  value - value to add
                   5817: //  mode - +1 to add value, -1 to remove value, 0 to toggle it
                   5818: Array.prototype.setItem = function(value,mode)
                   5819: {
                   5820:        var p = this.indexOf(value);
                   5821:        if(mode == 0)
                   5822:                mode = (p == -1) ? +1 : -1;
                   5823:        if(mode == +1)
                   5824:                {
                   5825:                if(p == -1)
                   5826:                        this.push(value);
                   5827:                }
                   5828:        else if(mode == -1)
                   5829:                {
                   5830:                if(p != -1)
                   5831:                        this.splice(p,1);
                   5832:                }
                   5833: }
                   5834: 
                   5835: // Return whether one of a list of values exists in an array
                   5836: Array.prototype.containsAny = function(items)
                   5837: {
                   5838:        for(var i=0; i<items.length; i++)
                   5839:                if (this.indexOf(items[i]) != -1)
                   5840:                        return true;
                   5841:        return false;
                   5842: };
                   5843: 
                   5844: // Return whether all of a list of values exists in an array
                   5845: Array.prototype.containsAll = function(items)
                   5846: {
                   5847:        for (var i = 0; i<items.length; i++)
                   5848:                if (this.indexOf(items[i]) == -1)
                   5849:                        return false;
                   5850:        return true;
                   5851: };
                   5852: 
                   5853: // 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
                   5854: Array.prototype.pushUnique = function(item,unique)
                   5855: {
                   5856:        if(unique != undefined && unique == false)
                   5857:                this.push(item);
                   5858:        else
                   5859:                {
                   5860:                if(this.indexOf(item) == -1)
                   5861:                        this.push(item);
                   5862:                }
                   5863: }
                   5864: 
                   5865: Array.prototype.remove = function(item)
                   5866: {
                   5867:        var p = this.indexOf(item);
                   5868:        if(p != -1)
                   5869:                this.splice(p,1);
                   5870: }
                   5871: 
                   5872: // Get characters from the right end of a string
                   5873: String.prototype.right = function(n)
                   5874: {
                   5875:        if(n < this.length)
                   5876:                return this.slice(this.length-n);
                   5877:        else
                   5878:                return this;
                   5879: }
                   5880: 
                   5881: // Trim whitespace from both ends of a string
                   5882: String.prototype.trim = function()
                   5883: {
                   5884:        return this.replace(/^\s*|\s*$/g,"");
                   5885: }
                   5886: 
                   5887: // Convert a string from a CSS style property name to a JavaScript style name ("background-color" -> "backgroundColor")
                   5888: String.prototype.unDash = function()
                   5889: {
                   5890:        var s = this.split("-");
                   5891:        if(s.length > 1)
                   5892:                for(var t=1; t<s.length; t++)
                   5893:                        s[t] = s[t].substr(0,1).toUpperCase() + s[t].substr(1);
                   5894:        return s.join("");
                   5895: }
                   5896: 
                   5897: // Substitute substrings from an array into a format string that includes '%1'-type specifiers
                   5898: String.prototype.format = function(substrings)
                   5899: {
                   5900:        var subRegExp = /(?:%(\d+))/mg;
                   5901:        var currPos = 0;
                   5902:        var r = [];
                   5903:        do {
                   5904:                var match = subRegExp.exec(this);
                   5905:                if(match && match[1])
                   5906:                        {
                   5907:                        if(match.index > currPos)
                   5908:                                r.push(this.substring(currPos,match.index));
                   5909:                        r.push(substrings[parseInt(match[1])]);
                   5910:                        currPos = subRegExp.lastIndex;
                   5911:                        }
                   5912:        } while(match);
                   5913:        if(currPos < this.length)
                   5914:                r.push(this.substring(currPos,this.length));
                   5915:        return r.join("");
                   5916: }
                   5917: 
                   5918: // Escape any special RegExp characters with that character preceded by a backslash
                   5919: String.prototype.escapeRegExp = function()
                   5920: {
                   5921:        var s = "\\^$*+?()=!|,{}[].";
                   5922:        var c = this;
                   5923:        for(var t=0; t<s.length; t++)
                   5924:                c = c.replace(new RegExp("\\" + s.substr(t,1),"g"),"\\" + s.substr(t,1));
                   5925:        return c;
                   5926: }
                   5927: 
                   5928: // Convert "\" to "\s", newlines to "\n" (and remove carriage returns)
                   5929: String.prototype.escapeLineBreaks = function()
                   5930: {
                   5931:        return this.replace(/\\/mg,"\\s").replace(/\n/mg,"\\n").replace(/\r/mg,"");
                   5932: }
                   5933: 
                   5934: // Convert "\n" to newlines, "\b" to " ", "\s" to "\" (and remove carriage returns)
                   5935: String.prototype.unescapeLineBreaks = function()
                   5936: {
                   5937:        return this.replace(/\\n/mg,"\n").replace(/\\b/mg," ").replace(/\\s/mg,"\\").replace(/\r/mg,"");
                   5938: }
                   5939: 
                   5940: // Convert & to "&amp;", < to "&lt;", > to "&gt;" and " to "&quot;"
                   5941: String.prototype.htmlEncode = function()
                   5942: {
                   5943:        return(this.replace(/&/mg,"&amp;").replace(/</mg,"&lt;").replace(/>/mg,"&gt;").replace(/\"/mg,"&quot;"));
                   5944: }
                   5945: 
                   5946: // Convert "&amp;" to &, "&lt;" to <, "&gt;" to > and "&quot;" to "
                   5947: String.prototype.htmlDecode = function()
                   5948: {
                   5949:        return(this.replace(/&amp;/mg,"&").replace(/&lt;/mg,"<").replace(/&gt;/mg,">").replace(/&quot;/mg,"\""));
                   5950: }
                   5951: 
                   5952: // Parse a space-separated string of name:value parameters where:
                   5953: //   - the name or the value can be optional (in which case separate defaults are used instead)
                   5954: //     - in case of ambiguity, a lone word is taken to be a value
                   5955: //     - if 'cascadeDefaults' is set to true, then the defaults are modified by updated by each specified name or value
                   5956: //     - name prefixes are not allowed if the 'noNames' parameter is true
                   5957: //   - if both the name and value are present they must be separated by a colon
                   5958: //   - the name and the value may both be quoted with single- or double-quotes, double-square brackets
                   5959: //   - names or values quoted with {{double-curly braces}} are evaluated as a JavaScript expression
                   5960: //     - as long as the 'allowEval' parameter is true
                   5961: // The result is an array of objects:
                   5962: //   result[0] = object with a member for each parameter name, value of that member being an array of values
                   5963: //   result[1..n] = one object for each parameter, with 'name' and 'value' members
                   5964: String.prototype.parseParams = function(defaultName,defaultValue,allowEval,noNames,cascadeDefaults)
                   5965: {
                   5966:        var parseToken = function(match,p)
                   5967:                {
                   5968:                var n;
                   5969:                if(match[p]) // Double quoted
                   5970:                        n = match[p];
                   5971:                else if(match[p+1]) // Single quoted
                   5972:                        n = match[p+1];
                   5973:                else if(match[p+2]) // Double-square-bracket quoted
                   5974:                        n = match[p+2];
                   5975:                else if(match[p+3]) // Double-brace quoted
                   5976:                        try
                   5977:                                {
                   5978:                                n = match[p+3];
                   5979:                                if(allowEval)
                   5980:                                        n = window.eval(n);
                   5981:                                }
                   5982:                        catch(e)
                   5983:                                {
                   5984:                                throw "Unable to evaluate {{" + match[p+3] + "}}: " + exceptionText(e);
                   5985:                                }
                   5986:                else if(match[p+4]) // Unquoted
                   5987:                        n = match[p+4];
                   5988:                else if(match[p+5]) // empty quote
                   5989:                        n = "";
                   5990:                return n;
                   5991:                };
                   5992:        var r = [{}];
                   5993:        var dblQuote = "(?:\"((?:(?:\\\\\")|[^\"])+)\")";
                   5994:        var sngQuote = "(?:'((?:(?:\\\\\')|[^'])+)')";
                   5995:        var dblSquare = "(?:\\[\\[((?:\\s|\\S)*?)\\]\\])";
                   5996:        var dblBrace = "(?:\\{\\{((?:\\s|\\S)*?)\\}\\})";
                   5997:        var unQuoted = noNames ? "([^\"'\\s]\\S*)" : "([^\"':\\s][^\\s:]*)";
                   5998:        var emptyQuote = "((?:\"\")|(?:''))";
                   5999:        var skipSpace = "(?:\\s*)";
                   6000:        var token = "(?:" + dblQuote + "|" + sngQuote + "|" + dblSquare + "|" + dblBrace + "|" + unQuoted + "|" + emptyQuote + ")";
                   6001:        var re = noNames
                   6002:                ? new RegExp(token,"mg")
                   6003:                : new RegExp(skipSpace + token + skipSpace + "(?:(\\:)" + skipSpace + token + ")?","mg");
                   6004:        var params = [];
                   6005:        do {
                   6006:                var match = re.exec(this);
                   6007:                if(match)
                   6008:                        {
                   6009:                        var n = parseToken(match,1);
                   6010:                        if(noNames)
                   6011:                                r.push({name: "", value: n});
                   6012:                        else
                   6013:                                {
                   6014:                                var v = parseToken(match,8);
                   6015:                                if(v == null && defaultName)
                   6016:                                        {
                   6017:                                        v = n;
                   6018:                                        n = defaultName;
                   6019:                                        }
                   6020:                                else if(v == null && defaultValue)
                   6021:                                        v = defaultValue;
                   6022:                                r.push({name: n, value: v});
                   6023:                                if(cascadeDefaults)
                   6024:                                        {
                   6025:                                        defaultName = n;
                   6026:                                        defaultValue = v;
                   6027:                                        }
                   6028:                                }
                   6029:                        }
                   6030:        } while(match);
                   6031:        // Summarise parameters into first element
                   6032:        for(var t=1; t<r.length; t++)
                   6033:                {
                   6034:                if(r[0][r[t].name])
                   6035:                        r[0][r[t].name].push(r[t].value);
                   6036:                else
                   6037:                        r[0][r[t].name] = [r[t].value];
                   6038:                }
                   6039:        return r;
                   6040: }
                   6041: 
                   6042: // Process a string list of macro parameters into an array. Parameters can be quoted with "", '',
                   6043: // [[]], {{ }} or left unquoted (and therefore space-separated). Double-braces {{}} results in
                   6044: // an *evaluated* parameter: e.g. {{config.options.txtUserName}} results in the current user's name.
                   6045: String.prototype.readMacroParams = function()
                   6046: {
                   6047:        var p = this.parseParams("list",null,true,true);
                   6048:        var n = [];
                   6049:        for(var t=1; t<p.length; t++)
                   6050:                n.push(p[t].value);
                   6051:        return n;
                   6052: }
                   6053: 
                   6054: // Process a string list of unique tiddler names into an array. Tiddler names that have spaces in them must be [[bracketed]]
                   6055: String.prototype.readBracketedList = function(unique)
                   6056: {
                   6057:        var p = this.parseParams("list",null,false,true);
                   6058:        var n = [];
                   6059:        for(var t=1; t<p.length; t++)
                   6060:                n.pushUnique(p[t].value,unique);
                   6061:        return n;
                   6062: }
                   6063: 
                   6064: // Returns array with start and end index of chunk between given start and end marker, or undefined.
                   6065: String.prototype.getChunkRange = function(start,end) 
                   6066: {
                   6067:        var s = this.indexOf(start);
                   6068:        if(s != -1)
                   6069:                {
                   6070:                s += start.length;
                   6071:                var e = this.indexOf(end,s);
                   6072:                if(e != -1)
                   6073:                        return [s, e];
                   6074:                }
                   6075: }
                   6076: 
                   6077: // Replace a chunk of a string given start and end markers
                   6078: String.prototype.replaceChunk = function(start,end,sub)
                   6079: {
                   6080:        var r = this.getChunkRange(start,end);
                   6081:        return r 
                   6082:                ? this.substring(0,r[0]) + sub + this.substring(r[1])
                   6083:                : this;
                   6084: }
                   6085: 
                   6086: // Returns a chunk of a string between start and end markers, or undefined
                   6087: String.prototype.getChunk = function(start,end)
                   6088: {
                   6089:        var r = this.getChunkRange(start,end);
                   6090:        if (r)
                   6091:                return this.substring(r[0],r[1]);
                   6092: }
                   6093: 
                   6094: 
                   6095: // Static method to bracket a string with double square brackets if it contains a space
                   6096: String.encodeTiddlyLink = function(title)
                   6097: {
                   6098:        if(title.indexOf(" ") == -1)
                   6099:                return(title);
                   6100:        else
                   6101:                return("[[" + title + "]]");
                   6102: }
                   6103: 
                   6104: // Static method to encodeTiddlyLink for every item in an array and join them with spaces
                   6105: String.encodeTiddlyLinkList = function(list)
                   6106: {
                   6107:        if(list)
                   6108:                {
                   6109:                var results = [];
                   6110:                for(var t=0; t<list.length; t++)
                   6111:                        results.push(String.encodeTiddlyLink(list[t]));
                   6112:                return results.join(" ");
                   6113:                }
                   6114:        else
                   6115:                return "";
                   6116: }
                   6117: 
                   6118: // Static method to left-pad a string with 0s to a certain width
                   6119: String.zeroPad = function(n,d)
                   6120: {
                   6121:        var s = n.toString();
                   6122:        if(s.length < d)
                   6123:                s = "000000000000000000000000000".substr(0,d-s.length) + s;
                   6124:        return(s);
                   6125: }
                   6126: 
                   6127: String.prototype.startsWith = function(prefix) 
                   6128: {
                   6129:        return !prefix || this.substring(0,prefix.length) == prefix;
                   6130: }
                   6131: 
                   6132: // Returns the first value of the given named parameter.
                   6133: //#
                   6134: //# @param params
                   6135: //#         as returned by parseParams or null/undefined
                   6136: //# @return [may be null/undefined]
                   6137: //#
                   6138: function getParam(params, name, defaultValue) {
                   6139:        if (!params)
                   6140:                return defaultValue;
                   6141:        var p = params[0][name];
                   6142:        return p ? p[0] : defaultValue;
                   6143: }
                   6144: 
                   6145: // Returns the first value of the given boolean named parameter.
                   6146: //#
                   6147: //# @param params
                   6148: //#         as returned by parseParams or null/undefined
                   6149: //#
                   6150: function getFlag(params, name, defaultValue) {
                   6151:        return !!getParam(params, name, defaultValue);
                   6152: } 
                   6153:        
                   6154: // Substitute date components into a string
                   6155: Date.prototype.formatString = function(template)
                   6156: {
                   6157:        var t = template.replace(/0hh12/g,String.zeroPad(this.getHours12(),2));
                   6158:        t = t.replace(/hh12/g,this.getHours12());
                   6159:        t = t.replace(/0hh/g,String.zeroPad(this.getHours(),2));
                   6160:        t = t.replace(/hh/g,this.getHours());
                   6161:        t = t.replace(/0mm/g,String.zeroPad(this.getMinutes(),2));
                   6162:        t = t.replace(/mm/g,this.getMinutes());
                   6163:        t = t.replace(/0ss/g,String.zeroPad(this.getSeconds(),2));
                   6164:        t = t.replace(/ss/g,this.getSeconds());
                   6165:        t = t.replace(/[ap]m/g,this.getAmPm().toLowerCase());
                   6166:        t = t.replace(/[AP]M/g,this.getAmPm().toUpperCase());
                   6167:        t = t.replace(/wYYYY/g,this.getYearForWeekNo());
                   6168:        t = t.replace(/wYY/g,String.zeroPad(this.getYearForWeekNo()-2000,2));
                   6169:        t = t.replace(/YYYY/g,this.getFullYear());
                   6170:        t = t.replace(/YY/g,String.zeroPad(this.getFullYear()-2000,2));
                   6171:        t = t.replace(/MMM/g,config.messages.dates.months[this.getMonth()]);
                   6172:        t = t.replace(/mmm/g,config.messages.dates.shortMonths[this.getMonth()]);
                   6173:        t = t.replace(/0MM/g,String.zeroPad(this.getMonth()+1,2));
                   6174:        t = t.replace(/MM/g,this.getMonth()+1);
                   6175:        t = t.replace(/0WW/g,String.zeroPad(this.getWeek(),2));
                   6176:        t = t.replace(/WW/g,this.getWeek());
                   6177:        t = t.replace(/DDD/g,config.messages.dates.days[this.getDay()]);
                   6178:        t = t.replace(/ddd/g,config.messages.dates.shortDays[this.getDay()]);
                   6179:        t = t.replace(/0DD/g,String.zeroPad(this.getDate(),2));
                   6180:        t = t.replace(/DDth/g,this.getDate()+this.daySuffix());
                   6181:        t = t.replace(/DD/g,this.getDate());
                   6182:        return t;
                   6183: }
                   6184: 
                   6185: Date.prototype.getWeek = function()
                   6186: {
                   6187:        var dt = new Date(this.getTime());
                   6188:        var d = dt.getDay();
                   6189:        if (d==0) d=7;// JavaScript Sun=0, ISO Sun=7
                   6190:        dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week to calculate weekNo
                   6191:        var n = Math.floor((dt.getTime()-new Date(dt.getFullYear(),0,1)+3600000)/86400000); 
                   6192:        return Math.floor(n/7)+1;
                   6193: }
                   6194: 
                   6195: Date.prototype.getYearForWeekNo = function()
                   6196: {
                   6197:        var dt = new Date(this.getTime());
                   6198:        var d = dt.getDay();
                   6199:        if (d==0) d=7;// JavaScript Sun=0, ISO Sun=7
                   6200:        dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week
                   6201:        return dt.getFullYear();
                   6202: }
                   6203: 
                   6204: Date.prototype.getHours12 = function()
                   6205: {
                   6206:        var h = this.getHours();
                   6207:        return h > 12 ? h-12 : ( h > 0 ? h : 12 );
                   6208: }
                   6209: 
                   6210: Date.prototype.getAmPm = function()
                   6211: {
                   6212:        return this.getHours() >= 12 ? "pm" : "am";
                   6213: }
                   6214: 
                   6215: Date.prototype.daySuffix = function()
                   6216: {
                   6217:        var num = this.getDate();
                   6218:        if (num >= 11 && num <= 13) return "th";
                   6219:        else if (num.toString().substr(-1)=="1") return "st";
                   6220:        else if (num.toString().substr(-1)=="2") return "nd";
                   6221:        else if (num.toString().substr(-1)=="3") return "rd";
                   6222:        return "th";
                   6223: }
                   6224: 
                   6225: // Convert a date to local YYYYMMDDHHMM string format
                   6226: Date.prototype.convertToLocalYYYYMMDDHHMM = function()
                   6227: {
                   6228:        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));
                   6229: }
                   6230: 
                   6231: // Convert a date to UTC YYYYMMDDHHMM string format
                   6232: Date.prototype.convertToYYYYMMDDHHMM = function()
                   6233: {
                   6234:        return(String.zeroPad(this.getUTCFullYear(),4) + String.zeroPad(this.getUTCMonth()+1,2) + String.zeroPad(this.getUTCDate(),2) + String.zeroPad(this.getUTCHours(),2) + String.zeroPad(this.getUTCMinutes(),2));
                   6235: }
                   6236: 
                   6237: // Convert a date to UTC YYYYMMDD.HHMMSSMMM string format
                   6238: Date.prototype.convertToYYYYMMDDHHMMSSMMM = function()
                   6239: {
                   6240:        return(String.zeroPad(this.getUTCFullYear(),4) + String.zeroPad(this.getUTCMonth()+1,2) + String.zeroPad(this.getUTCDate(),2) + "." + String.zeroPad(this.getUTCHours(),2) + String.zeroPad(this.getUTCMinutes(),2) + String.zeroPad(this.getUTCSeconds(),2) + String.zeroPad(this.getUTCMilliseconds(),4));
                   6241: }
                   6242: 
                   6243: // Static method to create a date from a UTC YYYYMMDDHHMM format string
                   6244: Date.convertFromYYYYMMDDHHMM = function(d)
                   6245: {
                   6246:        var theDate = new Date(Date.UTC(parseInt(d.substr(0,4),10),
                   6247:                                                        parseInt(d.substr(4,2),10)-1,
                   6248:                                                        parseInt(d.substr(6,2),10),
                   6249:                                                        parseInt(d.substr(8,2),10),
                   6250:                                                        parseInt(d.substr(10,2),10),0,0));
                   6251:        return(theDate);
                   6252: }
                   6253: 
                   6254: // ---------------------------------------------------------------------------------
                   6255: // Crypto functions and associated conversion routines
                   6256: // ---------------------------------------------------------------------------------
                   6257: 
                   6258: // Crypto "namespace"
                   6259: function Crypto() {}
                   6260: 
                   6261: // Convert a string to an array of big-endian 32-bit words
                   6262: Crypto.strToBe32s = function(str)
                   6263: {
                   6264:        var be = Array();
                   6265:        var len = Math.floor(str.length/4);
                   6266:        var i, j;
                   6267:        for(i=0, j=0; i<len; i++, j+=4)
                   6268:                {
                   6269:                be[i] = ((str.charCodeAt(j)&0xff) << 24)|((str.charCodeAt(j+1)&0xff) << 16)|((str.charCodeAt(j+2)&0xff) << 8)|(str.charCodeAt(j+3)&0xff);
                   6270:                }
                   6271:        while (j<str.length)
                   6272:                {
                   6273:                be[j>>2] |= (str.charCodeAt(j)&0xff)<<(24-(j*8)%32);
                   6274:                j++;
                   6275:                }
                   6276:        return be;
                   6277: }
                   6278: 
                   6279: // Convert an array of big-endian 32-bit words to a string
                   6280: Crypto.be32sToStr = function(be)
                   6281: {
                   6282:        var str = "";
                   6283:        for(var i=0;i<be.length*32;i+=8)
                   6284:                str += String.fromCharCode((be[i>>5]>>>(24-i%32)) & 0xff);
                   6285:        return str;
                   6286: }
                   6287: 
                   6288: // Convert an array of big-endian 32-bit words to a hex string
                   6289: Crypto.be32sToHex = function(be)
                   6290: {
                   6291:        var hex = "0123456789ABCDEF";
                   6292:        var str = "";
                   6293:        for(var i=0;i<be.length*4;i++)
                   6294:                str += hex.charAt((be[i>>2]>>((3-i%4)*8+4))&0xF) + hex.charAt((be[i>>2]>>((3-i%4)*8))&0xF);
                   6295:        return str;
                   6296: }
                   6297: 
                   6298: // Return, in hex, the SHA-1 hash of a string
                   6299: Crypto.hexSha1Str = function(str)
                   6300: {
                   6301:        return Crypto.be32sToHex(Crypto.sha1Str(str));
                   6302: }
                   6303: 
                   6304: // Return the SHA-1 hash of a string
                   6305: Crypto.sha1Str = function(str)
                   6306: {
                   6307:        return Crypto.sha1(Crypto.strToBe32s(str),str.length);
                   6308: }
                   6309: 
                   6310: // Calculate the SHA-1 hash of an array of blen bytes of big-endian 32-bit words
                   6311: Crypto.sha1 = function(x,blen)
                   6312: {
                   6313:        // Add 32-bit integers, wrapping at 32 bits
                   6314:        //# Uses 16-bit operations internally to work around bugs in some JavaScript interpreters.
                   6315:        add32 = function(a,b)
                   6316:        {
                   6317:                var lsw = (a&0xFFFF)+(b&0xFFFF);
                   6318:                var msw = (a>>16)+(b>>16)+(lsw>>16);
                   6319:                return (msw<<16)|(lsw&0xFFFF);
                   6320:        };
                   6321:        // Add five 32-bit integers, wrapping at 32 bits
                   6322:        //# Uses 16-bit operations internally to work around bugs in some JavaScript interpreters.
                   6323:        add32x5 = function(a,b,c,d,e)
                   6324:        {
                   6325:                var lsw = (a&0xFFFF)+(b&0xFFFF)+(c&0xFFFF)+(d&0xFFFF)+(e&0xFFFF);
                   6326:                var msw = (a>>16)+(b>>16)+(c>>16)+(d>>16)+(e>>16)+(lsw>>16);
                   6327:                return (msw<<16)|(lsw&0xFFFF);
                   6328:        };
                   6329:        // Bitwise rotate left a 32-bit integer by 1 bit
                   6330:        rol32 = function(n)
                   6331:        {
                   6332:                return (n>>>31)|(n<<1);
                   6333:        };
                   6334: 
                   6335:        var len = blen*8;
                   6336:        // Append padding so length in bits is 448 mod 512
                   6337:        x[len>>5] |= 0x80 << (24-len%32);
                   6338:        // Append length
                   6339:        x[((len+64>>9)<<4)+15] = len;
                   6340:        var w = Array(80);
                   6341: 
                   6342:        var k1 = 0x5A827999;
                   6343:        var k2 = 0x6ED9EBA1;
                   6344:        var k3 = 0x8F1BBCDC;
                   6345:        var k4 = 0xCA62C1D6;
                   6346: 
                   6347:        var h0 = 0x67452301;
                   6348:        var h1 = 0xEFCDAB89;
                   6349:        var h2 = 0x98BADCFE;
                   6350:        var h3 = 0x10325476;
                   6351:        var h4 = 0xC3D2E1F0;
                   6352: 
                   6353:        for(var i=0;i<x.length;i+=16)
                   6354:                {
                   6355:                var j,t;
                   6356:                var a = h0;
                   6357:                var b = h1;
                   6358:                var c = h2;
                   6359:                var d = h3;
                   6360:                var e = h4;
                   6361:                for(j = 0;j<16;j++)
                   6362:                        {
                   6363:                        w[j] = x[i+j];
                   6364:                        t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
                   6365:                        e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
                   6366:                        }
                   6367:                for(j=16;j<20;j++)
                   6368:                        {
                   6369:                        w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
                   6370:                        t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
                   6371:                        e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
                   6372:                        }
                   6373:                for(j=20;j<40;j++)
                   6374:                        {
                   6375:                        w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
                   6376:                        t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k2);
                   6377:                        e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
                   6378:                        }
                   6379:                for(j=40;j<60;j++)
                   6380:                        {
                   6381:                        w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
                   6382:                        t = add32x5(e,(a>>>27)|(a<<5),(b&c)|(d&(b|c)),w[j],k3);
                   6383:                        e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
                   6384:                        }
                   6385:                for(j=60;j<80;j++)
                   6386:                        {
                   6387:                        w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
                   6388:                        t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k4);
                   6389:                        e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
                   6390:                        }
                   6391: 
                   6392:                h0 = add32(h0,a);
                   6393:                h1 = add32(h1,b);
                   6394:                h2 = add32(h2,c);
                   6395:                h3 = add32(h3,d);
                   6396:                h4 = add32(h4,e);
                   6397:                }
                   6398:        return Array(h0,h1,h2,h3,h4);
                   6399: }
                   6400: 
                   6401: // ---------------------------------------------------------------------------------
                   6402: // RGB colour object
                   6403: // ---------------------------------------------------------------------------------
                   6404: 
                   6405: // Construct an RGB colour object from a '#rrggbb', '#rgb' or 'rgb(n,n,n)' string or from separate r,g,b values
                   6406: function RGB(r,g,b)
                   6407: {
                   6408:        this.r = 0;
                   6409:        this.g = 0;
                   6410:        this.b = 0;
                   6411:        if(typeof r == "string")
                   6412:                {
                   6413:                if(r.substr(0,1) == "#")
                   6414:                        {
                   6415:                        if(r.length == 7)
                   6416:                                {
                   6417:                                this.r = parseInt(r.substr(1,2),16)/255;
                   6418:                                this.g = parseInt(r.substr(3,2),16)/255;
                   6419:                                this.b = parseInt(r.substr(5,2),16)/255;
                   6420:                                }
                   6421:                        else
                   6422:                                {
                   6423:                                this.r = parseInt(r.substr(1,1),16)/15;
                   6424:                                this.g = parseInt(r.substr(2,1),16)/15;
                   6425:                                this.b = parseInt(r.substr(3,1),16)/15;
                   6426:                                }
                   6427:                        }
                   6428:                else
                   6429:                        {
                   6430:                        var rgbPattern = /rgb\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/ ;
                   6431:                        var c = r.match(rgbPattern);
                   6432:                        if (c)
                   6433:                                {
                   6434:                                this.r = parseInt(c[1],10)/255;
                   6435:                                this.g = parseInt(c[2],10)/255;
                   6436:                                this.b = parseInt(c[3],10)/255;
                   6437:                                }
                   6438:                        }
                   6439:                }
                   6440:        else
                   6441:                {
                   6442:                this.r = r;
                   6443:                this.g = g;
                   6444:                this.b = b;
                   6445:                }
                   6446:        return this;
                   6447: }
                   6448: 
                   6449: // Mixes this colour with another in a specified proportion
                   6450: // c = other colour to mix
                   6451: // f = 0..1 where 0 is this colour and 1 is the new colour
                   6452: // Returns an RGB object
                   6453: RGB.prototype.mix = function(c,f)
                   6454: {
                   6455:        return new RGB(this.r + (c.r-this.r) * f,this.g + (c.g-this.g) * f,this.b + (c.b-this.b) * f);
                   6456: }
                   6457: 
                   6458: // Return an rgb colour as a #rrggbb format hex string
                   6459: RGB.prototype.toString = function()
                   6460: {
                   6461:        var r = this.r.clamp(0,1);
                   6462:        var g = this.g.clamp(0,1);
                   6463:        var b = this.b.clamp(0,1);
                   6464:        return("#" + ("0" + Math.floor(r * 255).toString(16)).right(2) +
                   6465:                                 ("0" + Math.floor(g * 255).toString(16)).right(2) +
                   6466:                                 ("0" + Math.floor(b * 255).toString(16)).right(2));
                   6467: }
                   6468: 
                   6469: // ---------------------------------------------------------------------------------
                   6470: // DOM utilities - many derived from www.quirksmode.org
                   6471: // ---------------------------------------------------------------------------------
                   6472: 
                   6473: function drawGradient(place,horiz,colours)
                   6474: {
                   6475:        for(var t=0; t<= 100; t+=2)
                   6476:                {
                   6477:                var bar = document.createElement("div");
                   6478:                place.appendChild(bar);
                   6479:                bar.style.position = "absolute";
                   6480:                bar.style.left = horiz ? t + "%" : 0;
                   6481:                bar.style.top = horiz ? 0 : t + "%";
                   6482:                bar.style.width = horiz ? (101-t) + "%" : "100%";
                   6483:                bar.style.height = horiz ? "100%" : (101-t) + "%";
                   6484:                bar.style.zIndex = -1;
                   6485:                var f = t/100;
                   6486:                var p = f*(colours.length-1);
                   6487:                bar.style.backgroundColor = colours[Math.floor(p)].mix(colours[Math.ceil(p)],p-Math.floor(p)).toString();
                   6488:                }
                   6489: }
                   6490: 
                   6491: function createTiddlyText(theParent,theText)
                   6492: {
                   6493:        return theParent.appendChild(document.createTextNode(theText));
                   6494: }
                   6495: 
                   6496: function createTiddlyCheckbox(theParent,caption,checked,onChange)
                   6497: {
                   6498:        var cb = document.createElement("input");
                   6499:        cb.setAttribute("type","checkbox");
                   6500:        cb.onclick = onChange;
                   6501:        theParent.appendChild(cb);
                   6502:        cb.checked = checked;
                   6503:        cb.className = "chkOptionInput";
                   6504:        if(caption)
                   6505:                wikify(caption,theParent);
                   6506:        return cb;
                   6507: }
                   6508: 
                   6509: function createTiddlyElement(theParent,theElement,theID,theClass,theText)
                   6510: {
                   6511:        var e = document.createElement(theElement);
                   6512:        if(theClass != null)
                   6513:                e.className = theClass;
                   6514:        if(theID != null)
                   6515:                e.setAttribute("id",theID);
                   6516:        if(theText != null)
                   6517:                e.appendChild(document.createTextNode(theText));
                   6518:        if(theParent != null)
                   6519:                theParent.appendChild(e);
                   6520:        return(e);
                   6521: }
                   6522: 
                   6523: // Add an event handler
                   6524: // Thanks to John Resig, via QuirksMode
                   6525: function addEvent(obj,type,fn)
                   6526: {
                   6527:        if(obj.attachEvent)
                   6528:                {
                   6529:                obj['e'+type+fn] = fn;
                   6530:                obj[type+fn] = function(){obj['e'+type+fn](window.event);}
                   6531:                obj.attachEvent('on'+type,obj[type+fn]);
                   6532:                }
                   6533:        else
                   6534:                obj.addEventListener(type,fn,false);
                   6535: }
                   6536: 
                   6537: // Remove  an event handler
                   6538: // Thanks to John Resig, via QuirksMode
                   6539: function removeEvent(obj,type,fn)
                   6540: {
                   6541:        if(obj.detachEvent)
                   6542:                {
                   6543:                obj.detachEvent('on'+type,obj[type+fn]);
                   6544:                obj[type+fn] = null;
                   6545:                }
                   6546:        else
                   6547:                obj.removeEventListener(type,fn,false);
                   6548: }
                   6549: 
                   6550: function addClass(e,theClass)
                   6551: {
                   6552:        var currClass = e.className.split(" ");
                   6553:        if(currClass.indexOf(theClass) == -1)
                   6554:                e.className += " " + theClass;
                   6555: }
                   6556: 
                   6557: function removeClass(e,theClass)
                   6558: {
                   6559:        var currClass = e.className.split(" ");
                   6560:        var i = currClass.indexOf(theClass);
                   6561:        while(i != -1)
                   6562:                {
                   6563:                currClass.splice(i,1);
                   6564:                i = currClass.indexOf(theClass);
                   6565:                }
                   6566:        e.className = currClass.join(" ");
                   6567: }
                   6568: 
                   6569: function hasClass(e,theClass)
                   6570: {
                   6571:        if(e.className)
                   6572:                {
                   6573:                if(e.className.split(" ").indexOf(theClass) != -1)
                   6574:                        return true;
                   6575:                }
                   6576:        return false;
                   6577: }
                   6578: 
                   6579: // Find the closest relative with a given property value (property defaults to tagName, relative defaults to parentNode)
                   6580: function findRelated(e,value,name,relative)
                   6581: {
                   6582:        name = name ? name : "tagName";
                   6583:        relative = relative ? relative : "parentNode";
                   6584:        if(name == "className")
                   6585:                {
                   6586:                while(e && !hasClass(e,value))
                   6587:                        {
                   6588:                        e = e[relative];
                   6589:                        }
                   6590:                }
                   6591:        else
                   6592:                {
                   6593:                while(e && e[name] != value)
                   6594:                        {
                   6595:                        e = e[relative];
                   6596:                        }
                   6597:                }
                   6598:        return e;
                   6599: }
                   6600: 
                   6601: // Resolve the target object of an event
                   6602: function resolveTarget(e)
                   6603: {
                   6604:        var obj;
                   6605:        if (e.target)
                   6606:                obj = e.target;
                   6607:        else if (e.srcElement)
                   6608:                obj = e.srcElement;
                   6609:        if (obj.nodeType == 3) // defeat Safari bug
                   6610:                obj = obj.parentNode;
                   6611:        return(obj);
                   6612: }
                   6613: 
                   6614: // Return the content of an element as plain text with no formatting
                   6615: function getPlainText(e)
                   6616: {
                   6617:        var text = "";
                   6618:        if(e.innerText)
                   6619:                text = e.innerText;
                   6620:        else if(e.textContent)
                   6621:                text = e.textContent;
                   6622:        return text;
                   6623: }
                   6624: 
                   6625: // Get the scroll position for window.scrollTo necessary to scroll a given element into view
                   6626: function ensureVisible(e)
                   6627: {
                   6628:        var posTop = findPosY(e);
                   6629:        var posBot = posTop + e.offsetHeight;
                   6630:        var winTop = findScrollY();
                   6631:        var winHeight = findWindowHeight();
                   6632:        var winBot = winTop + winHeight;
                   6633:        if(posTop < winTop)
                   6634:                return(posTop);
                   6635:        else if(posBot > winBot)
                   6636:                {
                   6637:                if(e.offsetHeight < winHeight)
                   6638:                        return(posTop - (winHeight - e.offsetHeight));
                   6639:                else
                   6640:                        return(posTop);
                   6641:                }
                   6642:        else
                   6643:                return(winTop);
                   6644: }
                   6645: 
                   6646: // Get the current width of the display window
                   6647: function findWindowWidth()
                   6648: {
                   6649:        return(window.innerWidth ? window.innerWidth : document.documentElement.clientWidth);
                   6650: }
                   6651: 
                   6652: // Get the current height of the display window
                   6653: function findWindowHeight()
                   6654: {
                   6655:        return(window.innerHeight ? window.innerHeight : document.documentElement.clientHeight);
                   6656: }
                   6657: 
                   6658: // Get the current horizontal page scroll position
                   6659: function findScrollX()
                   6660: {
                   6661:        return(window.scrollX ? window.scrollX : document.documentElement.scrollLeft);
                   6662: }
                   6663: 
                   6664: // Get the current vertical page scroll position
                   6665: function findScrollY()
                   6666: {
                   6667:        return(window.scrollY ? window.scrollY : document.documentElement.scrollTop);
                   6668: }
                   6669: 
                   6670: function findPosX(obj)
                   6671: {
                   6672:        var curleft = 0;
                   6673:        while (obj.offsetParent)
                   6674:                {
                   6675:                curleft += obj.offsetLeft;
                   6676:                obj = obj.offsetParent;
                   6677:                }
                   6678:        return curleft;
                   6679: }
                   6680: 
                   6681: function findPosY(obj)
                   6682: {
                   6683:        var curtop = 0;
                   6684:        while (obj.offsetParent)
                   6685:                {
                   6686:                curtop += obj.offsetTop;
                   6687:                obj = obj.offsetParent;
                   6688:                }
                   6689:        return curtop;
                   6690: }
                   6691: 
                   6692: // Blur a particular element
                   6693: function blurElement(e)
                   6694: {
                   6695:        if(e != null && e.focus && e.blur)
                   6696:                {
                   6697:                e.focus();
                   6698:                e.blur();
                   6699:                }
                   6700: }
                   6701: 
                   6702: // Create a non-breaking space
                   6703: function insertSpacer(place)
                   6704: {
                   6705:        var e = document.createTextNode(String.fromCharCode(160));
                   6706:        if(place)
                   6707:                place.appendChild(e);
                   6708:        return e;
                   6709: }
                   6710: 
                   6711: // Remove all children of a node
                   6712: function removeChildren(e)
                   6713: {
                   6714:        while(e.hasChildNodes())
                   6715:                e.removeChild(e.firstChild);
                   6716: }
                   6717: 
                   6718: // Add a stylesheet, replacing any previous custom stylesheet
                   6719: function setStylesheet(s,id)
                   6720: {
                   6721:        if(!id)
                   6722:                id = "customStyleSheet";
                   6723:        var n = document.getElementById(id);
                   6724:        if(document.createStyleSheet) // Test for IE's non-standard createStyleSheet method
                   6725:                {
                   6726:                if(n)
                   6727:                        n.parentNode.removeChild(n);
                   6728:                // This failed without the &nbsp;
                   6729:                document.getElementsByTagName("head")[0].insertAdjacentHTML("beforeEnd","&nbsp;<style id='" + id + "'>" + s + "</style>");
                   6730:                }
                   6731:        else
                   6732:                {
                   6733:                if(n)
                   6734:                        n.replaceChild(document.createTextNode(s),n.firstChild);
                   6735:                else
                   6736:                        {
                   6737:                        var n = document.createElement("style");
                   6738:                        n.type = "text/css";
                   6739:                        n.id = id;
                   6740:                        n.appendChild(document.createTextNode(s));
                   6741:                        document.getElementsByTagName("head")[0].appendChild(n);
                   6742:                        }
                   6743:                }
                   6744: }
                   6745: 
                   6746: // Replace the current selection of a textarea or text input and scroll it into view
                   6747: 
                   6748: function replaceSelection(e,text)
                   6749: {
                   6750:        if (e.setSelectionRange)
                   6751:                {
                   6752:                var oldpos = e.selectionStart + 1;
                   6753:                e.value = e.value.substr(0,e.selectionStart) + text + e.value.substr(e.selectionStart);
                   6754:                e.setSelectionRange( oldpos, oldpos);
                   6755:                var linecount = e.value.split('\n').length;
                   6756:                var thisline = e.value.substr(0,e.selectionStart).split('\n').length-1;
                   6757:                e.scrollTop = Math.floor((thisline-e.rows/2)*e.scrollHeight/linecount);
                   6758:                }
                   6759:        else if (document.selection)
                   6760:                {
                   6761:                var range = document.selection.createRange();
                   6762:                if (range.parentElement() == e)
                   6763:                        {
                   6764:                        var isCollapsed = range.text == "";
                   6765:                        range.text = text;
                   6766:                         if (!isCollapsed)
                   6767:                                {
                   6768:                                range.moveStart('character', -text.length);
                   6769:                                range.select();
                   6770:                                }
                   6771:                        }
                   6772:                }
                   6773: }
                   6774: 
                   6775: // Returns the text of the given (text) node, possibly merging subsequent text nodes
                   6776: function getNodeText(e)
                   6777: {
                   6778:        var t = ""; 
                   6779:        while (e && e.nodeName == "#text")
                   6780:                {
                   6781:                t += e.nodeValue;
                   6782:                e = e.nextSibling;
                   6783:                }
                   6784:        return t;
                   6785: }
                   6786: //# -------------------------
                   6787: //# LoaderBase: A (abstract) storage loader that loads the tiddlers from a list of HTML elements.
                   6788: //# The format of the elements is defined by subclasses of this loader through the internalizeTiddler implementation.
                   6789: //# Subclasses must implement:
                   6790: //#                    function getTitle(store, e)
                   6791: //#
                   6792: //# store must implement:
                   6793: //#                    function createTiddler(title).
                   6794: //#
                   6795: 
                   6796: function LoaderBase()
                   6797: {
                   6798: }
                   6799: 
                   6800: LoaderBase.prototype.loadTiddler = function(store,e,tiddlers)
                   6801: {
                   6802:        var title = this.getTitle(store, e);
                   6803:        if (title)
                   6804:                {
                   6805:                var tiddler = store.createTiddler(title);
                   6806:                this.internalizeTiddler(store, tiddler, title, e);
                   6807:                tiddlers.push(tiddler);
                   6808:                }
                   6809: }
                   6810: 
                   6811: LoaderBase.prototype.loadTiddlers = function(store,nodes)
                   6812: {
                   6813:        var tiddlers = [];
                   6814:        for (var t = 0; t < nodes.length; t++)
                   6815:                {
                   6816:                try
                   6817:                        {
                   6818:                        this.loadTiddler(store, nodes[t], tiddlers);
                   6819:                        }
                   6820:                catch(e)
                   6821:                        {
                   6822:                        showException(e, config.messages.tiddlerLoadError.format([this.getTitle(store, nodes[t])]));
                   6823:                        }
                   6824:                }
                   6825:        return tiddlers;
                   6826: }
                   6827:        
                   6828: //# -------------------------
                   6829: //# SaverBase: a (abstract) storage saver that externalizes all tiddlers into a string, 
                   6830: //# with every tiddler individually externalized (using this.externalizeTiddler) and joined with newlines 
                   6831: //# Subclasses must implement:
                   6832: //#                    function externalizeTiddler(store, tiddler)
                   6833: //#
                   6834: //# store must implement:
                   6835: //#                    function getTiddlers(sortByFieldName)
                   6836: //#
                   6837: 
                   6838: function SaverBase()
                   6839: {
                   6840: }
                   6841: 
                   6842: SaverBase.prototype.externalize = function(store) 
                   6843: {
                   6844:        var results = [];
                   6845:        var tiddlers = store.getTiddlers("title");
                   6846:        for (var t = 0; t < tiddlers.length; t++)
                   6847:                results.push(this.externalizeTiddler(store, tiddlers[t]));
                   6848:        return results.join("\n");
                   6849: }
                   6850: //--------------------------------
                   6851: // TW21Loader (inherits from LoaderBase)
                   6852: 
                   6853: function TW21Loader() {};
                   6854: 
                   6855: TW21Loader.prototype = new LoaderBase();
                   6856: 
                   6857: TW21Loader.prototype.getTitle = function(store, e) {
                   6858:        var title = null;
                   6859:        if(e.getAttribute)
                   6860:                title = e.getAttribute("tiddler");
                   6861:        if(!title && e.id) {    
                   6862:                var lenPrefix = store.idPrefix.length;
                   6863:                if (e.id.substr(0,lenPrefix) == store.idPrefix)
                   6864:                        title = e.id.substr(lenPrefix);
                   6865:        }
                   6866:        return title;
                   6867: }
                   6868: 
                   6869: TW21Loader.prototype.internalizeTiddler = function(store, tiddler, title, data) {
                   6870:        var text = getNodeText(data.firstChild).unescapeLineBreaks();
                   6871:        var modifier = data.getAttribute("modifier");
                   6872:        var modified = Date.convertFromYYYYMMDDHHMM(data.getAttribute("modified"));
                   6873:        var c = data.getAttribute("created");
                   6874:        var created = c ? Date.convertFromYYYYMMDDHHMM(c) : modified;
                   6875:        var tags = data.getAttribute("tags");
                   6876:        var fields = {};
                   6877:        var attrs = data.attributes;
                   6878:        for(var i = attrs.length-1; i >= 0; i--) {
                   6879:                var name = attrs[i].name;
                   6880:                if (attrs[i].specified && !TiddlyWiki.isStandardField(name)) {
                   6881:                        fields[name] = attrs[i].value.unescapeLineBreaks();
                   6882:                }
                   6883:        }
                   6884:        tiddler.assign(title,text,modifier,modified,tags,created, fields);
                   6885:        return tiddler;
                   6886: };
                   6887: 
                   6888: //--------------------------------
                   6889: // TW21Saver (inherits from SaverBase)
                   6890: 
                   6891: function TW21Saver() {};
                   6892: 
                   6893: TW21Saver.prototype = new SaverBase();
                   6894: 
                   6895: TW21Saver.prototype.externalizeTiddler = function(store, tiddler) 
                   6896: {
                   6897:        try {
                   6898:                var extendedFieldAttributes = "";
                   6899:                store.forEachField(tiddler, 
                   6900:                        function(tiddler, fieldName, value) {
                   6901:                                // don't store stuff from the temp namespace
                   6902:                                if (!fieldName.match(/^temp\./))
                   6903:                                        extendedFieldAttributes += ' %0="%1"'.format([fieldName, value.escapeLineBreaks().htmlEncode()]);
                   6904:                        }, true);
                   6905:                return '<div tiddler="%0" modifier="%1" modified="%2" created="%3" tags="%4"%6>%5</div>'.format([
                   6906:                                tiddler.title.htmlEncode(),
                   6907:                                tiddler.modifier.htmlEncode(),
                   6908:                                tiddler.modified.convertToYYYYMMDDHHMM(),
                   6909:                                tiddler.created.convertToYYYYMMDDHHMM(),
                   6910:                                tiddler.getTags().htmlEncode(),
                   6911:                                tiddler.escapeLineBreaks().htmlEncode(),
                   6912:                                extendedFieldAttributes
                   6913:                        ]);
                   6914:        } catch (e) {
                   6915:                throw exceptionText(e, config.messages.tiddlerSaveError.format([tiddler.title]));
                   6916:        }
                   6917: }
                   6918: 
                   6919: // ---------------------------------------------------------------------------------
                   6920: // Deprecated code
                   6921: // ---------------------------------------------------------------------------------
                   6922: 
                   6923: // @Deprecated: Use createElementAndWikify and this.termRegExp instead
                   6924: config.formatterHelpers.charFormatHelper = function(w)
                   6925: {
                   6926:        w.subWikify(createTiddlyElement(w.output,this.element),this.terminator);
                   6927: }
                   6928: 
                   6929: // @Deprecated: Use enclosedTextHelper and this.lookaheadRegExp instead
                   6930: config.formatterHelpers.monospacedByLineHelper = function(w)
                   6931: {
                   6932:        var lookaheadRegExp = new RegExp(this.lookahead,"mg");
                   6933:        lookaheadRegExp.lastIndex = w.matchStart;
                   6934:        var lookaheadMatch = lookaheadRegExp.exec(w.source);
                   6935:        if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
                   6936:                {
                   6937:                var text = lookaheadMatch[1];
                   6938:                if(config.browser.isIE)
                   6939:                        text = text.replace(/\n/g,"\r");
                   6940:                createTiddlyElement(w.output,"pre",null,null,text);
                   6941:                w.nextMatch = lookaheadRegExp.lastIndex;
                   6942:                }
                   6943: }
                   6944: 
                   6945: // @Deprecated: Use <br> or <br /> instead of <<br>>
                   6946: config.macros.br.handler = function(place)
                   6947: {
                   6948:        createTiddlyElement(place,"br");
                   6949: }
                   6950: 
                   6951: // Find an entry in an array. Returns the array index or null
                   6952: // @Deprecated: Use indexOf instead
                   6953: Array.prototype.find = function(item)
                   6954: {
                   6955:        var i = this.indexOf(item);
                   6956:        return i == -1 ? null : i;
                   6957: }
                   6958: 
                   6959: // Load a tiddler from an HTML DIV. The caller should make sure to later call Tiddler.changed()
                   6960: // @Deprecated: Use store.getLoader().internalizeTiddler instead
                   6961: Tiddler.prototype.loadFromDiv = function(divRef,title)
                   6962: {
                   6963:        return store.getLoader().internalizeTiddler(store,this,title,divRef);
                   6964: }
                   6965: 
                   6966: // Format the text for storage in an HTML DIV
                   6967: // @Deprecated Use store.getSaver().externalizeTiddler instead.
                   6968: Tiddler.prototype.saveToDiv = function()
                   6969: {
                   6970:        return store.getSaver().externalizeTiddler(store,this);
                   6971: }
                   6972: 
                   6973: // @Deprecated: Use store.allTiddlersAsHtml() instead
                   6974: function allTiddlersAsHtml()
                   6975: {
                   6976:        return store.allTiddlersAsHtml();
                   6977: }
                   6978: 
                   6979: // @Deprecated: Use refreshPageTemplate instead
                   6980: function applyPageTemplate(title)
                   6981: {
                   6982:        refreshPageTemplate(title);
                   6983: }
                   6984: 
                   6985: // @Deprecated: Use story.displayTiddlers instead
                   6986: function displayTiddlers(srcElement,titles,template,unused1,unused2,animate,slowly)
                   6987: {
                   6988:        story.displayTiddlers(srcElement,titles,template,animate,slowly);
                   6989: }
                   6990: 
                   6991: // @Deprecated: Use story.displayTiddler instead
                   6992: function displayTiddler(srcElement,title,template,unused1,unused2,animate,slowly)
                   6993: {
                   6994:        story.displayTiddler(srcElement,title,template,animate,slowly);
                   6995: }
                   6996: 
                   6997: // @Deprecated: Use functions on right hand side directly instead
                   6998: var createTiddlerPopup = Popup.create;
                   6999: var scrollToTiddlerPopup = Popup.show;
                   7000: var hideTiddlerPopup = Popup.remove;
                   7001: 
                   7002: // @Deprecated: Use right hand side directly instead
                   7003: var regexpBackSlashEn = new RegExp("\\\\n","mg");
                   7004: var regexpBackSlash = new RegExp("\\\\","mg");
                   7005: var regexpBackSlashEss = new RegExp("\\\\s","mg");
                   7006: var regexpNewLine = new RegExp("\n","mg");
                   7007: var regexpCarriageReturn = new RegExp("\r","mg");
                   7008: // ---------------------------------------------------------------------------------
                   7009: // End of scripts
                   7010: merge(config.shadowTiddlers,{SiteTitle:'DevFire'});
                   7011: merge(config.shadowTiddlers,{MainMenu:"PageTemplate\nStyleSheet\nMainMenu\nDefaultTiddlers"});
                   7012: merge(config.shadowTiddlers,{SiteSubtitle:"a theme for ~TiddlyWiki"});
                   7013: merge(config.shadowTiddlers,{DefaultTiddlers:"LorumIpsum"});
                   7014: merge(config.shadowTiddlers,{LorumIpsum:"Aenean eros arcu, condimentum nec, dapibus ut, tincidunt sit amet, urna. Quisque viverra, eros sed imperdiet iaculis, est risus facilisis quam, id malesuada arcu nulla luctus urna. Nullam et est. Vestibulum velit sem, faucibus cursus, dapibus vestibulum, pellentesque et, urna. Donec luctus. Donec lectus. Aliquam eget eros facilisis tortor feugiat sollicitudin. Integer lobortis vulputate sapien. Sed iaculis erat ac nunc. Etiam eu enim. Mauris ipsum urna, rhoncus at, bibendum sit amet, euismod eget, dolor. Mauris fermentum quam vitae ligula. Vestibulum in libero feugiat justo dictum consectetuer. Vestibulum euismod purus eget elit. Nunc sed massa porta elit bibendum posuere. Nunc pulvinar justo sit amet odio. In sed est. Phasellus ornare elementum nulla. Nulla ipsum neque, cursus a, viverra a, imperdiet at, enim. Quisque facilisis, diam sed accumsan suscipit, odio arcu hendrerit dolor, quis aliquet massa nulla nec sem.\n!heading 1\n!!heading 2\n!!!heading3\n----\n<<tag button>>\nThis is a link to a [[StyleSheet]] tiddler.\n\n> This is a blockquote\n> This is a blockquote\n> This is a blockquote\n|>|>| !This is a header |h\n|column1|column2|column3|\n|row2| row2 |row2|\n|column1|column2|column3|\n|row2| row2 |row2|\n|column1|column2|column3|\n|row2| row2 |row2|"});
                   7015: // ---------------------------------------------------------------------------------
                   7016: //]]>
                   7017: </script>
                   7018: <style type="text/css">
                   7019: 
                   7020: #saveTest {
                   7021:        display: none;
                   7022: }
                   7023: 
                   7024: .zoomer {
                   7025:        display: none;
                   7026: }
                   7027: 
                   7028: #messageArea {
                   7029:        display: none;
                   7030: }
                   7031: 
                   7032: #copyright {
                   7033:        display: none;
                   7034: }
                   7035: 
                   7036: .popup {
                   7037:        position: absolute;
                   7038: }
                   7039: 
                   7040: #storeArea {
                   7041:        display: none;
                   7042:        margin: 4em 10em 3em;
                   7043: }
                   7044: 
                   7045: #storeArea div {
                   7046:  padding: 0.5em;
                   7047:  margin: 1em 0em 0em 0em;
                   7048:  border-color: #f0f0f0 #606060 #404040 #d0d0d0; 
                   7049:  border-style: solid; 
                   7050:  border-width: 2px;
                   7051:  overflow: auto;
                   7052: }
                   7053: 
                   7054: #javascriptWarning {
                   7055:        width: 100%;
                   7056:        text-align: center;
                   7057:        font-weight: bold;
                   7058:        background-color: #dd1100;
                   7059:        color: #fff;
                   7060:        padding:1em 0em; 
                   7061: }
                   7062: 
                   7063: </style>
                   7064: <!--POST-HEAD-START-->
                   7065: 
                   7066: <!--POST-HEAD-END-->
                   7067: </head>
                   7068: <body onload="main();" onunload="if(window.checkUnsavedChanges) checkUnsavedChanges();">
                   7069: <!--PRE-BODY-START-->
                   7070: 
                   7071: <!--PRE-BODY-END-->
                   7072:        <script type="text/javascript">
                   7073: //<![CDATA[
                   7074: if (useJavaSaver)
                   7075:        document.write("<applet style='position:absolute;left:-1px' name='TiddlySaver' code='TiddlySaver.class' archive='TiddlySaver.jar' width='1' height='1'></applet>");
                   7076: //]]>
                   7077:        </script>
                   7078:        <div id="copyright">
                   7079:        Welcome to TiddlyWiki by Jeremy Ruston, Copyright &copy; 2006 Osmosoft Limited
                   7080:        </div>
                   7081:        <noscript>
                   7082:                <div id="javascriptWarning">This page requires JavaScript to function properly</div>
                   7083:        </noscript>
                   7084:        <div id="saveTest"></div>
                   7085:        <div id="contentWrapper"></div>
                   7086:        <div id="contentStash"></div>
                   7087:        <div id="storeArea">
                   7088: <div tiddler="(built-in shadow tiddler)" modifier="CameronRich" modified="200702240024" created="200702240024" tags="">changes, notes and errata</div>
                   7089: <div tiddler="Cam" modifier="YourName" modified="200804011313" created="200804011313" tags="">Type the text for 'YourName'</div>
                   7090: <div tiddler="Changelog" modifier="YourName" modified="201207011056" created="200702240022" tags="">@@bgcolor(#ff0000):color(#ffffff):Changes for 1.4.8@@\n\n!!__SSL Library__\n* Fixed issue where the stdint typedefs were not imported by default (stdint.h had to be included explicity) (thanks Anthony G. Basile)\n\n!!__axhttpd__\n* The password hash broke due to an over zealous buffer overflow check. \n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.4.6@@\n\n!!__SSL Library__\n* Fixed issue where the stdint typedefs were not imported by default (stdint.h had to be included explicity) (thanks Anthony G. Basile)\n* Fixed RNG initialization issue where client library was performed incorrectly (thanks Gilles Boccon~-Gibod). \n\n!!__axhttpd__\n* Now compiles properly under TCP/IP v6.\n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.4.5@@\n\n!!__SSL Library__\n* Fixed possible buffer overflow when doing base64 decoding (thanks Emil Kvarnhammar).\n* Fixed unicode parsing error in certificates (thanks Eric Hu)\n\n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.4.4@@\n\n!!__axhttpd__\n* Allow other CGI applications (such as PHP) to call HTML files from their command line.\n\n!!__SSL Library__\n* Fixed memory leak with invalid certificates (thanks Jon Trauntvein)\n* Fixed issue with non-blocking client connections not working properly (thanks Richard Titmuss).\n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.4.3@@\n\n!!__SSL Library__\n* axtlswrap compilation error fixed.\n\n!!__axhttpd__\n* added '-w' command-line option to set the webroot directory.\n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.4.2@@\n\n!!__SSL Library__\n* bi_export  could have a buffer overrun with incorrect input (thanks Gilles ~Boccon-Gibod - 3334305)\n\n!!__axhttpd__\n* ~RFC1123 time format used in the headers.\n* Expires heading added (current time + ~CONFIG_HTTP_TIMEOUT)\n* UTC/localtime issue with ~If-Modified-Since header.\n\n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.4.1@@\n\n!!__SSL Library__\n* Allow reading of ~PKCS8/12 unencrypted keys in PEM format and mconf will allow the option in server mode (thanks Steve Bennett).\n* Issue where comparing a null and an empty string could return a false positive for cert check (thanks Gilles ~Boccon-Gibod - 3310885).\n* -fPIC added as a Linux compile option.\n\n!!__axhttpd__\n* Killing connections on session timeout is guaranteed.\n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.4.0@@\n\n!!__SSL Library__\n* TLS v1.1 implemented and is enabled by default.\n* Closure alerts implemented correctly.\n* Fixed issue with ~SSLv23 hello versioning. \n \n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.3.4@@\n\n!!__SSL Library__\n* SSL 2.0 client hello is turned off by default as per RFC 4346 Appendix E.\n* Client determines the cipher suite selected rather than the server as per RFC 4346 7.4.1.2.\n* Guard against timing HMAC timing attacks as per RFC 4346 6.2.3.2.\n* Fixed ~SOCKET_WRITE buffer issue (thanks  Hardy Griech - 3177419)\n* Fixed variable length MAC issue as used by gnutls.\n* Fixed version issue when TLS &gt;=1.1 is used.\n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.3.2@@\n\n!!__SSL Library__\n* Loading of PEM certificate bundles now loads CA certs properly.\n* ssl_client_new() can now be broken up into an ssl_client_new() and successive ssl_read()'s now by setting the ~SSL_CONNECT_IN_PARTS as an option in ssl_ctx_new().\n* Non-blocked mode is now not a requirement but calls may still be blocked.\n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.3.1@@\n\n!!__SSL Library__\n* Certificate bundles which contain &quot;invalid&quot; certificates (i.e. invalid digests types etc) are ignored rather than cause failure.\n\n!!__axhttpd__\n* ~HTTPv1.0 packets close a connection upon completion.\n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.3.0@@\n\n!!__SSL Library__\n* Close notify is now sent as an error code from ssl_read(). Server code should be modified to check for ~SSL_CLOSE_NOTIFY (thanks to Eric Hu - 3132700).\n* regular_square() issue fixed (3078672)\n* partial_multiply() removed and merged with regular_multiply() (3078372).\n* Invalid session id size now returns ~SSL_ERROR_INVALID_SESSION (thanks to Hardy Griech - 3072881)\n* q-dash issue with Barrett reduction fixed (thanks to Hardy Griech - 3079291).\n* PEM file detection now looks for &quot;-BEGIN&quot; in any part of the file rather than at the start (3123838).\n* 8/16/32 bit native int sizes can be selected in configuration.\n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.2.7@@\n\n!!__SSL Library__\n* A fix to find_max_exp_index() (thanks to Hardy Griech).\n* Check is made to get_cipher_info() if the appropriate cipher is not found (thanks to Hardy Griech).\n* Extra x509_free() removed from do_client_connect().\n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.2.5@@\n\n!!__SSL Library__\n* The custom RNG updated to use an entropy pool (with better hooks to use counters).\n\n!!__axhttpd__\n* Headers are case insensitive (thanks to Joe Pruett for this and the following).\n* Child zombie issue fixed.\n* EOF on ~POSTs fixed.\n* Expect is ignored.\n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.2.4@@\n\n!!__SSL Library__\n* Client renegotiation now results in an error. This is the result of a security flaw described in this paper http://extendedsubset.com/Renegotiating_TLS.pdf, and also is explained in detail here http://www.cupfighter.net/index.php/2009/11/tls-renegotiation-attack/.\n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.2.3@@\n\n!!__SSL Library__\n* v3 certificates with ~SANs now supports (thanks to Carsten Sørensen).\n* axtlswrap added - a port of sslwrap (thanks to Steve Bennett)\n\n!!__axhttpd__\n* shutdown() called before socket close in CGI (thanks to Tom Brown)\n* command-line parameters to specify the http/https port.\n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.2.2@@\n\n!!__axhttpd__\n* File uploads over 1kB (but under MAXPOSTDATASIZE) are now supported.\n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.2.1@@\n\n!!__SSL Library__\n* Certificate verification now works for Firefox.\n* Extended the openssl API.\n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.2.0@@\n\n!!__SSL Library__\n* A self-signed certificate will be verified as ok provided that that it is on the certificate authority list.\n* Certificates are not verified when added as certificate authorities (since self-signed and expired certificates can be added to browsers etc)\n\n@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.9@@\n\n!!__SSL Library__\n* Now support MS IIS resource kit certificates (thanks to Carsten Sørensen).\n* Fixed a memory leak when freeing more than one CA certificate.\n* The bigint library had a problem with squaring which affected classical reduction (thanks to Manuel Klimek).\n\n!!__axhttpd__\n* Brought back setuid()/setgid() as an option.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.8@@\n\n!!__SSL Library__\n* Now using a BSD style license.\n* Self-signed certificates can now be automatically generated (the keys still need to be provided).\n* A new API call //ssl_x509_create()// can be used to programatically  create the certificate.\n* Certificate/keys can be loaded automatically given a file location.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.7@@\n\n!!__SSL Library__\n\n* Variable sized session id's is now better handled for session caching. It has meant a new API call //ssl_get_session_id_size()// and a change to //ssl_client_new()// to define the session id size.\n* Muliple records with a single header are now better supported (thanks to Hervé Sibert).\n* ~MD2 added for Verisign root cert verification (thanks to Byron Rakitzis).\n* The ~MD5/~SHA1 digests are calculated incrementally to reduce memory (thanks to Byron Rakitzis).\n* The bigint cache is now cleared regularly to reduce memory.\n\n!!__axhttpd__\n\n* Improved the POST handling (thanks to Christian Melki).\n* CSS files now work properly.\n* Lua's CGI launcher location is configurable.\n* //vfork()// is now used for CGI for performance reasons.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.6@@\n\n!!__SSL Library__\n\n* ~RC4 speed improvements\n* Lua samples/bindings now work properly\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.5@@\n\n!!__SSL Library__\n\n* Session id's can now be variable lengths in server hello messages.\n* 0 length client certificates are now supported.\n* ssl_version() now returns just the version and not the date.\n* ssl_write() was not sending complete packets under load.\n\n!!__axhttpd__\n\n* Completely updated the CGI code.\n* Lua now integrated - Lua scripts and Lua Pages now run.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.4@@\n\n!!__SSL Library__\n\n* Fixed a Win32 crypto library issue with non-Administrator users\n* Removed compiler warnings that showed up in ~FC6.\n* GNU TLS certificates are now accepted.\n* Separated the send/receive headers for HMAC calculations.\n* Fixed a compilation problem with swig/perl/~FC6.\n* Fixed an issue with loading PEM CA certificates.\n\n!!__axhttpd__\n\n* Made //setuid()/setgid()// call an mconf option.\n* Made //chroot()// an mconf option. Default to //chdir()// instead.\n* Removed optional permissions checking.\n\n!@@bgcolor(#ff0000):color(#ffffff):Changes for 1.1.1@@\n\n!!__SSL Library__\n\n* AES should now work on 16bit processors (there was an alignment problem).\n* Various freed objects are cleared before freeing.\n* Header files now installed in ///usr/local/include/axTLS//.\n* -DCYGWIN replaced with -~DCONFIG_PLATFORM_CYGWIN (and the same for Solaris).\n* removed &quot;-noextern&quot; option in Swig. Fixed some other warnings in Win32.\n* SSLCTX changed to ~SSL_CTX (to be consistent with openssl). SSLCTX still exists for backwards compatibility.\n* malloc() and friends call abort() on failure.\n* Fixed a memory leak in directory listings.\n* Added openssl() compatibility functions.\n* Fixed Cygwin 'make install' issue.\n\n!!__axhttpd__\n\n* main.c now becomes axhttpd.c.\n* Header file issue fixed (in mime_types.c).\n* //chroot()// now used for better security.\n* Basic authentication implemented (via .htpasswd).\n* SSL access/denial protection implemented (via .htaccess).\n* Directory access protection implemented (via .htaccess).\n* Can now have more than one CGI file extension in mconf.\n* &quot;~If-Modified-Since&quot; request now handled properly.\n* Performance tweaks to remove //ssl_find()//.</div>
                   7091: <div tiddler="DefaultTiddlers" modifier="CameronRich" modified="200702240019" created="200702240019" tags="">[[Read Me]]</div>
                   7092: <div tiddler="License" modifier="YourName" modified="200804011309" created="200702240022" tags="">axTLS uses a BSD style license:\n\nCopyright (c) 2008, Cameron Rich All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this\nlist of conditions and the following disclaimer. Redistributions in binary\nform must reproduce the above copyright notice, this list of conditions and\nthe following disclaimer in the documentation and/or other materials\nprovided with the distribution. Neither the name of the axTLS Project nor\nthe names of its contributors may be used to endorse or promote products\nderived from this software without specific prior written permission. \n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot;\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\nLIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\nOUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH\nDAMAGE.</div>
                   7093: <div tiddler="MainMenu" modifier="CameronRich" modified="200702250353" created="200702240021" tags="">[[Read Me]] \n[[Changelog]]\n[[axhttpd]]\n[[License]]</div>
                   7094: <div tiddler="PageTemplate" modifier="YourName" modified="200701122313" created="200701122350" tags="DevFireTheme">&lt;div class='header' macro='gradient vert #390108 #900'&gt;\n&lt;div class='headerShadow'&gt;\n&lt;span class='siteTitle' refresh='content' tiddler='SiteTitle'&gt;&lt;/span&gt;&amp;nbsp;\n&lt;span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'&gt;&lt;/span&gt;\n&lt;/div&gt;\n&lt;div class='headerForeground'&gt;\n&lt;span class='siteTitle' refresh='content' tiddler='SiteTitle'&gt;&lt;/span&gt;&amp;nbsp;\n&lt;span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'&gt;&lt;/span&gt;\n&lt;/div&gt;\n&lt;/div&gt;\n&lt;div id='mainMenu'&gt;\n&lt;div refresh='content' tiddler='MainMenu'&gt;&lt;/div&gt;\n&lt;/div&gt;\n&lt;div id='sidebar'&gt;\n&lt;div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'&gt;&lt;/div&gt;\n&lt;div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'&gt;&lt;/div&gt;\n&lt;/div&gt;\n&lt;div id='displayArea'&gt;\n&lt;div id='messageArea'&gt;&lt;/div&gt;\n&lt;div id='tiddlerDisplay'&gt;&lt;/div&gt;\n&lt;/div&gt;</div>
                   7095: <div tiddler="Read Me" modifier="YourName" modified="201205252317" created="200702240020" tags="">!@@bgcolor(#ff0000):color(#ffffff):axTLS Quick Start Guide@@\n\nThis is a guide to get a small SSL web-server up and running quickly.\n\n!!__Introduction__\n\nThe axTLS project is an SSL client/server library using the ~TLSv1 protocol. It is designed to be small and fast, and is suited to embedded projects. A web server is included.\n\nThe basic web server + SSL library is around 60-70kB and is configurable for features or size.\n\n!!__Compilation__\n\nAll platforms require GNU make. This means on Win32 that Cygwin needs to be installed with &quot;make&quot; and various developer options selected.\n\nConfiguration now uses a tool called &quot;mconf&quot; which gives a nice way to configure options (similar to what is used in ~BusyBox and the Linux kernel).\n\nYou should be able to compile axTLS simply by extracting it, change into the extracted directory and typing:\n\n{{indent{{{{&gt;  make}}}\n\nSelect your platform type, save the configuration, exit, and then type &quot;make&quot; again.\n\nIf all goes well, you should end up with an executable called &quot;axhttpd&quot; (or axhttpd.exe) in the //_stage// directory.\n\nTo play with all the various axTLS options, type:\n\n{{indent{{{{&gt;  make menuconfig}}}\n\nSave the new configuration and rebuild.\n\n!!__Running it__\n\nTo run it, go to the //_stage// directory, and type (as superuser):\n\n{{indent{{{{&gt; axhttpd}}}\n\nNote: you may have to set your ~LD_LIBRARY_PATH - e.g. go to //_stage// and type //export ~LD_LIBRARY_PATH=`pwd`//\n\nAnd then point your browser at https://127.0.0.1 And you should see a this html page with a padlock appearing on your browser. or type http://127.0.0.1 to see the same page unencrypted.\n\n!!__The axssl utilities__\n\nThe axssl suite of tools are the SSL test tools in the various language bindings. They are:\n\n* axssl              - C sample\n* axssl.csharp    - C# sample\n* axssl.vbnet      - VB.NET sample\n* axtls.jar           - Java sample\n* axssl.pl           - Perl sample\n* axssl.lua         - Lua sample\n\nAll the tools have identical command-line parameters. e.g. to run something interesting:\n\n{{indent{{{{&gt; axssl s_server -verify -CAfile ../ssl/test/axTLS.ca_x509}}}\n\nand\n\n{{indent{{{{&gt; axssl s_client -cert ../ssl/test/axTLS.x509_1024 -key ../ssl/test/axTLS.key_1024 -reconnect}}}\n\n!!!!C#\n\nIf building under Linux or other non-Win32 platforms, Mono must be installed and the executable is run as:\n\n{{indent{{{{&gt; mono axssl.csharp.exe ...}}}\n\n!!!!Java\n\nThe java version is run as:\n\n{{indent{{{{&gt; java -jar axtls.jar &lt;options&gt;}}}\n\n!!!!Perl\n\n{{indent{{{{&gt; [perl] ./axssl.pl &lt;options&gt;}}}\n\nIf running under Win32, be sure to use the correct version of Perl (i.e. ~ActiveState's version works ok).\n\n!!!!Lua\n\n{{indent{{{{&gt; [lua] ./axssl.lua &lt;options&gt;}}}\n\n!__Known Issues__\n\n* Firefox doesn't handle legacy ~SSLv2 at all well. Disabling ~SSLv2 still initiates a ~SSLv23 handshake (v1.5). And continuous pressing of the &quot;Reload&quot; page instigates a change to ~SSLv3 for some reason (even though the TLS 1.0 option is selected). This will cause a &quot;Firefox and &lt;server&gt; cannot communicate securely because they have no common encryption algorithms&quot; (v1.5), or &quot;Firefox can't connect to &lt;server&gt; because the site uses a security protocol which isn't enabled&quot; (v2.0). See bugzilla issues 343543 and 359484 (Comment #7). It's all broken (hopefully fixed soon).\n* Perl/Java bindings don't work on 64 bit Linux machines. I can't even compile the latest version of Perl on an ~AMD64 box (using ~FC3).\n* Java 1.4 or better is required for the Java interfaces.\n* Processes that fork can't use session resumption unless some form of IPC is used.\n* Ensure libperl.so and libaxtls.so are in the shared library path when running with the perl bindings. A way to do this is with:\n\n{{indent{{{{&gt; export LD_LIBRARY_PATH=`perl -e 'use Config; print $Config{archlib};'`/CORE:.}}}\n* The lua sample requires the luabit library from http://luaforge.net/projects/bit.\n\n!!!!Win32 issues\n\n* Be careful about doing .NET executions on network drives - .NET complains with security exceptions on the binary. //TODO: Add a manifest file to prevent this.//\n* CGI has been removed from Win32 - it needs a lot more work to get it right.\n* The default Microsoft .NET SDK is v2.0.50727. Download from: http://msdn.microsoft.com/netframework/downloads/updates/default.aspx.\n\n!!!!Solaris issues\n\n* mconf doesn't work well - some manual tweaking is required for string values.\n* GNU make is required and needs to be in $PATH.\n* To get swig's library dependencies to work (and for the C library to be found), I needed to type:\n\n{{indent{{{{&gt; export LD_LIBRARY_PATH=/usr/local/gcc-3.3.1/lib:.}}}\n\n!!!!Cygwin issues\n\n* The bindings all compile but don't run under Cygwin with the exception of Perl. This is due to win32 executables being incompatible with Cygwin libraries.\n\n</div>
                   7096: <div tiddler="SiteSubtitle" modifier="CameronRich" modified="200702240025" created="200702240025" tags="">changes, notes and errata</div>
                   7097: <div tiddler="SiteTitle" modifier="CameronRich" modified="200702240023" created="200702240023" tags="">axTLS Embedded SSL</div>
                   7098: <div tiddler="SiteUrl" modifier="CameronRich" modified="200702240025" created="200702240025" tags="">http://axtls.cerocclub.com.au</div>
                   7099: <div tiddler="StyleSheet" modifier="CameronRich" modified="200702250600" created="200701122350" tags="DevFireTheme">/***\nhttp://tiddlystyles.com/#theme:DevFire\nAuthor: Clint Checketts\n***/\n\n/*{{{*/\nbody {\nbackground: #000;\n}\n/*}}}*/\n/***\n!Link styles /% ============================================================= %/\n***/\n/*{{{*/\na,\na.button,\n#mainMenu a.button,\n#sidebarOptions .sliderPanel a{\n color: #ffbf00;\n border: 0;\n background: transparent;\n}\n\na:hover,\na.button:hover,\n#mainMenu a.button:hover,\n#sidebarOptions .sliderPanel a:hover\n#sidebarOptions .sliderPanel a:active{\n color: #ff7f00;\n border: 0;\n border-bottom: #ff7f00 1px dashed;\n background: transparent;\n text-decoration: none;\n}\n\n#displayArea .button.highlight{\n color: #ffbf00;\n background: #4c4c4c;\n}\n/*}}}*/\n/***\n!Header styles /% ============================================================= %/\n***/\n/*{{{*/\n.header{\n border-bottom: 2px solid #ffbf00;\n color: #fff;\n}\n\n.headerForeground a {\n color: #fff;\n}\n\n.header a:hover {\n border-bottom: 1px dashed #fff;\n}\n/*}}}*/\n/***\n!Main menu styles /% ============================================================= %/\n***/\n/*{{{*/\n#mainMenu {color: #fff;}\n#mainMenu h1{\n font-size: 1.1em;\n}\n#mainMenu li,#mainMenu ul{\n list-style: none;\n margin: 0;\n padding: 0;\n}\n/*}}}*/\n/***\n!Sidebar styles /% ============================================================= %/\n***/\n/*{{{*/\n#sidebar {\n right: 0;\n color: #fff;\n border: 2px solid #ffbf00;\n border-width: 0 0 2px 2px;\n}\n#sidebarOptions {\n background-color: #4c4c4c;\n padding: 0;\n}\n\n#sidebarOptions a{\n margin: 0;\n color: #ffbf00;\n border: 0;\n}\n#sidebarOptions a:hover {\n color: #4c4c4c;\n background-color: #ffbf00;\n\n}\n\n#sidebarOptions a:active {\n color: #ffbf00;\n background-color: transparent;\n}\n\n#sidebarOptions .sliderPanel {\n background-color: #333;\n margin: 0;\n}\n\n#sidebarTabs {background-color: #4c4c4c;}\n#sidebarTabs .tabSelected {\n padding: 3px 3px;\n cursor: default;\n color: #ffbf00;\n background-color: #666;\n}\n#sidebarTabs .tabUnselected {\n color: #ffbf00;\n background-color: #5f5f5f;\n padding: 0 4px;\n}\n\n#sidebarTabs .tabUnselected:hover,\n#sidebarTabs .tabContents {\n background-color: #666;\n}\n\n.listTitle{color: #FFF;}\n#sidebarTabs .tabContents a{\n color: #ffbf00;\n}\n\n#sidebarTabs .tabContents a:hover{\n color: #ff7f00;\n background: transparent;\n}\n\n#sidebarTabs .txtMoreTab .tabSelected,\n#sidebarTabs .txtMoreTab .tab:hover,\n#sidebarTabs .txtMoreTab .tabContents{\n color: #ffbf00;\n background: #4c4c4c;\n}\n\n#sidebarTabs .txtMoreTab .tabUnselected {\n color: #ffbf00;\n background: #5f5f5f;\n}\n\n.tab.tabSelected, .tab.tabSelected:hover{color: #ffbf00; border: 0; background-color: #4c4c4c;cursor:default;}\n.tab.tabUnselected {background-color: #666;}\n.tab.tabUnselected:hover{color:#ffbf00; border: 0;background-color: #4c4c4c;}\n.tabContents {\n background-color: #4c4c4c;\n border: 0;\n}\n.tabContents .tabContents{background: #666;}\n.tabContents .tabSelected{background: #666;}\n.tabContents .tabUnselected{background: #5f5f5f;}\n.tabContents .tab:hover{background: #666;}\n/*}}}*/\n/***\n!Message area styles /% ============================================================= %/\n***/\n/*{{{*/\n#messageArea {background-color: #666; color: #fff; border: 2px solid #ffbf00;}\n#messageArea a:link, #messageArea a:visited {color: #ffbf00; text-decoration:none;}\n#messageArea a:hover {color: #ff7f00;}\n#messageArea a:active {color: #ff7f00;}\n#messageArea .messageToolbar a{\n border: 1px solid #ffbf00;\n background: #4c4c4c;\n}\n/*}}}*/\n/***\n!Popup styles /% ============================================================= %/\n***/\n/*{{{*/\n.popup {color: #fff; background-color: #4c4c4c; border: 1px solid #ffbf00;}\n.popup li.disabled{color: #fff;}\n.popup a {color: #ffbf00; }\n.popup a:hover { background: transparent; color: #ff7f00; border: 0;}\n.popup hr {color: #ffbf00; background: #ffbf00;}\n/*}}}*/\n/***\n!Tiddler Display styles /% ============================================================= %/\n***/\n/*{{{*/\n.title{color: #fff;}\nh1, h2, h3, h4, h5 {\n color: #fff;\n background-color: transparent;\n border-bottom: 1px solid #333;\n}\n\n.subtitle{\n color: #666;\n}\n\n.viewer {color: #fff; }\n\n.viewer table{background: #666; color: #fff;}\n\n.viewer th {background-color: #996; color: #fff;}\n\n.viewer pre, .viewer code {color: #ddd; background-color: #4c4c4c; border: 1px solid #ffbf00;}\n\n.viewer hr {color: #666;}\n\n.tiddler .button {color: #4c4c4c;}\n.tiddler .button:hover { color: #ffbf00; background-color: #4c4c4c;}\n.tiddler .button:active {color: #ffbf00; background-color: #4c4c4c;}\n\n.toolbar {\n color: #4c4c4c;\n}\n\n.toolbar a.button,\n.toolbar a.button:hover,\n.toolbar a.button:active,\n.editorFooter a{\n border: 0;\n}\n\n.footer {\n color: #ddd;\n}\n\n.selected .footer {\n color: #888;\n}\n\n.highlight, .marked {\n color: #000;\n background-color: #ffe72f;\n}\n.editorFooter {\n color: #aaa;\n}\n\n.tab{\n-moz-border-radius-topleft: 3px;\n-moz-border-radius-topright: 3px;\n}\n\n.tagging,\n.tagged{\n background: #4c4c4c;\n border: 1px solid #4c4c4c; \n}\n\n.selected .tagging,\n.selected .tagged{\n background-color: #333;\n border: 1px solid #ffbf00;\n}\n\n.tagging .listTitle,\n.tagged .listTitle{\n color: #fff;\n}\n\n.tagging .button,\n.tagged .button{\n color: #ffbf00;\n border: 0;\n padding: 0;\n}\n\n.tagging .button:hover,\n.tagged .button:hover{\nbackground: transparent;\n}\n\n.selected .isTag .tagging.simple,\n.selected .tagged.simple,\n.isTag .tagging.simple,\n.tagged.simple {\n float: none;\n display: inline;\n border: 0;\n background: transparent;\n color: #fff;\n margin: 0;\n}\n\n.cascade {\n background: #4c4c4c;\n color: #ddd;\n border: 1px solid #ffbf00;\n}\n/*}}}*/</div>
                   7100: <div tiddler="axhttpd" modifier="YourName" modified="201106241105" created="200702242231" tags="">axhttpd is a small embedded web server using the axTLS library. It is based originally on the web server written by Doug Currie which is at http://www.hcsw.org/awhttpd.\n\n!@@bgcolor(#ff0000):color(#ffffff):axhttpd Features@@ \n\n!!__Basic Authentication__\n\nBasic Authentication uses a password file called  &quot;.htpasswd&quot;, in the directory to be  protected. This file is formatted as the familiar colon-separated username/encrypted-password pair, records delimited by newlines. The protection does not carry over to subdirectories. The utility program htpasswd is included to help manually edit .htpasswd files.\n\nThe encryption of this password uses a proprietary algorithm due to the dependency of many crypt libraries on DES. An example is in [[/test_dir/no_http|https://localhost/test_dir/no_http]]  (username 'abcd', password is '1234').\n\n//Note: This is an mconf enabled configuration option.//\n\n!!__SSL Protection__\n\nDirectories/files can be accessed using the 'http' or 'https' uri prefix. If normal http access for a directory needs to be disabled, then put &quot;~SSLRequireSSL&quot; into a '.htaccess' file in the directory to be protected. \n\nConversely, use &quot;~SSLDenySSL&quot; to deny access to directories via SSL.\n\nAn example is in [[/test_dir/no_http|http://localhost/test_dir/no_http]] and [[/test_dir/no_ssl|https://localhost/test_dir/no_ssl]].\n\nEntire directories can be denied access with a &quot;Deny all&quot; directive (regardless of SSL or authentication). An example is in [[/test_dir/bin|http://localhost/test_dir/bin]]\n\n!!__CGI__\n\nMost of the CGI 1.1 variables are now placed into the script environment and should work as normal.\n\n!!__Lua and Lua Pages__\n\nThis is a small scripting language gaining popularity in embedded applications due to its small footprint and fast speed.\n\nLua has been incorporated into the build, so simply select it and it will automatically install. Try pointing your browser at [[test_main.html|http://localhost/lua/test_main.html]] to see an example of Lua Pages.\n\n//Note: This is an mconf enabled configuration option.//\n\nThe readline development library may have to be downloaded: //yum install readline-devel//\n\n!!__Directory Listing__\n\nAn mconf option. Allow the files in directories to be displayed. An example is in [[/test_dir|http://localhost/test_dir]]\n\n!!__Other Features__\n\n* Timeout - HTTP 1.1 allows for persistent connections. This is the time allowed for this connection in seconds.\n* Daemon - Puts the process in daemon mode. \n* SSL  session cache size - The size of the session cache (a heavily loaded server should maintain many sessions). A session will save on expensive SSL handshaking.\n\n</div>
                   7101: </div>
                   7102: <!--POST-BODY-START-->
                   7103: 
                   7104: <!--POST-BODY-END-->
                   7105:        </body>
                   7106: </html>

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>