2007-06-19 03:45:45 +02:00
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
< html xmlns = "http://www.w3.org/1999/xhtml" xml:lang = "en" lang = "en" >
< head >
< script type = "text/javascript" >
//< ![CDATA[
var version = {title: "TiddlyWiki", major: 2, minor: 2, revision: 3, date: new Date("Jun 17, 2007"), extensions: {}};
//]]>
< / script >
<!--
TiddlyWiki created by Jeremy Ruston, (jeremy [at] osmosoft [dot] com)
Copyright (c) UnaMesa Association 2004-2007
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
Neither the name of the UnaMesa Association nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
-->
< meta http-equiv = "Content-Type" content = "text/html;charset=utf-8" / >
<!-- PRE - HEAD - START -->
<!-- {{{ -->
< link rel = 'alternate' type = 'application/rss+xml' title = 'RSS' href = 'index.xml' / >
<!-- }}} -->
2007-08-08 04:50:02 +02:00
< style type = "text/css" > # contentWrapper { display : none ; } < / style > < div id = "SplashScreen" style = "border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;" > loading < b > Cinelerra Renderengine< / b > devel doku< blink > ...< / blink > < br > < br > < span style = "font-size: 14px; color:red;" > Requires Javascript.< / span > < / div >
2007-06-19 03:45:45 +02:00
<!-- PRE - HEAD - END -->
2007-08-08 04:50:02 +02:00
< title > Engine - some aspects of Cinelerra-3 design < / title >
2007-06-19 03:45:45 +02:00
< style type = "text/css" >
#saveTest {display:none;}
#messageArea {display:none;}
#copyright {display:none;}
#storeArea {display:none;}
#storeArea div {padding:0.5em; margin:1em 0em 0em 0em; border-color:#fff #666 #444 #ddd; border-style:solid; border-width:2px; overflow:auto;}
#shadowArea {display:none;}
#javascriptWarning {width:100%; text-align:center; font-weight:bold; background-color:#dd1100; color:#fff; padding:1em 0em;}
< / style >
<!-- POST - HEAD - START -->
<!-- POST - HEAD - END -->
< / head >
< body onload = "main();" onunload = "if(window.checkUnsavedChanges) checkUnsavedChanges(); if(window.scrubNodes) scrubNodes(document.body);" >
<!-- PRE - BODY - START -->
<!-- PRE - BODY - END -->
< div id = "copyright" >
Welcome to TiddlyWiki created by Jeremy Ruston, Copyright © 2007 UnaMesa Association
< / div >
< noscript >
< div id = "javascriptWarning" > This page requires JavaScript to function properly.< br / > < br / > If you are using Microsoft Internet Explorer you may need to click on the yellow bar above and select 'Allow Blocked Content'. You must then click 'Yes' on the following security warning.< / div >
< / noscript >
< div id = "saveTest" > < / div >
< div id = "backstageCloak" > < / div >
< div id = "backstageButton" > < / div >
< div id = "backstageArea" > < div id = "backstageToolbar" > < / div > < / div >
< div id = "backstage" >
< div id = "backstagePanel" > < / div >
< / div >
< div id = "contentWrapper" > < / div >
< div id = "contentStash" > < / div >
< div id = "shadowArea" >
< div title = "ColorPalette" >
< pre > Background: #fff
Foreground: #000
PrimaryPale: #8cf
PrimaryLight: #18f
PrimaryMid: #04b
PrimaryDark: #014
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88< / pre >
< / div >
< div title = "StyleSheetColors" >
< pre > /*{{{*/
body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
a {color:[[ColorPalette::PrimaryMid]];}
a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
a img {border:0;}
h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}
.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}
.header {background:[[ColorPalette::PrimaryMid]];}
.headerShadow {color:[[ColorPalette::Foreground]];}
.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
.headerForeground {color:[[ColorPalette::Background]];}
.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}
.tabSelected{color:[[ColorPalette::PrimaryDark]];
background:[[ColorPalette::TertiaryPale]];
border-left:1px solid [[ColorPalette::TertiaryLight]];
border-top:1px solid [[ColorPalette::TertiaryLight]];
border-right:1px solid [[ColorPalette::TertiaryLight]];
}
.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
.tabContents .button {border:0;}
#sidebar {}
#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}
.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
border:1px solid [[ColorPalette::PrimaryMid]];}
.wizardStep.wizardStepDone {background::[[ColorPalette::TertiaryLight]];}
.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}
#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}
.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}
.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}
.tiddler .defaultCommand {font-weight:bold;}
.shadow .title {color:[[ColorPalette::TertiaryDark]];}
.title {color:[[ColorPalette::SecondaryDark]];}
.subtitle {color:[[ColorPalette::TertiaryDark]];}
.toolbar {color:[[ColorPalette::PrimaryMid]];}
.toolbar a {color:[[ColorPalette::TertiaryLight]];}
.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}
.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
.tagging .button, .tagged .button {border:none;}
.footer {color:[[ColorPalette::TertiaryLight]];}
.selected .footer {color:[[ColorPalette::TertiaryMid]];}
.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
.sparktick {background:[[ColorPalette::PrimaryDark]];}
.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
.lowlight {background:[[ColorPalette::TertiaryLight]];}
.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}
.imageLink, #displayArea .imageLink {background:transparent;}
.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}
.viewer .listTitle {list-style-type:none; margin-left:-2em;}
.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}
.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}
.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
.viewer code {color:[[ColorPalette::SecondaryDark]];}
.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}
.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}
.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
.editorFooter {color:[[ColorPalette::TertiaryMid]];}
#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity:60)';}
/*}}}*/< / pre >
< / div >
< div title = "StyleSheetLayout" >
< pre > /*{{{*/
* html .tiddler {height:1%;}
body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}
h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
h4,h5,h6 {margin-top:1em;}
h1 {font-size:1.35em;}
h2 {font-size:1.25em;}
h3 {font-size:1.1em;}
h4 {font-size:1em;}
h5 {font-size:.9em;}
hr {height:1px;}
a {text-decoration:none;}
dt {font-weight:bold;}
ol {list-style-type:decimal;}
ol ol {list-style-type:lower-alpha;}
ol ol ol {list-style-type:lower-roman;}
ol ol ol ol {list-style-type:decimal;}
ol ol ol ol ol {list-style-type:lower-alpha;}
ol ol ol ol ol ol {list-style-type:lower-roman;}
ol ol ol ol ol ol ol {list-style-type:decimal;}
.txtOptionInput {width:11em;}
#contentWrapper .chkOptionInput {border:0;}
.externalLink {text-decoration:underline;}
.indent {margin-left:3em;}
.outdent {margin-left:3em; text-indent:-3em;}
code.escaped {white-space:nowrap;}
.tiddlyLinkExisting {font-weight:bold;}
.tiddlyLinkNonExisting {font-style:italic;}
/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
a.tiddlyLinkNonExisting.shadow {font-weight:bold;}
#mainMenu .tiddlyLinkExisting,
#mainMenu .tiddlyLinkNonExisting,
#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}
.header {position:relative;}
.header a:hover {background:transparent;}
.headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}
.siteTitle {font-size:3em;}
.siteSubtitle {font-size:1.2em;}
#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}
#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#sidebarOptions {padding-top:0.3em;}
#sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
#sidebarOptions input {margin:0.4em 0.5em;}
#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}
.wizard {padding:0.1em 1em 0em 2em;}
.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizardStep {padding:1em 1em 1em 1em;}
.wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
.wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
.wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
.wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}
#messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
.messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
#messageArea a {text-decoration:underline;}
.tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}
.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
.popup .popupMessage {padding:0.4em;}
.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
.popup li.disabled {padding:0.4em;}
.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
.listBreak {font-size:1px; line-height:1px;}
.listBreak div {margin:2px 0;}
.tabset {padding:1em 0em 0em 0.5em;}
.tab {margin:0em 0em 0em 0.25em; padding:2px;}
.tabContents {padding:0.5em;}
.tabContents ul, .tabContents ol {margin:0; padding:0;}
.txtMainTab .tabContents li {list-style:none;}
.tabContents li.listLink { margin-left:.75em;}
#contentWrapper {display:block;}
#splashScreen {display:none;}
#displayArea {margin:1em 17em 0em 14em;}
.toolbar {text-align:right; font-size:.9em;}
.tiddler {padding:1em 1em 0em 1em;}
.missing .viewer,.missing .title {font-style:italic;}
.title {font-size:1.6em; font-weight:bold;}
.missing .subtitle {display:none;}
.subtitle {font-size:1.1em;}
.tiddler .button {padding:0.2em 0.4em;}
.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
.isTag .tagging {display:block;}
.tagged {margin:0.5em; float:right;}
.tagging, .tagged {font-size:0.9em; padding:0.25em;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
.tagClear {clear:both;}
.footer {font-size:.9em;}
.footer li {display:inline;}
.annotation {padding:0.5em; margin:0.5em;}
* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer {line-height:1.4em; padding-top:0.5em;}
.viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}
.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
table.listView {font-size:0.85em; margin:0.8em 1.0em;}
table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}
.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
.viewer code {font-size:1.2em; line-height:1.4em;}
.editor {font-size:1.1em;}
.editor input, .editor textarea {display:block; width:100%; font:inherit;}
.editorFooter {padding:0.25em 0em; font-size:.9em;}
.editorFooter .button {padding-top:0px; padding-bottom:0px;}
.fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}
.sparkline {line-height:1em;}
.sparktick {outline:0;}
.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
.zoomer div {padding:1em;}
* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
#backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel {display:none; z-index:100; position:absolute; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}
.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/< / pre >
< / div >
< div title = "StyleSheetLocale" >
< pre > /***
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which use a logographic writing system and need larger font sizes.
***/
/*{{{*/
body {font-size:0.8em;}
#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}
.subtitle {font-size:0.8em;}
.viewer table.listView {font-size:0.95em;}
.htmlarea .toolbarHA table {border:1px solid ButtonFace; margin:0em 0em;}
/*}}}*/< / pre >
< / div >
< div title = "StyleSheetPrint" >
< pre > /*{{{*/
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton {display: none ! important;}
#displayArea {margin: 1em 1em 0em 1em;}
/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
noscript {display:none;}
}
/*}}}*/< / pre >
< / div >
< div title = "PageTemplate" >
< pre > < !--{{{-->
< div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
< div class='headerShadow'>
< span class='siteTitle' refresh='content' tiddler='SiteTitle'> < /span> & nbsp;
< span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'> < /span>
< /div>
< div class='headerForeground'>
< span class='siteTitle' refresh='content' tiddler='SiteTitle'> < /span> & nbsp;
< span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'> < /span>
< /div>
< /div>
< div id='mainMenu' refresh='content' tiddler='MainMenu'> < /div>
< div id='sidebar'>
< div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'> < /div>
< div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'> < /div>
< /div>
< div id='displayArea'>
< div id='messageArea'> < /div>
< div id='tiddlerDisplay'> < /div>
< /div>
< !--}}}--> < / pre >
< / div >
< div title = "ViewTemplate" >
< pre > < !--{{{-->
< div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler > fields syncing permalink references jump'> < /div>
< div class='title' macro='view title'> < /div>
< div class='subtitle'> < span macro='view modifier link'> < /span> , < span macro='view modified date'> < /span> (< span macro='message views.wikified.createdPrompt'> < /span> < span macro='view created date'> < /span> )< /div>
< div class='tagging' macro='tagging'> < /div>
< div class='tagged' macro='tags'> < /div>
< div class='viewer' macro='view text wikified'> < /div>
< div class='tagClear'> < /div>
< !--}}}--> < / pre >
< / div >
< div title = "EditTemplate" >
< pre > < !--{{{-->
< div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'> < /div>
< div class='title' macro='view title'> < /div>
< div class='editor' macro='edit title'> < /div>
< div macro='annotations'> < /div>
< div class='editor' macro='edit text'> < /div>
< div class='editor' macro='edit tags'> < /div> < div class='editorFooter'> < span macro='message views.editor.tagPrompt'> < /span> < span macro='tagChooser'> < /span> < /div>
< !--}}}--> < / pre >
< / div >
< div title = "GettingStarted" >
< pre > To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* MainMenu: The menu (usually on the left)
* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: < < option txtUserName> > < / pre >
< / div >
< div title = "OptionsPanel" >
< pre > These InterfaceOptions for customising TiddlyWiki are saved in your browser
Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)
< < option txtUserName> >
< < option chkSaveBackups> > SaveBackups
< < option chkAutoSave> > AutoSave
< < option chkRegExpSearch> > RegExpSearch
< < option chkCaseSensitiveSearch> > CaseSensitiveSearch
< < option chkAnimate> > EnableAnimations
----
Also see AdvancedOptions< / pre >
< / div >
< / div >
<!-- POST - SHADOWAREA -->
< div id = "storeArea" >
< div title = "Admin" modifier = "CehTeh" modified = "200706110324" created = "200706080535" server . type = "file" server . host = "file:///home/ct/.homepage/home.html" server . page . revision = "200706110324" >
< pre > PageTemplate
|> |SiteTitle - SiteSubtitle|
|> |MainMenu|
|DefaultTiddlers< < br> > < < br> > < < br> > ViewTemplate< < br> > < < br> > EditTemplate|SideBarOptions|
|~|OptionsPanel|
|~|SideBarTabs|
|~|AdvancedOptions|
|~|< < tiddler Configuration.SideBarTabs> > |
''StyleSheet:'' StyleSheetColors - StyleSheetLayout - StyleSheetPrint
ColorPalette
SiteUrl< / pre >
< / div >
2007-10-10 03:54:09 +02:00
< div title = "Asset" modifier = "Ichthyostega" modified = "200710092321" created = "200708100337" tags = "def classes" changecount = "14" >
2007-09-02 17:51:46 +02:00
< pre > Asset management is a subsystem on its own. Assets are " things" that can be loaded into a session, like Media, Clips, Effects, Transitions. It is the " bookkeeping view" , while the EDL is the " manipulation and process view" . Some Assets can be //loaded// and a collection of Assets is saved with eatch Session. Besides, there is a collection of basic Assets allways available by default.
2007-09-04 05:09:08 +02:00
The Assets are important reference points holding the information needed to access external resources. For example, an Clip asset can reference a Media asset, which in turn holds the external filename from which to get the media stream. For Effects, the situation is similar. Assets thus serve two quite distinct purposes. One is to load, list, group search and browse them, and to provide an entry point to create new or get at existing MObjects in the EDL, while the other purpose is to provide attribute and property informations to the inner parts of the engine, while at the same time isolating and decoupling them from environmental details.
2007-09-02 17:51:46 +02:00
We can distinguish several different Kinds of Assets, each one with specific properties. While all these Kinds of Assets implement the basic Asset interface, they themselfs are the __key abstractions__ of the asset management view. Mostly, their interfaces will be used directly, because they are quite different in behaviour. Thus it is common to see asset related operations being templated on the Asset Kind.
2007-09-04 05:09:08 +02:00
& rarr; see also [[Creating and registering Assets|AssetCreation]]
2007-09-02 17:51:46 +02:00
[img[Asset Classess|uml/fig130309.png]]
!Media Asset
2007-10-10 03:54:09 +02:00
Some piece of Media Data accessible at some external Location and able to be processed by Cinelerra. A Media File on Harddisk can be considered as the most basic form of Media Asset, with some important derived flavours, like a Placeholder for a currently unavailable Source, or Media available in different Resolutions or Formats.
2007-09-02 17:51:46 +02:00
* __outward interface operations__ include querying properties, creating an Clip MObject, controlling processing policy (low res proxy placeholders, interlacing and other generic pre- and postprocessing)
* __inward interface operations__ include querying filename, codec, offset and any other informations necessary for creating a source render node, getting additional processing policy decisions (handling of interlacing, aspect ratio).
& rarr; MediaAsset
!Processing Asset
Some software component able to work on media data in the Cinelerra Render engine Framework. This includes all sorts of loadable effects, as well as some of the standard, internal facilities (Mask, Projector). Note that Processing Assets typically provide some attachment Point or means of communication with GUI facilities.
* __outward interface operations__ include...
* __inward interface operations__ include...
& rarr; ProcAsset {{red{to be defined}}}
!Structural Asset
Some of the building blocks providing the framework for the objects placed into the current Session. Notable examples are Input/Output channels (Ports), Viewer attachment points, Tracks, etc.
* __outward interface operations__ include...
* __inward interface operations__ include...
& rarr; StructAsset {{red{to be defined}}}
!Meta Asset
Some additional, virtual facilities created in the course of the editing process. Examples are Automation data sets, Lables and reference points, Meta Clips (nested sub-~EDLs)
* __outward interface operations__ include...
* __inward interface operations__ include...
& rarr; MetaAsset {{red{to be defined}}}
!!!!still to be worked out..
2007-10-10 03:54:09 +02:00
is how to implement the relationship between [[MObject]]s and Assets. Do we use direct pointers, or do we prefer an ID + central registry approach? And how to handle the removal of an Asset.
2007-09-04 05:09:08 +02:00
& rarr; see also [[analysis of mem management|ManagementAssetRelation]]
& rarr; see also [[Creating Objects|ObjectCreation]], especially [[Assets|AssetCreation]]
2007-10-10 03:54:09 +02:00
//9/07: currently implementing it as follows: use a refcounting-ptr from Clip-~MObject to asset::Media while maintaining a dependency network between Asset objects. We'll see if this approach is viable//
2007-08-10 06:11:31 +02:00
< / pre >
< / div >
2007-09-04 05:09:08 +02:00
< div title = "AssetCreation" modifier = "Ichthyostega" created = "200709040307" changecount = "1" >
< pre > Assets are created by a Factories returning smart pointers; the Asset creation is bound to specific use cases and //only available// for these specific situations. There is no generic Asset Factory.
For every Asset we generate a __Ident tuple__ and a long ID (hash) derived from this Ident tuple. The constructor of the abstract base class {{{Asset}}} takes care of this step and automatically registeres the new Asset object with the AssetManager. Typically, the factory methods for concrete Asset classes provide some shortcuts providing sensible default values for some of the Ident tuple data fields. They may take additional parameters & mdash; for example the factory method for creating {{{asset::Media}}} takes a filename (and may at some point in the future aply " magic" based on examination of the file)< / pre >
< / div >
2007-09-20 05:36:08 +02:00
< div title = "AssetManager" modifier = "Ichthyostega" created = "200709200300" changecount = "1" >
< pre > The Asset Manager provides an Interface to some internal Database holding all Assets in the current Session and System state. It may be a real Database at some point (and for the moment it's a Hashtable). Each [[Asset]] is registered automatically with the Asset Manager; it can be queried either by it's //identification tuple// or by it's unique ID.< / pre >
< / div >
2007-08-10 06:11:31 +02:00
< div title = "Automation" modifier = "Ichthyostega" modified = "200708100315" created = "200706250751" tags = "def" changecount = "5" >
2007-06-25 09:56:36 +02:00
< pre > Automation is treated as a function over time. It is always tied to a specific Parameter (which can thus be variable over the course of the timeline). All details //how// this function is defined are completely abstracted away. The Parameter uses a ParamProvider to get the value for a given Time (point). Typically, this will use linear or bezier interpolation over a set of keyframes internally. Parameters can be configured to have different value ranges and distribution types (on-off, stepped, continuous, bounded)
2007-06-27 17:19:54 +02:00
[img[how to implement Automation|uml/fig129669.png]]
< / pre >
2007-06-25 09:56:36 +02:00
< / div >
2007-06-19 03:45:45 +02:00
< div title = "BetterTimelineMacro" modifier = "Saq" modified = "200701030924" created = "200607280926" tags = "lewcidExtension systemConfig" server . type = "file" server . host = "file:///home/ct/.homepage/home.html" server . page . revision = "200701030924" >
< pre > /***
|Name|BetterTimelineMacro|
|Created by|SaqImtiaz|
|Location|http://tw.lewcid.org/#BetterTimelineMacro|
|Version|0.5 beta|
|Requires|~TW2.x|
!!!Description:
A replacement for the core timeline macro that offers more features:
*list tiddlers with only specfic tag
*exclude tiddlers with a particular tag
*limit entries to any number of days, for example one week
*specify a start date for the timeline, only tiddlers after that date will be listed.
!!!Installation:
Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
Edit the ViewTemplate to add the fullscreen command to the toolbar.
!!!Syntax:
{{{< < timeline better:true> > }}}
''the param better:true enables the advanced features, without it you will get the old timeline behaviour.''
additonal params:
(use only the ones you want)
{{{< < timeline better:true onlyTag:Tag1 excludeTag:Tag2 sortBy:modified/created firstDay:YYYYMMDD maxDays:7 maxEntries:30> > }}}
''explanation of syntax:''
onlyTag: only tiddlers with this tag will be listed. Default is to list all tiddlers.
excludeTag: tiddlers with this tag will not be listed.
sortBy: sort tiddlers by date modified or date created. Possible values are modified or created.
firstDay: useful for starting timeline from a specific date. Example: 20060701 for 1st of July, 2006
maxDays: limits timeline to include only tiddlers from the specified number of days. If you use a value of 7 for example, only tiddlers from the last 7 days will be listed.
maxEntries: limit the total number of entries in the timeline.
!!!History:
*28-07-06: ver 0.5 beta, first release
!!!Code
***/
//{{{
// Return the tiddlers as a sorted array
TiddlyWiki.prototype.getTiddlers = function(field,excludeTag,includeTag)
{
var results = [];
this.forEachTiddler(function(title,tiddler)
{
if(excludeTag == undefined || tiddler.tags.find(excludeTag) == null)
if(includeTag == undefined || tiddler.tags.find(includeTag)!=null)
results.push(tiddler);
});
if(field)
results.sort(function (a,b) {if(a[field] == b[field]) return(0); else return (a[field] < b[field]) ? -1 : +1; });
return results;
}
//this function by Udo
function getParam(params, name, defaultValue)
{
if (!params)
return defaultValue;
var p = params[0][name];
return p ? p[0] : defaultValue;
}
window.old_timeline_handler= config.macros.timeline.handler;
config.macros.timeline.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
var args = paramString.parseParams(" list" ,null,true);
var betterMode = getParam(args, " better" , " false" );
if (betterMode == 'true')
{
var sortBy = getParam(args," sortBy" ," modified" );
var excludeTag = getParam(args," excludeTag" ,undefined);
var includeTag = getParam(args," onlyTag" ,undefined);
var tiddlers = store.getTiddlers(sortBy,excludeTag,includeTag);
var firstDayParam = getParam(args," firstDay" ,undefined);
var firstDay = (firstDayParam!=undefined)? firstDayParam: " 00010101" ;
var lastDay = " " ;
var field= sortBy;
var maxDaysParam = getParam(args," maxDays" ,undefined);
var maxDays = (maxDaysParam!=undefined)? maxDaysParam*24*60*60*1000: (new Date()).getTime() ;
var maxEntries = getParam(args," maxEntries" ,undefined);
var last = (maxEntries!=undefined) ? tiddlers.length-Math.min(tiddlers.length,parseInt(maxEntries)) : 0;
for(var t=tiddlers.length-1; t> =last; t--)
{
var tiddler = tiddlers[t];
var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
if ((theDay> =firstDay)& & (tiddler[field].getTime()> (new Date()).getTime() - maxDays))
{
if(theDay != lastDay)
{
var theDateList = document.createElement(" ul" );
place.appendChild(theDateList);
createTiddlyElement(theDateList," li" ,null," listTitle" ,tiddler[field].formatString(this.dateFormat));
lastDay = theDay;
}
var theDateListItem = createTiddlyElement(theDateList," li" ,null," listLink" ,null);
theDateListItem.appendChild(createTiddlyLink(place,tiddler.title,true));
}
}
}
else
{
window.old_timeline_handler.apply(this,arguments);
}
}
//}}}< / pre >
< / div >
2007-06-27 17:19:54 +02:00
< div title = "BuildProcess" modifier = "MichaelPloujnikov" modified = "200706271431" created = "200706190658" tags = "dynamic" changecount = "21" >
< pre > All decisions on //how // the RenderProcess has to be carried out are concentrated in this rather complicated Builder Subsystem. The benefit of this approach is, besides decoupling of subsystems, to keep the actual performance-intensive video processing code as simple and transparent as possible. The price, in terms of increased complexity & mdash; to pay in the Builder & mdash; can be handled by making the Build Process generic to a large degree. Using a Design By Contract approach we can decompose the various decisions into small decision modules without having to trace the actual workings of the Build Process as a whole.
2007-06-19 09:22:52 +02:00
2007-06-25 09:56:36 +02:00
[> img[Outline of the Build Process|uml/fig129413.png]]
2007-06-27 17:19:54 +02:00
The building itself will be broken down into several small tool application steps. Each of these steps has to be mapped to the MObjects found on the [[Timeline]]. Remember: the idea is that the so called " [[Fixture]]" contains only [[ExplicitPlacement]]s which in turn link to MObjects like Clips, Effects and Automation. So it is sufficient to traverse this list and map the build tools to the elements. Each of these build tools has its own state, which serves to build up the resulting Render Engine. So far I see two steps to be necessary:
2007-06-25 09:56:36 +02:00
* find the " Segments" , i.e. the locations where the overall configuration changes
* for each segment: generate a ProcNode for each found MObject and wire them accordingly
2007-06-27 17:19:54 +02:00
Note, //we still have to work out how exactly building, rendering and playback work// together with the backend-design. The build process as such doesn't overly depend on these decisions. It is easy to reconfigure this process. For example, it would be possible as well to build for each frame separately (as Cinelerra2 does), or to build one segment covering the whole timeline (and handle everything via [[Automation]]
2007-06-25 09:56:36 +02:00
& rarr;see also: BuilderStructures
2007-06-25 09:02:09 +02:00
[img[Colaborations in the Build Process|uml/fig128517.png]]
2007-06-25 09:56:36 +02:00
2007-06-22 08:46:15 +02:00
< / pre >
2007-06-19 09:22:52 +02:00
< / div >
2007-06-22 08:46:15 +02:00
< div title = "Builder" modifier = "Ichthyostega" created = "200706220317" tags = "def" changecount = "2" >
< pre > The Builder takes some MObject/[[Placement]] information (called Timeline) and generates out of this a Render Engine configuration able to render this Objects. It does all decisions and retrieves the current configuration of all objects and plugins, so the Render Engine can just process them stright forward.
The Builder is part of the [[Builder Pattern|http://en.wikipedia.org/wiki/Builder_pattern]]
< / pre >
2007-06-19 03:45:45 +02:00
< / div >
2007-06-25 09:56:36 +02:00
< div title = "BuilderStructures" modifier = "Ichthyostega" modified = "200706250743" created = "200706250734" changecount = "5" >
< pre > * the MObjects implement //Buildable//
* each Buildable can " recieve" a Tool object and aply it
* the different Tool objects are iterated/mapped onto the list of MObjects in the [[Timeline]]
* __Rationale__
* the MObject class hierarchy is rather fixed (it is unlikely the we will be adding much new MObject subclasses)
* so this design makes it easy to add new Tool subclasses, and within each Tool subclass, all operations on the different MObject classes are grouped together, so it is easy to see what is going on.
* a given Tool instance can carry state while beeing iterated, so we don't need any global (or object-global) variables to hold the result of the build process
This programming technique is often refered to as //double dispatch// or //visitor//
{{red{TODO:flesh out the actual Operations needed}}}
2007-08-08 04:50:02 +02:00
[img[Entities cooperating in the Builder|uml/fig129285.png]]
< / pre >
2007-06-25 09:56:36 +02:00
< / div >
2007-08-08 04:50:02 +02:00
< div title = "ColorPalette" modifier = "Ichthyostega" modified = "200706260455" created = "200706190033" tags = "excludeMissing" changecount = "11" >
2007-06-19 03:45:45 +02:00
< pre > Background: #fefefd
Foreground: #000
PrimaryPale: #8fb
PrimaryLight: #50d2ae
PrimaryMid: #1b9989
PrimaryDark: #0f3f56
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eef
TertiaryLight: #ccd
TertiaryMid: #99a
TertiaryDark: #667
Error: #f88< / pre >
< / div >
2007-08-10 06:11:31 +02:00
< div title = "Controller" modifier = "Ichthyostega" modified = "200708100409" created = "200706220319" tags = "def" changecount = "3" >
2007-06-22 08:46:15 +02:00
< pre > Here, in the context of the Render Engine, the Controller component is responsible for managing the global playback state, for triggering the build process and for activating the backend and the Render Engine configuration created by the Builder to carry out the actual rendering. So you can expect the Controller to encompass a State Machine.
2007-08-10 06:11:31 +02:00
!Facade
This is an very important external Interface, because it links together all three Layers of our current architecture. It can be used by the backend to initiate [[Render Processes (=StateProxy)|StateProxy]] and it will probably be used by the Dispatcher for GUI actions as well...
2007-06-22 08:46:15 +02:00
< / pre >
< / div >
2007-09-27 23:07:05 +02:00
< div title = "CurrentSession" modifier = "Ichthyostega" modified = "200709272058" created = "200709272057" tags = "decision design" changecount = "2" >
< pre > The question is where to put all the state-like information [[associated with the current session|SessionOverview]]. Because this is certainly " global" , but may depend on the session or need to be configured differently when loading another session. At the moment (9/07) Ichthyo considers the following solution:
* represent all configuration as [[Asset]]s
* find a way {{red{TODO}}} how to reload the contents of the [[AssetManager]].
* completely hide the Session object behind a ''~PImpl'' smart pointer, so the session object can be switched when reloading.
* the [[Fixture]] acts as isolation layer, and all objects refered from the Fixture are refcounting smart pointers. So, even when the session gets switched, the old objects remain valid as long as needed.< / pre >
< / div >
2007-06-19 03:45:45 +02:00
< div title = "DefaultTiddlers" modifier = "Ichthyostega" modified = "200706190047" created = "200706172308" changecount = "2" >
< pre > RenderEngine
< / pre >
< / div >
2007-06-27 17:19:54 +02:00
< div title = "DesignGoals" modifier = "MichaelPloujnikov" modified = "200706271507" created = "200706210557" tags = "design" changecount = "9" >
< pre > As always, the main goal is //to cut down complexity// by the usual approach to separate into small manageable chunks.
2007-06-21 08:34:21 +02:00
2007-06-27 17:19:54 +02:00
To achieve this, here we try to separate ''Configuration'' from ''Processing''. Further, in Configuration we try to separate the ''high level view'' (users view when editing) from the ''low level view'' (the actual configuration effective for the calculations). Finally, we try to factor out and encapsulate ''State'' in order to make State explicit.
2007-06-21 08:34:21 +02:00
The main tool used to implement this separation is the [[Builder Pattern|http://en.wikipedia.org/wiki/Builder_pattern]]. Here especially we move all decisions and parametrization into the BuildProcess. The Nodes in this Pipeline should process Video/Audio and do nothing else. No more decisions, tests and conditional operations when running the Pipeline. Move all of this out into the configuration of the pipeline, which is done by the Builder. Make the actual processing nodes Template classes, parametrized by the color model and number of components. Make all Nodes of equal footing with each other, able to be connected freely within the limitations of the necessary input and output. Make the OpenGL rendering into alternate implementation of some operations together with an alternate signal flow (usable only if the whole Pipeline can be built up to support this changed signal flow), thus factoring out all the complexities of managing the data flow between core and hardware accelerated rendering out of the implementation of the actual processing. Introduce separate control data connections for the automation data, separating the case of true multi-channel-effects from the case where one node just gets remote controlled by another node (or two nodes using the same automation data).
2007-06-27 17:19:54 +02:00
Another pertinent theme is to make the basic building blocks simpler, while on the other hand gaining much more flexibility for combining these building blocks. For example we try to unfold any " internal-multi" effects into separate instances (e.g. the possibility of having an arbitrary number of single masks at any point of the pipeline instead of having one special masking facility encompassing multiple sub-masks. Similarly, we treat the Objects in the EDL in a more uniform manner and gain the possibility to [[place|Placement]] them in various ways.
2007-06-22 08:46:15 +02:00
< / pre >
2007-06-21 08:34:21 +02:00
< / div >
2007-09-27 23:07:05 +02:00
< div title = "EDL" modifier = "Ichthyostega" modified = "200709272059" created = "200706210610" tags = "def" changecount = "6" >
2007-06-21 08:34:21 +02:00
< pre > ''EDL'' is a short-hand for __E__dit __D__ecision __L__ist. The use of this term can be confusing; for the usual meaning see the definition in [[Wikipedia|http://en.wikipedia.org/wiki/Edit_decision_list]]
2007-06-27 17:19:54 +02:00
Cinelerra uses this term in a related manner but with a somewhat shifted focus (and we just stick to this usage here): In Cinelerra the EDL is comprised of the whole set of clips and other media objects parametrized and placed onto the tracks by the user. It is the result of the user's //editing efforts.//
2007-06-21 08:34:21 +02:00
2007-09-27 23:07:05 +02:00
In this usage, the EDL in most cases will be almost synonymous to the [[Session|SessionOverview]], just the latter emphasizes more the state aspect, as it can be thought as the current EDL contents contained in a file or data structure together with additional Option values and settings for the GUI. The Session is what you save and load, while the EDL rather denotes a structured collection of Objects placed in time.
2007-06-22 08:46:15 +02:00
< / pre >
< / div >
2007-10-10 03:54:09 +02:00
< div title = "EditingOperations" modifier = "Ichthyostega" modified = "200710100119" created = "200709251610" tags = "design decision" changecount = "4" >
2007-09-25 23:39:46 +02:00
< pre > These are the tools provided to any client of the Proc layer for handling and manipulating the entities in the EDL. When defining such operations, //the goal should be to arrive at some uniformity in the way things are done.// Ideally, when writing client code, one should be able to guess how to achieve some desired result.
!guiding principle
The approach taken to define any operation is based primarily on the ''~OO-way of doing things'': entities operate themselfs. You don't advise some manager, session or other & raquo;god class& laquo; to manipulate them. And, secondly, the scope of each operation will be as large as possible, but not larger. This often means performing quite some elementary operations & mdash; sometimes a convenience shortcut provided by the higher levels of the application may come in handy & mdash; and basically this gives rise to several different paths of doing the same thing, all of which need to be equivalent.
!main tasks
* you ''create a clip'' either from a source media or from another clip (copy, maybe clone?). The new clip always reflects the full size (and other properties) of the source used for creating.
* you can request a clip to ''resize'', which always relates to its current dimensions.
2007-10-10 03:54:09 +02:00
* you can ''place or attach'' the clip to some point or other entity by creating a [[Placement]] from the clip. (& rarr; [[handling of Placements|PlacementHandling]])
2007-09-25 23:39:46 +02:00
* you can ''adjust'' a placement relative to some other placement, meta object (i.e. selection), label or media, and this may cause the placement to resize or even delete the clip as necessary
* you can perform ''adjustments'' on the whole EDL.
All these operations propagate to directly dependant objects and may schedule global reconfigurations.
!state, locking, policies
While performing any manipulative task, the state of the object is considered inconsistent, but it is guaranteed to be consistent after any such operation, irrespective of the result. There is no automatic atomicity and, consequently each propagation is considered a separate operation.
!!parallelism
At the level of fine grained operations on individual entities, there is ''no support for parallelism''. Individual entities don't lock themselves. Tasks perform locking and are to be scheduled. Thus, we need an isolation layer towards all inherently multithreaded parts of the system, like the GUI or the renderengine.
This has some obvious and some subtle consequences. Of course, manipulating //tasks// will be queued and scheduled somewhere. But as we rely on object identity and reference semantics for the entities in the EDL, some value changes are visible to operations going on in parallel threads (e.g. rendering) and each operation on the EDL entities //has to be aware of this.//
Consequently, sometimes there needs to be done a ''self-replacement by copy'' followed by manipulation of the new copy, while ongoing processes use the unaltered original object until they receive some sort of reset.
!!undo
Basically, each elementary operation has to record the informations necessary to be undone. It does so by registering a Memento with some central UndoManager facility. This Memento object contains a functor pre-bound with the needed parameter values. (Besides, the UndoManager is free to implement a second level of security by taking independent state snapshots).
{{red{to be defined in more detail later...}}}
< / pre >
< / div >
2007-06-22 08:46:15 +02:00
< div title = "Example1" modifier = "Ichthyostega" modified = "200706220253" created = "200706220239" tags = "example" changecount = "3" >
2007-06-26 08:20:31 +02:00
< pre > The & raquo;Timeline& laquo; is a sequence of ~MObjects -- here clips -- together with an ExplicitPlacement, locating each clip at a given time and track. (Note: I simplified the time format and wrote frame numbers to make it more clear)
2007-06-25 09:02:09 +02:00
[img[Example1: Objects in the EDL/Fixture|uml/fig128773.png]]
2007-06-22 08:46:15 +02:00
----
After beeing processed by the Builder, we get the following Render Engine configuration
2007-06-25 09:02:09 +02:00
[img[Example1: generated Render Engine|uml/fig129029.png]]
2007-06-22 08:46:15 +02:00
< / pre >
< / div >
< div title = "Example2" modifier = "Ichthyostega" created = "200706220251" tags = "example" changecount = "1" >
2007-06-26 08:20:31 +02:00
< pre > This Example showes the //high level// EDL as well. This needs to be transformed into a Fixture by some facility still to be designed. Basically, each [[Placement]] needs to be queried for this to get the corresponding ExplicitPlacement. The difficult part is to handle possible Placement constraints, e.g. one clip can't be placed at a timespan covered by another clip on the same track. In the current Cinelerra2, all of this is done directly by the GUI actions.
2007-06-22 08:46:15 +02:00
2007-06-26 08:20:31 +02:00
The & raquo;Timeline& laquo; is a sequence of ~MObjects -- note: using the same Object instances -- but now with the calculated ExplicitPlacement, locating the clip at a given time and track. The effect is located absolutely in time as well, but because it is the same Instance, it has the pointer to the ~RelativePlacement, wich basically attaches the effect to the clip. This structure may look complicated, but is easy to process if we go " backward" and just rely on the information contained in the ExplicitPlacement.
2007-06-25 09:02:09 +02:00
[img[Example2: Clip with Effect and generated Fixture for this EDL|uml/fig128901.png]]
2007-06-22 08:46:15 +02:00
----
After beeing processed by the Builder, we get a Render Engine configuration.< br>
It has to be segmented at least at every point with changes in the configuration, but some variations are possible, e.g. we could create a Render Engine for every Frame (as Cinelerra2 does) or we could optimize out some configurations (for example the effect extended beyond the end of the clip)
2007-06-25 09:02:09 +02:00
[img[Example2: generated Render Engine|uml/fig129157.png]]
2007-06-22 08:46:15 +02:00
< / pre >
< / div >
2007-06-27 17:19:54 +02:00
< div title = "Examples" modifier = "MichaelPloujnikov" modified = "200706271425" created = "200706220233" tags = "example" changecount = "6" >
< pre > !MObject assembly
2007-06-22 08:46:15 +02:00
To make the intended use of the classes more clear, consider the following two example Object graphs:
2007-06-26 08:20:31 +02:00
* a video clip and a audio clip placed (explicitly) on two tracks & rarr;[[Example1]]
2007-06-22 08:46:15 +02:00
* a video clip placed relatively, with an attached HUE effect & rarr;[[Example2]]
< / pre >
< / div >
< div title = "ExitNode" modifier = "Ichthyostega" created = "200706220322" tags = "def" changecount = "1" >
< pre > a special ProcNode which is used to pull the finished output of one Render Pipeline (Tree or Graph). This term is already used in the Cinelerra2 codebase. I am unsure at the moment if it is a distinct subclass or rahter a specially configured ProcNode (a general design rule tells us to err in favour of the latter if in doubt).
< / pre >
< / div >
2007-06-27 17:19:54 +02:00
< div title = "ExplicitPlacement" modifier = "MichaelPloujnikov" modified = "200706271458" created = "200706220304" tags = "def" changecount = "2" >
< pre > A special kind (subclass) of [[Placement]]. As such it is always linked to a //Subject//, i.e. a MObject. In addition to the properties of a (unspecific) Placement, the ExplicitPlacement specifies a absolute time and track position for locating the Subject
2007-08-10 06:11:31 +02:00
< / pre >
< / div >
2007-08-12 01:04:55 +02:00
< div title = "Factories" modifier = "Ichthyostega" modified = "200708112256" created = "200708100401" tags = "impl discuss" changecount = "18" >
2007-08-10 06:11:31 +02:00
< pre > We use Factories
* for centralizing [[memory management|MemoryManagement]]
* to support polymorphism (of course...)
!Requirements
* request the actual placement/allocation from the backend
* allways hand out a smart-pointer
* encapsulate / make configurable the smart-pointer type
* install a callback into the smart-pointer for destroying the resource.
* redirect the destroying request to the backend
!Implementation Questions
* how much genericity? (Ichthyo is rather inclined not to overdo this one. Often it is preferable to have repeated implementations follow a well known pattern, if this leads to short and simple implementations, while the complete general solution will be much more contrived).
* how to specify the actual type needed?
* how to implement the cases where a subtype needs to be selected (abstract factory pattern). Embody this into the Factory, pass it in as a Strategy or treat the Factory just as a simple service taking an explicit type-ID and providing the new object?
2007-08-12 01:04:55 +02:00
!!chosen design
My main goal is to have an easy-to-use interface for the implementer of individual classes using this factory mechanism. The intended use should mimic the standard use of operator new, and at the same time there should be a possibility to configure »real« Factory behaviour in.
For this reason I make Factory a Functor, and for handling the concrete memory allocation, it inherits from an Allocator class. The users of this factory template typically either parametrize it with existing smart-pointer types and some Allocator instantiation, or they may chose to create a specialized Factory derived from this Factory template. After typically hiding this configuration behind a typedef, the user adds a static field to the class intended to use the Factory and initializes this field with the concrete Factory (this may pass internal ~IDs to the constructor of the Factory and from there on to the underlying Allocator).
{{{
#include " common/factory.hpp"
class Blubb
{
int uii ;
public:
typedef cinelerra::factory::RefcountPtr< Blubb> Factory;
static Factory create;
Blubb() : uii(42) {} ;
};
/** a static Factory instance
* for creating refcounting Ptrs to Blubb objects
*/
Blubb::Factory Blubb::create; // < ----note this is a constructor call
}}}
Now, the clients of {{{class Blubb}}} can create ref-counting pointers to Blubb-objects by doing a fully qualified {{{create()}}} functor call:
{{{
std::tr1::shared_ptr< Blubb> huii = Blubb::create (); // < ----will invoke the default Blubb() ctor
std::tr1::shared_ptr< Blubb> pfuii = huii;
}}}
Some further details
* Functor inherits (protected) from Allocator, besides that there are no additional requirements.
* typically, Allocator provides a overloaded {{{operator new(size_t)}}} etc.
* thus, when Functor or any derived class issues a new XX(), our custom Allocator gains control
* the Functor-behaviour relies on a custom {{{operator()}}} which can be overridden in derived classes to take various parameter lists.
* then, such a Factory class derived from Functor can do specific magic and e.g. create some subclass
* and, as the created smart-pointer is a template parameter, such a custom Functor can create all sorts of Proxies, wrappers and the like
2007-06-22 08:46:15 +02:00
< / pre >
< / div >
2007-09-27 04:45:06 +02:00
< div title = "Fixture" modifier = "Ichthyostega" modified = "200709270236" created = "200706220324" tags = "def" changecount = "3" >
2007-06-22 08:46:15 +02:00
< pre > a specially configured EDL
* all MObjects have their position, length and configuration set up ready for rendering.
2007-09-27 04:45:06 +02:00
* compound objects (e.g. multichannel clips) have been resolved to single non-compound basic objects.
* every MObject is associated with an ExplicitPlacement, which declares a fixed position (Time, Track)
2007-06-26 08:20:31 +02:00
* this ~ExplicitPlacements are contained in a ordered List called the Timeline
2007-06-22 08:46:15 +02:00
2007-09-27 04:45:06 +02:00
As the builder and thus render engine //only consults the fixture,// while all editing operations finally propagate to the fixture as well, we get an isolation layer between the high level part of the Proc layer (editing, object manipulation) and the render engine
2007-06-22 08:46:15 +02:00
< / pre >
< / div >
< div title = "Frame" modifier = "Ichthyostega" modified = "200706220333" created = "200706220332" tags = "def" changecount = "2" >
< pre > This term has //two meanings, //so care has to be taken for not confusing them.
# in general use, a Frame means one full image of a video clip, i.e an array of rows of pixels. For interlaced footage, one Frame contains two halfimages, commonly called Fields. (Cinelerra2 confuses this terms)
# here in this design, we use Frame as an abstraction for a buffer of raw media data to be processed. If in doubt, we should label this " Dataframe" .
#* one video Dataframe contains a single video frame
#* one audio Dataframe contains a block of raw audio samples
#* one OpenGL Dataframe could contain raw texture data (but I am lacking expertise for this topic)
< / pre >
2007-06-21 08:34:21 +02:00
< / div >
2007-06-19 03:45:45 +02:00
< div title = "FullScreenPlugin" modifier = "CehTeh" modified = "200706110313" created = "200607241016" tags = "systemConfig lewcidExtension" server . type = "file" server . host = "file:///home/ct/.homepage/home.html" server . page . revision = "200706110313" >
< pre > /***
|Name|FullScreenPlugin|
|Created by|SaqImtiaz|
|Location|http://tw.lewcid.org/#FullScreenPlugin|
|Version|1.1|
|Requires|~TW2.x|
!Description:
Toggle between viewing tiddlers fullscreen and normally. Very handy for when you need more viewing space.
!Demo:
Click the ↕ button in the toolbar for this tiddler. Click it again to turn off fullscreen.
!Installation:
Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
Edit the ViewTemplate to add the fullscreen command to the toolbar.
!History:
*25-07-06: ver 1.1
*20-07-06: ver 1.0
!Code
***/
//{{{
var lewcidFullScreen = false;
config.commands.fullscreen =
{
text:" ↕ " ,
tooltip:" Fullscreen mode"
};
config.commands.fullscreen.handler = function (event,src,title)
{
if (lewcidFullScreen == false)
{
lewcidFullScreen = true;
setStylesheet('#sidebar, .header, #mainMenu{display:none;} #displayArea{margin:0em 0 0 0 !important;}'," lewcidFullScreenStyle" );
}
else
{
lewcidFullScreen = false;
setStylesheet(' '," lewcidFullScreenStyle" );
}
}
config.macros.fullscreen={};
config.macros.fullscreen.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
var label = params[0]||" ↕ " ;
var tooltip = params[1]||" Fullscreen mode" ;
createTiddlyButton(place,label,tooltip,config.commands.fullscreen.handler);
}
var lewcid_fullscreen_closeTiddler = Story.prototype.closeTiddler;
Story.prototype.closeTiddler =function(title,animate,slowly)
{
lewcid_fullscreen_closeTiddler.apply(this,arguments);
if (story.isEmpty() & & lewcidFullScreen == true)
config.commands.fullscreen.handler();
}
Slider.prototype.lewcidStop = Slider.prototype.stop;
Slider.prototype.stop = function()
{
this.lewcidStop();
if (story.isEmpty() & & lewcidFullScreen == true)
config.commands.fullscreen.handler();
}
//}}}< / pre >
< / div >
2007-06-22 08:46:15 +02:00
< div title = "GOPs" modifier = "Ichthyostega" modified = "200706220333" created = "200706220301" tags = "def" changecount = "2" >
< pre > __G__roup __of__ __P__ictures: several compressed video formats don't encode single frames. Normally, such formats are considered mere //delivery formates// but it was one of the key strenghts of Cinelrra from start to be able to do real non linear editing on such formats (like the ~MPEG2-ts unsed in HDV video). The problem of course is that the data backend needs to decode the whole GOP to be serve single raw video frames.
For this Cinelerra3 design, we could consider making GOP just another raw media data frame type and integrate this decoding into the render pipeline, similar to an effect based on several source frames for every calculated output frame.
& rarr;see in [[Wikipedia|http://en.wikipedia.org/wiki/Group_of_pictures]]
< / pre >
< / div >
2007-09-27 23:07:05 +02:00
< div title = "ImplementationDetails" modifier = "Ichthyostega" modified = "200709272050" created = "200708080322" tags = "overview" changecount = "9" >
2007-08-10 06:11:31 +02:00
< pre > This wiki page is the entry point to detail notes covering some technical decisions, details and problems encountered in the course of the implementation of the Cinelerra Renderengine, the Builder and the related parts.
2007-08-09 18:51:47 +02:00
2007-08-10 06:11:31 +02:00
* [[Packages, Interfaces and Namespaces|InterfaceNamespaces]]
* [[Memory Management Issues|MemoryManagement]]
2007-09-04 05:09:08 +02:00
* [[Creating and registering Assets|AssetCreation]]
* [[Creating new Objects|ObjectCreation]]
2007-09-20 05:36:08 +02:00
* [[Multichannel Media|MultichannelMedia]]
2007-09-25 23:39:46 +02:00
* [[Editing Operations|EditingOperations]]
2007-09-27 23:07:05 +02:00
* [[Handling of the current Session|CurrentSession]]
2007-08-10 06:11:31 +02:00
< / pre >
2007-08-09 18:51:47 +02:00
< / div >
2007-06-19 03:45:45 +02:00
< div title = "InlineJavaScript" modifier = "Jeremy" created = "200603090618" tags = "systemConfig" server . type = "file" server . host = "file:///home/ct/.homepage/home.html" server . page . revision = "200603090618" >
< pre > /***
''InlineJavascriptPlugin for ~TiddlyWiki version 1.2.x and 2.0''
^^author: Eric Shulman - ELS Design Studios
source: http://www.TiddlyTools.com/#InlineJavascriptPlugin
license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
Insert Javascript executable code directly into your tiddler content. Lets you ''call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
!!!!!Usage
< < <
When installed, this plugin adds new wiki syntax for surrounding tiddler content with {{{< script> }}} and {{{< /script> }}} markers, so that it can be treated as embedded javascript and executed each time the tiddler is rendered.
''Deferred execution from an 'onClick' link''
By including a label=" ..." parameter in the initial {{{< script> }}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.
''External script source files:''
You can also load javascript from an external source URL, by including a src=" ..." parameter in the initial {{{< script> }}} marker (e.g., {{{< script src=" demo.js" > < /script> }}}). This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins. The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.
''Defining javascript functions and libraries:''
Although the external javascript file is loaded while the tiddler content is being rendered, any functions it defines will not be available for use until //after// the rendering has been completed. Thus, you cannot load a library and //immediately// use it's functions within the same tiddler. However, once that tiddler has been loaded, the library functions can be freely used in any tiddler (even the one in which it was initially loaded).
To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that will be rendered as soon as your TiddlyWiki document is opened. For example, you could put your {{{< script src=" ..." > < /script> }}} syntax into a tiddler called LoadScripts, and then add {{{< < tiddler LoadScripts> > }}} in your MainMenu tiddler.
Since the MainMenu is always rendered immediately upon opening your document, the library will always be loaded before any other tiddlers that rely upon the functions it defines. Loading an external javascript library does not produce any direct output in the tiddler, so these definitions should have no impact on the appearance of your MainMenu.
''Creating dynamic tiddler content''
An important difference between this implementation of embedded scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document:
* In a typical web document, you use the document.write() function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
* However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered " on-the-fly" , so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and completely replaces the entire ~TiddlyWiki document in your browser window.
* To allow these scripts to work unmodified, the plugin automatically converts all occurences of document.write() so that the output is inserted into the tiddler content instead of replacing the entire ~TiddlyWiki document.
If your script does not use document.write() to create dynamically embedded content within a tiddler, your javascript can, as an alternative, explicitly return a text value that the plugin can then pass through the wikify() rendering engine to insert into the tiddler display. For example, using {{{return " thistext" }}} will produce the same output as {{{document.write(" thistext" )}}}.
//Note: your script code is automatically 'wrapped' inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler. To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.//
''Accessing the ~TiddlyWiki DOM''
The plugin provides one pre-defined variable, 'place', that is passed in to your javascript code so that it can have direct access to the containing DOM element into which the tiddler output is currently being rendered.
Access to this DOM element allows you to create scripts that can:
* vary their actions based upon the specific location in which they are embedded
* access 'tiddler-relative' information (use findContainingTiddler(place))
* perform direct DOM manipulations (when returning wikified text is not enough)
< < <
!!!!!Examples
< < <
an " alert" message box:
{{{
< script> alert('InlineJavascriptPlugin: this is a demonstration message');< /script>
}}}
< script> alert('InlineJavascriptPlugin: this is a demonstration message');< /script>
dynamic output:
{{{
< script> return (new Date()).toString();< /script>
}}}
< script> return (new Date()).toString();< /script>
wikified dynamic output:
{{{
< script> return " link to current user: [[" +config.options.txtUserName+" ]]" ;< /script>
}}}
< script> return " link to current user: [[" +config.options.txtUserName+" ]]" ;< /script>
dynamic output using 'place' to get size information for current tiddler
{{{
< script>
if (!window.story) window.story=window;
var title=story.findContainingTiddler(place).id.substr(7);
return title+" is using " +store.getTiddlerText(title).length+" bytes" ;
< /script>
}}}
< script>
if (!window.story) window.story=window;
var title=story.findContainingTiddler(place).id.substr(7);
return title+" is using " +store.getTiddlerText(title).length+" bytes" ;
< /script>
creating an 'onclick' button/link that runs a script
{{{
< script label=" click here" >
if (!window.story) window.story=window;
alert(" Hello World!\nlinktext='" +place.firstChild.data+" '\ntiddler='" +story.findContainingTiddler(place).id.substr(7)+" '" );
< /script>
}}}
< script label=" click here" >
if (!window.story) window.story=window;
alert(" Hello World!\nlinktext='" +place.firstChild.data+" '\ntiddler='" +story.findContainingTiddler(place).id.substr(7)+" '" );
< /script>
loading a script from a source url
{{{
< script src=" demo.js" > return " loading demo.js..." < /script>
< script label=" click to execute demo() function" > demo()< /script>
}}}
where http://www.TiddlyTools.com/demo.js contains:
> function demo() { alert('this output is from demo(), defined in demo.js') }
> alert('InlineJavascriptPlugin: demo.js has been loaded');
< script src=" demo.js" > return " loading demo.js..." < /script>
< script label=" click to execute demo() function" > demo()< /script>
< < <
!!!!!Installation
< < <
import (or copy/paste) the following tiddlers into your document:
''InlineJavascriptPlugin'' (tagged with < < tag systemConfig> > )
< < <
!!!!!Revision History
< < <
''2006.01.05 [1.4.0]''
added support 'onclick' scripts. When label=" ..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked. 'place' value is set to match the clicked button/link element.
''2005.12.13 [1.3.1]''
when catching eval error in IE, e.description contains the error text, instead of e.toString(). Fixed error reporting so IE shows the correct response text. Based on a suggestion by UdoBorkowski
''2005.11.09 [1.3.0]''
for 'inline' scripts (i.e., not scripts loaded with src=" ..." ), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content
Based on a suggestion by BradleyMeck
''2005.11.08 [1.2.0]''
handle loading of javascript from an external URL via src=" ..." syntax
''2005.11.08 [1.1.0]''
pass 'place' param into scripts to provide direct DOM access
''2005.11.08 [1.0.0]''
initial release
< < <
!!!!!Credits
< < <
This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
< < <
!!!!!Code
***/
//{{{
version.extensions.inlineJavascript= {major: 1, minor: 4, revision: 0, date: new Date(2006,1,5)};
config.formatters.push( {
name: " inlineJavascript" ,
match: " \\< script" ,
lookahead: " \\< script(?: src=\\\" ((?:.|\\n)*?)\\\" )?(?: label=\\\" ((?:.|\\n)*?)\\\" )?\\> ((?:.|\\n)*?)\\< /script\\> " ,
handler: function(w) {
var lookaheadRegExp = new RegExp(this.lookahead," mg" );
lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = lookaheadRegExp.exec(w.source)
if(lookaheadMatch & & lookaheadMatch.index == w.matchStart) {
if (lookaheadMatch[1]) { // load a script library
// make script tag, set src, add to body to execute, then remove for cleanup
var script = document.createElement(" script" ); script.src = lookaheadMatch[1];
document.body.appendChild(script); document.body.removeChild(script);
}
if (lookaheadMatch[2] & & lookaheadMatch[3]) { // create a link to an 'onclick' script
// add a link, define click handler, save code in link (pass 'place'), set link attributes
var link=createTiddlyElement(w.output," a" ,null," tiddlyLinkExisting" ,lookaheadMatch[2]);
link.onclick=function(){try{return(eval(this.code))}catch(e){alert(e.description?e.description:e.toString())}}
link.code=" function _out(place){" +lookaheadMatch[3]+" };_out(this);"
link.setAttribute(" href" ," javascript:;" ); link.setAttribute(" title" ," " ); link.style.cursor=" pointer" ;
}
else if (lookaheadMatch[3]) { // run inline script code
var code=" function _out(place){" +lookaheadMatch[3]+" };_out(w.output);"
code=code.replace(/document.write\(/gi,'place.innerHTML+=(');
try { var out = eval(code); } catch(e) { out = e.description?e.description:e.toString(); }
if (out & & out.length) wikify(out,w.output);
}
w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
}
}
} )
//}}}
< / pre >
< / div >
2007-08-09 18:51:47 +02:00
< div title = "InlineJavascriptPlugin" modifier = "Ichthyostega" created = "200708081511" tags = "plugin excludeLists systemConfig" changecount = "1" >
< pre > /***
|''Name:''|InlineJavascriptPlugin|
|''Source:''|http://www.TiddlyTools.com/#InlineJavascriptPlugin|
|''Author:''|Eric Shulman - ELS Design Studios|
|''License:''|[[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|''~CoreVersion:''|2.0.10|
Insert Javascript executable code directly into your tiddler content. Lets you ''call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
!!!!!Usage
< < <
When installed, this plugin adds new wiki syntax for surrounding tiddler content with {{{< script> }}} and {{{< /script> }}} markers, so that it can be treated as embedded javascript and executed each time the tiddler is rendered.
''Deferred execution from an 'onClick' link''
By including a label=" ..." parameter in the initial {{{< script> }}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.
''External script source files:''
You can also load javascript from an external source URL, by including a src=" ..." parameter in the initial {{{< script> }}} marker (e.g., {{{< script src=" demo.js" > < /script> }}}). This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins. The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.
''Display script source in tiddler output''
By including the keyword parameter " show" , in the initial {{{< script> }}} marker, the plugin will include the script source code in the output that it displays in the tiddler.
''Defining javascript functions and libraries:''
Although the external javascript file is loaded while the tiddler content is being rendered, any functions it defines will not be available for use until //after// the rendering has been completed. Thus, you cannot load a library and //immediately// use it's functions within the same tiddler. However, once that tiddler has been loaded, the library functions can be freely used in any tiddler (even the one in which it was initially loaded).
To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that will be rendered as soon as your TiddlyWiki document is opened. For example, you could put your {{{< script src=" ..." > < /script> }}} syntax into a tiddler called LoadScripts, and then add {{{< < tiddler LoadScripts> > }}} in your MainMenu tiddler.
Since the MainMenu is always rendered immediately upon opening your document, the library will always be loaded before any other tiddlers that rely upon the functions it defines. Loading an external javascript library does not produce any direct output in the tiddler, so these definitions should have no impact on the appearance of your MainMenu.
''Creating dynamic tiddler content''
An important difference between this implementation of embedded scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document:
* In a typical web document, you use the document.write() function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
* However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered " on-the-fly" , so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and completely replaces the entire ~TiddlyWiki document in your browser window.
* To allow these scripts to work unmodified, the plugin automatically converts all occurences of document.write() so that the output is inserted into the tiddler content instead of replacing the entire ~TiddlyWiki document.
If your script does not use document.write() to create dynamically embedded content within a tiddler, your javascript can, as an alternative, explicitly return a text value that the plugin can then pass through the wikify() rendering engine to insert into the tiddler display. For example, using {{{return " thistext" }}} will produce the same output as {{{document.write(" thistext" )}}}.
//Note: your script code is automatically 'wrapped' inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler. To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.//
''Accessing the ~TiddlyWiki DOM''
The plugin provides one pre-defined variable, 'place', that is passed in to your javascript code so that it can have direct access to the containing DOM element into which the tiddler output is currently being rendered.
Access to this DOM element allows you to create scripts that can:
* vary their actions based upon the specific location in which they are embedded
* access 'tiddler-relative' information (use findContainingTiddler(place))
* perform direct DOM manipulations (when returning wikified text is not enough)
< < <
!!!!!Examples
< < <
an " alert" message box:
> < script show>
alert('InlineJavascriptPlugin: this is a demonstration message');
< /script>
dynamic output:
> < script show>
return (new Date()).toString();
< /script>
wikified dynamic output:
> < script show>
return " link to current user: [[" +config.options.txtUserName+" ]]" ;
< /script>
dynamic output using 'place' to get size information for current tiddler:
> < script show>
if (!window.story) window.story=window;
var title=story.findContainingTiddler(place).id.substr(7);
return title+" is using " +store.getTiddlerText(title).length+" bytes" ;
< /script>
creating an 'onclick' button/link that runs a script:
> < script label=" click here" show>
if (!window.story) window.story=window;
alert(" Hello World!\nlinktext='" +place.firstChild.data+" '\ntiddler='" +story.findContainingTiddler(place).id.substr(7)+" '" );
< /script>
loading a script from a source url:
> http://www.TiddlyTools.com/demo.js contains:
> > {{{function demo() { alert('this output is from demo(), defined in demo.js') } }}}
> > {{{alert('InlineJavascriptPlugin: demo.js has been loaded'); }}}
> < script src=" demo.js" show>
return " loading demo.js..."
< /script>
> < script label=" click to execute demo() function" show>
demo()
< /script>
< < <
!!!!!Installation
< < <
import (or copy/paste) the following tiddlers into your document:
''InlineJavascriptPlugin'' (tagged with < < tag systemConfig> > )
< < <
!!!!!Revision History
< < <
''2006.06.01 [1.5.1]'' when calling wikify() on script return value, pass hightlightRegExp and tiddler params so macros that rely on these values can render properly
''2006.04.19 [1.5.0]'' added 'show' parameter to force display of javascript source code in tiddler output
''2006.01.05 [1.4.0]'' added support 'onclick' scripts. When label=" ..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked. 'place' value is set to match the clicked button/link element.
''2005.12.13 [1.3.1]'' when catching eval error in IE, e.description contains the error text, instead of e.toString(). Fixed error reporting so IE shows the correct response text. Based on a suggestion by UdoBorkowski
''2005.11.09 [1.3.0]'' for 'inline' scripts (i.e., not scripts loaded with src=" ..." ), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content. Based on a suggestion by BradleyMeck
''2005.11.08 [1.2.0]'' handle loading of javascript from an external URL via src=" ..." syntax
''2005.11.08 [1.1.0]'' pass 'place' param into scripts to provide direct DOM access
''2005.11.08 [1.0.0]'' initial release
< < <
!!!!!Credits
< < <
This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
< < <
!!!!!Code
***/
//{{{
version.extensions.inlineJavascript= {major: 1, minor: 5, revision: 1, date: new Date(2006,6,1)};
config.formatters.push( {
name: " inlineJavascript" ,
match: " \\< script" ,
lookahead: " \\< script(?: src=\\\" ((?:.|\\n)*?)\\\" )?(?: label=\\\" ((?:.|\\n)*?)\\\" )?( show)?\\> ((?:.|\\n)*?)\\< /script\\> " ,
handler: function(w) {
var lookaheadRegExp = new RegExp(this.lookahead," mg" );
lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = lookaheadRegExp.exec(w.source)
if(lookaheadMatch & & lookaheadMatch.index == w.matchStart) {
if (lookaheadMatch[1]) { // load a script library
// make script tag, set src, add to body to execute, then remove for cleanup
var script = document.createElement(" script" ); script.src = lookaheadMatch[1];
document.body.appendChild(script); document.body.removeChild(script);
}
if (lookaheadMatch[4]) { // there is script code
if (lookaheadMatch[3]) // show inline script code in tiddler output
wikify(" {{{\n" +lookaheadMatch[0]+" \n}}}\n" ,w.output);
if (lookaheadMatch[2]) { // create a link to an 'onclick' script
// add a link, define click handler, save code in link (pass 'place'), set link attributes
var link=createTiddlyElement(w.output," a" ,null," tiddlyLinkExisting" ,lookaheadMatch[2]);
link.onclick=function(){try{return(eval(this.code))}catch(e){alert(e.description?e.description:e.toString())}}
link.code=" function _out(place){" +lookaheadMatch[4]+" };_out(this);"
link.setAttribute(" href" ," javascript:;" ); link.setAttribute(" title" ," " ); link.style.cursor=" pointer" ;
}
else { // run inline script code
var code=" function _out(place){" +lookaheadMatch[4]+" };_out(w.output);"
code=code.replace(/document.write\(/gi,'place.innerHTML+=(');
try { var out = eval(code); } catch(e) { out = e.description?e.description:e.toString(); }
if (out & & out.length) wikify(out,w.output,w.highlightRegExp,w.tiddler);
}
}
w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
}
}
} )
//}}}< / pre >
< / div >
2007-08-13 09:55:32 +02:00
< div title = "InterfaceNamespaces" modifier = "Ichthyostega" modified = "200708120302" created = "200708080338" tags = "impl decision discuss" changecount = "15" >
2007-08-09 18:51:47 +02:00
< pre > Because we rely on strong decoupling and separation into self contained components, there is not much need for a common quasi-global namespace. Operations needing the cooperation of another subsystem will be delegated or even dispatched, consequently implementation code needs only the service acces points from " direct cooperation partner" subsystems. Hierarchical scopes besides classes are needed only when multiple subsystems share a set of common abstractions. Interface and Implementation use separate namespaces.
!common definitions
# surely there will be the need to use some macros (albeit code written in C++ can successfully avoid macros to some extent)
# there will be one global ''Application State'' representation (and some application services)
# we have some lib facilities, especially a common [[Time]] abstraction
For these we have one special (bilingual) header file __cinelerra.h__, which places in its C++ part some declarations into __namespace cinelerra__. These declarations should be pulled into the specific namespaces or (still better) into the implementation //on demand//. There is no nesting (we deliberately don't want an symbol appearing //automatically// in every part of the system).
!subsystem interface and facade
These large scale interfaces reside in special namespaces " ~XXX_interface" (where XXX is the subsystem). The accompanning //definitions// depend on the implementation namespace and are placed in the top-level source folder of the corresponding subsystem.
& rarr; Example: [[Interfaces/Namespaces of the Session Subsystem(s)|InterfacesSession]]
!contract checks and test code
From experiences with other middle scale projects, I prefer having the test code in a separate tree (because test code easily doubles the number of source files). But of course it should be placed into the same namespace as the code being checked, or (better?) into a nested namespace " test" . It is esp. desirable to have good coverage on the contracts of the subsystem interfaces and mayor components (while it is not always feasible or advisable to cover every implementation detail).
2007-08-13 09:55:32 +02:00
& rarr; see also [[testsuite documentation in the main wiki|index.html#TestSuite]]
2007-08-09 18:51:47 +02:00
< / pre >
< / div >
< div title = "InterfacesSession" modifier = "Ichthyostega" modified = "200708081535" created = "200708080639" changecount = "7" >
< pre > * Subdir src/proc contains Interface within namespace proc_interface
* Subdir src/proc/mobject contains commonly used entities (namespace mobject)
** nested namespace controller
** nested namespace builder
** nested namespace session
* Subdir src/proc/engine (namespace engine) uses directly the (local) interface components StateProxy and ParamProvider; triggering of the render process is initiated by the controller and can be requested via the controller facade. Normally, the playback/render controller located in the backend will just use this interface and won't be aware of the build process at all.
[img[Example: Interfaces/Namespaces of the ~Session-Subsystems|uml/fig130053.png]]
< / pre >
< / div >
2007-09-24 16:20:41 +02:00
< div title = "Loading Media" modifier = "Ichthyostega" modified = "200709240049" created = "200709220005" tags = "design spec" changecount = "2" >
2007-09-22 02:53:03 +02:00
< pre > Opening and accessing media files on disk poses several problems, most of which belong to the domain of cinelerra's data backend. Here, we focus on the questions related to making media data available to the EDL and the render engine. Each media will be represented by an MediaAsset object, which indeed could be a compound object (in case of MultichannelMedia). Building this asset object thus includes getting informations from the real file on disk. For delegating this to the backend, we use the following query interface:
2007-09-24 16:20:41 +02:00
* {{{queryFile(char* name)}}} requests accessing the file and yields some (opaque) handle when successful.
* {{{queryChannel(FHandle, int)}}} will then be issued in sequence with ascending index numbers, until it returns {{{NULL}}}.
2007-09-22 02:53:03 +02:00
* the returned struct (pointer) will provide the following information:
** some identifier which can be used to create a name for the corresponding media (channel) asset
** some identifier characterizing the access method (codec) needed to get at the media data. This should be rather a high level description of the media stream type, e.g. " H264"
** some (opaque) handle usable for accessing this specific stream. When the render engine later on pulls data for this channel, it will pass this handle down to the backend.
{{red{to be defined in more detail later...}}}
{{red{how to create a test stub for this interface...?}}}< / pre >
< / div >
2007-10-10 03:54:09 +02:00
< div title = "MObject" modifier = "Ichthyostega" modified = "200710092343" created = "200706220312" tags = "def classes" changecount = "2" >
< pre > All sorts of " things" to be placed and manipulated by the user in the EDL. This interface abstracts the details and just supposes
* the media object has a duration
* it is allways //placed// in some manner, i.e. it is allways accessed via a [[Placement]]
2007-06-22 08:46:15 +02:00
* {{red{and what else?}}}
< / pre >
< / div >
2007-10-10 03:54:09 +02:00
< div title = "MObjects" modifier = "Ichthyostega" modified = "200710100118" created = "200706190636" tags = "overview" changecount = "9" >
2007-09-04 05:09:08 +02:00
< pre > The ~MObjects Subsystem contains everything related to the [[EDL]] and the various Media Objects placed within. It is complemented by the Asset Management (see & rarr; [[Asset]]). Examples for [[MObjects|MObject]] being:
2007-06-22 08:46:15 +02:00
* audio/video clips
* effects and plugins
* special facilities like mask and projector
* [[Automation]] sets
* labels and other (maybe functional) markup
2007-06-19 09:22:52 +02:00
2007-06-22 08:46:15 +02:00
This Design strives to achieve a StrongSeparation between the low-level Structures used to carry out the actual rendering and the high level Entities living in the EDL and being manipulated by the user. In this high level view, the Objects are grouped and located by [[Placements|Placement]], providing a flexible and open way to express different groupings, locations and ordering constraints between the Media Objects.
2007-10-10 03:54:09 +02:00
& rarr; EditingOperations
& rarr; PlacementHandling
& rarr; SessionOverview
2007-06-19 09:22:52 +02:00
2007-06-25 09:02:09 +02:00
[img[Classess related to the EDL|uml/fig128133.png]]
2007-06-22 08:46:15 +02:00
< / pre >
2007-06-19 09:22:52 +02:00
< / div >
2007-08-09 18:51:47 +02:00
< div title = "MainMenu" modifier = "Ichthyostega" modified = "200708080314" created = "200706172305" changecount = "9" >
2007-06-20 00:38:02 +02:00
< pre > ''[[Cinelerra3|index.html]]''
[[RenderEngine]]
2007-06-19 03:45:45 +02:00
[[MObjects]]
2007-08-09 18:51:47 +02:00
[[Implementation|ImplementationDetails]]
2007-06-19 03:45:45 +02:00
[[Admin]]
< < fullscreen> > < / pre >
< / div >
2007-09-02 17:51:46 +02:00
< div title = "ManagementAssetRelation" modifier = "Ichthyostega" modified = "200709021538" created = "200708100337" tags = "impl" changecount = "3" >
< pre > Problem is: when removing an Asset, all corresponding MObjects need to disappear. This means, besides the obvious ~Ref-Link (MObject refering to an asset) we need backlinks or a sort of registry. And still worse: we need to remove the affetcted MObject from the object network in the EDL and rebuild the Fixture...
As a //first shot// Ichthyo considers the following approach:
* all references between MObjects and Assets are implemented as refcounting boost::shared_ptr
* MObjects and Assets implement an {{{unlink()}}} function releasing the internal links to other entities.
* Instead of a delete, we call this unlink() function and let the shared_ptr handle the actual deletion.
* we don't use a registry, rather we model the real dependencies by individual dependency links. So a MediaAsset gets links to all Clips created from this Asset and by traversing this tree, we can handle the deletion
* after the deletion, the Fixture needs to be rebuilt.
* but any render processes still can have pointers to the Asset to be removed, and the shared_ptr will ensure, that the refered objects stay alive as long as needed.
2007-08-10 06:11:31 +02:00
{{red{to be considered in more detail later}}}
< / pre >
< / div >
2007-08-08 04:50:02 +02:00
< div title = "MarkupPreHead" modifier = "Ichthyostega" modified = "200708080243" created = "200706172303" changecount = "1" >
2007-06-19 03:45:45 +02:00
< pre > < !--{{{-->
< link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>
< !--}}}-->
2007-08-08 04:50:02 +02:00
< style type=" text/css" > #contentWrapper {display:none;}< /style> < div id=" SplashScreen" style=" border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;" > loading < b> Cinelerra Renderengine< /b> devel doku< blink> ...< /blink> < br> < br> < span style=" font-size: 14px; color:red;" > Requires Javascript.< /span> < /div> < / pre >
2007-06-19 03:45:45 +02:00
< / div >
2007-09-22 16:00:36 +02:00
< div title = "MediaAsset" modifier = "Ichthyostega" modified = "200709221337" created = "200709021530" tags = "def classes" changecount = "2" >
2007-09-02 17:51:46 +02:00
< pre > The Interface asset::Media is a //key abstraction// It ties together several concepts and enables to deal with them on the interfaces in a uniform manner. Besides, as every Asset kind it belongs rather to the bookkeeping view: it holds the specific properties and parametrisation of the media source it stands for. Regarding the __inward interface__ & mdash; as used from within the [[EDL]] or the [[Render Nodes|ProcNode]], it is irrelevant if a given asset::Media object stands for a complete media source, just a clip taken from this source or if a placeholder version of the real media source is used instead.
2007-09-22 16:00:36 +02:00
[img[Asset Classess|uml/fig130437.png]]
< / pre >
2007-09-02 17:51:46 +02:00
< / div >
2007-08-10 06:11:31 +02:00
< div title = "MemoryManagement" modifier = "Ichthyostega" modified = "200708100408" created = "200708100225" tags = "impl decision discuss" changecount = "8" >
< pre > Of course: Cinelerra currently leaks memory and crashes regularilly. For the newly written code, besides retaining the same performance level, a main goal is to use methods and techniques known to support the writing of quality code. So, besides the MultithreadConsiderations, a solid strategy for managing the ownership of allocated memory blocks is necessary right from start.
!Problems
# Memory management needs to work correct in a //fault tolerant environment//. That means that we need to be prepared to //handle on a non-local scale// some sorts of error conditions (without aborting the application). To be more precise: some error condition arises locally, which leads to a local abort and just the disabling/failing of some subsystem without affecting the application as a whole. This can happen on a regular base (e.g. rendering fails) and thus is __no excuse for leaking memory__
# Some (not all) parts of the core application are non-deterministic. That means, we can't tie the memory management to any assumptions on behalf of the execution path
!C++ solution
First of all -- this doesn't concern //every// allocation. It rather means there are certain //dangerous areas// which need to be identified. And as always, instead of carrying the inherent complexities of the problem into the solution, we should rather look for a common solution pattern which helps factoring out the complexity.
For the case in question this seems to be the ''resource allocation is construction'' pattern. Which boils down to basically never using bare pointers when concerned with ownership. Instead, ownership should be handled by smart-pointers.
!!usage scenarios
# __existence is being used__: Objects just live for being referred to in a object network. In this case, use refcounting smart-pointers for every ref. (note: problem with cyclic refs)
# __entity bound ownership__: Objects can be tied to some long living entity in the program, which holds the smart-pointer
#* if the existence of these ref-holding entity can be //guaranteed// (like a contract), then the other users can build a object network with conventional pointers
#* otherwise, when the ref-holding entity //can disappear// in a regular program state, we need weak-refs and checking (because by our postulate the controlled resource needs to be destructed immediately, otherwise we would have the first case, existence == being used)
!!!dangerous uses
* the render nodes & rarr; [[detail analysis|ManagementRenderNodes]] {{red{TODO}}}
* the MObjects in the EDL & rarr; [[detail analysis|ManagementMObjects]] {{red{TODO}}}
* Asset - MObject relationship. & rarr; [[detail analysis|ManagementAssetRelation]] {{red{TODO}}}
!!!rather harmless
* Frames (buffers), because they belong to a given [[RenderProcess (=StateProxy)|StateProxy]] and are just passed in into the individual [[ProcNode]]s. This can be handled consistently with conventional methods.
* each StateProxy belongs to one top-level call to the [[Controller-Facade|Controller]]
* same for the builder tools
* the EDL and the defined [[Asset]]s belong together to one Session. If the Session is closed, this means a internal shutdown of the whole ProcLayer, i.e. closing of all GUI representations and terminating all render processes. If these calles are implemented as blocking operations, we can assert that as long as any GUI representation or any render process is running, there is a valid Session and EDL.
!using Factories
And, last but not least, doing all actual allocations is the job of the backend. Exceptions being long-lived objects, like the Session or the EDL, which are created once and don't bear the danger of causing memory pressure. Besides that, the ProcLayer code shouldn't issue " new" and " delete" , rather it should use some central [[Factories]] for all allocation and freeing, so we can redirect these calls down to the backend, which may use pooling or special placement allocators or the like. The rationale is, for modern hardware/architectures, care has to be taken with heap allocations, esp. with many small objects and irregular usage patterns.
< / pre >
< / div >
2007-10-10 03:54:09 +02:00
< div title = "MultichannelMedia" modifier = "Ichthyostega" modified = "200710090015" created = "200709200255" tags = "design" changecount = "5" >
2007-09-20 05:36:08 +02:00
< pre > Based on practical experiences, Ichthyo tends to consider Multichannel Media as the base case, while counting media files providing just one single media stream as exotic corner cases. This may seem counter intuitive at first sight; you should think of it as an attempt to avoid right from start some of the common shortcomings found in many video editors, especially
* having to deal with keeping a " link" between audio and video clips
2007-10-10 03:54:09 +02:00
* silly limitations on the supported audio setups (e.g. " sound is mono, stereo or Dolby-5.1" )
* unnecessary complexity when dealing with more realistic setups, esp. when working on dialogue scenes
2007-09-20 05:36:08 +02:00
* inability to edit stereoscopic (3D) video in a natural fashion
!Compound Media
2007-09-20 15:42:26 +02:00
Basically, each [[media asset|MediaAsset]] is considered to be a compound of several elementary media (tracks), possibly of various different media kinds. Adding support for placeholders (''proxy clips'') at some point in future will add still more complexity (because then there will be even dependencies between some of these elementary media). To handle, edit and render compound media, we need to impose some structural limitations. But anyhow, we try to configure as much as possible already at the " asset level" and make the rest of the proc layer behave just according to the configuration given with each asset.
2007-09-20 05:36:08 +02:00
2007-09-20 15:42:26 +02:00
So, when creating a clip out of such a compound media asset, the clip has to be a compound of elementary clips mirroring the given media asset's structure. Besides, it should be possible to //detach// and //attach// elementary clips from a compound clip. On the other hand, the [[Fixture]] created from the current state of the [[EDL]] is explicit to a great extent. So, in the Fixture we deal only with elementary clips placed to absolute positions, and thus the builder will see only simple non-compound clips and translate them into the corresponding source reading nodes.
!Handling
2007-10-10 03:54:09 +02:00
* from a Media asset, we can get a [[Processing Pattern (ProcPatt)|ProcPatt]] describing how to build a render pipeline for this media
2007-09-20 15:42:26 +02:00
* we can create a Clip (MObject) from each Media, which will be linked back to the media asset internally.
* moreover, creating a Clip will create and register a Clip asset as well, and this Clip asset will be tied to the original Clip and will show up in some special Category
* media can be compound and the created Clips will mirror this compound structure
2007-09-22 02:53:03 +02:00
* we distinguish elementay (non-compound) Clips from compound clips by concrete subtype. The builder can only handle elementary clips, because he needs to build a separate pipeline for every output channel. So the work of splitting common effect stacks for clips with several channels needs to be done when calculating the Fixture for the current EDL. The Builder expects to be able to build the render nodes corresponding to each entity found in the Fixture one by one.
2007-09-20 15:42:26 +02:00
* the Builder gets at the ProcPatt (descriptor) of the underlying media for each clip and uses this description as a template to build the render pipeline. That is, the ProcPatt specifies the codec asset and maybe some additional effect assets (deinterlace, scale) necessary for feeding media data corresponding to this clip/media into the render nodes network.< / pre >
2007-09-20 05:36:08 +02:00
< / div >
2007-10-10 03:54:09 +02:00
< div title = "ObjectCreation" modifier = "Ichthyostega" modified = "200710090219" created = "200709030139" tags = "impl design" changecount = "15" >
< pre > We have to consider carefully how to handle the Creation of new class instances. Because, when done naively, it can defeat all efforts of separating subsystems, or & mdash; the other extreme & mdash; lead to a //switch-on-typeID// programming style. We strive at a solution somewhere in the middle by utilizing __Abstract Factories__ on Interface or key abstraction classes, but providing specialized overloads for the different use cases. So in each use case we have to decide if we want to create a instance of some general concept (Interface), or if we have a direct collaboration and thus need the Factory to provide a more specific sub-Interface or even a concrete type.
2007-09-04 05:09:08 +02:00
!Object creation use cases
!![[Assets|Asset]]
|!Action|> |!creates |
|loading a media file|asset::Media, asset::Codec| |
2007-10-10 03:54:09 +02:00
|viewing media|asset::Clip| for the whole Media, if not already existent|
2007-09-04 05:09:08 +02:00
|mark selection as clip|asset::Clip| doesn't add to EDL|
|loading Plugin|asset::Effect| usually at program startup|
|create Session|asset::Track, asset::OutPort| |
& rarr; [[Creating and registering Assets|AssetCreation]]
!![[MObjects|MObject]]
|add media to EDL|asset::Clip, session::Clip, FixedPlacement| creating whole-media clip on-the-fly |
|add Clip|session::Clip, FixedPlacement| |
|attach Effect|session::Effect, RelativePlacement| |
|start using Automation|session::Auto, asset::Dataset, RelativePlacement| |
2007-10-10 03:54:09 +02:00
!Invariants
when creating Objects, certain invariants have to be maintained. Because creating an Object can be considered an atomic operation and must not leave any related objects in an inconsistent state. Each of our interfaces implies some invariants:
* every Placement has a Subject it places
* MObjects are always created to be placed in some way or the other
* [[Assets|Asset]] manage a dependency graph. Creating a derived Object (e.g. a Clip from a Media) implies a new dependency. (& rarr; [[memory management|ManagementAssetRelation]] relies on this)< / pre >
2007-09-04 05:09:08 +02:00
< / div >
2007-06-22 08:46:15 +02:00
< div title = "OpenGL" modifier = "Ichthyostega" modified = "200706220359" created = "200706220345" tags = "def discuss" changecount = "3" >
< pre > Cinelerra2 introduced OpenGL support for rendering previews. I must admit, I am very unhappy with this, because
* it just supports some hardware
* it makes building difficult
* it can't handle all color models Cinelerra is capable of
* it introduces a separate codepath including some complicated copying of video data into the textures (and back?)
* it can't be used for rendering
So my judgement would be: in contrary to a realtime/gaming application, for quality video editing it is not worth the effort implementing OpenGL support in all details and with all its complexity. I would accept ~OpenGL as an option, if it could be pushed down into a Library, so it can be handled and maintained transparently and doesnt bind our limited developer manpower.
But because I know the opinions on this topc are varying (users tend to be delighted if they hear " ~OpenGL" , because it carries notion of " fast" and " power" todays) & mdash; I try to integrate ~OpenGL as apossibility into this design of the Render Engine. Obviousely, I have the hard requirement that it //must not jeopardize the code structure.//
My proposed aproach is to treat OpenGL as a separate video raw data type, requiring separete and specialized [[Processing Nodes|ProcNode]] for all calculations. Thus the Builder could connect OpenGL nodes if it is possible to cover the whole render path for preview and fall back to the normal ~ProcNodes for all relevant renders
< / pre >
< / div >
2007-09-04 05:09:08 +02:00
< div title = "Overview" modifier = "Ichthyostega" modified = "200709030125" created = "200706190300" tags = "overview" changecount = "4" >
< pre > The Cinelerra-3 Processing Layer is comprised of various subsystems and can be separated into a low-level and a high-level part. At the low-level end is the [[Render Engine|OverviewRenderEngine]] which basically is a network of render nodes cooperating closely with the Backend Layer in order to carry out the actual playback and media transforming calculations. Whereas on the high-level side we find several different [[Media Objects|MObjects]] that can be placed into the [[EDL]], edited and manipulated. This is complemented by the [[Asset Management|Asset]], which is the " bookkeeping view" of all the different " things" within each Session.
[img[Block Diagram|uml/fig128005.png]]
2007-06-22 08:46:15 +02:00
< / pre >
2007-06-19 08:18:13 +02:00
< / div >
2007-06-27 17:19:54 +02:00
< div title = "OverviewRenderEngine" modifier = "MichaelPloujnikov" modified = "200706271432" created = "200706190647" tags = "overview" changecount = "17" >
< pre > Render Engine, [[Builder]] and [[Controller]] are closely related Subsystems. Actually, the [[Builder]] //creates// a newly configured Render Engine //for every// RenderProcess. Before doing so, it queries from the Session (or, to be more precise, from the [[Fixture]] within the current Session) all necessary Media Object Placement information. The [[Builder]] then derives from this information the actual assembly of [[Processing Nodes|ProcNode]] comprising the Render Engine. Thus:
2007-06-22 08:46:15 +02:00
* the source of the build process is a sequence of absolute (explicit) [[Placements|Placement]] called the [[Playlist]]
2007-06-20 15:36:01 +02:00
* the [[build process|BuildProcess]] is driven, configured and controlled by the [[Controller]] subsystem component. It encompasses the actual playback configuration and State of the System.
2007-06-22 08:46:15 +02:00
* the resulting Render Engine is a list of [[Processors]], each configured to calculate a segment of the timeline with uniform properties. Each of these Processors in turn is a graph of interconnected ProcNode.s.
2007-06-19 09:22:52 +02:00
see also: RenderEntities
2007-06-25 09:02:09 +02:00
[img[Overview: Components of the Renderengine|uml/fig128261.png]]
2007-06-22 08:46:15 +02:00
< / pre >
2007-06-19 09:22:52 +02:00
< / div >
2007-06-26 08:20:31 +02:00
< div title = "PageTemplate" modifier = "Ichthyostega" modified = "200706260500" created = "200701131624" tags = "MPTWTheme excludeMissing" server . type = "file" server . host = "file:///home/ct/.homepage/home.html" server . page . revision = "200706110330" changecount = "1" >
2007-06-19 03:45:45 +02:00
< pre > < !--{{{-->
< div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
< div class='headerShadow'>
< span class='siteTitle' refresh='content' tiddler='SiteTitle'> < /span> & nbsp;
< span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'> < /span>
< /div>
< div class='headerForeground'>
< span class='siteTitle' refresh='content' tiddler='SiteTitle'> < /span> & nbsp;
< span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'> < /span>
< /div>
< /div>
< !-- horizontal MainMenu -->
< div id='topMenu' refresh='content' tiddler='MainMenu'> < /div>
< !-- original MainMenu menu -->
< !-- < div id='mainMenu' refresh='content' tiddler='MainMenu'> < /div> -->
< div id='sidebar'>
< div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'> < /div>
< div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'> < /div>
< /div>
< div id='displayArea'>
< div id='messageArea'> < /div>
< div id='tiddlerDisplay'> < /div>
< /div>
< !--}}}-->
< / pre >
< / div >
2007-06-27 17:19:54 +02:00
< div title = "ParamProvider" modifier = "MichaelPloujnikov" modified = "200706271458" created = "200706220517" tags = "def" changecount = "7" >
2007-06-22 08:46:15 +02:00
< pre > A ParamProvider is the counterpart for (one or many) Parameter instances. It implements the value access function made available by the Parameter object to its clients.
To give a concrete example:
* a Fade Plugin needs the actual fade value for Frame t=xxx
2007-06-27 17:19:54 +02:00
* the Plugin has a Parameter Object (from which we could query the information of this parameter being a continuous float function)
2007-06-22 08:46:15 +02:00
* this Parameter Object provides a getValue() function, which is internally linked (i.e. by configuration) to a //Parameter Provider//
* the actual object implementing the ParamProvider Interface could be a Automation MObject located somewhere in the EDL and would do bezier interpolation on a given keyframe set.
* while building the Render Engine configuration actually at work, the Builder would have to setup this link between the Plugin Parameter Object and the ParamProvider; he can do so, because he sees the link between the Automation MObject and the corresponding Effect MObject
2007-06-25 09:56:36 +02:00
2007-06-27 17:19:54 +02:00
& rarr; see the class diagram for [[Automation]]
< / pre >
2007-06-22 08:46:15 +02:00
< / div >
2007-06-27 17:19:54 +02:00
< div title = "Parameters" modifier = "MichaelPloujnikov" modified = "200706271456" created = "200706220505" tags = "def" changecount = "3" >
2007-06-22 08:46:15 +02:00
< pre > Parameters are all probably variable control values used within the Render Engine. Contrast this with configuration values, which are considered to be fixed and need an internal reset of the application state to take effect.
A ''Parameter Object'' provides a descriptor of the kind of parameter, together with a function used to pull the //actual value// of this parameter. Here, //actual// has a two-fold meaning:
* if called without a time specification, it is either a global (but variable) system or session parameter or a default value for automated Parameters. (the intention is to treat this cases uniformly)
* if called with a time specification, it is the query for an & mdash; probably interpolated & mdash; [[Automation]] value at this absolute time. The corresponding ParamProvider should fall back transparently to a default or session value if no time varying data is available
{{red{TODO: define how Automation works}}}
< / pre >
< / div >
2007-06-19 03:45:45 +02:00
< div title = "PartTiddlerPlugin" modifier = "PauloSoares" modified = "200611082325" created = "200601251216" tags = "excludeLists plugin systemConfig" server . type = "file" server . host = "file:///home/ct/.homepage/home.html" server . page . revision = "200611082325" >
< pre > /***
|< html> < a name=" Top" /> < /html> ''Name:''|PartTiddlerPlugin|
|''Version:''|1.0.6 (2006-11-07)|
|''Source:''|http://tiddlywiki.abego-software.de/#PartTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license]]|
|''TiddlyWiki:''|2.0|
|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
!Table of Content< html> < a name=" TOC" /> < /html>
* < html> < a href=" javascript:;" onclick=" window.scrollAnchorVisible('Description',null, event)" > Description, Syntax< /a> < /html>
* < html> < a href=" javascript:;" onclick=" window.scrollAnchorVisible('Applications',null, event)" > Applications< /a> < /html>
** < html> < a href=" javascript:;" onclick=" window.scrollAnchorVisible('LongTiddler',null, event)" > Refering to Paragraphs of a Longer Tiddler< /a> < /html>
** < html> < a href=" javascript:;" onclick=" window.scrollAnchorVisible('Citation',null, event)" > Citation Index< /a> < /html>
** < html> < a href=" javascript:;" onclick=" window.scrollAnchorVisible('TableCells',null, event)" > Creating " multi-line" Table Cells< /a> < /html>
** < html> < a href=" javascript:;" onclick=" window.scrollAnchorVisible('Tabs',null, event)" > Creating Tabs< /a> < /html>
** < html> < a href=" javascript:;" onclick=" window.scrollAnchorVisible('Sliders',null, event)" > Using Sliders< /a> < /html>
* < html> < a href=" javascript:;" onclick=" window.scrollAnchorVisible('Revisions',null, event)" > Revision History< /a> < /html>
* < html> < a href=" javascript:;" onclick=" window.scrollAnchorVisible('Code',null, event)" > Code< /a> < /html>
!Description< html> < a name=" Description" /> < /html>
With the {{{< part aPartName> ... < /part> }}} feature you can structure your tiddler text into separate (named) parts.
Each part can be referenced as a " normal" tiddler, using the " //tiddlerName//''/''//partName//" syntax (e.g. " About/Features" ). E.g. you may create links to the parts, use it in {{{< < tiddler...> > }}} or {{{< < tabs...> > }}} macros etc.
''Syntax:''
|> |''< part'' //partName// [''hidden''] ''> '' //any tiddler content// ''< /part> ''|
|//partName//|The name of the part. You may reference a part tiddler with the combined tiddler name " //nameOfContainerTidder//''/''//partName//.|
|''hidden''|When defined the content of the part is not displayed in the container tiddler. But when the part is explicitly referenced (e.g. in a {{{< < tiddler...> > }}} macro or in a link) the part's content is displayed.|
|< html> < i> any& nbsp;tiddler& nbsp;content< /i> < /html> |< html> The content of the part.< br> A part can have any content that a " normal" tiddler may have, e.g. you may use all the formattings and macros defined.< /html> |
|> |~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
< html> < sub> < a href=" javascript:;" onclick=" window.scrollAnchorVisible('Top',null, event)" > [Top]< /sub> < /a> < /html>
!Applications< html> < a name=" Applications" /> < /html>
!!Refering to Paragraphs of a Longer Tiddler< html> < a name=" LongTiddler" /> < /html>
Assume you have written a long description in a tiddler and now you want to refer to the content of a certain paragraph in that tiddler (e.g. some definition.) Just wrap the text with a ''part'' block, give it a nice name, create a " pretty link" (like {{{[[Discussion Groups|Introduction/DiscussionGroups]]}}}) and you are done.
Notice this complements the approach to first writing a lot of small tiddlers and combine these tiddlers to one larger tiddler in a second step (e.g. using the {{{< < tiddler...> > }}} macro). Using the ''part'' feature you can first write a " classic" (longer) text that can be read " from top to bottom" and later " reuse" parts of this text for some more " non-linear" reading.
< html> < sub> < a href=" javascript:;" onclick=" window.scrollAnchorVisible('Top',null, event)" > [Top]< /sub> < /a> < /html>
!!Citation Index< html> < a name=" Citation" /> < /html>
Create a tiddler " Citations" that contains your " citations" .
Wrap every citation with a part and a proper name.
''Example''
{{{
< part BAX98> Baxter, Ira D. et al: //Clone Detection Using Abstract Syntax Trees.//
in //Proc. ICSM//, 1998.< /part>
< part BEL02> Bellon, Stefan: //Vergleich von Techniken zur Erkennung duplizierten Quellcodes.//
Thesis, Uni Stuttgart, 2002.< /part>
< part DUC99> Ducasse, Stéfane et al: //A Language Independent Approach for Detecting Duplicated Code.//
in //Proc. ICSM//, 1999.< /part>
}}}
You may now " cite" them just by using a pretty link like {{{[[Citations/BAX98]]}}} or even more pretty, like this {{{[[BAX98|Citations/BAX98]]}}}.
< html> < sub> < a href=" javascript:;" onclick=" window.scrollAnchorVisible('Top',null, event)" > [Top]< /sub> < /a> < /html>
!!Creating " multi-line" Table Cells< html> < a name=" TableCells" /> < /html>
You may have noticed that it is hard to create table cells with " multi-line" content. E.g. if you want to create a bullet list inside a table cell you cannot just write the bullet list
{{{
* Item 1
* Item 2
* Item 3
}}}
into a table cell (i.e. between the | ... | bars) because every bullet item must start in a new line but all cells of a table row must be in one line.
Using the ''part'' feature this problem can be solved. Just create a hidden part that contains the cells content and use a {{{< < tiddler > > }}} macro to include its content in the table's cell.
''Example''
{{{
|!Subject|!Items|
|subject1|< < tiddler ./Cell1> > |
|subject2|< < tiddler ./Cell2> > |
< part Cell1 hidden>
* Item 1
* Item 2
* Item 3
< /part>
...
}}}
Notice that inside the {{{< < tiddler ...> > }}} macro you may refer to the " current tiddler" using the " ." .
BTW: The same approach can be used to create bullet lists with items that contain more than one line.
< html> < sub> < a href=" javascript:;" onclick=" window.scrollAnchorVisible('Top',null, event)" > [Top]< /sub> < /a> < /html>
!!Creating Tabs< html> < a name=" Tabs" /> < /html>
The build-in {{{< < tabs ...> > }}} macro requires that you defined an additional tiddler for every tab it displays. When you want to have " nested" tabs you need to define a tiddler for the " main tab" and one for every tab it contains. I.e. the definition of a set of tabs that is visually displayed at one place is distributed across multiple tiddlers.
With the ''part'' feature you can put the complete definition in one tiddler, making it easier to keep an overview and maintain the tab sets.
''Example''
The standard tabs at the sidebar are defined by the following eight tiddlers:
* SideBarTabs
* TabAll
* TabMore
* TabMoreMissing
* TabMoreOrphans
* TabMoreShadowed
* TabTags
* TabTimeline
Instead of these eight tiddlers one could define the following SideBarTabs tiddler that uses the ''part'' feature:
{{{
< < tabs txtMainTab
Timeline Timeline SideBarTabs/Timeline
All 'All tiddlers' SideBarTabs/All
Tags 'All tags' SideBarTabs/Tags
More 'More lists' SideBarTabs/More> >
< part Timeline hidden> < < timeline> > < /part>
< part All hidden> < < list all> > < /part>
< part Tags hidden> < < allTags> > < /part>
< part More hidden> < < tabs txtMoreTab
Missing 'Missing tiddlers' SideBarTabs/Missing
Orphans 'Orphaned tiddlers' SideBarTabs/Orphans
Shadowed 'Shadowed tiddlers' SideBarTabs/Shadowed> > < /part>
< part Missing hidden> < < list missing> > < /part>
< part Orphans hidden> < < list orphans> > < /part>
< part Shadowed hidden> < < list shadowed> > < /part>
}}}
Notice that you can easily " overwrite" individual parts in separate tiddlers that have the full name of the part.
E.g. if you don't like the classic timeline tab but only want to see the 100 most recent tiddlers you could create a tiddler " ~SideBarTabs/Timeline" with the following content:
{{{
< < forEachTiddler
sortBy 'tiddler.modified' descending
write '(index < 100) ? " * [[" +tiddler.title+" ]]\n" :" " '> >
}}}
< html> < sub> < a href=" javascript:;" onclick=" window.scrollAnchorVisible('Top',null, event)" > [Top]< /sub> < /a> < /html>
!!Using Sliders< html> < a name=" Sliders" /> < /html>
Very similar to the build-in {{{< < tabs ...> > }}} macro (see above) the {{{< < slider ...> > }}} macro requires that you defined an additional tiddler that holds the content " to be slid" . You can avoid creating this extra tiddler by using the ''part'' feature
''Example''
In a tiddler " About" we may use the slider to show some details that are documented in the tiddler's " Details" part.
{{{
...
< < slider chkAboutDetails About/Details details " Click here to see more details" > >
< part Details hidden>
To give you a better overview ...
< /part>
...
}}}
Notice that putting the content of the slider into the slider's tiddler also has an extra benefit: When you decide you need to edit the content of the slider you can just doubleclick the content, the tiddler opens for editing and you can directly start editing the content (in the part section). In the " old" approach you would doubleclick the tiddler, see that the slider is using tiddler X, have to look for the tiddler X and can finally open it for editing. So using the ''part'' approach results in a much short workflow.
< html> < sub> < a href=" javascript:;" onclick=" window.scrollAnchorVisible('Top',null, event)" > [Top]< /sub> < /a> < /html>
!Revision history< html> < a name=" Revisions" /> < /html>
* v1.0.6 (2006-11-07)
** Bugfix: cannot edit tiddler when UploadPlugin by Bidix is installed. Thanks to José Luis González Castro for reporting the bug.
* v1.0.5 (2006-03-02)
** Bugfix: Example with multi-line table cells does not work in IE6. Thanks to Paulo Soares for reporting the bug.
* v1.0.4 (2006-02-28)
** Bugfix: Shadow tiddlers cannot be edited (in TW 2.0.6). Thanks to Torsten Vanek for reporting the bug.
* v1.0.3 (2006-02-26)
** Adapt code to newly introduced Tiddler.prototype.isReadOnly() function (in TW 2.0.6). Thanks to Paulo Soares for reporting the problem.
* v1.0.2 (2006-02-05)
** Also allow other macros than the " tiddler" macro use the " ." in the part reference (to refer to " this" tiddler)
* v1.0.1 (2006-01-27)
** Added Table of Content for plugin documentation. Thanks to RichCarrillo for suggesting.
** Bugfix: newReminder plugin does not work when PartTiddler is installed. Thanks to PauloSoares for reporting.
* v1.0.0 (2006-01-25)
** initial version
< html> < sub> < a href=" javascript:;" onclick=" window.scrollAnchorVisible('Top',null, event)" > [Top]< /sub> < /a> < /html>
!Code< html> < a name=" Code" /> < /html>
< html> < sub> < a href=" javascript:;" onclick=" window.scrollAnchorVisible('Top',null, event)" > [Top]< /sub> < /a> < /html>
***/
//{{{
//============================================================================
// PartTiddlerPlugin
// Ensure that the PartTiddler Plugin is only installed once.
//
if (!version.extensions.PartTiddlerPlugin) {
version.extensions.PartTiddlerPlugin = {
major: 1, minor: 0, revision: 6,
date: new Date(2006, 10, 7),
type: 'plugin',
source: " http://tiddlywiki.abego-software.de/#PartTiddlerPlugin"
};
if (!window.abego) window.abego = {};
if (version.major < 2) alertAndThrow(" PartTiddlerPlugin requires TiddlyWiki 2.0 or newer." );
//============================================================================
// Common Helpers
// Looks for the next newline, starting at the index-th char of text.
//
// If there are only whitespaces between index and the newline
// the index behind the newline is returned,
// otherwise (or when no newline is found) index is returned.
//
var skipEmptyEndOfLine = function(text, index) {
var re = /(\n|[^\s])/g;
re.lastIndex = index;
var result = re.exec(text);
return (result & & text.charAt(result.index) == '\n')
? result.index+1
: index;
}
//============================================================================
// Constants
var partEndOrStartTagRE = /(< \/part> )|(< part(?:\s+)((?:[^> ])+)> )/mg;
var partEndTagREString = " < \\/part> " ;
var partEndTagString = " < /part> " ;
//============================================================================
// Plugin Specific Helpers
// Parse the parameters inside a < part ...> tag and return the result.
//
// @return [may be null] {partName: ..., isHidden: ...}
//
var parseStartTagParams = function(paramText) {
var params = paramText.readMacroParams();
if (params.length == 0 || params[0].length == 0) return null;
var name = params[0];
var paramsIndex = 1;
var hidden = false;
if (paramsIndex < params.length) {
hidden = params[paramsIndex] == " hidden" ;
paramsIndex++;
}
return {
partName: name,
isHidden: hidden
};
}
// Returns the match to the next (end or start) part tag in the text,
// starting the search at startIndex.
//
// When no such tag is found null is returned, otherwise a " Match" is returned:
// [0]: full match
// [1]: matched " end" tag (or null when no end tag match)
// [2]: matched " start" tag (or null when no start tag match)
// [3]: content of start tag (or null if no start tag match)
//
var findNextPartEndOrStartTagMatch = function(text, startIndex) {
var re = new RegExp(partEndOrStartTagRE);
re.lastIndex = startIndex;
var match = re.exec(text);
return match;
}
//============================================================================
// Formatter
// Process the < part ...> ... < /part> starting at (w.source, w.matchStart) for formatting.
//
// @return true if a complete part section (including the end tag) could be processed, false otherwise.
//
var handlePartSection = function(w) {
var tagMatch = findNextPartEndOrStartTagMatch(w.source, w.matchStart);
if (!tagMatch) return false;
if (tagMatch.index != w.matchStart || !tagMatch[2]) return false;
// Parse the start tag parameters
var arguments = parseStartTagParams(tagMatch[3]);
if (!arguments) return false;
// Continue processing
var startTagEndIndex = skipEmptyEndOfLine(w.source, tagMatch.index + tagMatch[0].length);
var endMatch = findNextPartEndOrStartTagMatch(w.source, startTagEndIndex);
if (endMatch & & endMatch[1]) {
if (!arguments.isHidden) {
w.nextMatch = startTagEndIndex;
w.subWikify(w.output,partEndTagREString);
}
w.nextMatch = skipEmptyEndOfLine(w.source, endMatch.index + endMatch[0].length);
return true;
}
return false;
}
config.formatters.push( {
name: " part" ,
match: " < part\\s+[^> ]+> " ,
handler: function(w) {
if (!handlePartSection(w)) {
w.outputText(w.output,w.matchStart,w.matchStart+w.matchLength);
}
}
} )
//============================================================================
// Extend " fetchTiddler" functionality to also recognize " part" s of tiddlers
// as tiddlers.
var currentParent = null; // used for the " ." parent (e.g. in the " tiddler" macro)
// Return the match to the first < part ...> tag of the text that has the
// requrest partName.
//
// @return [may be null]
//
var findPartStartTagByName = function(text, partName) {
var i = 0;
while (true) {
var tagMatch = findNextPartEndOrStartTagMatch(text, i);
if (!tagMatch) return null;
if (tagMatch[2]) {
// Is start tag
// Check the name
var arguments = parseStartTagParams(tagMatch[3]);
if (arguments & & arguments.partName == partName) {
return tagMatch;
}
}
i += tagMatch[0].length;
}
}
// Return the part " partName" of the given parentTiddler as a " readOnly" Tiddler
// object, using fullName as the Tiddler's title.
//
// All remaining properties of the new Tiddler (tags etc.) are inherited from
// the parentTiddler.
//
// @return [may be null]
//
var getPart = function(parentTiddler, partName, fullName) {
var text = parentTiddler.text;
var startTag = findPartStartTagByName(text, partName);
if (!startTag) return null;
var endIndexOfStartTag = skipEmptyEndOfLine(text, startTag.index+startTag[0].length);
var indexOfEndTag = text.indexOf(partEndTagString, endIndexOfStartTag);
if (indexOfEndTag > = 0) {
var partTiddlerText = text.substring(endIndexOfStartTag,indexOfEndTag);
var partTiddler = new Tiddler();
partTiddler.set(
fullName,
partTiddlerText,
parentTiddler.modifier,
parentTiddler.modified,
parentTiddler.tags,
parentTiddler.created);
partTiddler.abegoIsPartTiddler = true;
return partTiddler;
}
return null;
}
// Hijack the store.fetchTiddler to recognize the " part" addresses.
//
var oldFetchTiddler = store.fetchTiddler ;
store.fetchTiddler = function(title) {
var result = oldFetchTiddler.apply(this, arguments);
if (!result & & title) {
var i = title.lastIndexOf('/');
if (i > 0) {
var parentName = title.substring(0, i);
var partName = title.substring(i+1);
var parent = (parentName == " ." )
? currentParent
: oldFetchTiddler.apply(this, [parentName]);
if (parent) {
return getPart(parent, partName, parent.title+" /" +partName);
}
}
}
return result;
};
// The user must not edit a readOnly/partTiddler
//
config.commands.editTiddler.oldIsReadOnlyFunction = Tiddler.prototype.isReadOnly;
Tiddler.prototype.isReadOnly = function() {
// Tiddler.isReadOnly was introduced with TW 2.0.6.
// For older version we explicitly check the global readOnly flag
if (config.commands.editTiddler.oldIsReadOnlyFunction) {
if (config.commands.editTiddler.oldIsReadOnlyFunction.apply(this, arguments)) return true;
} else {
if (readOnly) return true;
}
return this.abegoIsPartTiddler;
}
config.commands.editTiddler.handler = function(event,src,title)
{
var t = store.getTiddler(title);
// Edit the tiddler if it either is not a tiddler (but a shadowTiddler)
// or the tiddler is not readOnly
if(!t || !t.abegoIsPartTiddler)
{
clearMessage();
story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
story.focusTiddler(title," text" );
return false;
}
}
// To allow the " ./partName" syntax in macros we need to hijack
// the invokeMacro to define the " currentParent" while it is running.
//
var oldInvokeMacro = window.invokeMacro;
function myInvokeMacro(place,macro,params,wikifier,tiddler) {
var oldCurrentParent = currentParent;
if (tiddler) currentParent = tiddler;
try {
oldInvokeMacro.apply(this, arguments);
} finally {
currentParent = oldCurrentParent;
}
}
window.invokeMacro = myInvokeMacro;
// Scroll the anchor anchorName in the viewer of the given tiddler visible.
// When no tiddler is defined use the tiddler of the target given event is used.
window.scrollAnchorVisible = function(anchorName, tiddler, evt) {
var tiddlerElem = null;
if (tiddler) {
tiddlerElem = document.getElementById(story.idPrefix + tiddler);
}
if (!tiddlerElem & & evt) {
var target = resolveTarget(evt);
tiddlerElem = story.findContainingTiddler(target);
}
if (!tiddlerElem) return;
var children = tiddlerElem.getElementsByTagName(" a" );
for (var i = 0; i < children.length; i++) {
var child = children[i];
var name = child.getAttribute(" name" );
if (name == anchorName) {
var y = findPosY(child);
window.scrollTo(0,y);
return;
}
}
}
} // of " install only once"
//}}}
/***
< html> < sub> < a href=" javascript:;" onclick=" scrollAnchorVisible('Top',null, event)" > [Top]< /sub> < /a> < /html>
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2006 ([[www.abego-software.de|http://www.abego-software.de]])
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
Neither the name of abego Software nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
< html> < sub> < a href=" javascript:;" onclick=" scrollAnchorVisible('Top',null, event)" > [Top]< /sub> < /a> < /html>
***/< / pre >
< / div >
2007-10-10 03:54:09 +02:00
< div title = "Placement" modifier = "Ichthyostega" modified = "200710100120" created = "200706220306" tags = "def" changecount = "6" >
2007-06-27 17:19:54 +02:00
< pre > A Placement represents a //relation:// it is always linked to a //Subject// (this being a [[Media Object|MObject]]) and has the meaning to //place// this Subject in some manner, either relatively to other Media Objects, or by some Constraint or simply absolute at (time,track). The latter case is especially important and represented by a special [[Sub-Interface|ExplicitPlacement]]
2007-10-10 03:54:09 +02:00
The fact of being placed in the [[Session|SessionOverview]]/[[EDL]]is constitutive for all sorts of [[MObject]]s, without Placement they make no sense. Thus & mdash; technically & mdash; Placements act as ''smart pointers''. Of course, there are several kinds of Placements and they are templated on the type of MObject they are refering to. Placements can be //aggregated// to increasingly constrain the resulting " location" of the refered ~MObject. See & rarr; [[handling of Placements|PlacementHandling]] for more details< / pre >
< / div >
< div title = "PlacementHandling" modifier = "Ichthyostega" modified = "200710100148" created = "200710100124" tags = "design" changecount = "3" >
< pre > [[Placement]]s are at the very core of all [[editing operations|EditingOperations]], because they act as handles (smart pointers) to access the [[media objects|MObject]] to be manipulated. Moreover, Placements are the actual content of the EDL(s) and Fixture and thus are small objects with //value semantics//. Many editing tasks include finding some Placement in the EDL or directly take a ref to some Placement. By acting on the Placement object, we can in some cases change parameters of the way the media object is placed (e.g. adjust an offset), while by dereferencing the Placement object, we access the " real" media object (e.g. for trimming its length).
Placements are ''templated'' on the type of the actual ~MObject they refer to, thus defining the interface/methods usable on this object. Each Placement has a ''kind'', which determines its actual placing and locating behaviour, but besides that, we don't stress the identity of a placement object (~MObjects on the other hand //do have// a distinguishable identity): initially, you create a Placement of some specific kind (fixed, relative,...), but later on, you treat the placement polymorphically and don't care about its kind. The sole purpose of the placement's kind is to select some virtual function implementing the desired behaviour.
There is no limitation to one single Placement per ~MObject, indeed we can ''aggregate'' several Placements, resulting in their properties and constraints being combined to yield the actual position of the ~MObject refered by those Placements.
* {{red{to be worked out...}}} how to implement this, esp. the aggregation. Use a special " placement aggregation" subtype, or rather chain aggregated placements directly in the way a decorator works? And how to handle the case of a //over constrained// Placement?
2007-06-22 08:46:15 +02:00
< / pre >
< / div >
2007-06-25 09:56:36 +02:00
< div title = "Playlist" modifier = "Ichthyostega" modified = "200706250727" created = "200706220456" tags = "def" changecount = "3" >
< pre > Playlist is a sequence of individual Render Engine Processors able to render a segment of the timeline. So, together these Processors are able to render the whole timeline (or part of the timeline if only a part has to be rendered).
//Note, we have yet to specify how exactly the building and rendering will work together with the backend. There are several possibilities how to structure the Playlist//
2007-06-22 08:46:15 +02:00
< / pre >
< / div >
2007-09-25 23:39:46 +02:00
< div title = "ProblemsTodo" modifier = "Ichthyostega" modified = "200709251721" created = "200708050524" tags = "design discuss" changecount = "15" >
2007-08-05 17:54:36 +02:00
< pre > Open issues, Things to be worked out, Problems still to be solved...
!!Parameter Handling
2007-08-08 04:50:02 +02:00
The requirements are not quite clear; obviously Parameters are the foundation for getting automation right and for providing effect editing interfaces, so it seems to me we need some sort of introspection, i.e. Parameters need to be discovered, enumerated and described at runtime.
''Automation Type'': Directly connected is the problem of handling the //type// of parameters sensible, including the value type of automation data. My first (somewhat naive) approach was to " make everything a double" . But this soon leads into quite some of the same problems haunting the automation solution implemented in the current Cinelerra codebase. What makes the issue difficult is the fact we both need static diversity as well as dynamic flexibility. Usually, when combining hierarchies and templates, one has to be very careful; so I just note the problem down at the moment and will revisit it later, when I have a more clear understanding of the demands put onto the [[ProcNode]]s
2007-08-05 17:54:36 +02:00
!!Treatment of Time (points) and Intervals
At the moment we have no clear picture what is needed and what problems we may face in that domain.
From experience, mainly with other applications, we can draw the following conclusions
* drift and rounding errors are dangerous, because time in our context usually is understood as a fixed grid (Frames, samples...)
* fine grained time values easily get very large
* Cinelerra currently uses the approach of simply counting natural values for each media type separately. In an environment mixing several different media types freely, this seems a bit too simplistic (because it actually brings in the danger of rounding errors, just think at drop frame TC)
!!Organizing of Output Channels
How to handle the simultaneous rendering of several output streams (video, audio channels). Shall we treat the EDL as one entity containing different output channels, or should it rather be seen as a composite of several sub-~EDLs, each for only one output channel? This decision will be reflected in the overall structure of the network of render nodes: We could have a list of channel-output generating pipelines in each processor (for every segment), or we could have independently segmented lists of Processors for every output channel/type. The problem is, //it is not clear what approach to prefer at the moment// because we are just guessing.
!!Tracks, Channels, Layers
2007-09-21 03:40:04 +02:00
Closely related to this is the not-so-obvious problem how to understand the common global structures found in most audio and video editing applications. Mostly, they stem from imitating hardware recording and editing solutions, thus easing the transition for professionals grown up with analogue hardware based media. But as digital media are the de-facto standard nowadays, we could rethink some of this accidental complexity introduced by sticking to the hardware tool metaphor.
2007-08-05 17:54:36 +02:00
* is it really necessary to have fixed global tracks?
* is it really helpful to feed " source tracks" into global processing busses/channels?
Users accustomed with modern GUI applications typically expect that //everything is a object// and can be pulled around and manipulated individually. This seems natural at start, but raises the problem of providing a efficient workflow for handling larger projects and editing tasks. So, if we don't have a hard wired multitrack+bus architecture, we need some sort of templating to get the standard editing use case done efficiently.
2007-09-21 03:40:04 +02:00
!!Compound and Multiplicity
Simple relations can be hard wired. But, on the contrary, it would be as naive to define a Clip as having a Video track and two audio tracks, as it would be naive to overlook the problem of holding video and corresponding audio together. And, moreover, the default case has to be processed in a //straight forward// fashion, with as few tests and decisions as possible. So, basically each component participating in getting the core processing done has to mirror the structure pattern of the other parts, so that processing can be done without testing and forking. But this leaves us with the problem where to put the initial knowledge about the structural pattern used for building up the compound structures and & mdash; especially & mdash; the problem how to treat different kinds of structural patterns, how to detect the pattern to be applied and how to treat multiple instances of the same structural pattern.
2007-09-25 23:39:46 +02:00
One example of this problem is the [[handling of multichannel media|MultichannelMedia]]. Following the above reasoning, we end with having a [[" structural processing pattern" |ProcPatt]], typically one video stream with MPEG decoder and a pair of audio streams which need either to be routed to some " left" and " right" output ports, or have to be passed through a panning filter accordingly. Now the problem is: //create a new instance of this structure for each new media, or detect which media to subsume under a existing pattern instance.//
!!Parallelism
We need to work out guidelines for dealing with operations going on simultaneously. Certainly, this will divide the application in several different regions. As always, the primary goal is to avoid multithread problems altogether. Typically, this can be achieved by making matters explicit: externalizing state, make the processing subsystems stateless, queue and schedule tasks, use isolation layers.
* the StateProxy is a key for the individual render processes state, which is managed in separate [[StateFrame]]s in the backend. The [[processing network|ProcNode]] is stateless.
* the [[Fixture]] provides an isolation layer between the renderengine and the Session/EDL
* all EditingOperations are not threadsafe intentionally, because they are [[scheduled|ProcLayerScheduler]]
< / pre >
2007-08-10 06:11:31 +02:00
< / div >
2007-09-22 16:00:36 +02:00
< div title = "ProcAsset" modifier = "Ichthyostega" created = "200709221343" tags = "def classes" changecount = "1" >
< pre > All Assets of kind asset::Proc represent //processing algorithms// in the bookkeeping view. They enable loading, browsing and maybe even parametrizing all the Effects, Plugins and Codecs available for use within the Cinelerra Session.
Besides, they provide an important __inward interface__ for the [[ProcNode]]s, which will use these asset entries to dispatch the actual processing call when rendering.
{{red{todo: the naming scheme??}}}
[img[Asset Classess|uml/fig131077.png]]
< / pre >
< / div >
2007-08-10 06:11:31 +02:00
< div title = "ProcLayer" modifier = "Ichthyostega" modified = "200708100338" created = "200708100333" tags = "def" changecount = "2" >
< pre > The middle Layer of our current Architecture plan, i.e. the layer managing all processing and manipulation, while the actual data handling is done in the backend and the user interaction belongs to the GUI Layer.
& rarr; see the [[Overview]]
2007-08-05 17:54:36 +02:00
< / pre >
< / div >
2007-06-27 17:19:54 +02:00
< div title = "ProcNode" modifier = "MichaelPloujnikov" modified = "200706271500" created = "200706220409" tags = "def" changecount = "3" >
< pre > A data processing node within the Render Engine. Its key feature is the possibility to pull from it one (freely addressable) [[Frame]] of calculated data. Further, each ~ProcNode has the ability to be wired with other nodes and [[Parameter Providers|ParamProvider]]
2007-06-22 08:46:15 +02:00
< / pre >
< / div >
2007-09-22 02:53:03 +02:00
< div title = "ProcPatt" modifier = "Ichthyostega" modified = "200709212329" created = "200709212315" tags = "def design" changecount = "4" >
< pre > This special type of [[structural Asset|StructAsset]] represents information how to build some part of the render engine's processing nodes network. It can be thought of as a template or blueprint for construction. Most notably, it is used for creating nodes reading, decoding and delivering source media material to the render network. Each [[media Asset|MediaAsset]] has associated processing patterns describing the codecs and other transformations needed to get at the media data of this asset. (and because media assets are typically compound objects, the referred ~ProcPatt will be compound too). Obviously, the possibilities opened by using processing patterns go far beyond.
Technically, a processing pattern is a list of building instructions, which will be //executed// by the [[Builder]] on the render node network under construction. This implies the possibility to define further instruction kinds when needed in future; at the moment the relevant sorts of instructions are
* attach the given sequence of nodes to the specified point
* recursively execute a nested ~ProcPatt
More specifically, a sequence of nodes is given by a sequence of prototypical effect and codec assets (and from each of them we can create the corresponding render node). And the point to attach these nodes is given by an identifier & mdash; in most cases just " {{current}}" , denoting the point the builder was just working at, when he treated some MObject which in turn yielded this processing pattern in question.
Like all [[structural assets|StructAsset]], ~ProcPatt employs a special naming scheme within the asset name field, which directly mirrors its purpose and allows to bind to existing processing pattern instances when needed. The idea is letting all assets in need of a similar processing pattern refer to one shared ~ProcPatt instance. For example, within a MPEG video media asset, at some point there will be a ~ProcPatt labeled " {{{stream(mpeg)}}}" . In consequence, all MPEG video will use the same pattern of node wiring. And, of course, this pattern could be changed, either globally, or by binding a single clip to some other processing pattern (for making a punctual exception from the general rule)
//Note,// nothing has been said about how processing patterns are defined. One can expect for some //default patterns// beeing defined somewhere in the application, any session could overrule these defaults, and when there is no default, we can expect some mechanism deriving sensible fallback patterns //for every specific use case// of processing patterns. See the problem of [[Loading Media]] as an example.
< / pre >
< / div >
2007-06-22 08:46:15 +02:00
< div title = "Processors" modifier = "Ichthyostega" created = "200706220412" tags = "def" changecount = "1" >
< pre > a given Render Engine configuration is a list of Processors. Each Processor in turn contains a Graph of ProcNode.s to do the acutal data processing. In order to cary out any calculations, the Processor needs to be called with a StateProxy containing the state information for this RenderProcess
< / pre >
< / div >
2007-08-09 18:51:47 +02:00
< div title = "RSSReaderPlugin" modifier = "Ichthyostega" created = "200708081515" tags = "systemConfig" changecount = "1" >
< pre > /***
|''Name:''|RSSReaderPlugin|
|''Description:''|This plugin provides a RSSReader for TiddlyWiki|
|''Version:''|1.1.1|
|''Date:''|Apr 21, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#RSSReaderPlugin|
|''Documentation:''|http://tiddlywiki.bidix.info/#RSSReaderPluginDoc|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''Credit:''|BramChen for RssNewsMacro|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0|
|''OptionalRequires:''|http://www.tiddlytools.com/#NestedSlidersPlugin|
***/
//{{{
version.extensions.RSSReaderPlugin = {
major: 1, minor: 1, revision: 1,
date: new Date(" Apr 21, 2007" ),
source: " http://TiddlyWiki.bidix.info/#RSSReaderPlugin" ,
author: " BidiX" ,
coreVersion: '2.2.0'
};
config.macros.rssReader = {
dateFormat: " DDD, DD MMM YYYY" ,
itemStyle: " display: block;border: 1px solid black;padding: 5px;margin: 5px;" , //useed '@@'+itemStyle+itemText+'@@'
msg:{
permissionDenied: " Permission to read preferences was denied." ,
noRSSFeed: " No RSS Feed at this address %0" ,
urlNotAccessible: " Access to %0 is not allowed"
},
cache: [], // url => XMLHttpRequest.responseXML
desc: " noDesc" ,
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var desc = params[0];
var feedURL = params[1];
var toFilter = (params[2] ? true : false);
var filterString = (toFilter?(params[2].substr(0,1) == ' '? tiddler.title:params[2]):'');
var place = createTiddlyElement(place, " div" , " RSSReader" );
wikify(" ^^< < rssFeedUpdate " +feedURL+" [[" + tiddler.title + " ]]> > ^^\n" ,place);
if (this.cache[feedURL]) {
this.displayRssFeed(this.cache[feedURL], feedURL, place, desc, toFilter, filterString);
}
else {
var r = loadRemoteFile(feedURL,config.macros.rssReader.processResponse, [place, desc, toFilter, filterString]);
if (typeof r == " string" )
displayMessage(r);
}
},
// callback for loadRemoteFile
// params : [place, desc, toFilter, filterString]
processResponse: function(status, params, responseText, url, xhr) { // feedURL, place, desc, toFilter, filterString) {
if (window.netscape){
try {
if (document.location.protocol.indexOf(" http" ) == -1) {
netscape.security.PrivilegeManager.enablePrivilege(" UniversalBrowserRead" );
}
}
catch (e) { displayMessage(e.description?e.description:e.toString()); }
}
if (xhr.status == httpStatus.NotFound)
{
displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
return;
}
if (!status)
{
displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
return;
}
if (xhr.responseXML) {
// response is interpreted as XML
config.macros.rssReader.cache[url] = xhr.responseXML;
config.macros.rssReader.displayRssFeed(xhr.responseXML, params[0], url, params[1], params[2], params[3]);
}
else {
if (responseText.substr(0,5) == " < ?xml" ) {
// response exists but not return as XML -> try to parse it
var dom = (new DOMParser()).parseFromString(responseText, " text/xml" );
if (dom) {
// parsing successful so use it
config.macros.rssReader.cache[url] = dom;
config.macros.rssReader.displayRssFeed(dom, params[0], url, params[1], params[2], params[3]);
return;
}
}
// no XML display as html
wikify(" < html> " + responseText + " < /html> " , params[0]);
displayMessage(config.macros.rssReader.msg.noRSSFeed.format([url]));
}
},
// explore down the DOM tree
displayRssFeed: function(xml, place, feedURL, desc, toFilter, filterString){
// Channel
var chanelNode = xml.getElementsByTagName('channel').item(0);
var chanelTitleElement = (chanelNode ? chanelNode.getElementsByTagName('title').item(0) : null);
var chanelTitle = " " ;
if ((chanelTitleElement) & & (chanelTitleElement.firstChild))
chanelTitle = chanelTitleElement.firstChild.nodeValue;
var chanelLinkElement = (chanelNode ? chanelNode.getElementsByTagName('link').item(0) : null);
var chanelLink = " " ;
if (chanelLinkElement)
chanelLink = chanelLinkElement.firstChild.nodeValue;
var titleTxt = " !![[" +chanelTitle+" |" +chanelLink+" ]]\n" ;
var title = createTiddlyElement(place," div" ,null," ChanelTitle" ,null);
wikify(titleTxt,title);
// ItemList
var itemList = xml.getElementsByTagName('item');
var article = createTiddlyElement(place," ul" ,null,null,null);
var lastDate;
var re;
if (toFilter)
re = new RegExp(filterString.escapeRegExp());
for (var i=0; i< itemList.length; i++){
var titleElm = itemList[i].getElementsByTagName('title').item(0);
var titleText = (titleElm ? titleElm.firstChild.nodeValue : '');
if (toFilter & & ! titleText.match(re)) {
continue;
}
var descText = '';
descElem = itemList[i].getElementsByTagName('description').item(0);
if (descElem){
try{
for (var ii=0; ii< descElem.childNodes.length; ii++) {
descText += descElem.childNodes[ii].nodeValue;
}
}
catch(e){}
descText = descText.replace(/< br \/> /g,'\n');
if (desc == " asHtml" )
descText = " < html> " +descText+" < /html> " ;
}
var linkElm = itemList[i].getElementsByTagName(" link" ).item(0);
var linkURL = linkElm.firstChild.nodeValue;
var pubElm = itemList[i].getElementsByTagName('pubDate').item(0);
var pubDate;
if (!pubElm) {
pubElm = itemList[i].getElementsByTagName('date').item(0); // for del.icio.us
if (pubElm) {
pubDate = pubElm.firstChild.nodeValue;
pubDate = this.formatDateString(this.dateFormat, pubDate);
}
else {
pubDate = '0';
}
}
else {
pubDate = (pubElm ? pubElm.firstChild.nodeValue : 0);
pubDate = this.formatDate(this.dateFormat, pubDate);
}
titleText = titleText.replace(/\[|\]/g,'');
var rssText = '*'+'[[' + titleText + '|' + linkURL + ']]' + '' ;
if ((desc != " noDesc" ) & & descText){
rssText = rssText.replace(/\n/g,' ');
descText = '@@'+this.itemStyle+descText + '@@\n';
if (version.extensions.nestedSliders){
descText = '+++[...]' + descText + '===';
}
rssText = rssText + descText;
}
var story;
if ((lastDate != pubDate) & & ( pubDate != '0')) {
story = createTiddlyElement(article," li" ,null," RSSItem" ,pubDate);
lastDate = pubDate;
}
else {
lastDate = pubDate;
}
story = createTiddlyElement(article," div" ,null," RSSItem" ,null);
wikify(rssText,story);
}
},
formatDate: function(template, date){
var dateString = new Date(date);
// template = template.replace(/hh|mm|ss/g,'');
return dateString.formatString(template);
},
formatDateString: function(template, date){
var dateString = new Date(date.substr(0,4), date.substr(5,2) - 1, date.substr(8,2)
);
return dateString.formatString(template);
}
};
config.macros.rssFeedUpdate = {
label: " Update" ,
prompt: " Clear the cache and redisplay this RssFeed" ,
handler: function(place,macroName,params) {
var feedURL = params[0];
var tiddlerTitle = params[1];
createTiddlyButton(place, this.label, this.prompt,
function () {
if (config.macros.rssReader.cache[feedURL]) {
config.macros.rssReader.cache[feedURL] = null;
}
story.refreshTiddler(tiddlerTitle,null, true);
return false;});
}
};
//}}}
< / pre >
< / div >
2007-08-05 17:54:36 +02:00
< div title = "RenderEngine" modifier = "Ichthyostega" modified = "200708050630" created = "200706190056" tags = "overview" changecount = "40" >
2007-06-21 08:34:21 +02:00
< pre > The Render Engine is the part of the application doing the actual video calculations. Its operations are guided by the Objects and Parameters edited by the user in [[the EDL|EDL]] and it retrieves the raw audio and video data from the [[Data backend|backend.html]]. Because the inner workings of the Render Engine are closely related to the structures used in the EDL, this design covers [[this aspect|MObjects]] as well.
The key idea of Ichthyo's Design-draft is to use the [[Builder Pattern|http://en.wikipedia.org/wiki/Builder_pattern]] for the Render Engine, thus separating completely the //building// of the Render Pipeline from //running,// i.e. doing the actual Render. The Nodes in this Pipeline should process Video/Audio and do nothing else. No more decisions, tests and conditional operations when running the Pipeline. Move all of this out into the configuration of the pipeline, which is done by the Builder.
([[more on the Design Goals|DesignGoals]])
2007-06-19 03:45:45 +02:00
!Why doesn't the current Design succeed with this?
2007-06-19 09:22:52 +02:00
The design of Cinelerra 2 basically follows this design, but __fails because of two reasons__
2007-06-21 19:39:49 +02:00
# too much differentiation is put into the class hierarchy instead of configuring Instances differently.< br> This causes overly heavy use of virtual functions and -- in order to ameliorate this -- falling back to hard wired branching
# far too much back-coupling to the internals of the [[EDL]], forcing a overly rigid structure on the latter
2007-06-19 03:45:45 +02:00
!Try to learn from this
2007-06-22 08:46:15 +02:00
* build up an [[Node Abstraction|ProcNode]] powerful enough to express //all necessary Operations// without the need to recure on the actual object type
2007-06-21 19:39:49 +02:00
* need to redesign the internals of the EDL in a far more open manner. See MObjects
* strive at a StrongSeparation between EDL and Render Engine
2007-06-19 03:45:45 +02:00
!!see also
2007-06-21 19:39:49 +02:00
& rarr; [[Overview]] of Subsystems and Components, and DesignGoals
2007-06-22 08:46:15 +02:00
& rarr; [[An Introduction|WalkThrough]] discussing the key features
2007-06-21 19:39:49 +02:00
& rarr; [[Overview Render Engine|OverviewRenderEngine]]
2007-08-09 18:51:47 +02:00
& rarr; BuildProcess and RenderProcess
2007-06-22 08:46:15 +02:00
& rarr; [[Two Examples|Examples]] (Object diagrams)
2007-08-05 17:54:36 +02:00
& rarr; how [[Automation]] works {{red{to be defined in more detail}}}
& rarr; [[Problems|ProblemsTodo]] {{red{to be solved}}}
2007-08-09 18:51:47 +02:00
& rarr; [[Implementation Details|ImplementationDetails]] {{red{WIP}}}
2007-06-22 08:46:15 +02:00
< / pre >
2007-06-19 03:45:45 +02:00
< / div >
2007-06-22 08:46:15 +02:00
< div title = "RenderEntities" modifier = "Ichthyostega" modified = "200706220406" created = "200706190715" changecount = "6" >
< pre > The Render Engine only carries out the low-level and performance critical tasks. All configuration and decision concerns are to be handled by [[Builder]] and [[Controller]]. While the actual connection of the Render Nodes can be highly complex, basically each Segment of the Timeline with uniform characteristics is handled by one Processor, which is a graph of [[Processing Nodes|ProcNode]] discharging into a ExitNode. The Render Engine Components as such are //stateless// themselves; for the actual calculations they are combined with a StateProxy object generated by and connected internally to the [[Controller]], while at the same time holding the Data Buffers (Frames) for the actual calculations.
2007-06-19 09:22:52 +02:00
2007-06-25 09:02:09 +02:00
[img[Entities comprising the Render Engine|uml/fig128389.png]]
2007-06-22 08:46:15 +02:00
< / pre >
2007-06-19 09:22:52 +02:00
< / div >
2007-06-27 17:19:54 +02:00
< div title = "RenderProcess" modifier = "MichaelPloujnikov" modified = "200706271425" created = "200706190705" tags = "dynamic" changecount = "6" >
2007-06-22 08:46:15 +02:00
< pre > {{red{TODO: describe the Render Process.}}}
2007-06-19 09:22:52 +02:00
2007-06-27 17:19:54 +02:00
* see also the [[Entities involved in Rendering|RenderEntities]]
2007-06-22 08:46:15 +02:00
< / pre >
< / div >
2007-09-27 23:07:05 +02:00
< div title = "SessionOverview" modifier = "Ichthyostega" created = "200709272105" changecount = "1" >
< pre > The Session (sometimes also called //Project//) contains all informations and objects to be edited by the User. It can be saved and loaded. The individual Objects within the Session, i.e. Clips, Media, Effects, are contained in one (or several) collections within the Session, which we call [[EDL (Edit Decision List)|EDL]]. Moreover, the sesion contains references to all the Media files used, and it contains various default or user defined configuration. At any given time, there is //only one current session// opened within the application.
!!!larger projects
For larger editing projects the simple structure of a session containing " the" timeline is not sufficient. Rather, we have several timelines, e.g. one for each scene. Or we could have several layered or nested timelines (compositional work, multimedia productions). To support these cases without making the default case more complicated, Cinelerra-3 introduces a //focus// for selecting the //current EDL,// which will receive all editing operations.
!!!the definitive state
With all the structural complexities possible within such a session, we need an isolation layer to provide __one__ definitive state where all configuration has been made explicit. Thus the session manages one special object list, the [[Fixture]], which can be seen as all currently active object placed onto a single timeline.
< / pre >
< / div >
2007-06-20 02:50:10 +02:00
< div title = "SideBarOptions" modifier = "CehTeh" created = "200706200048" changecount = "1" >
< pre > < < search> > < < closeAll> > < < permaview> > < < newTiddler> > < < saveChanges> > < < slider chkSliderOptionsPanel OptionsPanel " options »" " Change TiddlyWiki advanced options" > > < / pre >
< / div >
2007-06-19 03:45:45 +02:00
< div title = "SiteSubtitle" modifier = "Ichthyostega" created = "200706190044" changecount = "1" >
< pre > some aspects of Cinelerra-3 design< / pre >
< / div >
2007-08-08 04:50:02 +02:00
< div title = "SiteTitle" modifier = "Ichthyostega" modified = "200708080212" created = "200706190042" changecount = "2" >
< pre > Engine< / pre >
2007-06-19 03:45:45 +02:00
< / div >
2007-06-22 08:46:15 +02:00
< div title = "SplashScreen" modifier = "just me" created = "200706220430" changecount = "1" >
< pre > {{red{killme}}}< / pre >
< / div >
2007-06-19 03:45:45 +02:00
< div title = "SplashScreenPlugin" modifier = "Saq" modified = "200607202048" created = "200607191631" tags = "lewcidExtension systemConfig" server . type = "file" server . host = "file:///home/ct/.homepage/home.html" server . page . revision = "200607202048" >
< pre > /***
''Inspired by [[TiddlyPom|http://www.warwick.ac.uk/~tuspam/tiddlypom.html]]''
|Name|SplashScreenPlugin|
|Created by|SaqImtiaz|
|Location|http://tw.lewcid.org/#SplashScreenPlugin|
|Version|0.21 |
|Requires|~TW2.08+|
!Description:
Provides a simple splash screen that is visible while the TW is loading.
!Installation
Copy the source text of this tiddler to your TW in a new tiddler, tag it with systemConfig and save and reload. The SplashScreen will now be installed and will be visible the next time you reload your TW.
!Customizing
Once the SplashScreen has been installed and you have reloaded your TW, the splash screen html will be present in the MarkupPreHead tiddler. You can edit it and customize to your needs.
!History
* 20-07-06 : version 0.21, modified to hide contentWrapper while SplashScreen is displayed.
* 26-06-06 : version 0.2, first release
!Code
***/
//{{{
var old_lewcid_splash_restart=restart;
restart = function()
{ if (document.getElementById(" SplashScreen" ))
document.getElementById(" SplashScreen" ).style.display = " none" ;
if (document.getElementById(" contentWrapper" ))
document.getElementById(" contentWrapper" ).style.display = " block" ;
old_lewcid_splash_restart();
if (splashScreenInstall)
{if(config.options.chkAutoSave)
{saveChanges();}
displayMessage(" TW SplashScreen has been installed, please save and refresh your TW." );
}
}
var oldText = store.getTiddlerText(" MarkupPreHead" );
if (oldText.indexOf(" SplashScreen" )==-1)
{var siteTitle = store.getTiddlerText(" SiteTitle" );
var splasher='\n\n< style type=" text/css" > #contentWrapper {display:none;}< /style> < div id=" SplashScreen" style=" border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;" > < b> '+siteTitle +'< /b> is loading< blink> ...< /blink> < br> < br> < span style=" font-size: 14px; color:red;" > Requires Javascript.< /span> < /div> ';
if (! store.tiddlerExists(" MarkupPreHead" ))
{var myTiddler = store.createTiddler(" MarkupPreHead" );}
else
{var myTiddler = store.getTiddler(" MarkupPreHead" );}
myTiddler.set(myTiddler.title,oldText+splasher,config.options.txtUserName,null,null);
store.setDirty(true);
var splashScreenInstall = true;
}
//}}}< / pre >
< / div >
2007-06-27 17:19:54 +02:00
< div title = "StateProxy" modifier = "MichaelPloujnikov" modified = "200706271456" created = "200706220352" tags = "def" changecount = "2" >
2007-06-22 08:46:15 +02:00
< pre > An Object representing a //Render Process// and containing associated state information.
* it is created in the Controller subsystem while initiating the BuildProcess
* it is passed on to the generated Render Engine, which in turn passes it down to the individual Processors
* moreover, it contains methods to communicate with other state relevant parts of the system, thereby shielding the rendering code from any complexities of Thread communication if necessary. (thus the name Proxy)
< / pre >
< / div >
2007-06-27 17:19:54 +02:00
< div title = "StrongSeparation" modifier = "MichaelPloujnikov" modified = "200706271504" created = "200706220452" tags = "design" changecount = "5" >
2007-06-22 08:46:15 +02:00
< pre > This design lays great emphasis on separating all those components and subsystems, which are considered not to have a //natural link// of their underlying concepts. This often means putting some additional constraints on the implementation, so basically we need to rely on the actual implementation to live up to this goal. In many cases it may seem to be more natural to " just access the necessary information" . But on the long run this coupling of not-directly related components makes the whole codebase monolithic and introduces lots of //accidental complexity.//
2007-06-27 17:19:54 +02:00
Instead, we should try to just connect the various subsystems via Interfaces and & mdash; instead of just using some information, rather use some service to be located on an Interface to query other components for this information. The best approach of course is always to avoid the dependency altogether.
2007-06-22 08:46:15 +02:00
!Examples
* There is a separation between the __high level [[EDL]] view__ and the [[Fixture]]: the latter only accesses the MObjects and the Placement Interfaces.
* same holds true for the Builder: it just uses the same Interfaces. The actual coupling is done rather //by type//, i.e. the Builder relies on several types of MObjects to exist and treats them via overloaded methods. He doesn't rely on a actual object structure layout in the EDL besides the requirement of having a [[Playlist]]
* the Builder itself is a separation layer. Neither do the Objects in the EDL access directly [[Render Nodes|ProcNode]], nor do the latter call back into the EDL. Both connections seem to be necessary at first sight, but both can be avoided by using the Builder Pattern
2007-06-27 17:19:54 +02:00
* another separation exists between the Render Engine and the individual Nodes: The Render Engine doesn't need to know the details of the data types processed by the Nodes. It relies on the Builder having done the correct connections and just pulls out the calculated results. If there needs to be additional control information to be passed, then I would prefer to do a direct wiring of separate control connections to specialized components, which in turn could instruct the controller to change the rendering process.
* to shield the rendering code of all complexities of thread communication and synchronization, we use the StateProxy
2007-06-22 08:46:15 +02:00
< / pre >
< / div >
2007-09-22 16:00:36 +02:00
< div title = "StructAsset" modifier = "Ichthyostega" created = "200709221353" tags = "def classes" changecount = "1" >
< pre > Structural Assets are intended mainly for internal use, but the user should be able to see and query them. By changing the parametrisation of some structural Asset, we can customize the default behaviour of Cinelerra to some extent.
* [[Processing Patterns|ProcPatt]] encode the information, how to get at the actual media data when rendering a clip.
* Tracks are one of the dimensions used for organizing the EDL. Besides, they carry parametrisation of output port, overlay mode etc.
* Output Ports {{red{still need to be defined...}}}
The Asset name field of structural Assets utilizes a special naming scheme, which allows to derive the name based on the capabilities of the structural asset. For example, by default all media clips with a given media stream type (e.g. H264) will use the same [[processing Pattern|ProcPatt]] for rendering. {{red{todo: work out the details of this naming scheme??}}}
[img[Asset Classess|uml/fig131205.png]]
< / pre >
< / div >
2007-09-04 05:09:08 +02:00
< div title = "StyleSheet" modifier = "Ichthyostega" modified = "200709040043" created = "200701131624" tags = "MPTWTheme excludeMissing" server . type = "file" server . host = "file:///home/ct/.homepage/home.html" server . page . revision = "200706090017" changecount = "14" >
2007-06-19 03:45:45 +02:00
< pre > /*{{{*/
/* a contrasting background so I can see where one tiddler ends and the other begins */
body {
background: [[ColorPalette::TertiaryLight]];
}
/* sexy colours and font for the header */
.headerForeground {
color: [[ColorPalette::PrimaryPale]];
}
.headerShadow, .headerShadow a {
color: [[ColorPalette::PrimaryMid]];
}
.headerForeground, .headerShadow {
padding: 1em 1em 0;
font-family: 'Trebuchet MS' sans-serif;
font-weight:bold;
}
.headerForeground .siteSubtitle {
color: [[ColorPalette::PrimaryLight]];
}
.headerShadow .siteSubtitle {
color: [[ColorPalette::PrimaryMid]];
}
/* make shadow go and down right instead of up and left */
.headerShadow {
left: 2px;
top: 3px;
}
/* prefer monospace for editing */
.editor textarea {
font-family: 'Consolas' monospace;
}
/* sexy tiddler titles */
.title {
font-size: 250%;
color: [[ColorPalette::PrimaryLight]];
font-family: 'Trebuchet MS' sans-serif;
}
/* more subtle tiddler subtitle */
.subtitle {
padding:0px;
margin:0px;
padding-left:0.5em;
font-size: 90%;
color: [[ColorPalette::TertiaryMid]];
}
.subtitle .tiddlyLink {
color: [[ColorPalette::TertiaryMid]];
}
/* a little bit of extra whitespace */
.viewer {
padding-bottom:3px;
}
/* don't want any background color for headings */
h1,h2,h3,h4,h5,h6 {
background: [[ColorPalette::Background]];
color: [[ColorPalette::Foreground]];
}
/* give tiddlers 3d style border and explicit background */
.tiddler {
background: [[ColorPalette::Background]];
border-right: 2px [[ColorPalette::TertiaryMid]] solid;
border-bottom: 2px [[ColorPalette::TertiaryMid]] solid;
margin-bottom: 1em;
padding-bottom: 2em;
}
/* make options slider look nicer */
#sidebarOptions .sliderPanel {
border:solid 1px [[ColorPalette::PrimaryLight]];
}
/* the borders look wrong with the body background */
#sidebar .button {
border-style: none;
}
/* displays the list of a tiddler's tags horizontally. used in ViewTemplate */
.tagglyTagged li.listTitle {
display:none
}
.tagglyTagged li {
display: inline; font-size:90%;
}
.tagglyTagged ul {
margin:0px; padding:0px;
}
/* this means you can put line breaks in SidebarOptions for readability */
#sidebarOptions br {
display:none;
}
/* undo the above in OptionsPanel */
#sidebarOptions .sliderPanel br {
display:inline;
}
/* horizontal main menu stuff */
#displayArea {
margin: 1em 15.7em 0em 1em; /* use the freed up space */
}
#topMenu br {
display: none;
}
#topMenu {
background: [[ColorPalette::PrimaryMid]];
color:[[ColorPalette::PrimaryPale]];
}
#topMenu {
padding:2px;
}
#topMenu .button, #topMenu .tiddlyLink, #topMenu a {
margin-left: 0.5em;
margin-right: 0.5em;
padding-left: 3px;
padding-right: 3px;
color: [[ColorPalette::PrimaryPale]];
font-size: 115%;
}
#topMenu .button:hover, #topMenu .tiddlyLink:hover {
background: [[ColorPalette::PrimaryDark]];
}
/* make it print a little cleaner */
@media print {
#topMenu {
display: none ! important;
}
/* not sure if we need all the importants */
.tiddler {
border-style: none ! important;
margin:0px ! important;
padding:0px ! important;
padding-bottom:2em ! important;
}
.tagglyTagging .button, .tagglyTagging .hidebutton {
display: none ! important;
}
.headerShadow {
visibility: hidden ! important;
}
.tagglyTagged .quickopentag, .tagged .quickopentag {
border-style: none ! important;
}
.quickopentag a.button, .miniTag {
display: none ! important;
}
}
2007-06-21 08:34:21 +02:00
/* *** Additions by Ichthyostega *** */
.red {
background: #ffcc99;
color: #ff2210;
padding: 0px 0.8ex;
}
2007-09-04 05:09:08 +02:00
.viewer th {
background: #91a6af;
}
2007-06-19 03:45:45 +02:00
/*}}}*/
< / pre >
< / div >
2007-08-08 04:50:02 +02:00
< div title = "TabTimeline" modifier = "Ichthyostega" modified = "200708080211" created = "200706191949" changecount = "2" >
< pre > < < timeline better:true maxDays:28 maxEntries:20> > < / pre >
2007-06-22 08:46:15 +02:00
< / div >
2007-06-19 03:45:45 +02:00
< div title = "TextAreaPlugin" modifier = "Jeremy" created = "200601261745" tags = "systemConfig" server . type = "file" server . host = "file:///home/ct/.homepage/home.html" server . page . revision = "200601261745" >
< pre > /***
''TextAreaPlugin for TiddlyWiki version 2.0''
^^author: Eric Shulman - ELS Design Studios
source: http://www.elsdesign.com/tiddlywiki/#TextAreaPlugin
license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
This plugin 'hijacks' the TW core function, ''Story.prototype.focusTiddler()'', so it can add special 'keyDown' handlers to adjust several behaviors associated with the textarea control used in the tiddler editor. Specifically, it:
* Adds text search INSIDE of edit fields.^^
Use ~CTRL-F for " Find" (prompts for search text), and ~CTRL-G for " Find Next" (uses previous search text)^^
* Enables TAB characters to be entered into field content^^
(instead of moving to next field)^^
* Option to set cursor at top of edit field instead of auto-selecting contents^^
(see configuration section for checkbox)^^
!!!!!Configuration
< < <
< < option chkDisableAutoSelect> > place cursor at start of textarea instead of pre-selecting content
< < option chkTextAreaExtensions> > add control-f (find), control-g (find again) and allow TABs as input in textarea
< < <
!!!!!Installation
< < <
Import (or copy/paste) the following tiddlers into your document:
''TextAreaPlugin'' (tagged with < < tag systemConfig> > )
< < <
!!!!!Revision History
< < <
''2006.01.22 [1.0.1]''
only add extra key processing for TEXTAREA elements (not other edit fields).
added option to enable/disable textarea keydown extensions (default is " standard keys" only)
''2006.01.22 [1.0.0]''
Moved from temporary " System Tweaks" tiddler into 'real' TextAreaPlugin tiddler.
< < <
!!!!!Code
***/
//{{{
version.extensions.textAreaPlugin= {major: 1, minor: 0, revision: 1, date: new Date(2006,1,23)};
//}}}
//{{{
if (!config.options.chkDisableAutoSelect) config.options.chkDisableAutoSelect=false; // default to standard action
if (!config.options.chkTextAreaExtensions) config.options.chkTextAreaExtensions=false; // default to standard action
// Focus a specified tiddler. Attempts to focus the specified field, otherwise the first edit field it finds
Story.prototype.focusTiddler = function(title,field)
{
var tiddler = document.getElementById(this.idPrefix + title);
if(tiddler != null)
{
var children = tiddler.getElementsByTagName(" *" )
var e = null;
for (var t=0; t< children.length; t++)
{
var c = children[t];
if(c.tagName.toLowerCase() == " input" || c.tagName.toLowerCase() == " textarea" )
{
if(!e)
e = c;
if(c.getAttribute(" edit" ) == field)
e = c;
}
}
if(e)
{
e.focus();
e.select(); // select entire contents
// TWEAK: add TAB and " find" key handlers
if (config.options.chkTextAreaExtensions) // add extra key handlers
addKeyDownHandlers(e);
// TWEAK: option to NOT autoselect contents
if (config.options.chkDisableAutoSelect) // set cursor to start of field content
if (e.setSelectionRange) e.setSelectionRange(0,0); // for FF
else if (e.createTextRange) { var r=e.createTextRange(); r.collapse(true); r.select(); } // for IE
}
}
}
//}}}
//{{{
function addKeyDownHandlers(e)
{
// exit if not textarea or element doesn't allow selections
if (e.tagName.toLowerCase()!=" textarea" || !e.setSelectionRange) return;
// utility function: exits keydown handler and prevents browser from processing the keystroke
var processed=function(ev) { ev.cancelBubble=true; if (ev.stopPropagation) ev.stopPropagation(); return false; }
// capture keypress in edit field
e.onkeydown = function(ev) { if (!ev) var ev=window.event;
// process TAB
if (!ev.shiftKey & & ev.keyCode==9) {
// replace current selection with a TAB character
var start=e.selectionStart; var end=e.selectionEnd;
e.value=e.value.substr(0,start)+String.fromCharCode(9)+e.value.substr(end);
// update insertion point, scroll it into view
e.setSelectionRange(start+1,start+1);
var linecount=e.value.split('\n').length;
var thisline=e.value.substr(0,e.selectionStart).split('\n').length-1;
e.scrollTop=Math.floor((thisline-e.rows/2)*e.scrollHeight/linecount);
return processed(ev);
}
// process CTRL-F (find matching text) or CTRL-G (find next match)
if (ev.ctrlKey & & (ev.keyCode==70||ev.keyCode==71)) {
// if ctrl-f or no previous search, prompt for search text (default to previous text or current selection)... if no search text, exit
if (ev.keyCode==70||!e.find||!e.find.length)
{ var f=prompt(" find:" ,e.find?e.find:e.value.substring(e.selectionStart,e.selectionEnd)); e.focus(); e.find=f?f:e.find; }
if (!e.find||!e.find.length) return processed(ev);
// do case-insensitive match with 'wraparound'... if not found, alert and exit
var newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase(),e.selectionStart+1);
if (newstart==-1) newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase());
if (newstart==-1) { alert(" '" +e.find+" ' not found" ); e.focus(); return processed(ev); }
// set new selection, scroll it into view, and report line position in status bar
e.setSelectionRange(newstart,newstart+e.find.length);
var linecount=e.value.split('\n').length;
var thisline=e.value.substr(0,e.selectionStart).split('\n').length;
e.scrollTop=Math.floor((thisline-1-e.rows/2)*e.scrollHeight/linecount);
window.status=" line: " +thisline+" /" +linecount;
return processed(ev);
}
}
}
//}}}< / pre >
< / div >
2007-06-22 08:46:15 +02:00
< div title = "TiddlyWiki" modifier = "Ichthyostega" created = "200706220430" changecount = "1" >
< pre > http://tiddlywiki.com/< / pre >
< / div >
2007-06-25 09:56:36 +02:00
< div title = "Timeline" modifier = "Ichthyostega" created = "200706250721" tags = "def" changecount = "1" >
2007-06-26 08:20:31 +02:00
< pre > Timeline is the name of a specific facility located in the [[Fixture]] (EDL): It is a ordered list of ExplicitPlacement.s of MObjects. By traversing the Playlist you get at all elements actually to be rendered; the [[Builder]] uses this Timeline-list to construct actual Render Engine configurations to carry out the calculations.
2007-06-25 09:56:36 +02:00
< / pre >
< / div >
2007-06-27 17:19:54 +02:00
< div title = "WalkThrough" modifier = "MichaelPloujnikov" modified = "200706271449" created = "200706210625" tags = "overview" changecount = "34" >
2007-06-22 04:15:13 +02:00
< pre > The Intention of this text is to help you understanding the design and to show some notable details.
!!!!Starting Point
2007-06-27 17:19:54 +02:00
Design is an experiment to find out how things are related. We can't //plan// a Design top down, rather we have to start at some point with some hypothesis and look how it works out. The point of origin for Ichthyo's design is the observation that the Render Engine needs some Separation of Concerns to get the complexity down. And especially, this design ''uses three different Levels'' or Layers within the Render Engine and EDL.
* the __high level__ within the EDL uses uniformly treated MObjects which are assembled/glued together by a network of [[Placements|Placement]].< br> It is supposed that the GUI will present this and //only this view //to the user, giving him the ability to work with the objects
* the __builder level__ works on a stripped-down subset of this ~MObject network: it uses the //same Object instances// but only assembled by [[Explicit Placements|ExplicitPlacement]] which locate the objects //on a simple (track, time) grid.// It's the job of the builder to create out of this simplified Network the Configuration of [[Render Nodes|ProcNode]] needed to do the actual rendering
* the __engine level__ uses solely [[Render Pipeline Nodes (ProcNode)|ProcNode]], i.e. a Graph of interconnected processing nodes. The important constraint here is that //any decisions are ruled out//. The core Render Engine with all its nodes is lacking the ability to do any tests and checks and has no possibility to branch or reconfigure anything. (this is an especially important lesson I draw from studying the current Cinelerra source code)
2007-06-22 04:15:13 +02:00
!!!!Performance Considerations
2007-06-27 17:19:54 +02:00
* within the Engine the Render Nodes are containing the ''inner loop'', whose contents are to be executed hundred thousands to million times per frame. Every dispensable concern, which is not strictly necessary to get the job done, is worth the effort of factoring out here.
* performance pressure at the builder level is far lower, albeit still existent. Compared to the effort of calculating a single processing step, looping even over some hundred nodes and executing quite some logic is negligible. Danger bears on creating memory pressure or causing awkward execution patterns (in the backend) rather. So the main concern should be the ability of reconfiguring different aspects separately without much effort. If for example a given render strategy works out to create lots of deadlocks and waitstates in the backend, the design should account for the possibility to exchange it with another strategy without having to modify the inner workings of the build process.< br> On the other hand, I wouldn't be overly concerned to trigger the build process yet another time to get some specific problem solved. However, the possibility to share one Render configuration for, say, 20 sec of video, instead of triggering the build process 500 times for every frame in this timespan, would sure be worth considering if it's not overly complicated to achieve.
* contrary to this, the EDL level is harmless with respect to performance. Getting acceptable responsiveness on user interactions is sufficient. We could consider using high level languages here, for it is much more important being able to express and handle complicated object relationships with relative ease. The only (indirect) concern is to avoid generating memory pressure inadvertently. Edit actions generating memory peaks could interfere with an ongoing background render process. If we decide to use lots of relation objects or transient objects, we should use an object pool or still better an garbage collector.
2007-06-22 04:15:13 +02:00
!!!!Concepts and Interfaces
2007-06-27 17:19:54 +02:00
This design strives to build each level and subsystem around some central concepts, which are directly expressed as Interfaces. Commonly used Interfaces clamp the different layers.
2007-06-22 04:15:13 +02:00
* MObject gives an uniform view on all the various entities to be arranged in the EDL.
2007-06-26 08:20:31 +02:00
* all the arranging and relating of ~MObjects is abstracted as [[Placement]]. The contract of a Placement is that it always has a related Subject, that we can call some test methods on it (still to be defined), and, finally, that we can get an ExplicitPlacement from it.
2007-06-27 17:19:54 +02:00
* albeit being a special form of a Placement, the ExplicitPlacement is treated as a separate concept. With respect to edit operations within the EDL, it can stand for any sort of Placement. On the other hand the Builder takes a list of ~ExplicitPlacements as input for building up the Render Engine(s). This corresponds to the fact that the render process needs to organize the things to be done on a simple two dimensional grid of (output channel / time). The (extended) contract of an ~ExplicitPlacement provides us with this information (track,time).
* on the lower end of the builder, everything is organized around the Concept of a ProcNode, which enables us to //pull// one (freely addressable) Frame of calculated data. Further, the ProcNode has the ability to be wired with other nodes and [[Parameter Providers|ParamProvider]]
2007-06-22 04:15:13 +02:00
* the various types of data to be processed are abstracted away under the notion of a [[Frame]]. Basically, a Frame is an Buffer containing an Array of raw data and it can be located by some generic scheme, including (at least) the absolute starting time (and probably some type or channel id).
2007-06-27 17:19:54 +02:00
* All sorts of (target domain) [[Parameters]] are treated uniformly. There is a distinction between Parameters (which //could// be variable) and Configuration (which is considered to be fixed). In this context, Automation just appears as a special kind of ParamProvider.
* and finally, the calculation //process// together with its current state is represented by a StateProxy. I call this a " proxy" , because it should encapsulate and hide all tedious details of communication, be it even asynchronous communication with some Controller or Dispatcher running in another Thread. In order to maintain a view on the current state of the render process, it could eventually be necessary to register as an observer somewhere or to send notifications to other parts of the system.
2007-06-22 04:15:13 +02:00
!!!!Handling Diversity
2007-06-27 17:19:54 +02:00
An important goal of this approach is to be able to push down the treatment of variations and special cases. We don't need to know what kind of Placement links one MObject to another, because it is sufficient for us to get an ExplicitPlacement. The Render Engine doesn't need to know if it is pulling audio Frames or video Frames or GOPs or OpenGL textures. It simply relies on the Builder wiring together the correct node types. And the Builder in turn does so by using some overloaded function of an iterator or visitor. There is no need for the video [[ProcNodes|ProcNode]] to test for the colormodel on every screen line, because the Data Frame can be a Template parametrized by the colormodel. All of this reduces complexity and quite some misconceptions can be detected already by the compiler.
2007-06-22 04:15:13 +02:00
2007-06-26 08:20:31 +02:00
!!!!Explicit structural differences
2007-06-27 17:19:54 +02:00
In case it's not already clear: we don't have " the" Render Engine, rather we construct a Render Engine for each structurally differing part of the timeline. (please relate this to the current Cinelerra code base, which constructs and builds up the render pipeline for each frame separately). No need to call back from within the pipeline to find out if a given plugin is enabled or to see if there are any automation keyframes. We don't need to pose any constraints on the structuring of the objects in the EDL, besides the requirement to get an ExplicitPlacement for each. We could even loosen the use of the common metaphor of placing media sequences on fixed tracks, if we want to get at a more advanced GUI at some point in the future.
2007-06-22 04:15:13 +02:00
!!!!Stateless Subsystems
2007-06-27 17:19:54 +02:00
The & raquo;current setup& laquo; of the objects in the EDL is sort of a global state. Same holds true for the Controller, as the Engine can be at playback, it can run a background render or scrub single frames. But the whole complicated subsystem of the Builder and one given Render Engine configuration can be made ''stateless''. As a benefit of this we can run this subsystems multi-threaded without the need of any precautions (locking, synchronizing). Because all state information is just passed in as function parameters and lives in local variables on the stack, or is contained in the StateProxy which represents the given render //process// and is passed down as function parameter as well. (note: I use this term in the usual, slightly relaxed manner; of course there are some configuration values contained in instance variables of the objects carrying out the calculations, but this values are considered to be constant over the course of the object usage).
2007-06-22 08:46:15 +02:00
< / pre >
2007-06-21 08:34:21 +02:00
< / div >
2007-06-19 03:45:45 +02:00
< / div >
<!-- POST - STOREAREA -->
<!-- POST - BODY - START -->
<!-- POST - BODY - END -->
< script type = "text/javascript" >
//< ![CDATA[
//
// Please note:
//
// * This code is designed to be readable but for compactness it only includes brief comments. You can see fuller comments
// in the project Subversion repository at http://svn.tiddlywiki.org/Trunk/core/
//
// * You should never need to modify this source code directly. TiddlyWiki is carefully designed to allow deep customisation
// without changing the core code. Please consult the development group at http://groups.google.com/group/TiddlyWikiDev
//
//--
//-- Configuration repository
//--
// Miscellaneous options
var config = {
numRssItems: 20, // Number of items in the RSS feed
animDuration: 400, // Duration of UI animations in milliseconds
cascadeFast: 20, // Speed for cascade animations (higher == slower)
cascadeSlow: 60, // Speed for EasterEgg cascade animations
cascadeDepth: 5 // Depth of cascade animation
};
// Adaptors
config.adaptors = {};
// Backstage tasks
config.tasks = {};
// Annotations
config.annotations = {};
// Custom fields to be automatically added to new tiddlers
config.defaultCustomFields = {};
// Messages
config.messages = {
messageClose: {},
dates: {},
tiddlerPopup: {}
};
// Options that can be set in the options panel and/or cookies
config.options = {
chkRegExpSearch: false,
chkCaseSensitiveSearch: false,
chkAnimate: true,
chkSaveBackups: true,
chkAutoSave: false,
chkGenerateAnRssFeed: false,
chkSaveEmptyTemplate: false,
chkOpenInNewWindow: true,
chkToggleLinks: false,
chkHttpReadOnly: true,
chkForceMinorUpdate: false,
chkConfirmDelete: true,
chkInsertTabs: false,
chkUsePreForStorage: true, // Whether to use < pre > format for storage
chkDisplayStartupTime: false,
txtBackupFolder: "",
txtMainTab: "tabTimeline",
txtMoreTab: "moreTabAll",
txtMaxEditRows: "30",
txtFileSystemCharSet: "UTF-8"
};
config.optionsDesc = {};
// List of notification functions to be called when certain tiddlers are changed or deleted
config.notifyTiddlers = [
{name: "StyleSheetLayout", notify: refreshStyles},
{name: "StyleSheetColors", notify: refreshStyles},
{name: "StyleSheet", notify: refreshStyles},
{name: "StyleSheetPrint", notify: refreshStyles},
{name: "PageTemplate", notify: refreshPageTemplate},
{name: "SiteTitle", notify: refreshPageTitle},
{name: "SiteSubtitle", notify: refreshPageTitle},
{name: "ColorPalette", notify: refreshColorPalette},
{name: null, notify: refreshDisplay}
];
// Default tiddler templates
var DEFAULT_VIEW_TEMPLATE = 1;
var DEFAULT_EDIT_TEMPLATE = 2;
config.tiddlerTemplates = {
1: "ViewTemplate",
2: "EditTemplate"
};
// More messages (rather a legacy layout that shouldn't really be like this)
config.views = {
wikified: {
tag: {}
},
editor: {
tagChooser: {}
}
};
// Backstage tasks
config.backstageTasks = ["save","sync","importTask","tweak","plugins"];
// Macros; each has a 'handler' member that is inserted later
config.macros = {
today: {},
version: {},
search: {sizeTextbox: 15},
tiddler: {},
tag: {},
tags: {},
tagging: {},
timeline: {},
allTags: {},
list: {
all: {},
missing: {},
orphans: {},
shadowed: {},
touched: {}
},
closeAll: {},
permaview: {},
saveChanges: {},
slider: {},
option: {},
options: {},
newTiddler: {},
newJournal: {},
sparkline: {},
tabs: {},
gradient: {},
message: {},
view: {},
edit: {},
tagChooser: {},
toolbar: {},
br: {},
plugins: {},
refreshDisplay: {},
importTiddlers: {},
sync: {},
annotations: {}
};
// Commands supported by the toolbar macro
config.commands = {
closeTiddler: {},
closeOthers: {},
editTiddler: {},
saveTiddler: {hideReadOnly: true},
cancelTiddler: {},
deleteTiddler: {hideReadOnly: true},
permalink: {},
references: {type: "popup"},
jump: {type: "popup"},
syncing: {type: "popup"},
fields: {type: "popup"}
};
// Browser detection... In a very few places, there's nothing else for it but to know what browser we're using.
config.userAgent = navigator.userAgent.toLowerCase();
config.browser = {
isIE: config.userAgent.indexOf("msie") != -1 & & config.userAgent.indexOf("opera") == -1,
isGecko: config.userAgent.indexOf("gecko") != -1,
ieVersion: /MSIE (\d.\d)/i.exec(config.userAgent), // config.browser.ieVersion[1], if it exists, will be the IE version string, eg "6.0"
isSafari: config.userAgent.indexOf("applewebkit") != -1,
isBadSafari: !((new RegExp("[\u0150\u0170]","g")).test("\u0150")),
firefoxDate: /gecko\/(\d{8})/i.exec(config.userAgent), // config.browser.firefoxDate[1], if it exists, will be Firefox release date as "YYYYMMDD"
isOpera: config.userAgent.indexOf("opera") != -1,
isLinux: config.userAgent.indexOf("linux") != -1,
isUnix: config.userAgent.indexOf("x11") != -1,
isMac: config.userAgent.indexOf("mac") != -1,
isWindows: config.userAgent.indexOf("win") != -1
};
// Basic regular expressions
config.textPrimitives = {
upperLetter: "[A-Z\u00c0-\u00de\u0150\u0170]",
lowerLetter: "[a-z0-9_\\-\u00df-\u00ff\u0151\u0171]",
anyLetter: "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]",
anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]"
};
if(config.browser.isBadSafari) {
config.textPrimitives = {
upperLetter: "[A-Z\u00c0-\u00de]",
lowerLetter: "[a-z0-9_\\-\u00df-\u00ff]",
anyLetter: "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff]",
anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff]"
};
}
config.textPrimitives.sliceSeparator = "::";
config.textPrimitives.urlPattern = "[a-z]{3,8}:[^\\s:'\"][^\\s'\"]*(?:/|\\b)";
config.textPrimitives.unWikiLink = "~";
config.textPrimitives.wikiLink = "(?:(?:" + config.textPrimitives.upperLetter + "+" +
config.textPrimitives.lowerLetter + "+" +
config.textPrimitives.upperLetter +
config.textPrimitives.anyLetter + "*)|(?:" +
config.textPrimitives.upperLetter + "{2,}" +
config.textPrimitives.lowerLetter + "+))";
config.textPrimitives.cssLookahead = "(?:(" + config.textPrimitives.anyLetter + "+)\\(([^\\)\\|\\n]+)(?:\\):))|(?:(" + config.textPrimitives.anyLetter + "+):([^;\\|\\n]+);)";
config.textPrimitives.cssLookaheadRegExp = new RegExp(config.textPrimitives.cssLookahead,"mg");
config.textPrimitives.brackettedLink = "\\[\\[([^\\]]+)\\]\\]";
config.textPrimitives.titledBrackettedLink = "\\[\\[([^\\[\\]\\|]+)\\|([^\\[\\]\\|]+)\\]\\]";
config.textPrimitives.tiddlerForcedLinkRegExp = new RegExp("(?:" + config.textPrimitives.titledBrackettedLink + ")|(?:" +
config.textPrimitives.brackettedLink + ")|(?:" +
config.textPrimitives.urlPattern + ")","mg");
config.textPrimitives.tiddlerAnyLinkRegExp = new RegExp("("+ config.textPrimitives.wikiLink + ")|(?:" +
config.textPrimitives.titledBrackettedLink + ")|(?:" +
config.textPrimitives.brackettedLink + ")|(?:" +
config.textPrimitives.urlPattern + ")","mg");
config.glyphs = {
browsers: [
function() {return config.browser.isIE;},
function() {return true}
],
currBrowser: null,
codes: {
downTriangle: ["\u25BC","\u25BE"],
downArrow: ["\u2193","\u2193"],
bentArrowLeft: ["\u2190","\u21A9"],
bentArrowRight: ["\u2192","\u21AA"]
}
};
//--
//-- Shadow tiddlers
//--
config.shadowTiddlers = {
StyleSheet: "",
MarkupPreHead: "<!-- {{{ --> \n< link rel = 'alternate' type = 'application/rss+xml' title = 'RSS' href = 'index.xml' / > \n<!-- }}} --> ",
MarkupPostHead: "",
MarkupPreBody: "",
MarkupPostBody: "",
TabTimeline: '< < timeline > >',
TabAll: '< < list all > >',
TabTags: '< < allTags excludeLists > >',
TabMoreMissing: '< < list missing > >',
TabMoreOrphans: '< < list orphans > >',
TabMoreShadowed: '< < list shadowed > >',
AdvancedOptions: '< < options > >',
PluginManager: '< < plugins > >',
ImportTiddlers: '< < importTiddlers > >'
};
//--
//-- Translateable strings
//--
// Strings in "double quotes" should be translated; strings in 'single quotes' should be left alone
merge(config.options,{
txtUserName: "YourName"});
merge(config.tasks,{
save: {text: "save", tooltip: "Save your changes to this TiddlyWiki", action: saveChanges},
sync: {text: "sync", tooltip: "Synchronise changes with other TiddlyWiki files and servers", content: '< < sync > >'},
importTask: {text: "import", tooltip: "Import tiddlers and plugins from other TiddlyWiki files and servers", content: '< < importTiddlers > >'},
tweak: {text: "tweak", tooltip: "Tweak the appearance and behaviour of TiddlyWiki", content: '< < options > >'},
plugins: {text: "plugins", tooltip: "Manage installed plugins", content: '< < plugins > >'}
});
// Options that can be set in the options panel and/or cookies
merge(config.optionsDesc,{
txtUserName: "Username for signing your edits",
chkRegExpSearch: "Enable regular expressions for searches",
chkCaseSensitiveSearch: "Case-sensitive searching",
chkAnimate: "Enable animations",
chkSaveBackups: "Keep backup file when saving changes",
chkAutoSave: "Automatically save changes",
chkGenerateAnRssFeed: "Generate an RSS feed when saving changes",
chkSaveEmptyTemplate: "Generate an empty template when saving changes",
chkOpenInNewWindow: "Open external links in a new window",
chkToggleLinks: "Clicking on links to open tiddlers causes them to close",
chkHttpReadOnly: "Hide editing features when viewed over HTTP",
chkForceMinorUpdate: "Don't update modifier username and date when editing tiddlers",
chkConfirmDelete: "Require confirmation before deleting tiddlers",
chkInsertTabs: "Use the tab key to insert tab characters instead of moving between fields",
txtBackupFolder: "Name of folder to use for backups",
txtMaxEditRows: "Maximum number of rows in edit boxes",
txtFileSystemCharSet: "Default character set for saving changes (Firefox/Mozilla only)"});
merge(config.messages,{
customConfigError: "Problems were encountered loading plugins. See PluginManager for details",
pluginError: "Error: %0",
pluginDisabled: "Not executed because disabled via 'systemConfigDisable' tag",
pluginForced: "Executed because forced via 'systemConfigForce' tag",
pluginVersionError: "Not executed because this plugin needs a newer version of TiddlyWiki",
nothingSelected: "Nothing is selected. You must select one or more items first",
savedSnapshotError: "It appears that this TiddlyWiki has been incorrectly saved. Please see http://www.tiddlywiki.com/#DownloadSoftware for details",
subtitleUnknown: "(unknown)",
undefinedTiddlerToolTip: "The tiddler '%0' doesn't yet exist",
shadowedTiddlerToolTip: "The tiddler '%0' doesn't yet exist, but has a pre-defined shadow value",
tiddlerLinkTooltip: "%0 - %1, %2",
externalLinkTooltip: "External link to %0",
noTags: "There are no tagged tiddlers",
notFileUrlError: "You need to save this TiddlyWiki to a file before you can save changes",
cantSaveError: "It's not possible to save changes. Possible reasons include:\n- your browser doesn't support saving (Firefox, Internet Explorer, Safari and Opera all work if properly configured)\n- the pathname to your TiddlyWiki file contains illegal characters\n- the TiddlyWiki HTML file has been moved or renamed",
invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
backupSaved: "Backup saved",
backupFailed: "Failed to save backup file",
rssSaved: "RSS feed saved",
rssFailed: "Failed to save RSS feed file",
emptySaved: "Empty template saved",
emptyFailed: "Failed to save empty template file",
mainSaved: "Main TiddlyWiki file saved",
mainFailed: "Failed to save main TiddlyWiki file. Your changes have not been saved",
macroError: "Error in macro < < \%0>>",
macroErrorDetails: "Error while executing macro < < \%0>>:\n%1",
missingMacro: "No such macro",
overwriteWarning: "A tiddler named '%0' already exists. Choose OK to overwrite it",
unsavedChangesWarning: "WARNING! There are unsaved changes in TiddlyWiki\n\nChoose OK to save\nChoose CANCEL to discard",
confirmExit: "--------------------------------\n\nThere are unsaved changes in TiddlyWiki. If you continue you will lose those changes\n\n--------------------------------",
saveInstructions: "SaveChanges",
unsupportedTWFormat: "Unsupported TiddlyWiki format '%0'",
tiddlerSaveError: "Error when saving tiddler '%0'",
tiddlerLoadError: "Error when loading tiddler '%0'",
wrongSaveFormat: "Cannot save with storage format '%0'. Using standard format for save.",
invalidFieldName: "Invalid field name %0",
fieldCannotBeChanged: "Field '%0' cannot be changed",
loadingMissingTiddler: "Attempting to retrieve the tiddler '%0' from the '%1' server at:\n\n'%2' in the workspace '%3'"});
merge(config.messages.messageClose,{
text: "close",
tooltip: "close this message area"});
config.messages.backstage = {
open: {text: "backstage", tooltip: "Open the backstage area to perform authoring and editing tasks"},
close: {text: "close", tooltip: "Close the backstage area"},
prompt: "backstage: ",
decal: {
edit: {text: "edit", tooltip: "Edit the tiddler '%0'"}
}
};
config.messages.listView = {
tiddlerTooltip: "Click for the full text of this tiddler",
previewUnavailable: "(preview not available)"
};
config.messages.dates.months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November","December"];
config.messages.dates.days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
config.messages.dates.shortMonths = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
config.messages.dates.shortDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
// suffixes for dates, eg "1st","2nd","3rd"..."30th","31st"
config.messages.dates.daySuffixes = ["st","nd","rd","th","th","th","th","th","th","th",
"th","th","th","th","th","th","th","th","th","th",
"st","nd","rd","th","th","th","th","th","th","th",
"st"];
config.messages.dates.am = "am";
config.messages.dates.pm = "pm";
merge(config.messages.tiddlerPopup,{
});
merge(config.views.wikified.tag,{
labelNoTags: "no tags",
labelTags: "tags: ",
openTag: "Open tag '%0'",
tooltip: "Show tiddlers tagged with '%0'",
openAllText: "Open all",
openAllTooltip: "Open all of these tiddlers",
popupNone: "No other tiddlers tagged with '%0'"});
merge(config.views.wikified,{
defaultText: "The tiddler '%0' doesn't yet exist. Double-click to create it",
defaultModifier: "(missing)",
shadowModifier: "(built-in shadow tiddler)",
dateFormat: "DD MMM YYYY",
createdPrompt: "created"});
merge(config.views.editor,{
tagPrompt: "Type tags separated with spaces, [[use double square brackets]] if necessary, or add existing",
defaultText: "Type the text for '%0'"});
merge(config.views.editor.tagChooser,{
text: "tags",
tooltip: "Choose existing tags to add to this tiddler",
popupNone: "There are no tags defined",
tagTooltip: "Add the tag '%0'"});
merge(config.messages,{
sizeTemplates:
[
{unit: 1024*1024*1024, template: "%0\u00a0GB"},
{unit: 1024*1024, template: "%0\u00a0MB"},
{unit: 1024, template: "%0\u00a0KB"},
{unit: 1, template: "%0\u00a0B"}
]});
merge(config.macros.search,{
label: "search",
prompt: "Search this TiddlyWiki",
accessKey: "F",
successMsg: "%0 tiddlers found matching %1",
failureMsg: "No tiddlers found matching %0"});
merge(config.macros.tagging,{
label: "tagging: ",
labelNotTag: "not tagging",
tooltip: "List of tiddlers tagged with '%0'"});
merge(config.macros.timeline,{
dateFormat: "DD MMM YYYY"});
merge(config.macros.allTags,{
tooltip: "Show tiddlers tagged with '%0'",
noTags: "There are no tagged tiddlers"});
config.macros.list.all.prompt = "All tiddlers in alphabetical order";
config.macros.list.missing.prompt = "Tiddlers that have links to them but are not defined";
config.macros.list.orphans.prompt = "Tiddlers that are not linked to from any other tiddlers";
config.macros.list.shadowed.prompt = "Tiddlers shadowed with default contents";
config.macros.list.touched.prompt = "Tiddlers that have been modified locally";
merge(config.macros.closeAll,{
label: "close all",
prompt: "Close all displayed tiddlers (except any that are being edited)"});
merge(config.macros.permaview,{
label: "permaview",
prompt: "Link to an URL that retrieves all the currently displayed tiddlers"});
merge(config.macros.saveChanges,{
label: "save changes",
prompt: "Save all tiddlers to create a new TiddlyWiki",
accessKey: "S"});
merge(config.macros.newTiddler,{
label: "new tiddler",
prompt: "Create a new tiddler",
title: "New Tiddler",
accessKey: "N"});
merge(config.macros.newJournal,{
label: "new journal",
prompt: "Create a new tiddler from the current date and time",
accessKey: "J"});
merge(config.macros.options,{
wizardTitle: "Tweak advanced options",
step1Title: "These options are saved in cookies in your browser",
step1Html: "< input type = 'hidden' name = 'markList' > < / input > < br > < input type = 'checkbox' checked = 'false' name = 'chkUnknown' > Show unknown options< / input > ",
unknownDescription: "//(unknown)//",
listViewTemplate: {
columns: [
{name: 'Option', field: 'option', title: "Option", type: 'String'},
{name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
{name: 'Name', field: 'name', title: "Name", type: 'String'}
],
rowClasses: [
{className: 'lowlight', field: 'lowlight'}
]}
});
merge(config.macros.plugins,{
wizardTitle: "Manage plugins",
step1Title: "Currently loaded plugins",
step1Html: "< input type = 'hidden' name = 'markList' > < / input > ", // DO NOT TRANSLATE
skippedText: "(This plugin has not been executed because it was added since startup)",
noPluginText: "There are no plugins installed",
confirmDeleteText: "Are you sure you want to delete these plugins:\n\n%0",
removeLabel: "remove systemConfig tag",
removePrompt: "Remove systemConfig tag",
deleteLabel: "delete",
deletePrompt: "Delete these tiddlers forever",
listViewTemplate: {
columns: [
{name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
{name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'},
{name: 'Forced', field: 'forced', title: "Forced", tag: 'systemConfigForce', type: 'TagCheckbox'},
{name: 'Disabled', field: 'disabled', title: "Disabled", tag: 'systemConfigDisable', type: 'TagCheckbox'},
{name: 'Executed', field: 'executed', title: "Loaded", type: 'Boolean', trueText: "Yes", falseText: "No"},
{name: 'Startup Time', field: 'startupTime', title: "Startup Time", type: 'String'},
{name: 'Error', field: 'error', title: "Status", type: 'Boolean', trueText: "Error", falseText: "OK"},
{name: 'Log', field: 'log', title: "Log", type: 'StringList'}
],
rowClasses: [
{className: 'error', field: 'error'},
{className: 'warning', field: 'warning'}
]}
});
merge(config.macros.toolbar,{
moreLabel: "more",
morePrompt: "Reveal further commands"
});
merge(config.macros.refreshDisplay,{
label: "refresh",
prompt: "Redraw the entire TiddlyWiki display"
});
merge(config.macros.importTiddlers,{
readOnlyWarning: "You cannot import into a read-only TiddlyWiki file. Try opening it from a file:// URL",
wizardTitle: "Import tiddlers from another file or server",
step1Title: "Step 1: Locate the server or TiddlyWiki file",
step1Html: "Specify the type of the server: < select name = 'selTypes' > < option value = '' > Choose...< / option > < / select > < br > Enter the URL or pathname here: < input type = 'text' size = 50 name = 'txtPath' > < br > ...or browse for a file: < input type = 'file' size = 50 name = 'txtBrowse' > < br > < hr > ...or select a pre-defined feed: < select name = 'selFeeds' > < option value = '' > Choose...< / option > < / select > ",
openLabel: "open",
openPrompt: "Open the connection to this file or server",
openError: "There were problems fetching the tiddlywiki file",
statusOpenHost: "Opening the host",
statusGetWorkspaceList: "Getting the list of available workspaces",
step2Title: "Step 2: Choose the workspace",
step2Html: "Enter a workspace name: < input type = 'text' size = 50 name = 'txtWorkspace' > < br > ...or select a workspace: < select name = 'selWorkspace' > < option value = '' > Choose...< / option > < / select > ",
cancelLabel: "cancel",
cancelPrompt: "Cancel this import",
statusOpenWorkspace: "Opening the workspace",
statusGetTiddlerList: "Getting the list of available tiddlers",
step3Title: "Step 3: Choose the tiddlers to import",
step3Html: "< input type = 'hidden' name = 'markList' > < / input > < br > < input type = 'checkbox' checked = 'true' name = 'chkSync' > Keep these tiddlers linked to this server so that you can synchronise subsequent changes< / input > < br > < input type = 'checkbox' name = 'chkSave' > Save the details of this server in a 'systemServer' tiddler called:< / input > < input type = 'text' size = 25 name = 'txtSaveTiddler' > ",
importLabel: "import",
importPrompt: "Import these tiddlers",
confirmOverwriteText: "Are you sure you want to overwrite these tiddlers:\n\n%0",
step4Title: "Step 4: Importing %0 tiddler(s)",
step4Html: "< input type = 'hidden' name = 'markReport' > < / input > ", // DO NOT TRANSLATE
doneLabel: "done",
donePrompt: "Close this wizard",
statusDoingImport: "Importing tiddlers",
statusDoneImport: "All tiddlers imported",
systemServerNamePattern: "%2 on %1",
systemServerNamePatternNoWorkspace: "%1",
confirmOverwriteSaveTiddler: "The tiddler '%0' already exists. Click 'OK' to overwrite it with the details of this server, or 'Cancel' to leave it unchanged",
serverSaveTemplate: "|''Type:''|%0|\n|''URL:''|%1|\n|''Workspace:''|%2|\n\nThis tiddler was automatically created to record the details of this server",
serverSaveModifier: "(System)",
listViewTemplate: {
columns: [
{name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
{name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'},
{name: 'Tags', field: 'tags', title: "Tags", type: 'Tags'}
],
rowClasses: [
]}
});
merge(config.macros.sync,{
listViewTemplate: {
columns: [
{name: 'Selected', field: 'selected', rowName: 'title', type: 'Selector'},
{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
{name: 'Server Type', field: 'serverType', title: "Server type", type: 'String'},
{name: 'Server Host', field: 'serverHost', title: "Server host", type: 'String'},
{name: 'Server Workspace', field: 'serverWorkspace', title: "Server workspace", type: 'String'},
{name: 'Status', field: 'status', title: "Synchronisation status", type: 'String'},
{name: 'Server URL', field: 'serverUrl', title: "Server URL", text: "View", type: 'Link'}
],
rowClasses: [
],
buttons: [
{caption: "Sync these tiddlers", name: 'sync'}
]},
wizardTitle: "Synchronize with external servers and files",
step1Title: "Choose the tiddlers you want to synchronize",
step1Html: "< input type = 'hidden' name = 'markList' > < / input > ", // DO NOT TRANSLATE
syncLabel: "sync",
syncPrompt: "Sync these tiddlers",
hasChanged: "Changed while unplugged",
hasNotChanged: "Unchanged while unplugged",
syncStatusList: {
none: {text: "...", color: "none"},
changedServer: {text: "Changed on server", color: '#80ff80'},
changedLocally: {text: "Changed while unplugged", color: '#80ff80'},
changedBoth: {text: "Changed while unplugged and on server", color: '#ff8080'},
notFound: {text: "Not found on server", color: '#ffff80'},
putToServer: {text: "Saved update on server", color: '#ff80ff'},
gotFromServer: {text: "Retrieved update from server", color: '#80ffff'}
}
});
merge(config.macros.annotations,{
});
merge(config.commands.closeTiddler,{
text: "close",
tooltip: "Close this tiddler"});
merge(config.commands.closeOthers,{
text: "close others",
tooltip: "Close all other tiddlers"});
merge(config.commands.editTiddler,{
text: "edit",
tooltip: "Edit this tiddler",
readOnlyText: "view",
readOnlyTooltip: "View the source of this tiddler"});
merge(config.commands.saveTiddler,{
text: "done",
tooltip: "Save changes to this tiddler"});
merge(config.commands.cancelTiddler,{
text: "cancel",
tooltip: "Undo changes to this tiddler",
warning: "Are you sure you want to abandon your changes to '%0'?",
readOnlyText: "done",
readOnlyTooltip: "View this tiddler normally"});
merge(config.commands.deleteTiddler,{
text: "delete",
tooltip: "Delete this tiddler",
warning: "Are you sure you want to delete '%0'?"});
merge(config.commands.permalink,{
text: "permalink",
tooltip: "Permalink for this tiddler"});
merge(config.commands.references,{
text: "references",
tooltip: "Show tiddlers that link to this one",
popupNone: "No references"});
merge(config.commands.jump,{
text: "jump",
tooltip: "Jump to another open tiddler"});
merge(config.commands.syncing,{
text: "syncing",
tooltip: "Control synchronisation of this tiddler with a server or external file",
currentlySyncing: "< div > Currently syncing via < span class = 'popupHighlight' > '%0'< / span > to:< /"+"div>< div > host: < span class = 'popupHighlight' > %1< / span > < /"+"div>< div > workspace: < span class = 'popupHighlight' > %2< / span > < /"+"div>", // Note escaping of closing < div > tag
notCurrentlySyncing: "Not currently syncing",
captionUnSync: "Stop synchronising this tiddler",
chooseServer: "Synchronise this tiddler with another server:",
currServerMarker: "\u25cf ",
notCurrServerMarker: " "});
merge(config.commands.fields,{
text: "fields",
tooltip: "Show the extended fields of this tiddler",
emptyText: "There are no extended fields for this tiddler",
listViewTemplate: {
columns: [
{name: 'Field', field: 'field', title: "Field", type: 'String'},
{name: 'Value', field: 'value', title: "Value", type: 'String'}
],
rowClasses: [
],
buttons: [
]}});
merge(config.shadowTiddlers,{
DefaultTiddlers: "GettingStarted",
MainMenu: "GettingStarted",
SiteTitle: "My TiddlyWiki",
SiteSubtitle: "a reusable non-linear personal web notebook",
SiteUrl: "http://www.tiddlywiki.com/",
SideBarOptions: '< < search > >< < closeAll > >< < permaview > >< < newTiddler > >< < newJournal " DD MMM YYYY " > >< < saveChanges > >< < slider chkSliderOptionsPanel OptionsPanel " options » " " Change TiddlyWiki advanced options " > >',
SideBarTabs: '< < tabs txtMainTab " Timeline " " Timeline " TabTimeline " All " " All tiddlers " TabAll " Tags " " All tags " TabTags " More " " More lists " TabMore > >',
TabMore: '< < tabs txtMoreTab " Missing " " Missing tiddlers " TabMoreMissing " Orphans " " Orphaned tiddlers " TabMoreOrphans " Shadowed " " Shadowed tiddlers " TabMoreShadowed > >'});
merge(config.annotations,{
AdvancedOptions: "This shadow tiddler provides access to several advanced options",
ColorPalette: "These values in this shadow tiddler determine the colour scheme of the ~TiddlyWiki user interface",
DefaultTiddlers: "The tiddlers listed in this shadow tiddler will be automatically displayed when ~TiddlyWiki starts up",
EditTemplate: "The HTML template in this shadow tiddler determines how tiddlers look while they are being edited",
GettingStarted: "This shadow tiddler provides basic usage instructions",
ImportTiddlers: "This shadow tiddler provides access to importing tiddlers",
MainMenu: "This shadow tiddler is used as the contents of the main menu in the left-hand column of the screen",
MarkupPreHead: "This tiddler is inserted at the top of the < head > section of the TiddlyWiki HTML file",
MarkupPostHead: "This tiddler is inserted at the bottom of the < head > section of the TiddlyWiki HTML file",
MarkupPreBody: "This tiddler is inserted at the top of the < body > section of the TiddlyWiki HTML file",
MarkupPostBody: "This tiddler is inserted at the end of the < body > section of the TiddlyWiki HTML file immediately before the script block",
OptionsPanel: "This shadow tiddler is used as the contents of the options panel slider in the right-hand sidebar",
PageTemplate: "The HTML template in this shadow tiddler determines the overall ~TiddlyWiki layout",
PluginManager: "This shadow tiddler provides access to the plugin manager",
SideBarOptions: "This shadow tiddler is used as the contents of the option panel in the right-hand sidebar",
SideBarTabs: "This shadow tiddler is used as the contents of the tabs panel in the right-hand sidebar",
SiteSubtitle: "This shadow tiddler is used as the second part of the page title",
SiteTitle: "This shadow tiddler is used as the first part of the page title",
SiteUrl: "This shadow tiddler should be set to the full target URL for publication",
StyleSheetColours: "This shadow tiddler contains CSS definitions related to the color of page elements",
StyleSheet: "This tiddler can contain custom CSS definitions",
StyleSheetLayout: "This shadow tiddler contains CSS definitions related to the layout of page elements",
StyleSheetLocale: "This shadow tiddler contains CSS definitions related to the translation locale",
StyleSheetPrint: "This shadow tiddler contains CSS definitions for printing",
TabAll: "This shadow tiddler contains the contents of the 'All' tab in the right-hand sidebar",
TabMore: "This shadow tiddler contains the contents of the 'More' tab in the right-hand sidebar",
TabMoreMissing: "This shadow tiddler contains the contents of the 'Missing' tab in the right-hand sidebar",
TabMoreOrphans: "This shadow tiddler contains the contents of the 'Orphans' tab in the right-hand sidebar",
TabMoreShadowed: "This shadow tiddler contains the contents of the 'Shadowed' tab in the right-hand sidebar",
TabTags: "This shadow tiddler contains the contents of the 'Tags' tab in the right-hand sidebar",
TabTimeline: "This shadow tiddler contains the contents of the 'Timeline' tab in the right-hand sidebar",
ViewTemplate: "The HTML template in this shadow tiddler determines how tiddlers look"
});
//--
//-- Main
//--
var params = null; // Command line parameters
var store = null; // TiddlyWiki storage
var story = null; // Main story
var formatter = null; // Default formatters for the wikifier
config.parsers = {}; // Hashmap of alternative parsers for the wikifier
var anim = new Animator(); // Animation engine
var readOnly = false; // Whether we're in readonly mode
var highlightHack = null; // Embarrassing hack department...
var hadConfirmExit = false; // Don't warn more than once
var safeMode = false; // Disable all plugins and cookies
var installedPlugins = []; // Information filled in when plugins are executed
var startingUp = false; // Whether we're in the process of starting up
var pluginInfo,tiddler; // Used to pass information to plugins in loadPlugins()
// Whether to use the JavaSaver applet
var useJavaSaver = config.browser.isSafari || config.browser.isOpera;
// Starting up
function main()
{
var t9,t8,t7,t6,t5,t4,t3,t2,t1,t0 = new Date();
startingUp = true;
window.onbeforeunload = function(e) {if(window.confirmExit) return confirmExit();};
params = getParameters();
if(params)
params = params.parseParams("open",null,false);
store = new TiddlyWiki();
invokeParamifier(params,"oninit");
story = new Story("tiddlerDisplay","tiddler");
addEvent(document,"click",Popup.onDocumentClick);
saveTest();
loadOptionsCookie();
for(var s=0; s< config.notifyTiddlers.length ; s + + )
store.addNotification(config.notifyTiddlers[s].name,config.notifyTiddlers[s].notify);
t1 = new Date();
store.loadFromDiv("storeArea","store",true);
t2 = new Date();
loadShadowTiddlers();
t3 = new Date();
invokeParamifier(params,"onload");
t4 = new Date();
readOnly = (window.location.protocol == "file:") ? false : config.options.chkHttpReadOnly;
var pluginProblem = loadPlugins();
t5 = new Date();
formatter = new Formatter(config.formatters);
invokeParamifier(params,"onconfig");
t6 = new Date();
store.notifyAll();
t7 = new Date();
restart();
t8 = new Date();
if(pluginProblem) {
story.displayTiddler(null,"PluginManager");
displayMessage(config.messages.customConfigError);
}
for(var m in config.macros) {
if(config.macros[m].init)
config.macros[m].init();
}
if(!readOnly)
backstage.init();
t9 = new Date();
if(config.options.chkDisplayStartupTime) {
displayMessage("Load in " + (t2-t1) + " ms");
displayMessage("Loadshadows in " + (t3-t2) + " ms");
displayMessage("Loadplugins in " + (t5-t4) + " ms");
displayMessage("Notify in " + (t7-t6) + " ms");
displayMessage("Restart in " + (t8-t7) + " ms");
displayMessage("Total startup in " + (t9-t0) + " ms");
}
startingUp = false;
}
// Restarting
function restart()
{
invokeParamifier(params,"onstart");
if(story.isEmpty()) {
var defaultParams = store.getTiddlerText("DefaultTiddlers").parseParams("open",null,false);
invokeParamifier(defaultParams,"onstart");
}
window.scrollTo(0,0);
}
function saveTest()
{
var s = document.getElementById("saveTest");
if(s.hasChildNodes())
alert(config.messages.savedSnapshotError);
s.appendChild(document.createTextNode("savetest"));
}
function loadShadowTiddlers()
{
var shadows = new TiddlyWiki();
shadows.loadFromDiv("shadowArea","shadows",true);
shadows.forEachTiddler(function(title,tiddler){config.shadowTiddlers[title] = tiddler.text;});
delete shadows;
}
function loadPlugins()
{
if(safeMode)
return false;
var tiddlers = store.getTaggedTiddlers("systemConfig");
var toLoad = [];
var nLoaded = 0;
var map = {};
var nPlugins = tiddlers.length;
installedPlugins = [];
for(var i=0; i< nPlugins ; i + + ) {
var p = getPluginInfo(tiddlers[i]);
installedPlugins[i] = p;
var n = p.Name;
if(n)
map[n] = p;
if(n = p.Source)
map[n] = p;
}
var visit = function(p) {
if(!p || p.done)
return;
p.done = 1;
var reqs = p.Requires;
if(reqs) {
reqs = reqs.readBracketedList();
for(var i=0; i< reqs.length ; i + + )
visit(map[reqs[i]]);
}
toLoad.push(p);
};
for(i=0; i< nPlugins ; i + + )
visit(installedPlugins[i]);
for(i=0; i< toLoad.length ; i + + ) {
p = toLoad[i];
pluginInfo = p;
tiddler = p.tiddler;
if(isPluginExecutable(p)) {
if(isPluginEnabled(p)) {
p.executed = true;
var startTime = new Date();
try {
if(tiddler.text)
window.eval(tiddler.text);
nLoaded++;
} catch(ex) {
p.log.push(config.messages.pluginError.format([exceptionText(ex)]));
p.error = true;
}
pluginInfo.startupTime = String((new Date()) - startTime) + "ms";
} else {
nPlugins--;
}
} else {
p.warning = true;
}
}
return nLoaded != nPlugins;
}
function getPluginInfo(tiddler)
{
var p = store.getTiddlerSlices(tiddler.title,["Name","Description","Version","Requires","CoreVersion","Date","Source","Author","License","Browsers"]);
p.tiddler = tiddler;
p.title = tiddler.title;
p.log = [];
return p;
}
// Check that a particular plugin is valid for execution
function isPluginExecutable(plugin)
{
if(plugin.tiddler.isTagged("systemConfigForce"))
return verifyTail(plugin,true,config.messages.pluginForced);
if(plugin["CoreVersion"]) {
var coreVersion = plugin["CoreVersion"].split(".");
var w = parseInt(coreVersion[0]) - version.major;
if(w == 0 & & coreVersion[1])
w = parseInt(coreVersion[1]) - version.minor;
if(w == 0 & & coreVersion[2])
w = parseInt(coreVersion[2]) - version.revision;
if(w > 0)
return verifyTail(plugin,false,config.messages.pluginVersionError);
}
return true;
}
function isPluginEnabled(plugin)
{
if(plugin.tiddler.isTagged("systemConfigDisable"))
return verifyTail(plugin,false,config.messages.pluginDisabled);
return true;
}
function verifyTail(plugin,result,message)
{
plugin.log.push(message);
return result;
}
function invokeMacro(place,macro,params,wikifier,tiddler)
{
try {
var m = config.macros[macro];
if(m & & m.handler)
m.handler(place,macro,params.readMacroParams(),wikifier,params,tiddler);
else
createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,config.messages.missingMacro]));
} catch(ex) {
createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,ex.toString()]));
}
}
//--
//-- Paramifiers
//--
function getParameters()
{
var p = null;
if(window.location.hash) {
p = decodeURI(window.location.hash.substr(1));
if(config.browser.firefoxDate != null & & config.browser.firefoxDate[1] < "20051111")
p = convertUTF8ToUnicode(p);
}
return p;
}
function invokeParamifier(params,handler)
{
if(!params || params.length == undefined || params.length < = 1)
return;
for(var t=1; t< params.length ; t + + ) {
var p = config.paramifiers[params[t].name];
if(p & & p[handler] instanceof Function)
p[handler](params[t].value);
}
}
config.paramifiers = {};
config.paramifiers.start = {
oninit: function(v) {
safeMode = v.toLowerCase() == "safe";
}
};
config.paramifiers.open = {
onstart: function(v) {
story.displayTiddler("bottom",v,null,false,null);
}
};
config.paramifiers.story = {
onstart: function(v) {
var list = store.getTiddlerText(v,"").parseParams("open",null,false);
invokeParamifier(list,"onstart");
}
};
config.paramifiers.search = {
onstart: function(v) {
story.search(v,false,false);
}
};
config.paramifiers.searchRegExp = {
onstart: function(v) {
story.prototype.search(v,false,true);
}
};
config.paramifiers.tag = {
onstart: function(v) {
var tagged = store.getTaggedTiddlers(v,"title");
for(var t=0; t< tagged.length ; t + + )
story.displayTiddler("bottom",tagged[t].title,null,false,null);
}
};
config.paramifiers.newTiddler = {
onstart: function(v) {
if(!readOnly) {
story.displayTiddler(null,v,DEFAULT_EDIT_TEMPLATE);
story.focusTiddler(v,"text");
}
}
};
config.paramifiers.newJournal = {
onstart: function(v) {
if(!readOnly) {
var now = new Date();
var title = now.formatString(v.trim());
story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
story.focusTiddler(title,"text");
}
}
};
config.paramifiers.readOnly = {
onconfig: function(v) {
var p = v.toLowerCase();
readOnly = p == "yes" ? true : (p == "no" ? false : readOnly);
}
};
//--
//-- Formatter helpers
//--
function Formatter(formatters)
{
this.formatters = [];
var pattern = [];
for(var n=0; n< formatters.length ; n + + ) {
pattern.push("(" + formatters[n].match + ")");
this.formatters.push(formatters[n]);
}
this.formatterRegExp = new RegExp(pattern.join("|"),"mg");
}
config.formatterHelpers = {
createElementAndWikify: function(w)
{
w.subWikifyTerm(createTiddlyElement(w.output,this.element),this.termRegExp);
},
inlineCssHelper: function(w)
{
var styles = [];
config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
var lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
while(lookaheadMatch & & lookaheadMatch.index == w.nextMatch) {
var s,v;
if(lookaheadMatch[1]) {
s = lookaheadMatch[1].unDash();
v = lookaheadMatch[2];
} else {
s = lookaheadMatch[3].unDash();
v = lookaheadMatch[4];
}
if (s=="bgcolor")
s = "backgroundColor";
styles.push({style: s, value: v});
w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
}
return styles;
},
applyCssHelper: function(e,styles)
{
for(var t=0; t< style s . length ; t + + ) {
try {
e.style[styles[t].style] = styles[t].value;
} catch (ex) {
}
}
},
enclosedTextHelper: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch & & lookaheadMatch.index == w.matchStart) {
var text = lookaheadMatch[1];
if(config.browser.isIE)
text = text.replace(/\n/g,"\r");
createTiddlyElement(w.output,this.element,null,null,text);
w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
}
},
isExternalLink: function(link)
{
if(store.tiddlerExists(link) || store.isShadowTiddler(link)) {
return false;
}
var urlRegExp = new RegExp(config.textPrimitives.urlPattern,"mg");
if(urlRegExp.exec(link)) {
return true;
}
if (link.indexOf(".")!=-1 || link.indexOf("\\")!=-1 || link.indexOf("/")!=-1){
return true;
}
return false;
}
};
//--
//-- Standard formatters
//--
config.formatters = [
{
name: "table",
match: "^\\|(?:[^\\n]*)\\|(?:[fhck]?)$",
lookaheadRegExp: /^\|([^\n]*)\|([fhck]?)$/mg,
rowTermRegExp: /(\|(?:[fhck]?)$\n?)/mg,
cellRegExp: /(?:\|([^\n\|]*)\|)|(\|[fhck]?$\n?)/mg,
cellTermRegExp: /((?:\x20*)\|)/mg,
rowTypes: {"c":"caption", "h":"thead", "":"tbody", "f":"tfoot"},
handler: function(w)
{
var table = createTiddlyElement(w.output,"table",null,"twtable");
var prevColumns = [];
var currRowType = null;
var rowContainer;
var rowCount = 0;
w.nextMatch = w.matchStart;
this.lookaheadRegExp.lastIndex = w.nextMatch;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
while(lookaheadMatch & & lookaheadMatch.index == w.nextMatch) {
var nextRowType = lookaheadMatch[2];
if(nextRowType == "k") {
table.className = lookaheadMatch[1];
w.nextMatch += lookaheadMatch[0].length+1;
} else {
if(nextRowType != currRowType) {
rowContainer = createTiddlyElement(table,this.rowTypes[nextRowType]);
currRowType = nextRowType;
}
if(currRowType == "c") {
// Caption
w.nextMatch++;
if(rowContainer != table.firstChild)
table.insertBefore(rowContainer,table.firstChild);
rowContainer.setAttribute("align",rowCount == 0?"top":"bottom");
w.subWikifyTerm(rowContainer,this.rowTermRegExp);
} else {
this.rowHandler(w,createTiddlyElement(rowContainer,"tr",null,(rowCount&1)?"oddRow":"evenRow"),prevColumns);
rowCount++;
}
}
this.lookaheadRegExp.lastIndex = w.nextMatch;
lookaheadMatch = this.lookaheadRegExp.exec(w.source);
}
},
rowHandler: function(w,e,prevColumns)
{
var col = 0;
var colSpanCount = 1;
var prevCell = null;
this.cellRegExp.lastIndex = w.nextMatch;
var cellMatch = this.cellRegExp.exec(w.source);
while(cellMatch & & cellMatch.index == w.nextMatch) {
if(cellMatch[1] == "~") {
// Rowspan
var last = prevColumns[col];
if(last) {
last.rowSpanCount++;
last.element.setAttribute("rowspan",last.rowSpanCount);
last.element.setAttribute("rowSpan",last.rowSpanCount); // Needed for IE
last.element.valign = "center";
}
w.nextMatch = this.cellRegExp.lastIndex-1;
} else if(cellMatch[1] == ">") {
// Colspan
colSpanCount++;
w.nextMatch = this.cellRegExp.lastIndex-1;
} else if(cellMatch[2]) {
// End of row
if(prevCell & & colSpanCount > 1) {
prevCell.setAttribute("colspan",colSpanCount);
prevCell.setAttribute("colSpan",colSpanCount); // Needed for IE
}
w.nextMatch = this.cellRegExp.lastIndex;
break;
} else {
// Cell
w.nextMatch++;
var styles = config.formatterHelpers.inlineCssHelper(w);
var spaceLeft = false;
var chr = w.source.substr(w.nextMatch,1);
while(chr == " ") {
spaceLeft = true;
w.nextMatch++;
chr = w.source.substr(w.nextMatch,1);
}
var cell;
if(chr == "!") {
cell = createTiddlyElement(e,"th");
w.nextMatch++;
} else {
cell = createTiddlyElement(e,"td");
}
prevCell = cell;
prevColumns[col] = {rowSpanCount:1,element:cell};
if(colSpanCount > 1) {
cell.setAttribute("colspan",colSpanCount);
cell.setAttribute("colSpan",colSpanCount); // Needed for IE
colSpanCount = 1;
}
config.formatterHelpers.applyCssHelper(cell,styles);
w.subWikifyTerm(cell,this.cellTermRegExp);
if(w.matchText.substr(w.matchText.length-2,1) == " ") // spaceRight
cell.align = spaceLeft ? "center" : "left";
else if(spaceLeft)
cell.align = "right";
w.nextMatch--;
}
col++;
this.cellRegExp.lastIndex = w.nextMatch;
cellMatch = this.cellRegExp.exec(w.source);
}
}
},
{
name: "heading",
match: "^!{1,6}",
termRegExp: /(\n)/mg,
handler: function(w)
{
w.subWikifyTerm(createTiddlyElement(w.output,"h" + w.matchLength),this.termRegExp);
}
},
{
name: "list",
match: "^(?:[\\*#;:]+)",
lookaheadRegExp: /^(?:(?:(\*)|(#)|(;)|(:))+)/mg,
termRegExp: /(\n)/mg,
handler: function(w)
{
var stack = [w.output];
var currLevel = 0, currType = null;
var listLevel, listType, itemType;
w.nextMatch = w.matchStart;
this.lookaheadRegExp.lastIndex = w.nextMatch;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
while(lookaheadMatch & & lookaheadMatch.index == w.nextMatch) {
if(lookaheadMatch[1]) {
listType = "ul";
itemType = "li";
} else if(lookaheadMatch[2]) {
listType = "ol";
itemType = "li";
} else if(lookaheadMatch[3]) {
listType = "dl";
itemType = "dt";
} else if(lookaheadMatch[4]) {
listType = "dl";
itemType = "dd";
}
listLevel = lookaheadMatch[0].length;
w.nextMatch += lookaheadMatch[0].length;
var t;
if(listLevel > currLevel) {
for(t=currLevel; t< listLevel ; t + + ) {
var target = (currLevel == 0) ? stack[stack.length-1] : stack[stack.length-1].lastChild;
stack.push(createTiddlyElement(target,listType));
}
} else if(listLevel < currLevel ) {
for(t=currLevel; t>listLevel; t--)
stack.pop();
} else if(listLevel == currLevel & & listType != currType) {
stack.pop();
stack.push(createTiddlyElement(stack[stack.length-1].lastChild,listType));
}
currLevel = listLevel;
currType = listType;
var e = createTiddlyElement(stack[stack.length-1],itemType);
w.subWikifyTerm(e,this.termRegExp);
this.lookaheadRegExp.lastIndex = w.nextMatch;
lookaheadMatch = this.lookaheadRegExp.exec(w.source);
}
}
},
{
name: "quoteByBlock",
match: "^< < < \\n",
termRegExp: /(^< < < (\n|$))/mg,
element: "blockquote",
handler: config.formatterHelpers.createElementAndWikify
},
{
name: "quoteByLine",
match: "^>+",
lookaheadRegExp: /^>+/mg,
termRegExp: /(\n)/mg,
element: "blockquote",
handler: function(w)
{
var stack = [w.output];
var currLevel = 0;
var newLevel = w.matchLength;
var t;
do {
if(newLevel > currLevel) {
for(t=currLevel; t< newLevel ; t + + )
stack.push(createTiddlyElement(stack[stack.length-1],this.element));
} else if(newLevel < currLevel ) {
for(t=currLevel; t>newLevel; t--)
stack.pop();
}
currLevel = newLevel;
w.subWikifyTerm(stack[stack.length-1],this.termRegExp);
createTiddlyElement(stack[stack.length-1],"br");
this.lookaheadRegExp.lastIndex = w.nextMatch;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
var matched = lookaheadMatch & & lookaheadMatch.index == w.nextMatch;
if(matched) {
newLevel = lookaheadMatch[0].length;
w.nextMatch += lookaheadMatch[0].length;
}
} while(matched);
}
},
{
name: "rule",
match: "^----+$\\n?",
handler: function(w)
{
createTiddlyElement(w.output,"hr");
}
},
{
name: "monospacedByLine",
match: "^\\{\\{\\{\\n",
lookaheadRegExp: /^\{\{\{\n((?:^[^\n]*\n)+?)(^\}\}\}$\n?)/mg,
element: "pre",
handler: config.formatterHelpers.enclosedTextHelper
},
{
name: "monospacedByLineForCSS",
match: "^/\\*\\{\\{\\{\\*/\\n",
lookaheadRegExp: /\/\*\{\{\{\*\/\n*((?:^[^\n]*\n)+?)(\n*^\/\*\}\}\}\*\/$\n?)/mg,
element: "pre",
handler: config.formatterHelpers.enclosedTextHelper
},
{
name: "monospacedByLineForPlugin",
match: "^//\\{\\{\\{\\n",
lookaheadRegExp: /^\/\/\{\{\{\n\n*((?:^[^\n]*\n)+?)(\n*^\/\/\}\}\}$\n?)/mg,
element: "pre",
handler: config.formatterHelpers.enclosedTextHelper
},
{
name: "monospacedByLineForTemplate",
match: "^<!-- \\{\\{\\{ --> \\n",
lookaheadRegExp: /<!-- \{\{\{ --> \n*((?:^[^\n]*\n)+?)(\n*^<!-- \}\}\} --> $\n?)/mg,
element: "pre",
handler: config.formatterHelpers.enclosedTextHelper
},
{
name: "wikifyCommentForPlugin",
match: "^/\\*\\*\\*\\n",
termRegExp: /(^\*\*\*\/\n)/mg,
handler: function(w)
{
w.subWikifyTerm(w.output,this.termRegExp);
}
},
{
name: "wikifyCommentForTemplate",
match: "^<!-- - \\n",
termRegExp: /(^--->\n)/mg,
handler: function(w)
{
w.subWikifyTerm(w.output,this.termRegExp);
}
},
{
name: "macro",
match: "< < ",
lookaheadRegExp: /< < ([^>\s]+)(?:\s*)((?:[^>]|(?:>(?!>)))*)>>/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch & & lookaheadMatch.index == w.matchStart & & lookaheadMatch[1]) {
w.nextMatch = this.lookaheadRegExp.lastIndex;
invokeMacro(w.output,lookaheadMatch[1],lookaheadMatch[2],w,w.tiddler);
}
}
},
{
name: "prettyLink",
match: "\\[\\[",
lookaheadRegExp: /\[\[(.*?)(?:\|(~)?(.*?))?\]\]/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch & & lookaheadMatch.index == w.matchStart) {
var e;
var text = lookaheadMatch[1];
if(lookaheadMatch[3]) {
// Pretty bracketted link
var link = lookaheadMatch[3];
e = (!lookaheadMatch[2] & & config.formatterHelpers.isExternalLink(link)) ?
createExternalLink(w.output,link) : createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
} else {
// Simple bracketted link
e = createTiddlyLink(w.output,text,false,null,w.isStatic,w.tiddler);
}
createTiddlyText(e,text);
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: "unWikiLink",
match: config.textPrimitives.unWikiLink+config.textPrimitives.wikiLink,
handler: function(w)
{
w.outputText(w.output,w.matchStart+1,w.nextMatch);
}
},
{
name: "wikiLink",
match: config.textPrimitives.wikiLink,
handler: function(w)
{
if(w.matchStart > 0) {
var preRegExp = new RegExp(config.textPrimitives.anyLetterStrict,"mg");
preRegExp.lastIndex = w.matchStart-1;
var preMatch = preRegExp.exec(w.source);
if(preMatch.index == w.matchStart-1) {
w.outputText(w.output,w.matchStart,w.nextMatch);
return;
}
}
if(w.autoLinkWikiWords == true || store.isShadowTiddler(w.matchText)) {
var link = createTiddlyLink(w.output,w.matchText,false,null,w.isStatic,w.tiddler);
w.outputText(link,w.matchStart,w.nextMatch);
} else {
w.outputText(w.output,w.matchStart,w.nextMatch);
}
}
},
{
name: "urlLink",
match: config.textPrimitives.urlPattern,
handler: function(w)
{
w.outputText(createExternalLink(w.output,w.matchText),w.matchStart,w.nextMatch);
}
},
{
name: "image",
match: "\\[[< >]?[Ii][Mm][Gg]\\[",
lookaheadRegExp: /\[([< ]?)(>?)[Ii][Mm][Gg]\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch & & lookaheadMatch.index == w.matchStart) {
var e = w.output;
if(lookaheadMatch[5]) {
var link = lookaheadMatch[5];
e = config.formatterHelpers.isExternalLink(link) ? createExternalLink(w.output,link) : createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
addClass(e,"imageLink");
}
var img = createTiddlyElement(e,"img");
if(lookaheadMatch[1])
img.align = "left";
else if(lookaheadMatch[2])
img.align = "right";
if(lookaheadMatch[3])
img.title = lookaheadMatch[3];
img.src = lookaheadMatch[4];
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: "html",
match: "< [Hh][Tt][Mm][Ll]>",
lookaheadRegExp: /< [Hh][Tt][Mm][Ll]>((?:.|\n)*?)< \/[Hh][Tt][Mm][Ll]>/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch & & lookaheadMatch.index == w.matchStart) {
createTiddlyElement(w.output,"span").innerHTML = lookaheadMatch[1];
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: "commentByBlock",
match: "/%",
lookaheadRegExp: /\/%((?:.|\n)*?)%\//mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch & & lookaheadMatch.index == w.matchStart)
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
},
{
name: "boldByChar",
match: "''",
termRegExp: /('')/mg,
element: "strong",
handler: config.formatterHelpers.createElementAndWikify
},
{
name: "italicByChar",
match: "//",
termRegExp: /(\/\/)/mg,
element: "em",
handler: config.formatterHelpers.createElementAndWikify
},
{
name: "underlineByChar",
match: "__",
termRegExp: /(__)/mg,
element: "u",
handler: config.formatterHelpers.createElementAndWikify
},
{
name: "strikeByChar",
match: "--(?!\\s|$)",
termRegExp: /((?!\s)--|(?=\n\n))/mg,
element: "strike",
handler: config.formatterHelpers.createElementAndWikify
},
{
name: "superscriptByChar",
match: "\\^\\^",
termRegExp: /(\^\^)/mg,
element: "sup",
handler: config.formatterHelpers.createElementAndWikify
},
{
name: "subscriptByChar",
match: "~~",
termRegExp: /(~~)/mg,
element: "sub",
handler: config.formatterHelpers.createElementAndWikify
},
{
name: "monospacedByChar",
match: "\\{\\{\\{",
lookaheadRegExp: /\{\{\{((?:.|\n)*?)\}\}\}/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch & & lookaheadMatch.index == w.matchStart) {
createTiddlyElement(w.output,"code",null,null,lookaheadMatch[1]);
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: "styleByChar",
match: "@@",
termRegExp: /(@@)/mg,
handler: function(w)
{
var e = createTiddlyElement(w.output,"span");
var styles = config.formatterHelpers.inlineCssHelper(w);
if(styles.length == 0)
e.className = "marked";
else
config.formatterHelpers.applyCssHelper(e,styles);
w.subWikifyTerm(e,this.termRegExp);
}
},
{
name: "lineBreak",
match: "\\n|< br ? / ? > ",
handler: function(w)
{
createTiddlyElement(w.output,"br");
}
},
{
name: "rawText",
match: "\\\"{3}|< nowiki > ",
lookaheadRegExp: /(?:\"{3}|< nowiki > )((?:.|\n)*?)(?:\"{3}|< \/nowiki>)/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch & & lookaheadMatch.index == w.matchStart) {
createTiddlyElement(w.output,"span",null,null,lookaheadMatch[1]);
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: "mdash",
match: "--",
handler: function(w)
{
createTiddlyElement(w.output,"span").innerHTML = "— ";
}
},
{
name: "htmlEntitiesEncoding",
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}; )",
handler: function(w)
{
createTiddlyElement(w.output,"span").innerHTML = w.matchText;
}
},
{
name: "customClasses",
match: "\\{\\{",
termRegExp: /(\}\}\})/mg,
lookaheadRegExp: /\{\{[\s]*([\w]+[\s\w]*)[\s]*\{(\n?)/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch) {
var e = createTiddlyElement(w.output,lookaheadMatch[2] == "\n" ? "div" : "span",null,lookaheadMatch[1]);
w.nextMatch = this.lookaheadRegExp.lastIndex;
w.subWikifyTerm(e,this.termRegExp);
}
}
}
];
//--
//-- Wikifier
//--
function getParser(tiddler,format)
{
if(tiddler) {
if(!format)
format = tiddler.fields["wikiformat"];
if(format) {
for(var i in config.parsers) {
if(format == config.parsers[i].format)
return config.parsers[i];
}
} else {
for(var i in config.parsers) {
if(tiddler.isTagged(config.parsers[i].formatTag))
return config.parsers[i];
}
}
}
return formatter;
}
function wikify(source,output,highlightRegExp,tiddler)
{
if(source & & source != "") {
var wikifier = new Wikifier(source,getParser(tiddler),highlightRegExp,tiddler);
wikifier.subWikifyUnterm(output);
}
}
function wikifyStatic(source,highlightRegExp,tiddler,format)
{
var e = createTiddlyElement(document.body,"div");
e.style.display = "none";
var html = "";
if(source & & source != "") {
var wikifier = new Wikifier(source,getParser(tiddler,format),highlightRegExp,tiddler);
wikifier.isStatic = true;
wikifier.subWikifyUnterm(e);
html = e.innerHTML;
removeNode(e);
}
return html;
}
function wikifyPlain(title,theStore,limit)
{
if(!theStore)
theStore = store;
if(theStore.tiddlerExists(title) || theStore.isShadowTiddler(title)) {
return wikifyPlainText(theStore.getTiddlerText(title),limit,tiddler);
} else {
return "";
}
}
function wikifyPlainText(text,limit,tiddler)
{
if(limit > 0)
text = text.substr(0,limit);
var wikifier = new Wikifier(text,formatter,null,tiddler);
return wikifier.wikifyPlain();
}
function highlightify(source,output,highlightRegExp,tiddler)
{
if(source & & source != "") {
var wikifier = new Wikifier(source,formatter,highlightRegExp,tiddler);
wikifier.outputText(output,0,source.length);
}
}
function Wikifier(source,formatter,highlightRegExp,tiddler)
{
this.source = source;
this.output = null;
this.formatter = formatter;
this.nextMatch = 0;
this.autoLinkWikiWords = tiddler & & tiddler.autoLinkWikiWords() == false ? false : true;
this.highlightRegExp = highlightRegExp;
this.highlightMatch = null;
this.isStatic = false;
if(highlightRegExp) {
highlightRegExp.lastIndex = 0;
this.highlightMatch = highlightRegExp.exec(source);
}
this.tiddler = tiddler;
}
Wikifier.prototype.wikifyPlain = function()
{
var e = createTiddlyElement(document.body,"div");
this.subWikify(e);
var text = getPlainText(e);
removeNode(e);
return text;
};
Wikifier.prototype.subWikify = function(output,terminator)
{
if(terminator)
this.subWikifyTerm(output,new RegExp("(" + terminator + ")","mg"));
else
this.subWikifyUnterm(output);
};
Wikifier.prototype.subWikifyUnterm = function(output)
{
// subWikify() can be indirectly recursive, so we need to save the old output pointer
var oldOutput = this.output;
this.output = output;
this.formatter.formatterRegExp.lastIndex = this.nextMatch;
var formatterMatch = this.formatter.formatterRegExp.exec(this.source);
while(formatterMatch) {
// Output any text before the match
if(formatterMatch.index > this.nextMatch)
this.outputText(this.output,this.nextMatch,formatterMatch.index);
// Set the match parameters for the handler
this.matchStart = formatterMatch.index;
this.matchLength = formatterMatch[0].length;
this.matchText = formatterMatch[0];
this.nextMatch = this.formatter.formatterRegExp.lastIndex;
for(var t=1; t< formatterMatch.length ; t + + ) {
if(formatterMatch[t]) {
this.formatter.formatters[t-1].handler(this);
this.formatter.formatterRegExp.lastIndex = this.nextMatch;
break;
}
}
formatterMatch = this.formatter.formatterRegExp.exec(this.source);
}
if(this.nextMatch < this.source.length ) {
this.outputText(this.output,this.nextMatch,this.source.length);
this.nextMatch = this.source.length;
}
this.output = oldOutput;
};
Wikifier.prototype.subWikifyTerm = function(output,terminatorRegExp)
{
// subWikify() can be indirectly recursive, so we need to save the old output pointer
var oldOutput = this.output;
this.output = output;
// Get the first matches for the formatter and terminator RegExps
terminatorRegExp.lastIndex = this.nextMatch;
var terminatorMatch = terminatorRegExp.exec(this.source);
this.formatter.formatterRegExp.lastIndex = this.nextMatch;
var formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
while(terminatorMatch || formatterMatch) {
if(terminatorMatch & & (!formatterMatch || terminatorMatch.index < = formatterMatch.index)) {
if(terminatorMatch.index > this.nextMatch)
this.outputText(this.output,this.nextMatch,terminatorMatch.index);
this.matchText = terminatorMatch[1];
this.matchLength = terminatorMatch[1].length;
this.matchStart = terminatorMatch.index;
this.nextMatch = this.matchStart + this.matchLength;
this.output = oldOutput;
return;
}
if(formatterMatch.index > this.nextMatch)
this.outputText(this.output,this.nextMatch,formatterMatch.index);
this.matchStart = formatterMatch.index;
this.matchLength = formatterMatch[0].length;
this.matchText = formatterMatch[0];
this.nextMatch = this.formatter.formatterRegExp.lastIndex;
for(var t=1; t< formatterMatch.length ; t + + ) {
if(formatterMatch[t]) {
this.formatter.formatters[t-1].handler(this);
this.formatter.formatterRegExp.lastIndex = this.nextMatch;
break;
}
}
terminatorRegExp.lastIndex = this.nextMatch;
terminatorMatch = terminatorRegExp.exec(this.source);
formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
}
if(this.nextMatch < this.source.length ) {
this.outputText(this.output,this.nextMatch,this.source.length);
this.nextMatch = this.source.length;
}
this.output = oldOutput;
};
Wikifier.prototype.outputText = function(place,startPos,endPos)
{
while(this.highlightMatch & & (this.highlightRegExp.lastIndex > startPos) & & (this.highlightMatch.index < endPos ) & & ( startPos < endPos ) ) {
if(this.highlightMatch.index > startPos) {
createTiddlyText(place,this.source.substring(startPos,this.highlightMatch.index));
startPos = this.highlightMatch.index;
}
var highlightEnd = Math.min(this.highlightRegExp.lastIndex,endPos);
var theHighlight = createTiddlyElement(place,"span",null,"highlight",this.source.substring(startPos,highlightEnd));
startPos = highlightEnd;
if(startPos >= this.highlightRegExp.lastIndex)
this.highlightMatch = this.highlightRegExp.exec(this.source);
}
if(startPos < endPos ) {
createTiddlyText(place,this.source.substring(startPos,endPos));
}
};
//--
//-- Macro definitions
//--
config.macros.today.handler = function(place,macroName,params)
{
var now = new Date();
var text;
if(params[0])
text = now.formatString(params[0].trim());
else
text = now.toLocaleString();
createTiddlyElement(place,"span",null,null,text);
};
config.macros.version.handler = function(place)
{
createTiddlyElement(place,"span",null,null,version.major + "." + version.minor + "." + version.revision + (version.beta ? " (beta " + version.beta + ")" : ""));
};
config.macros.list.handler = function(place,macroName,params)
{
var type = params[0] ? params[0] : "all";
var list = document.createElement("ul");
place.appendChild(list);
if(this[type].prompt)
createTiddlyElement(list,"li",null,"listTitle",this[type].prompt);
var results;
if(this[type].handler)
results = this[type].handler(params);
for(var t = 0; t < results.length ; t + + ) {
var li = document.createElement("li");
list.appendChild(li);
createTiddlyLink(li,typeof results[t] == "string" ? results[t] : results[t].title,true);
}
};
config.macros.list.all.handler = function(params)
{
return store.reverseLookup("tags","excludeLists",false,"title");
};
config.macros.list.missing.handler = function(params)
{
return store.getMissingLinks();
};
config.macros.list.orphans.handler = function(params)
{
return store.getOrphans();
};
config.macros.list.shadowed.handler = function(params)
{
return store.getShadowed();
};
config.macros.list.touched.handler = function(params)
{
return store.getTouched();
};
config.macros.allTags.handler = function(place,macroName,params)
{
var tags = store.getTags(params[0]);
var ul = createTiddlyElement(place,"ul");
if(tags.length == 0)
createTiddlyElement(ul,"li",null,"listTitle",this.noTags);
for(var t=0; t< tags.length ; t + + ) {
var title = tags[t][0];
var info = getTiddlyLinkInfo(title);
var li =createTiddlyElement(ul,"li");
var btn = createTiddlyButton(li,title + " (" + tags[t][1] + ")",this.tooltip.format([title]),onClickTag,info.classes);
btn.setAttribute("tag",title);
btn.setAttribute("refresh","link");
btn.setAttribute("tiddlyLink",title);
}
};
config.macros.timeline.handler = function(place,macroName,params)
{
var field = params[0] ? params[0] : "modified";
var tiddlers = store.reverseLookup("tags","excludeLists",false,field);
var lastDay = "";
var last = params[1] ? tiddlers.length-Math.min(tiddlers.length,parseInt(params[1])) : 0;
for(var t=tiddlers.length-1; t>=last; t--) {
var tiddler = tiddlers[t];
var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
if(theDay != lastDay) {
var theDateList = document.createElement("ul");
place.appendChild(theDateList);
createTiddlyElement(theDateList,"li",null,"listTitle",tiddler[field].formatString(this.dateFormat));
lastDay = theDay;
}
var theDateListItem = createTiddlyElement(theDateList,"li",null,"listLink");
theDateListItem.appendChild(createTiddlyLink(place,tiddler.title,true));
}
};
config.macros.search.handler = function(place,macroName,params)
{
var searchTimeout = null;
var btn = createTiddlyButton(place,this.label,this.prompt,this.onClick);
var txt = createTiddlyElement(place,"input",null,"txtOptionInput");
if(params[0])
txt.value = params[0];
txt.onkeyup = this.onKeyPress;
txt.onfocus = this.onFocus;
txt.setAttribute("size",this.sizeTextbox);
txt.setAttribute("accessKey",this.accessKey);
txt.setAttribute("autocomplete","off");
txt.setAttribute("lastSearchText","");
if(config.browser.isSafari) {
txt.setAttribute("type","search");
txt.setAttribute("results","5");
} else {
txt.setAttribute("type","text");
}
};
// Global because there's only ever one outstanding incremental search timer
config.macros.search.timeout = null;
config.macros.search.doSearch = function(txt)
{
if(txt.value.length > 0) {
story.search(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch);
txt.setAttribute("lastSearchText",txt.value);
}
};
config.macros.search.onClick = function(e)
{
config.macros.search.doSearch(this.nextSibling);
return false;
};
config.macros.search.onKeyPress = function(e)
{
if(!e) var e = window.event;
switch(e.keyCode) {
case 13: // Ctrl-Enter
case 10: // Ctrl-Enter on IE PC
config.macros.search.doSearch(this);
break;
case 27: // Escape
this.value = "";
clearMessage();
break;
}
if(this.value.length > 2) {
if(this.value != this.getAttribute("lastSearchText")) {
if(config.macros.search.timeout)
clearTimeout(config.macros.search.timeout);
var txt = this;
config.macros.search.timeout = setTimeout(function() {config.macros.search.doSearch(txt);},500);
}
} else {
if(config.macros.search.timeout)
clearTimeout(config.macros.search.timeout);
}
};
config.macros.search.onFocus = function(e)
{
this.select();
};
config.macros.tiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
params = paramString.parseParams("name",null,true,false,true);
var names = params[0]["name"];
var tiddlerName = names[0];
var className = names[1] ? names[1] : null;
var args = params[0]["with"];
var wrapper = createTiddlyElement(place,"span",null,className);
if(!args) {
wrapper.setAttribute("refresh","content");
wrapper.setAttribute("tiddler",tiddlerName);
}
var text = store.getTiddlerText(tiddlerName);
if(text) {
var stack = config.macros.tiddler.tiddlerStack;
if(stack.indexOf(tiddlerName) !== -1)
return;
stack.push(tiddlerName);
try {
var n = args ? Math.min(args.length,9) : 0;
for(var i=0; i< n ; i + + ) {
var placeholderRE = new RegExp("\\$" + (i + 1),"mg");
text = text.replace(placeholderRE,args[i]);
}
config.macros.tiddler.renderText(wrapper,text,tiddlerName,params);
} finally {
stack.pop();
}
}
};
config.macros.tiddler.renderText = function(place,text,tiddlerName,params)
{
wikify(text,place,null,store.getTiddler(tiddlerName));
};
config.macros.tiddler.tiddlerStack = [];
config.macros.tag.handler = function(place,macroName,params)
{
createTagButton(place,params[0]);
};
config.macros.tags.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
params = paramString.parseParams("anon",null,true,false,false);
var theList = createTiddlyElement(place,"ul");
var title = getParam(params,"anon","");
if(title & & store.tiddlerExists(title))
tiddler = store.getTiddler(title);
var sep = getParam(params,"sep"," ");
var lingo = config.views.wikified.tag;
var prompt = tiddler.tags.length == 0 ? lingo.labelNoTags : lingo.labelTags;
createTiddlyElement(theList,"li",null,"listTitle",prompt.format([tiddler.title]));
for(var t=0; t< tiddler.tags.length ; t + + ) {
createTagButton(createTiddlyElement(theList,"li"),tiddler.tags[t],tiddler.title);
if(t< tiddler.tags.length-1 )
createTiddlyText(theList,sep);
}
};
config.macros.tagging.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
params = paramString.parseParams("anon",null,true,false,false);
var theList = createTiddlyElement(place,"ul");
var title = getParam(params,"anon","");
if(title == "" & & tiddler instanceof Tiddler)
title = tiddler.title;
var sep = getParam(params,"sep"," ");
theList.setAttribute("title",this.tooltip.format([title]));
var tagged = store.getTaggedTiddlers(title);
var prompt = tagged.length == 0 ? this.labelNotTag : this.label;
createTiddlyElement(theList,"li",null,"listTitle",prompt.format([title,tagged.length]));
for(var t=0; t< tagged.length ; t + + ) {
createTiddlyLink(createTiddlyElement(theList,"li"),tagged[t].title,true);
if(t< tagged.length-1 )
createTiddlyText(theList,sep);
}
};
config.macros.closeAll.handler = function(place)
{
createTiddlyButton(place,this.label,this.prompt,this.onClick);
};
config.macros.closeAll.onClick = function(e)
{
story.closeAllTiddlers();
return false;
};
config.macros.permaview.handler = function(place)
{
createTiddlyButton(place,this.label,this.prompt,this.onClick);
};
config.macros.permaview.onClick = function(e)
{
story.permaView();
return false;
};
config.macros.saveChanges.handler = function(place)
{
if(!readOnly)
createTiddlyButton(place,this.label,this.prompt,this.onClick,null,null,this.accessKey);
};
config.macros.saveChanges.onClick = function(e)
{
saveChanges();
return false;
};
config.macros.slider.onClickSlider = function(e)
{
if(!e) var e = window.event;
var n = this.nextSibling;
var cookie = n.getAttribute("cookie");
var isOpen = n.style.display != "none";
if(config.options.chkAnimate & & anim & & typeof Slider == "function")
anim.startAnimating(new Slider(n,!isOpen,null,"none"));
else
n.style.display = isOpen ? "none" : "block";
config.options[cookie] = !isOpen;
saveOptionCookie(cookie);
return false;
};
config.macros.slider.createSlider = function(place,cookie,title,tooltip)
{
var cookie = cookie ? cookie : "";
var btn = createTiddlyButton(place,title,tooltip,this.onClickSlider);
var panel = createTiddlyElement(null,"div",null,"sliderPanel");
panel.setAttribute("cookie",cookie);
panel.style.display = config.options[cookie] ? "block" : "none";
place.appendChild(panel);
return panel;
};
config.macros.slider.handler = function(place,macroName,params)
{
var panel = this.createSlider(place,params[0],params[2],params[3]);
var text = store.getTiddlerText(params[1]);
panel.setAttribute("refresh","content");
panel.setAttribute("tiddler",params[1]);
if(text)
wikify(text,panel,null,store.getTiddler(params[1]));
};
config.macros.option.genericCreate = function(place,type,opt,className,desc)
{
var typeInfo = config.macros.option.types[type];
var c = document.createElement(typeInfo.elementType);
if(typeInfo.typeValue)
c.setAttribute("type",typeInfo.typeValue);
c[typeInfo.eventName] = typeInfo.onChange;
c.setAttribute("option",opt);
if(className)
c.className = className;
else
c.className = typeInfo.className;
if(config.optionsDesc[opt])
c.setAttribute("title",config.optionsDesc[opt]);
place.appendChild(c);
if(desc != "no")
createTiddlyText(place,config.optionsDesc[opt] ? config.optionsDesc[opt] : opt);
c[typeInfo.valueField] = config.options[opt];
return c;
};
config.macros.option.genericOnChange = function(e)
{
var opt = this.getAttribute("option");
if(opt) {
var optType = opt.substr(0,3);
var handler = config.macros.option.types[optType];
if (handler.elementType & & handler.valueField)
config.macros.option.propagateOption(opt,handler.valueField,this[handler.valueField],handler.elementType);
}
return true;
};
config.macros.option.types = {
'txt': {
elementType: "input",
valueField: "value",
eventName: "onkeyup",
className: "txtOptionInput",
create: config.macros.option.genericCreate,
onChange: config.macros.option.genericOnChange
},
'chk': {
elementType: "input",
valueField: "checked",
eventName: "onclick",
className: "chkOptionInput",
typeValue: "checkbox",
create: config.macros.option.genericCreate,
onChange: config.macros.option.genericOnChange
}
};
config.macros.option.propagateOption = function(opt,valueField,value,elementType)
{
config.options[opt] = value;
saveOptionCookie(opt);
var nodes = document.getElementsByTagName(elementType);
for(var t=0; t< nodes.length ; t + + ) {
var optNode = nodes[t].getAttribute("option");
if(opt == optNode)
nodes[t][valueField] = value;
}
};
config.macros.option.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
params = paramString.parseParams("anon",null,true,false,false);
var opt = (params[1] & & params[1].name == "anon") ? params[1].value : getParam(params,"name",null);
var className = (params[2] & & params[2].name == "anon") ? params[2].value : getParam(params,"class",null);
var desc = getParam(params,"desc","no");
var type = opt.substr(0,3);
var h = config.macros.option.types[type];
if (h & & h.create)
h.create(place,type,opt,className,desc);
};
config.macros.options.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
params = paramString.parseParams("anon",null,true,false,false);
var showUnknown = getParam(params,"showUnknown","no");
var wizard = new Wizard();
wizard.createWizard(place,this.wizardTitle);
wizard.addStep(this.step1Title,this.step1Html);
var markList = wizard.getElement("markList");
var chkUnknown = wizard.getElement("chkUnknown");
chkUnknown.checked = showUnknown == "yes";
chkUnknown.onchange = this.onChangeUnknown;
var listWrapper = document.createElement("div");
markList.parentNode.insertBefore(listWrapper,markList);
wizard.setValue("listWrapper",listWrapper);
this.refreshOptions(listWrapper,showUnknown == "yes");
};
config.macros.options.refreshOptions = function(listWrapper,showUnknown)
{
var opts = [];
for(var n in config.options) {
var opt = {};
opt.option = "";
opt.name = n;
opt.lowlight = !config.optionsDesc[n];
opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
if(!opt.lowlight || showUnknown)
opts.push(opt);
}
opts.sort(function(a,b) {return a.name.substr(3) < b.name.substr ( 3 ) ? -1 : ( a . name . substr ( 3 ) = = b . name . substr ( 3 ) ? 0 : + 1 ) ; } ) ;
var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
for(n=0; n< opts.length ; n + + ) {
var type = opts[n].name.substr(0,3);
var h = config.macros.option.types[type];
if (h & & h.create) {
h.create(opts[n].colElements['option'],type,opts[n].name,null,"no");
}
}
};
config.macros.options.onChangeUnknown = function(e)
{
var wizard = new Wizard(this);
var listWrapper = wizard.getValue("listWrapper");
removeChildren(listWrapper);
config.macros.options.refreshOptions(listWrapper,this.checked);
return false;
};
config.macros.newTiddler.createNewTiddlerButton = function(place,title,params,label,prompt,accessKey,newFocus,isJournal)
{
var tags = [];
for(var t=1; t< params.length ; t + + ) {
if((params[t].name == "anon" & & t != 1) || (params[t].name == "tag"))
tags.push(params[t].value);
}
label = getParam(params,"label",label);
prompt = getParam(params,"prompt",prompt);
accessKey = getParam(params,"accessKey",accessKey);
newFocus = getParam(params,"focus",newFocus);
var customFields = getParam(params,"fields","");
if(!customFields & & !store.isShadowTiddler(title))
customFields = String.encodeHashMap(config.defaultCustomFields);
var btn = createTiddlyButton(place,label,prompt,this.onClickNewTiddler,null,null,accessKey);
btn.setAttribute("newTitle",title);
btn.setAttribute("isJournal",isJournal ? "true" : "false");
if(tags.length > 0)
btn.setAttribute("params",tags.join("|"));
btn.setAttribute("newFocus",newFocus);
btn.setAttribute("newTemplate",getParam(params,"template",DEFAULT_EDIT_TEMPLATE));
if(customFields !== "")
btn.setAttribute("customFields",customFields);
var text = getParam(params,"text");
if(text !== undefined)
btn.setAttribute("newText",text);
return btn;
};
config.macros.newTiddler.onClickNewTiddler = function()
{
var title = this.getAttribute("newTitle");
if(this.getAttribute("isJournal") == "true") {
var now = new Date();
title = now.formatString(title.trim());
}
var params = this.getAttribute("params");
var tags = params ? params.split("|") : [];
var focus = this.getAttribute("newFocus");
var template = this.getAttribute("newTemplate");
var customFields = this.getAttribute("customFields");
story.displayTiddler(null,title,template,false,null,null);
var tiddlerElem = document.getElementById(story.idPrefix + title);
story.addCustomFields(tiddlerElem,customFields);
var text = this.getAttribute("newText");
if(typeof text == "string")
story.getTiddlerField(title,"text").value = text.format([title]);
for(var t=0;t< tags.length ; t + + )
story.setTiddlerTag(title,tags[t],+1);
story.focusTiddler(title,focus);
return false;
};
config.macros.newTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
if(!readOnly) {
params = paramString.parseParams("anon",null,true,false,false);
var title = params[1] & & params[1].name == "anon" ? params[1].value : this.title;
title = getParam(params,"title",title);
this.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"title",false);
}
};
config.macros.newJournal.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
if(!readOnly) {
params = paramString.parseParams("anon",null,true,false,false);
var title = params[1] & & params[1].name == "anon" ? params[1].value : "";
title = getParam(params,"title",title);
config.macros.newTiddler.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"text",true);
}
};
config.macros.sparkline.handler = function(place,macroName,params)
{
var data = [];
var min = 0;
var max = 0;
for(var t=0; t< params.length ; t + + ) {
var v = parseInt(params[t]);
if(v < min )
min = v;
if(v > max)
max = v;
data.push(v);
}
if(data.length < 1 )
return;
var box = createTiddlyElement(place,"span",null,"sparkline",String.fromCharCode(160));
box.title = data.join(",");
var w = box.offsetWidth;
var h = box.offsetHeight;
box.style.paddingRight = (data.length * 2 - w) + "px";
box.style.position = "relative";
for(var d=0; d< data.length ; d + + ) {
var tick = document.createElement("img");
tick.border = 0;
tick.className = "sparktick";
tick.style.position = "absolute";
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";
tick.style.left = d*2 + "px";
tick.style.width = "2px";
var v = Math.floor(((data[d] - min)/(max-min)) * h);
tick.style.top = (h-v) + "px";
tick.style.height = v + "px";
box.appendChild(tick);
}
};
config.macros.tabs.handler = function(place,macroName,params)
{
var cookie = params[0];
var numTabs = (params.length-1)/3;
var wrapper = createTiddlyElement(null,"div",null,cookie);
var tabset = createTiddlyElement(wrapper,"div",null,"tabset");
tabset.setAttribute("cookie",cookie);
var validTab = false;
for(var t=0; t< numTabs ; t + + ) {
var label = params[t*3+1];
var prompt = params[t*3+2];
var content = params[t*3+3];
var tab = createTiddlyButton(tabset,label,prompt,this.onClickTab,"tab tabUnselected");
tab.setAttribute("tab",label);
tab.setAttribute("content",content);
tab.title = prompt;
if(config.options[cookie] == label)
validTab = true;
}
if(!validTab)
config.options[cookie] = params[1];
place.appendChild(wrapper);
this.switchTab(tabset,config.options[cookie]);
};
config.macros.tabs.onClickTab = function(e)
{
config.macros.tabs.switchTab(this.parentNode,this.getAttribute("tab"));
return false;
};
config.macros.tabs.switchTab = function(tabset,tab)
{
var cookie = tabset.getAttribute("cookie");
var theTab = null;
var nodes = tabset.childNodes;
for(var t=0; t< nodes.length ; t + + ) {
if(nodes[t].getAttribute & & nodes[t].getAttribute("tab") == tab) {
theTab = nodes[t];
theTab.className = "tab tabSelected";
} else {
nodes[t].className = "tab tabUnselected";
}
}
if(theTab) {
if(tabset.nextSibling & & tabset.nextSibling.className == "tabContents")
removeNode(tabset.nextSibling);
var tabContent = createTiddlyElement(null,"div",null,"tabContents");
tabset.parentNode.insertBefore(tabContent,tabset.nextSibling);
var contentTitle = theTab.getAttribute("content");
wikify(store.getTiddlerText(contentTitle),tabContent,null,store.getTiddler(contentTitle));
if(cookie) {
config.options[cookie] = tab;
saveOptionCookie(cookie);
}
}
};
// < < gradient [ [ tiddler name ] ] vert | horiz rgb rgb rgb rgb . . . > >
config.macros.gradient.handler = function(place,macroName,params,wikifier)
{
var terminator = ">>";
var panel;
if(wikifier)
panel = createTiddlyElement(place,"div",null,"gradient");
else
panel = place;
panel.style.position = "relative";
panel.style.overflow = "hidden";
panel.style.zIndex = "0";
var t;
if(wikifier) {
var styles = config.formatterHelpers.inlineCssHelper(wikifier);
config.formatterHelpers.applyCssHelper(panel,styles);
}
var colours = [];
for(t=1; t< params.length ; t + + ) {
var c = new RGB(params[t]);
if(c)
colours.push(c);
}
drawGradient(panel,params[0] != "vert",colours);
if(wikifier)
wikifier.subWikify(panel,terminator);
if(document.all) {
panel.style.height = "100%";
panel.style.width = "100%";
}
};
config.macros.message.handler = function(place,macroName,params)
{
if(params[0]) {
var m = config;
var p = params[0].split(".");
for(var t=0; t< p.length ; t + + ) {
if(p[t] in m)
m = m[p[t]];
else
break;
}
createTiddlyText(place,m.toString().format(params.splice(1)));
}
};
config.macros.view.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
if((tiddler instanceof Tiddler) & & params[0]) {
var value = store.getValue(tiddler,params[0]);
if(value != undefined) {
switch(params[1]) {
case undefined:
highlightify(value,place,highlightHack,tiddler);
break;
case "link":
createTiddlyLink(place,value,true);
break;
case "wikified":
wikify(value,place,highlightHack,tiddler);
break;
case "date":
value = Date.convertFromYYYYMMDDHHMM(value);
createTiddlyText(place,value.formatString(params[2] ? params[2] : config.views.wikified.dateFormat));
break;
}
}
}
};
config.macros.edit.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
var field = params[0];
var rows = params[1];
if((tiddler instanceof Tiddler) & & field) {
story.setDirty(tiddler.title,true);
if(field != "text" & & !rows) {
var e = createTiddlyElement(null,"input");
if(tiddler.isReadOnly())
e.setAttribute("readOnly","readOnly");
e.setAttribute("edit",field);
e.setAttribute("type","text");
var v = store.getValue(tiddler,field);
if(!v)
v = "";
e.value = v;
e.setAttribute("size","40");
e.setAttribute("autocomplete","off");
place.appendChild(e);
} else {
var wrapper1 = createTiddlyElement(null,"fieldset",null,"fieldsetFix");
var wrapper2 = createTiddlyElement(wrapper1,"div");
var e = createTiddlyElement(wrapper2,"textarea");
if(tiddler.isReadOnly())
e.setAttribute("readOnly","readOnly");
var v = store.getValue(tiddler,field);
if(!v)
v = "";
e.value = v;
var rows = rows ? rows : 10;
var lines = v.match(/\n/mg);
var maxLines = Math.max(parseInt(config.options.txtMaxEditRows),5);
if(lines != null & & lines.length > rows)
rows = lines.length + 5;
rows = Math.min(rows,maxLines);
e.setAttribute("rows",rows);
e.setAttribute("edit",field);
place.appendChild(wrapper1);
}
}
};
config.macros.tagChooser.onClick = function(e)
{
if(!e) var e = window.event;
var lingo = config.views.editor.tagChooser;
var popup = Popup.create(this);
var tags = store.getTags();
if(tags.length == 0)
createTiddlyText(createTiddlyElement(popup,"li"),lingo.popupNone);
for(var t=0; t< tags.length ; t + + ) {
var theTag = createTiddlyButton(createTiddlyElement(popup,"li"),tags[t][0],lingo.tagTooltip.format([tags[t][0]]),config.macros.tagChooser.onTagClick);
theTag.setAttribute("tag",tags[t][0]);
theTag.setAttribute("tiddler",this.getAttribute("tiddler"));
}
Popup.show();
e.cancelBubble = true;
if(e.stopPropagation) e.stopPropagation();
return false;
};
config.macros.tagChooser.onTagClick = function(e)
{
if(!e) var e = window.event;
var tag = this.getAttribute("tag");
var title = this.getAttribute("tiddler");
if(!readOnly)
story.setTiddlerTag(title,tag,0);
return false;
};
config.macros.tagChooser.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
if(tiddler instanceof Tiddler) {
var title = tiddler.title;
var lingo = config.views.editor.tagChooser;
var btn = createTiddlyButton(place,lingo.text,lingo.tooltip,this.onClick);
btn.setAttribute("tiddler",title);
}
};
// Create a toolbar command button
config.macros.toolbar.createCommand = function(place,commandName,tiddler,theClass)
{
if(typeof commandName != "string") {
var c = null;
for(var t in config.commands) {
if(config.commands[t] == commandName)
c = t;
}
commandName = c;
}
if((tiddler instanceof Tiddler) & & (typeof commandName == "string")) {
var command = config.commands[commandName];
if(command.isEnabled ? command.isEnabled(tiddler) : this.isCommandEnabled(command,tiddler)) {
var text = command.getText ? command.getText(tiddler) : this.getCommandText(command,tiddler);
var tooltip = command.getTooltip ? command.getTooltip(tiddler) : this.getCommandTooltip(command,tiddler);
var cmd;
switch(command.type) {
case "popup":
cmd = this.onClickPopup;
break;
case "command":
default:
cmd = this.onClickCommand;
break;
}
var btn = createTiddlyButton(null,text,tooltip,cmd);
btn.setAttribute("commandName",commandName);
btn.setAttribute("tiddler",tiddler.title);
if(theClass)
addClass(btn,theClass);
place.appendChild(btn);
}
}
};
config.macros.toolbar.isCommandEnabled = function(command,tiddler)
{
var title = tiddler.title;
var ro = tiddler.isReadOnly();
var shadow = store.isShadowTiddler(title) & & !store.tiddlerExists(title);
return (!ro || (ro & & !command.hideReadOnly)) & & !(shadow & & command.hideShadow);
};
config.macros.toolbar.getCommandText = function(command,tiddler)
{
return tiddler.isReadOnly() & & command.readOnlyText ? command.readOnlyText : command.text;
};
config.macros.toolbar.getCommandTooltip = function(command,tiddler)
{
return tiddler.isReadOnly() & & command.readOnlyTooltip ? command.readOnlyTooltip : command.tooltip;
};
config.macros.toolbar.onClickCommand = function(e)
{
if(!e) var e = window.event;
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
var command = config.commands[this.getAttribute("commandName")];
return command.handler(e,this,this.getAttribute("tiddler"));
};
config.macros.toolbar.onClickPopup = function(e)
{
if(!e) var e = window.event;
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
var popup = Popup.create(this);
var command = config.commands[this.getAttribute("commandName")];
var title = this.getAttribute("tiddler");
var tiddler = store.fetchTiddler(title);
popup.setAttribute("tiddler",title);
command.handlePopup(popup,title);
Popup.show();
return false;
};
// Invoke the first command encountered from a given place that is tagged with a specified class
config.macros.toolbar.invokeCommand = function(place,theClass,event)
{
var children = place.getElementsByTagName("a");
for(var t=0; t< children.length ; t + + ) {
var c = children[t];
if(hasClass(c,theClass) & & c.getAttribute & & c.getAttribute("commandName")) {
if(c.onclick instanceof Function)
c.onclick.call(c,event);
break;
}
}
};
config.macros.toolbar.onClickMore = function(e)
{
var e = this.nextSibling;
e.style.display = "inline";
removeNode(this);
return false;
};
config.macros.toolbar.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
for(var t=0; t< params.length ; t + + ) {
var c = params[t];
switch(c) {
case '>':
var btn = createTiddlyButton(place,this.moreLabel,this.morePrompt,config.macros.toolbar.onClickMore);
addClass(btn,"moreCommand");
var e = createTiddlyElement(place,"span",null,"moreCommand");
e.style.display = "none";
place = e;
break;
default:
var theClass = "";
switch(c.substr(0,1)) {
case "+":
theClass = "defaultCommand";
c = c.substr(1);
break;
case "-":
theClass = "cancelCommand";
c = c.substr(1);
break;
}
if(c in config.commands)
this.createCommand(place,c,tiddler,theClass);
break;
}
}
};
config.macros.refreshDisplay.handler = function(place)
{
createTiddlyButton(place,this.label,this.prompt,this.onClick);
};
config.macros.refreshDisplay.onClick = function(e)
{
refreshAll();
return false;
};
config.macros.annotations.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
var title = tiddler ? tiddler.title : null;
var a = title ? config.annotations[title] : null;
if(!tiddler || !title || !a)
return;
var text = a.format([title]);
wikify(text,createTiddlyElement(place,"div",null,"annotation"),null,tiddler);
};
//--
//-- Menu and toolbar commands
//--
config.commands.closeTiddler.handler = function(event,src,title)
{
story.closeTiddler(title,true);
return false;
};
config.commands.closeOthers.handler = function(event,src,title)
{
story.closeAllTiddlers(title);
return false;
};
config.commands.editTiddler.handler = function(event,src,title)
{
clearMessage();
var tiddlerElem = document.getElementById(story.idPrefix + title);
var fields = tiddlerElem.getAttribute("tiddlyFields");
story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE,false,null,fields);
story.focusTiddler(title,"text");
return false;
};
config.commands.saveTiddler.handler = function(event,src,title)
{
var newTitle = story.saveTiddler(title,event.shiftKey);
if(newTitle)
story.displayTiddler(null,newTitle);
return false;
};
config.commands.cancelTiddler.handler = function(event,src,title)
{
if(story.hasChanges(title) & & !readOnly) {
if(!confirm(this.warning.format([title])))
return false;
}
story.setDirty(title,false);
story.displayTiddler(null,title);
return false;
};
config.commands.deleteTiddler.handler = function(event,src,title)
{
var deleteIt = true;
if (config.options.chkConfirmDelete)
deleteIt = confirm(this.warning.format([title]));
if (deleteIt) {
store.removeTiddler(title);
story.closeTiddler(title,true);
autoSaveChanges();
}
return false;
};
config.commands.permalink.handler = function(event,src,title)
{
var t = encodeURIComponent(String.encodeTiddlyLink(title));
if(window.location.hash != t)
window.location.hash = t;
return false;
};
config.commands.references.handlePopup = function(popup,title)
{
var references = store.getReferringTiddlers(title);
var c = false;
for(var r=0; r< references.length ; r + + ) {
if(references[r].title != title & & !references[r].isTagged("excludeLists")) {
createTiddlyLink(createTiddlyElement(popup,"li"),references[r].title,true);
c = true;
}
}
if(!c)
createTiddlyText(createTiddlyElement(popup,"li",null,"disabled"),this.popupNone);
};
config.commands.jump.handlePopup = function(popup,title)
{
story.forEachTiddler(function(title,element) {
createTiddlyLink(createTiddlyElement(popup,"li"),title,true,null,false,null,true);
});
};
config.commands.syncing.handlePopup = function(popup,title)
{
var tiddler = store.fetchTiddler(title);
if(!tiddler)
return;
var serverType = tiddler.getServerType();
var serverHost = tiddler.fields['server.host'];
var serverWorkspace = tiddler.fields['server.workspace'];
if(!serverWorkspace)
serverWorkspace = "";
if(serverType) {
var e = createTiddlyElement(popup,"li",null,"popupMessage");
e.innerHTML = config.commands.syncing.currentlySyncing.format([serverType,serverHost,serverWorkspace]);
} else {
createTiddlyElement(popup,"li",null,"popupMessage",config.commands.syncing.notCurrentlySyncing);
}
if(serverType) {
createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
var btn = createTiddlyButton(createTiddlyElement(popup,"li"),this.captionUnSync,null,config.commands.syncing.onChooseServer);
btn.setAttribute("tiddler",title);
btn.setAttribute("server.type","");
}
createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
createTiddlyElement(popup,"li",null,"popupMessage",config.commands.syncing.chooseServer);
var feeds = store.getTaggedTiddlers("systemServer","title");
for(var t=0; t< feeds.length ; t + + ) {
var f = feeds[t];
var feedServerType = store.getTiddlerSlice(f.title,"Type");
if(!feedServerType)
feedServerType = "file";
var feedServerHost = store.getTiddlerSlice(f.title,"URL");
if(!feedServerHost)
feedServerHost = "";
var feedServerWorkspace = store.getTiddlerSlice(f.title,"Workspace");
if(!feedServerWorkspace)
feedServerWorkspace = "";
var caption = f.title;
if(serverType == feedServerType & & serverHost == feedServerHost & & serverWorkspace == feedServerWorkspace) {
caption = config.commands.syncing.currServerMarker + caption;
} else {
caption = config.commands.syncing.notCurrServerMarker + caption;
}
btn = createTiddlyButton(createTiddlyElement(popup,"li"),caption,null,config.commands.syncing.onChooseServer);
btn.setAttribute("tiddler",title);
btn.setAttribute("server.type",feedServerType);
btn.setAttribute("server.host",feedServerHost);
btn.setAttribute("server.workspace",feedServerWorkspace);
}
};
config.commands.syncing.onChooseServer = function(e)
{
var tiddler = this.getAttribute("tiddler");
var serverType = this.getAttribute("server.type");
if(serverType) {
store.addTiddlerFields(tiddler,{
'server.type': serverType,
'server.host': this.getAttribute("server.host"),
'server.workspace': this.getAttribute("server.workspace")
});
} else {
store.setValue(tiddler,'server',null);
}
return false;
};
config.commands.fields.handlePopup = function(popup,title)
{
var tiddler = store.fetchTiddler(title);
if(!tiddler)
return;
var fields = {};
store.forEachField(tiddler,function(tiddler,fieldName,value) {fields[fieldName] = value;},true);
var items = [];
for(var t in fields) {
items.push({field: t,value: fields[t]});
}
items.sort(function(a,b) {return a.field < b.field ? -1 : ( a . field = = b . field ? 0 : + 1 ) ; } ) ;
if(items.length > 0)
ListView.create(popup,items,this.listViewTemplate);
else
createTiddlyElement(popup,"div",null,null,this.emptyText);
};
//--
//-- Tiddler() object
//--
function Tiddler(title)
{
this.title = title;
this.text = null;
this.modifier = null;
this.modified = new Date();
this.created = new Date();
this.links = [];
this.linksUpdated = false;
this.tags = [];
this.fields = {};
return this;
}
Tiddler.prototype.getLinks = function()
{
if(this.linksUpdated==false)
this.changed();
return this.links;
};
// Returns the fields that are inherited in string field:"value" field2:"value2" format
Tiddler.prototype.getInheritedFields = function()
{
var f = {};
for(i in this.fields) {
if(i=="server.host" || i=="server.workspace" || i=="wikiformat"|| i=="server.type") {
f[i] = this.fields[i];
}
}
return String.encodeHashMap(f);
};
// Increment the changeCount of a tiddler
Tiddler.prototype.incChangeCount = function()
{
var c = this.fields['changecount'];
c = c ? parseInt(c) : 0;
this.fields['changecount'] = String(c+1);
};
// Clear the changeCount of a tiddler
Tiddler.prototype.clearChangeCount = function()
{
if(this.fields['changecount']) {
delete this.fields['changecount'];
}
};
// Returns true if the tiddler has been updated since the tiddler was created or downloaded
Tiddler.prototype.isTouched = function()
{
var changeCount = this.fields['changecount'];
if(changeCount === undefined)
changeCount = 0;
return changeCount > 0;
};
// Format the text for storage in an RSS item
Tiddler.prototype.saveToRss = function(url)
{
var s = [];
s.push("< item > ");
s.push("< title " + " > " + this.title.htmlEncode() + "< /title" + ">");
s.push("< description > " + wikifyStatic(this.text,null,this).htmlEncode() + "< / description > ");
for(var t=0; t< this.tags.length ; t + + )
s.push("< category > " + this.tags[t] + "< / category > ");
s.push("< link > " + url + "#" + encodeURIComponent(String.encodeTiddlyLink(this.title)) + "< / link > ");
s.push("< pubDate > " + this.modified.toGMTString() + "< / pubDate > ");
s.push("< / item > ");
return s.join("\n");
};
// Change the text and other attributes of a tiddler
Tiddler.prototype.set = function(title,text,modifier,modified,tags,created,fields)
{
this.assign(title,text,modifier,modified,tags,created,fields);
this.changed();
return this;
};
// Change the text and other attributes of a tiddler without triggered a tiddler.changed() call
Tiddler.prototype.assign = function(title,text,modifier,modified,tags,created,fields)
{
if(title != undefined)
this.title = title;
if(text != undefined)
this.text = text;
if(modifier != undefined)
this.modifier = modifier;
if(modified != undefined)
this.modified = modified;
if(created != undefined)
this.created = created;
if(fields != undefined)
this.fields = fields;
if(tags != undefined)
this.tags = (typeof tags == "string") ? tags.readBracketedList() : tags;
else if(this.tags == undefined)
this.tags = [];
return this;
};
// Get the tags for a tiddler as a string (space delimited, using [[brackets]] for tags containing spaces)
Tiddler.prototype.getTags = function()
{
return String.encodeTiddlyLinkList(this.tags);
};
// Test if a tiddler carries a tag
Tiddler.prototype.isTagged = function(tag)
{
return this.tags.indexOf(tag) != -1;
};
// Static method to convert "\n" to newlines, "\s" to "\"
Tiddler.unescapeLineBreaks = function(text)
{
return text ? text.unescapeLineBreaks() : "";
};
// Convert newlines to "\n", "\" to "\s"
Tiddler.prototype.escapeLineBreaks = function()
{
return this.text.escapeLineBreaks();
};
// Updates the secondary information (like links[] array) after a change to a tiddler
Tiddler.prototype.changed = function()
{
this.links = [];
var t = this.autoLinkWikiWords() ? 0 : 1;
var tiddlerLinkRegExp = t==0 ? config.textPrimitives.tiddlerAnyLinkRegExp : config.textPrimitives.tiddlerForcedLinkRegExp;
tiddlerLinkRegExp.lastIndex = 0;
var formatMatch = tiddlerLinkRegExp.exec(this.text);
while(formatMatch) {
var lastIndex = tiddlerLinkRegExp.lastIndex;
if(t==0 & & formatMatch[1] & & formatMatch[1] != this.title) {
// wikiWordLink
if(formatMatch.index > 0) {
var preRegExp = new RegExp(config.textPrimitives.unWikiLink+"|"+config.textPrimitives.anyLetter,"mg");
preRegExp.lastIndex = formatMatch.index-1;
var preMatch = preRegExp.exec(this.text);
if(preMatch.index != formatMatch.index-1)
this.links.pushUnique(formatMatch[1]);
} else {
this.links.pushUnique(formatMatch[1]);
}
}
else if(formatMatch[2-t] & & !config.formatterHelpers.isExternalLink(formatMatch[3-t])) // titledBrackettedLink
this.links.pushUnique(formatMatch[3-t]);
else if(formatMatch[4-t] & & formatMatch[4-t] != this.title) // brackettedLink
this.links.pushUnique(formatMatch[4-t]);
tiddlerLinkRegExp.lastIndex = lastIndex;
formatMatch = tiddlerLinkRegExp.exec(this.text);
}
this.linksUpdated = true;
};
Tiddler.prototype.getSubtitle = function()
{
var theModifier = this.modifier;
if(!theModifier)
theModifier = config.messages.subtitleUnknown;
var theModified = this.modified;
if(theModified)
theModified = theModified.toLocaleString();
else
theModified = config.messages.subtitleUnknown;
return config.messages.tiddlerLinkTooltip.format([this.title,theModifier,theModified]);
};
Tiddler.prototype.isReadOnly = function()
{
return readOnly;
};
Tiddler.prototype.autoLinkWikiWords = function()
{
return !(this.isTagged("systemConfig") || this.isTagged("excludeMissing"));
};
Tiddler.prototype.generateFingerprint = function()
{
return "0x" + Crypto.hexSha1Str(this.text);
};
Tiddler.prototype.getServerType = function()
{
var serverType = null;
if(this.fields & & this.fields['server.type'])
serverType = this.fields['server.type'];
if(!serverType)
serverType = this.fields['wikiformat'];
if(serverType & & !config.adaptors[serverType])
serverType = null;
return serverType;
};
Tiddler.prototype.getAdaptor = function()
{
var serverType = this.getServerType();
if(serverType)
return new config.adaptors[serverType];
else
return null;
};
//--
//-- TiddlyWiki() object contains Tiddler()s
//--
function TiddlyWiki()
{
var tiddlers = {}; // Hashmap by name of tiddlers
this.tiddlersUpdated = false;
this.namedNotifications = []; // Array of {name:,notify:} of notification functions
this.notificationLevel = 0;
this.slices = {}; // map tiddlerName->(map sliceName->sliceValue). Lazy.
this.clear = function() {
tiddlers = {};
this.setDirty(false);
};
this.fetchTiddler = function(title) {
return tiddlers[title];
};
this.deleteTiddler = function(title) {
delete this.slices[title];
delete tiddlers[title];
};
this.addTiddler = function(tiddler) {
delete this.slices[tiddler.title];
tiddlers[tiddler.title] = tiddler;
};
this.forEachTiddler = function(callback) {
for(var t in tiddlers) {
var tiddler = tiddlers[t];
if(tiddler instanceof Tiddler)
callback.call(this,t,tiddler);
}
};
}
TiddlyWiki.prototype.setDirty = function(dirty)
{
this.dirty = dirty;
};
TiddlyWiki.prototype.isDirty = function()
{
return this.dirty;
};
TiddlyWiki.prototype.suspendNotifications = function()
{
this.notificationLevel--;
};
TiddlyWiki.prototype.resumeNotifications = function()
{
this.notificationLevel++;
};
// Invoke the notification handlers for a particular tiddler
TiddlyWiki.prototype.notify = function(title,doBlanket)
{
if(!this.notificationLevel) {
for(var t=0; t< this.namedNotifications.length ; t + + ) {
var n = this.namedNotifications[t];
if((n.name == null & & doBlanket) || (n.name == title))
n.notify(title);
}
}
};
// Invoke the notification handlers for all tiddlers
TiddlyWiki.prototype.notifyAll = function()
{
if(!this.notificationLevel) {
for(var t=0; t< this.namedNotifications.length ; t + + ) {
var n = this.namedNotifications[t];
if(n.name)
n.notify(n.name);
}
}
};
// Add a notification handler to a tiddler
TiddlyWiki.prototype.addNotification = function(title,fn)
{
for(var i=0; i< this.namedNotifications.length ; i + + ) {
if((this.namedNotifications[i].name == title) & & (this.namedNotifications[i].notify == fn))
return this;
}
this.namedNotifications.push({name: title, notify: fn});
return this;
};
TiddlyWiki.prototype.removeTiddler = function(title)
{
var tiddler = this.fetchTiddler(title);
if(tiddler) {
this.deleteTiddler(title);
this.notify(title,true);
this.setDirty(true);
}
};
TiddlyWiki.prototype.tiddlerExists = function(title)
{
var t = this.fetchTiddler(title);
return t != undefined;
};
TiddlyWiki.prototype.isShadowTiddler = function(title)
{
return typeof config.shadowTiddlers[title] == "string";
};
TiddlyWiki.prototype.getTiddler = function(title)
{
var t = this.fetchTiddler(title);
if(t != undefined)
return t;
else
return null;
};
TiddlyWiki.prototype.getTiddlerText = function(title,defaultText)
{
var tiddler = this.fetchTiddler(title);
if(tiddler)
return tiddler.text;
if(!title)
return defaultText;
var pos = title.indexOf(config.textPrimitives.sliceSeparator);
if(pos != -1) {
var slice = this.getTiddlerSlice(title.substr(0,pos),title.substr(pos + config.textPrimitives.sliceSeparator.length));
if(slice)
return slice;
}
if(this.isShadowTiddler(title))
return config.shadowTiddlers[title];
if(defaultText != undefined)
return defaultText;
return null;
};
TiddlyWiki.prototype.slicesRE = /(?:[\'\/]*~?([\.\w]+)[\'\/]*\:[\'\/]*\s*(.*?)\s*$)|(?:\|[\'\/]*~?([\.\w]+)\:?[\'\/]*\|\s*(.*?)\s*\|)/gm;
// @internal
TiddlyWiki.prototype.calcAllSlices = function(title)
{
var slices = {};
var text = this.getTiddlerText(title,"");
this.slicesRE.lastIndex = 0;
do {
var m = this.slicesRE.exec(text);
if(m) {
if(m[1])
slices[m[1]] = m[2];
else
slices[m[3]] = m[4];
}
} while(m);
return slices;
};
// Returns the slice of text of the given name
TiddlyWiki.prototype.getTiddlerSlice = function(title,sliceName)
{
var slices = this.slices[title];
if(!slices) {
slices = this.calcAllSlices(title);
this.slices[title] = slices;
}
return slices[sliceName];
};
// Build an hashmap of the specified named slices of a tiddler
TiddlyWiki.prototype.getTiddlerSlices = function(title,sliceNames)
{
var r = {};
for(var t=0; t< sliceNames.length ; t + + ) {
var slice = this.getTiddlerSlice(title,sliceNames[t]);
if(slice)
r[sliceNames[t]] = slice;
}
return r;
};
TiddlyWiki.prototype.getRecursiveTiddlerText = function(title,defaultText,depth)
{
var bracketRegExp = new RegExp("(?:\\[\\[([^\\]]+)\\]\\])","mg");
var text = this.getTiddlerText(title,null);
if(text == null)
return defaultText;
var textOut = [];
var lastPos = 0;
do {
var match = bracketRegExp.exec(text);
if(match) {
textOut.push(text.substr(lastPos,match.index-lastPos));
if(match[1]) {
if(depth < = 0)
textOut.push(match[1]);
else
textOut.push(this.getRecursiveTiddlerText(match[1],"[[" + match[1] + "]]",depth-1));
}
lastPos = match.index + match[0].length;
} else {
textOut.push(text.substr(lastPos));
}
} while(match);
return textOut.join("");
};
TiddlyWiki.prototype.setTiddlerTag = function(title,status,tag)
{
var tiddler = this.fetchTiddler(title);
if(tiddler) {
var t = tiddler.tags.indexOf(tag);
if(t != -1)
tiddler.tags.splice(t,1);
if(status)
tiddler.tags.push(tag);
tiddler.changed();
this.incChangeCount(title);
this.notify(title,true);
this.setDirty(true);
}
};
TiddlyWiki.prototype.addTiddlerFields = function(title,fields)
{
var tiddler = this.fetchTiddler(title);
if(!tiddler)
return;
merge(tiddler.fields,fields);
tiddler.changed();
this.incChangeCount(title);
this.notify(title,true);
this.setDirty(true);
};
TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created)
{
var tiddler = this.fetchTiddler(title);
if(tiddler) {
created = created ? created : tiddler.created; // Preserve created date
this.deleteTiddler(title);
} else {
created = created ? created : modified;
tiddler = new Tiddler();
}
tiddler.set(newTitle,newBody,modifier,modified,tags,created,fields);
this.addTiddler(tiddler);
if(clearChangeCount)
tiddler.clearChangeCount();
else
tiddler.incChangeCount();
if(title != newTitle)
this.notify(title,true);
this.notify(newTitle,true);
this.setDirty(true);
return tiddler;
};
// Reset the sync status of a freshly synced tiddler
TiddlyWiki.prototype.resetTiddler = function(title)
{
var tiddler = this.fetchTiddler(title);
if(tiddler) {
tiddler.clearChangeCount();
this.notify(title,true);
this.setDirty(true);
}
};
TiddlyWiki.prototype.incChangeCount = function(title)
{
var tiddler = this.fetchTiddler(title);
if(tiddler)
tiddler.incChangeCount();
};
TiddlyWiki.prototype.createTiddler = function(title)
{
var tiddler = this.fetchTiddler(title);
if(!tiddler) {
tiddler = new Tiddler();
tiddler.title = title;
this.addTiddler(tiddler);
this.setDirty(true);
}
return tiddler;
};
// Load contents of a TiddlyWiki from an HTML DIV
TiddlyWiki.prototype.loadFromDiv = function(src,idPrefix,noUpdate)
{
this.idPrefix = idPrefix;
var storeElem = (typeof src == "string") ? document.getElementById(src) : src;
if(!storeElem)
return;
var tiddlers = this.getLoader().loadTiddlers(this,storeElem.childNodes);
this.setDirty(false);
if(!noUpdate) {
for(var i = 0;i< tiddlers.length ; i + + )
tiddlers[i].changed();
}
};
// Load contents of a TiddlyWiki from a string
// Returns null if there's an error
TiddlyWiki.prototype.importTiddlyWiki = function(text)
{
var posDiv = locateStoreArea(text);
if(!posDiv)
return null;
var content = "< " + "html>< " + "body>" + text.substring(posDiv[0],posDiv[1] + endSaveArea.length) + "< " + "/body>< " + "/html>";
// Create the iframe
var iframe = document.createElement("iframe");
iframe.style.display = "none";
document.body.appendChild(iframe);
var doc = iframe.document;
if(iframe.contentDocument)
doc = iframe.contentDocument; // For NS6
else if(iframe.contentWindow)
doc = iframe.contentWindow.document; // For IE5.5 and IE6
// Put the content in the iframe
doc.open();
doc.writeln(content);
doc.close();
// Load the content into a TiddlyWiki() object
var storeArea = doc.getElementById("storeArea");
this.loadFromDiv(storeArea,"store");
// Get rid of the iframe
iframe.parentNode.removeChild(iframe);
return this;
};
TiddlyWiki.prototype.updateTiddlers = function()
{
this.tiddlersUpdated = true;
this.forEachTiddler(function(title,tiddler) {
tiddler.changed();
});
};
// Return all tiddlers formatted as an HTML string
TiddlyWiki.prototype.allTiddlersAsHtml = function()
{
return store.getSaver().externalize(store);
};
// Return an array of tiddlers matching a search regular expression
TiddlyWiki.prototype.search = function(searchRegExp,sortField,excludeTag)
{
var candidates = this.reverseLookup("tags",excludeTag,false);
var results = [];
for(var t=0; t< candidates.length ; t + + ) {
if((candidates[t].title.search(searchRegExp) != -1) || (candidates[t].text.search(searchRegExp) != -1))
results.push(candidates[t]);
}
if(!sortField)
sortField = "title";
results.sort(function(a,b) {return a[sortField] < b [ sortField ] ? -1 : ( a [ sortField ] = = b [ sortField ] ? 0 : + 1 ) ; } ) ;
return results;
};
// 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
TiddlyWiki.prototype.getTags = function(excludeTag)
{
var results = [];
this.forEachTiddler(function(title,tiddler) {
for(var g=0; g< tiddler.tags.length ; g + + ) {
var tag = tiddler.tags[g];
if(excludeTag) {
var t = store.fetchTiddler(tag);
if(t & & t.isTagged(excludeTag))
return false;
}
var f = false;
for(var c=0; c< results.length ; c + + ) {
if(results[c][0] == tag) {
f = true;
results[c][1]++;
}
}
if(!f)
results.push([tag,1]);
}
});
results.sort(function(a,b) {return a[0].toLowerCase() < b [ 0 ] . toLowerCase ( ) ? -1 : ( a [ 0 ] . toLowerCase ( ) = = b [ 0 ] . toLowerCase ( ) ? 0 : + 1 ) ; } ) ;
return results;
};
// Return an array of the tiddlers that are tagged with a given tag
TiddlyWiki.prototype.getTaggedTiddlers = function(tag,sortField)
{
return this.reverseLookup("tags",tag,true,sortField);
};
// Return an array of the tiddlers that link to a given tiddler
TiddlyWiki.prototype.getReferringTiddlers = function(title,unusedParameter,sortField)
{
if(!this.tiddlersUpdated)
this.updateTiddlers();
return this.reverseLookup("links",title,true,sortField);
};
// Return an array of the tiddlers that do or do not have a specified entry in the specified storage array (ie, "links" or "tags")
// lookupMatch == true to match tiddlers, false to exclude tiddlers
TiddlyWiki.prototype.reverseLookup = function(lookupField,lookupValue,lookupMatch,sortField)
{
var results = [];
this.forEachTiddler(function(title,tiddler) {
var f = !lookupMatch;
for(var lookup=0; lookup< tiddler [ lookupField ] . length ; lookup + + ) {
if(tiddler[lookupField][lookup] == lookupValue)
f = lookupMatch;
}
if(f)
results.push(tiddler);
});
if(!sortField)
sortField = "title";
results.sort(function(a,b) {return a[sortField] < b [ sortField ] ? -1 : ( a [ sortField ] = = b [ sortField ] ? 0 : + 1 ) ; } ) ;
return results;
};
// Return the tiddlers as a sorted array
TiddlyWiki.prototype.getTiddlers = function(field,excludeTag)
{
var results = [];
this.forEachTiddler(function(title,tiddler) {
if(excludeTag == undefined || !tiddler.isTagged(excludeTag))
results.push(tiddler);
});
if(field)
results.sort(function(a,b) {return a[field] < b [ field ] ? -1 : ( a [ field ] = = b [ field ] ? 0 : + 1 ) ; } ) ;
return results;
};
// Return array of names of tiddlers that are referred to but not defined
TiddlyWiki.prototype.getMissingLinks = function(sortField)
{
if(!this.tiddlersUpdated)
this.updateTiddlers();
var results = [];
this.forEachTiddler(function (title,tiddler) {
if(tiddler.isTagged("excludeMissing") || tiddler.isTagged("systemConfig"))
return;
for(var n=0; n< tiddler.links.length ; n + + ) {
var link = tiddler.links[n];
if(this.fetchTiddler(link) == null & & !this.isShadowTiddler(link))
results.pushUnique(link);
}
});
results.sort();
return results;
};
// Return an array of names of tiddlers that are defined but not referred to
TiddlyWiki.prototype.getOrphans = function()
{
var results = [];
this.forEachTiddler(function (title,tiddler) {
if(this.getReferringTiddlers(title).length == 0 & & !tiddler.isTagged("excludeLists"))
results.push(title);
});
results.sort();
return results;
};
// Return an array of names of all the shadow tiddlers
TiddlyWiki.prototype.getShadowed = function()
{
var results = [];
for(var t in config.shadowTiddlers) {
if(typeof config.shadowTiddlers[t] == "string")
results.push(t);
}
results.sort();
return results;
};
// Return an array of tiddlers that have been touched since they were downloaded or created
TiddlyWiki.prototype.getTouched = function()
{
var results = [];
this.forEachTiddler(function(title,tiddler) {
if(tiddler.isTouched())
results.push(tiddler);
});
results.sort();
return results;
};
// Resolves a Tiddler reference or tiddler title into a Tiddler object, or null if it doesn't exist
TiddlyWiki.prototype.resolveTiddler = function(tiddler)
{
var t = (typeof tiddler == 'string') ? this.getTiddler(tiddler) : tiddler;
return t instanceof Tiddler ? t : null;
};
TiddlyWiki.prototype.getLoader = function()
{
if(!this.loader)
this.loader = new TW21Loader();
return this.loader;
};
TiddlyWiki.prototype.getSaver = function()
{
if(!this.saver)
this.saver = new TW21Saver();
return this.saver;
};
// Returns true if path is a valid field name (path),
// i.e. a sequence of identifiers, separated by '.'
TiddlyWiki.isValidFieldName = function(name)
{
var match = /[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*/.exec(name);
return match & & (match[0] == name);
};
// Throws an exception when name is not a valid field name.
TiddlyWiki.checkFieldName = function(name)
{
if(!TiddlyWiki.isValidFieldName(name))
throw config.messages.invalidFieldName.format([name]);
};
function StringFieldAccess(n,readOnly)
{
this.set = readOnly ?
function(t,v) {if(v != t[n]) throw config.messages.fieldCannotBeChanged.format([n]);} :
function(t,v) {if(v != t[n]) {t[n] = v; return true;}};
this.get = function(t) {return t[n];};
}
function DateFieldAccess(n)
{
this.set = function(t,v) {
var d = v instanceof Date ? v : Date.convertFromYYYYMMDDHHMM(v);
if(d != t[n]) {
t[n] = d; return true;
}
};
this.get = function(t) {return t[n].convertToYYYYMMDDHHMM();};
}
function LinksFieldAccess(n)
{
this.set = function(t,v) {
var s = (typeof v == "string") ? v.readBracketedList() : v;
if(s.toString() != t[n].toString()) {
t[n] = s; return true;
}
};
this.get = function(t) {return String.encodeTiddlyLinkList(t[n]);};
}
TiddlyWiki.standardFieldAccess = {
// The set functions return true when setting the data has changed the value.
"title": new StringFieldAccess("title",true),
// Handle the "tiddler" field name as the title
"tiddler": new StringFieldAccess("title",true),
"text": new StringFieldAccess("text"),
"modifier": new StringFieldAccess("modifier"),
"modified": new DateFieldAccess("modified"),
"created": new DateFieldAccess("created"),
"tags": new LinksFieldAccess("tags")
};
TiddlyWiki.isStandardField = function(name)
{
return TiddlyWiki.standardFieldAccess[name] != undefined;
};
// Sets the value of the given field of the tiddler to the value.
// Setting an ExtendedField's value to null or undefined removes the field.
// Setting a namespace to undefined removes all fields of that namespace.
// The fieldName is case-insensitive.
// All values will be converted to a string value.
TiddlyWiki.prototype.setValue = function(tiddler,fieldName,value)
{
TiddlyWiki.checkFieldName(fieldName);
var t = this.resolveTiddler(tiddler);
if(!t)
return;
fieldName = fieldName.toLowerCase();
var isRemove = (value === undefined) || (value === null);
var accessor = TiddlyWiki.standardFieldAccess[fieldName];
if(accessor) {
if(isRemove)
// don't remove StandardFields
return;
var h = TiddlyWiki.standardFieldAccess[fieldName];
if(!h.set(t,value))
return;
} else {
var oldValue = t.fields[fieldName];
if(isRemove) {
if(oldValue !== undefined) {
// deletes a single field
delete t.fields[fieldName];
} else {
// no concrete value is defined for the fieldName
// so we guess this is a namespace path.
// delete all fields in a namespace
var re = new RegExp('^'+fieldName+'\\.');
var dirty = false;
for(var n in t.fields) {
if(n.match(re)) {
delete t.fields[n];
dirty = true;
}
}
if(!dirty)
return;
}
} else {
// the "normal" set case. value is defined (not null/undefined)
// For convenience provide a nicer conversion Date->String
value = value instanceof Date ? value.convertToYYYYMMDDHHMMSSMMM() : String(value);
if(oldValue == value)
return;
t.fields[fieldName] = value;
}
}
// When we are here the tiddler/store really was changed.
this.notify(t.title,true);
if(!fieldName.match(/^temp\./))
this.setDirty(true);
};
// Returns the value of the given field of the tiddler.
// The fieldName is case-insensitive.
// Will only return String values (or undefined).
TiddlyWiki.prototype.getValue = function(tiddler,fieldName)
{
var t = this.resolveTiddler(tiddler);
if(!t)
return undefined;
fieldName = fieldName.toLowerCase();
var accessor = TiddlyWiki.standardFieldAccess[fieldName];
if(accessor) {
return accessor.get(t);
}
return t.fields[fieldName];
};
// Calls the callback function for every field in the tiddler.
// When callback function returns a non-false value the iteration stops
// and that value is returned.
// The order of the fields is not defined.
// @param callback a function(tiddler,fieldName,value).
TiddlyWiki.prototype.forEachField = function(tiddler,callback,onlyExtendedFields)
{
var t = this.resolveTiddler(tiddler);
if(!t)
return undefined;
for(var n in t.fields) {
var result = callback(t,n,t.fields[n]);
if(result)
return result;
}
if(onlyExtendedFields)
return undefined;
for(var n in TiddlyWiki.standardFieldAccess) {
if(n == "tiddler")
// even though the "title" field can also be referenced through the name "tiddler"
// we only visit this field once.
continue;
var result = callback(t,n,TiddlyWiki.standardFieldAccess[n].get(t));
if(result)
return result;
}
return undefined;
};
//--
//-- Story functions
//--
function Story(container,idPrefix)
{
this.container = container;
this.idPrefix = idPrefix;
this.highlightRegExp = null;
}
Story.prototype.forEachTiddler = function(fn)
{
var place = document.getElementById(this.container);
if(!place)
return;
var e = place.firstChild;
while(e) {
var n = e.nextSibling;
var title = e.getAttribute("tiddler");
fn.call(this,title,e);
e = n;
}
};
Story.prototype.displayTiddlers = function(srcElement,titles,template,animate,unused,customFields,toggle)
{
for(var t = titles.length-1;t>=0;t--)
this.displayTiddler(srcElement,titles[t],template,animate,unused,customFields);
};
Story.prototype.displayTiddler = function(srcElement,title,template,animate,unused,customFields,toggle)
{
var place = document.getElementById(this.container);
var tiddlerElem = document.getElementById(this.idPrefix + title);
if(tiddlerElem) {
if(toggle)
this.closeTiddler(title,true);
else
this.refreshTiddler(title,template,false,customFields);
} else {
var before = this.positionTiddler(srcElement);
tiddlerElem = this.createTiddler(place,before,title,template,customFields);
}
if(srcElement & & typeof srcElement !== "string") {
if(config.options.chkAnimate & & (animate == undefined || animate == true) & & anim & & typeof Zoomer == "function" & & typeof Scroller == "function")
anim.startAnimating(new Zoomer(title,srcElement,tiddlerElem),new Scroller(tiddlerElem));
else
window.scrollTo(0,ensureVisible(tiddlerElem));
}
};
Story.prototype.positionTiddler = function(srcElement)
{
var place = document.getElementById(this.container);
var before = null;
if(typeof srcElement == "string") {
switch(srcElement) {
case "top":
before = place.firstChild;
break;
case "bottom":
before = null;
break;
}
} else {
var after = this.findContainingTiddler(srcElement);
if(after == null) {
before = place.firstChild;
} else if(after.nextSibling) {
before = after.nextSibling;
if(before.nodeType != 1)
before = null;
}
}
return before;
};
Story.prototype.createTiddler = function(place,before,title,template,customFields)
{
var tiddlerElem = createTiddlyElement(null,"div",this.idPrefix + title,"tiddler");
tiddlerElem.setAttribute("refresh","tiddler");
if(customFields)
tiddlerElem.setAttribute("tiddlyFields",customFields);
place.insertBefore(tiddlerElem,before);
var defaultText = null;
if(!store.tiddlerExists(title) & & !store.isShadowTiddler(title))
defaultText = this.loadMissingTiddler(title,customFields,tiddlerElem);
this.refreshTiddler(title,template,false,customFields,defaultText);
return tiddlerElem;
};
Story.prototype.loadMissingTiddler = function(title,fields,tiddlerElem)
{
var tiddler = new Tiddler(title);
tiddler.fields = typeof fields == "string" ? fields.decodeHashMap() : (fields ? fields : {});
var serverType = tiddler.getServerType();
var host = tiddler.fields['server.host'];
var workspace = tiddler.fields['server.workspace'];
if(!serverType | !host)
return null;
var sm = new SyncMachine(serverType,{
start: function() {
return this.openHost(host,"openWorkspace");
},
openWorkspace: function() {
return this.openWorkspace(workspace,"getTiddler");
},
getTiddler: function() {
return this.getTiddler(title,"gotTiddler");
},
gotTiddler: function(tiddler) {
if(tiddler & & tiddler.text) {
var downloaded = new Date();
if(!tiddler.created)
tiddler.created = downloaded;
if(!tiddler.modified)
tiddler.modified = tiddler.created;
store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields,true,tiddler.created);
autoSaveChanges();
}
delete this;
return true;
},
error: function(message) {
displayMessage("Error loading missing tiddler from %0: %1".format([host,message]));
}
});
sm.go();
return config.messages.loadingMissingTiddler.format([title,serverType,host,workspace]);
};
Story.prototype.chooseTemplateForTiddler = function(title,template)
{
if(!template)
template = DEFAULT_VIEW_TEMPLATE;
if(template == DEFAULT_VIEW_TEMPLATE || template == DEFAULT_EDIT_TEMPLATE)
template = config.tiddlerTemplates[template];
return template;
};
Story.prototype.getTemplateForTiddler = function(title,template,tiddler)
{
return store.getRecursiveTiddlerText(template,null,10);
};
Story.prototype.refreshTiddler = function(title,template,force,customFields,defaultText)
{
var tiddlerElem = document.getElementById(this.idPrefix + title);
if(tiddlerElem) {
if(tiddlerElem.getAttribute("dirty") == "true" & & !force)
return tiddlerElem;
template = this.chooseTemplateForTiddler(title,template);
var currTemplate = tiddlerElem.getAttribute("template");
if((template != currTemplate) || force) {
var tiddler = store.getTiddler(title);
if(!tiddler) {
tiddler = new Tiddler();
if(store.isShadowTiddler(title)) {
tiddler.set(title,store.getTiddlerText(title),config.views.wikified.shadowModifier,version.date,[],version.date);
} else {
var text = template=="EditTemplate" ?
config.views.editor.defaultText.format([title]) :
config.views.wikified.defaultText.format([title]);
text = defaultText ? defaultText : text;
var fields = customFields ? customFields.decodeHashMap() : null;
tiddler.set(title,text,config.views.wikified.defaultModifier,version.date,[],version.date,fields);
}
}
tiddlerElem.setAttribute("tags",tiddler.tags.join(" "));
tiddlerElem.setAttribute("tiddler",title);
tiddlerElem.setAttribute("template",template);
var me = this;
tiddlerElem.onmouseover = this.onTiddlerMouseOver;
tiddlerElem.onmouseout = this.onTiddlerMouseOut;
tiddlerElem.ondblclick = this.onTiddlerDblClick;
tiddlerElem[window.event?"onkeydown":"onkeypress"] = this.onTiddlerKeyPress;
var html = this.getTemplateForTiddler(title,template,tiddler);
tiddlerElem.innerHTML = html;
applyHtmlMacros(tiddlerElem,tiddler);
if(store.getTaggedTiddlers(title).length > 0)
addClass(tiddlerElem,"isTag");
else
removeClass(tiddlerElem,"isTag");
if(!store.tiddlerExists(title)) {
if(store.isShadowTiddler(title))
addClass(tiddlerElem,"shadow");
else
addClass(tiddlerElem,"missing");
} else {
removeClass(tiddlerElem,"shadow");
removeClass(tiddlerElem,"missing");
}
if(customFields)
this.addCustomFields(tiddlerElem,customFields);
forceReflow();
}
}
return tiddlerElem;
};
Story.prototype.addCustomFields = function(place,customFields)
{
var fields = customFields.decodeHashMap();
var w = document.createElement("div");
w.style.display = "none";
place.appendChild(w);
for(var t in fields) {
var e = document.createElement("input");
e.setAttribute("type","text");
e.setAttribute("value",fields[t]);
w.appendChild(e);
e.setAttribute("edit",t);
}
};
Story.prototype.refreshAllTiddlers = function()
{
var place = document.getElementById(this.container);
var e = place.firstChild;
if(!e)
return;
this.refreshTiddler(e.getAttribute("tiddler"),e.getAttribute("template"),true);
while((e = e.nextSibling) != null)
this.refreshTiddler(e.getAttribute("tiddler"),e.getAttribute("template"),true);
};
Story.prototype.onTiddlerMouseOver = function(e)
{
if(window.addClass instanceof Function)
addClass(this,"selected");
};
Story.prototype.onTiddlerMouseOut = function(e)
{
if(window.removeClass instanceof Function)
removeClass(this,"selected");
};
Story.prototype.onTiddlerDblClick = function(e)
{
if(!e) var e = window.event;
var theTarget = resolveTarget(e);
if(theTarget & & theTarget.nodeName.toLowerCase() != "input" & & theTarget.nodeName.toLowerCase() != "textarea") {
if(document.selection & & document.selection.empty)
document.selection.empty();
config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
e.cancelBubble = true;
if(e.stopPropagation) e.stopPropagation();
return true;
} else {
return false;
}
};
Story.prototype.onTiddlerKeyPress = function(e)
{
if(!e) var e = window.event;
clearMessage();
var consume = false;
var title = this.getAttribute("tiddler");
var target = resolveTarget(e);
switch(e.keyCode) {
case 9: // Tab
if(config.options.chkInsertTabs & & target.tagName.toLowerCase() == "textarea") {
replaceSelection(target,String.fromCharCode(9));
consume = true;
}
if(config.isOpera) {
target.onblur = function() {
this.focus();
this.onblur = null;
};
}
break;
case 13: // Ctrl-Enter
case 10: // Ctrl-Enter on IE PC
case 77: // Ctrl-Enter is "M" on some platforms
if(e.ctrlKey) {
blurElement(this);
config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
consume = true;
}
break;
case 27: // Escape
blurElement(this);
config.macros.toolbar.invokeCommand(this,"cancelCommand",e);
consume = true;
break;
}
e.cancelBubble = consume;
if(consume) {
if(e.stopPropagation) e.stopPropagation(); // Stop Propagation
e.returnValue = true; // Cancel The Event in IE
if(e.preventDefault ) e.preventDefault(); // Cancel The Event in Moz
}
return !consume;
};
Story.prototype.getTiddlerField = function(title,field)
{
var tiddlerElem = document.getElementById(this.idPrefix + title);
var e = null;
if(tiddlerElem != null) {
var children = tiddlerElem.getElementsByTagName("*");
for(var t=0; t< children.length ; t + + ) {
var c = children[t];
if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea") {
if(!e)
e = c;
if(c.getAttribute("edit") == field)
e = c;
}
}
}
return e;
};
Story.prototype.focusTiddler = function(title,field)
{
var e = this.getTiddlerField(title,field);
if(e) {
e.focus();
e.select();
}
};
Story.prototype.blurTiddler = function(title)
{
var tiddlerElem = document.getElementById(this.idPrefix + title);
if(tiddlerElem != null & & tiddlerElem.focus & & tiddlerElem.blur) {
tiddlerElem.focus();
tiddlerElem.blur();
}
};
Story.prototype.setTiddlerField = function(title,tag,mode,field)
{
var c = story.getTiddlerField(title,field);
var tags = c.value.readBracketedList();
tags.setItem(tag,mode);
c.value = String.encodeTiddlyLinkList(tags);
};
Story.prototype.setTiddlerTag = function(title,tag,mode)
{
Story.prototype.setTiddlerField(title,tag,mode,"tags");
};
Story.prototype.closeTiddler = function(title,animate,unused)
{
var tiddlerElem = document.getElementById(this.idPrefix + title);
if(tiddlerElem != null) {
clearMessage();
this.scrubTiddler(tiddlerElem);
if(config.options.chkAnimate & & animate & & anim & & typeof Slider == "function")
anim.startAnimating(new Slider(tiddlerElem,false,null,"all"));
else {
removeNode(tiddlerElem);
forceReflow();
}
}
};
Story.prototype.scrubTiddler = function(tiddlerElem)
{
tiddlerElem.id = null;
};
Story.prototype.setDirty = function(title,dirty)
{
var tiddlerElem = document.getElementById(this.idPrefix + title);
if(tiddlerElem != null)
tiddlerElem.setAttribute("dirty",dirty ? "true" : "false");
};
Story.prototype.isDirty = function(title)
{
var tiddlerElem = document.getElementById(this.idPrefix + title);
if(tiddlerElem != null)
return tiddlerElem.getAttribute("dirty") == "true";
return null;
};
Story.prototype.areAnyDirty = function()
{
var r = false;
this.forEachTiddler(function(title,element) {
if(this.isDirty(title))
r = true;
});
return r;
};
Story.prototype.closeAllTiddlers = function(exclude)
{
clearMessage();
this.forEachTiddler(function(title,element) {
if((title != exclude) & & element.getAttribute("dirty") != "true")
this.closeTiddler(title);
});
window.scrollTo(0,ensureVisible(this.container));
};
Story.prototype.isEmpty = function()
{
var place = document.getElementById(this.container);
return place & & place.firstChild == null;
};
Story.prototype.search = function(text,useCaseSensitive,useRegExp)
{
this.closeAllTiddlers();
highlightHack = new RegExp(useRegExp ? text : text.escapeRegExp(),useCaseSensitive ? "mg" : "img");
var matches = store.search(highlightHack,"title","excludeSearch");
var titles = [];
for(var t=0;t< matches.length ; t + + )
titles.push(matches[t].title);
this.displayTiddlers(null,titles);
highlightHack = null;
var q = useRegExp ? "/" : "'";
if(matches.length > 0)
displayMessage(config.macros.search.successMsg.format([titles.length.toString(),q + text + q]));
else
displayMessage(config.macros.search.failureMsg.format([q + text + q]));
};
Story.prototype.findContainingTiddler = function(e)
{
while(e & & !hasClass(e,"tiddler"))
e = e.parentNode;
return e;
};
Story.prototype.gatherSaveFields = function(e,fields)
{
if(e & & e.getAttribute) {
var f = e.getAttribute("edit");
if(f)
fields[f] = e.value.replace(/\r/mg,"");
if(e.hasChildNodes()) {
var c = e.childNodes;
for(var t=0; t< c.length ; t + + )
this.gatherSaveFields(c[t],fields);
}
}
};
Story.prototype.hasChanges = function(title)
{
var e = document.getElementById(this.idPrefix + title);
if(e != null) {
var fields = {};
this.gatherSaveFields(e,fields);
var tiddler = store.fetchTiddler(title);
if(!tiddler)
return false;
for(var n in fields) {
if(store.getValue(title,n) != fields[n])
return true;
}
}
return false;
};
Story.prototype.saveTiddler = function(title,minorUpdate)
{
var tiddlerElem = document.getElementById(this.idPrefix + title);
if(tiddlerElem != null) {
var fields = {};
this.gatherSaveFields(tiddlerElem,fields);
var newTitle = fields.title ? fields.title : title;
if(store.tiddlerExists(newTitle) & & newTitle != title) {
if(!confirm(config.messages.overwriteWarning.format([newTitle.toString()])))
return null;
}
if(newTitle != title)
this.closeTiddler(newTitle,false);
tiddlerElem.id = this.idPrefix + newTitle;
tiddlerElem.setAttribute("tiddler",newTitle);
tiddlerElem.setAttribute("template",DEFAULT_VIEW_TEMPLATE);
tiddlerElem.setAttribute("dirty","false");
if(config.options.chkForceMinorUpdate)
minorUpdate = !minorUpdate;
if(!store.tiddlerExists(newTitle))
minorUpdate = false;
var newDate = new Date();
var extendedFields = store.tiddlerExists(newTitle) ? store.fetchTiddler(newTitle).fields : {};
for(var n in fields) {
if(!TiddlyWiki.isStandardField(n))
extendedFields[n] = fields[n];
}
var tiddler = store.saveTiddler(title,newTitle,fields.text,minorUpdate ? undefined : config.options.txtUserName,minorUpdate ? undefined : newDate,fields.tags,extendedFields);
autoSaveChanges(null,[tiddler]);
return newTitle;
}
return null;
};
Story.prototype.permaView = function()
{
var links = [];
this.forEachTiddler(function(title,element) {
links.push(String.encodeTiddlyLink(title));
});
var t = encodeURIComponent(links.join(" "));
if(t == "")
t = "#";
if(window.location.hash != t)
window.location.hash = t;
};
//--
//-- Backstage
//--
var backstage = {
area: null,
toolbar: null,
button: null,
showButton: null,
hideButton: null,
cloak: null,
panel: null,
panelBody: null,
panelFooter: null,
currTabName: null,
currTabElem: null,
content: null,
init: function() {
var cmb = config.messages.backstage;
this.area = document.getElementById("backstageArea");
this.toolbar = document.getElementById("backstageToolbar");
this.button = document.getElementById("backstageButton");
this.button.style.display = "block";
var t = cmb.open.text + " " + glyph("bentArrowLeft");
this.showButton = createTiddlyButton(this.button,t,cmb.open.tooltip,
function (e) {backstage.show(); return false;},null,"backstageShow");
t = glyph("bentArrowRight") + " " + cmb.close.text;
this.hideButton = createTiddlyButton(this.button,t,cmb.close.tooltip,
function (e) {backstage.hide(); return false;},null,"backstageHide");
this.cloak = document.getElementById("backstageCloak");
this.panel = document.getElementById("backstagePanel");
this.panelFooter = createTiddlyElement(this.panel,"div",null,"backstagePanelFooter");
this.panelBody = createTiddlyElement(this.panel,"div",null,"backstagePanelBody");
this.cloak.onmousedown = function(e) {
backstage.switchTab(null);
};
createTiddlyText(this.toolbar,cmb.prompt);
for(t=0; t< config.backstageTasks.length ; t + + ) {
var taskName = config.backstageTasks[t];
var task = config.tasks[taskName];
var handler = task.action ? this.onClickCommand : this.onClickTab;
var text = task.text + (task.action ? "" : glyph("downTriangle"));
var btn = createTiddlyButton(this.toolbar,text,task.tooltip,handler,"backstageTab");
btn.setAttribute("task",taskName);
addClass(btn,task.action ? "backstageAction" : "backstageTask");
}
this.content = document.getElementById("contentWrapper");
if(config.options.chkBackstage)
this.show();
else
this.hide();
},
isVisible: function () {
return this.area ? this.area.style.display == "block" : false;
},
show: function() {
this.area.style.display = "block";
if(anim & & config.options.chkAnimate) {
backstage.toolbar.style.left = findWindowWidth() + "px";
var p = [
{style: "left", start: findWindowWidth(), end: 0, template: "%0px"}
];
anim.startAnimating(new Morpher(backstage.toolbar,config.animDuration,p));
} else {
backstage.area.style.left = "0px";
}
this.showButton.style.display = "none";
this.hideButton.style.display = "block";
config.options.chkBackstage = true;
saveOptionCookie("chkBackstage");
addClass(this.content,"backstageVisible");
},
hide: function() {
if(this.currTabElem) {
this.switchTab(null);
} else {
backstage.toolbar.style.left = "0px";
if(anim & & config.options.chkAnimate) {
var p = [
{style: "left", start: 0, end: findWindowWidth(), template: "%0px"}
];
var c = function(element,properties) {backstage.area.style.display = "none";};
anim.startAnimating(new Morpher(backstage.toolbar,config.animDuration,p,c));
} else {
this.area.style.display = "none";
}
this.showButton.style.display = "block";
this.hideButton.style.display = "none";
config.options.chkBackstage = false;
saveOptionCookie("chkBackstage");
removeClass(this.content,"backstageVisible");
}
},
onClickCommand: function(e) {
var task = config.tasks[this.getAttribute("task")];
displayMessage(task);
if(task.action) {
backstage.switchTab(null);
task.action();
}
return false;
},
onClickTab: function(e) {
backstage.switchTab(this.getAttribute("task"));
return false;
},
// Switch to a given tab, or none if null is passed
switchTab: function(tabName) {
var tabElem = null;
var e = this.toolbar.firstChild;
while(e)
{
if(e.getAttribute & & e.getAttribute("task") == tabName)
tabElem = e;
e = e.nextSibling;
}
if(tabName == backstage.currTabName)
return;
if(backstage.currTabElem) {
removeClass(this.currTabElem,"backstageSelTab");
}
if(tabElem & & tabName) {
backstage.preparePanel();
addClass(tabElem,"backstageSelTab");
var task = config.tasks[tabName];
wikify(task.content,backstage.panelBody,null,null);
backstage.showPanel();
} else if(backstage.currTabElem) {
backstage.hidePanel();
}
backstage.currTabName = tabName;
backstage.currTabElem = tabElem;
},
isPanelVisible: function() {
return backstage.panel ? backstage.panel.style.display == "block" : false;
},
preparePanel: function() {
backstage.cloak.style.height = findWindowHeight() + "px";
backstage.cloak.style.display = "block";
removeChildren(backstage.panelBody);
return backstage.panelBody;
},
showPanel: function() {
backstage.panel.style.display = "block";
if(anim & & config.options.chkAnimate) {
backstage.panel.style.top = (-backstage.panel.offsetHeight) + "px";
var p = [
{style: "top", start: -backstage.panel.offsetHeight, end: 0, template: "%0px"}
];
anim.startAnimating(new Morpher(backstage.panel,config.animDuration,p),new Scroller(backstage.panel,false));
} else {
backstage.panel.style.top = "0px";
}
return backstage.panelBody;
},
hidePanel: function() {
backstage.currTabName = null;
backstage.currTabElem = null;
if(anim & & config.options.chkAnimate) {
var p = [
{style: "top", start: 0, end: -(backstage.panel.offsetHeight), template: "%0px"},
{style: "display", atEnd: "none"}
];
var c = function(element,properties) {backstage.cloak.style.display = "none";};
anim.startAnimating(new Morpher(backstage.panel,config.animDuration,p,c));
} else {
backstage.panel.style.display = "none";
backstage.cloak.style.display = "none";
}
}
};
config.macros.backstage = {};
config.macros.backstage.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
var backstageTask = config.tasks[params[0]];
if(backstageTask)
createTiddlyButton(place,backstageTask.text,backstageTask.tooltip,function(e) {backstage.switchTab(params[0]); return false;});
};
//--
//-- ImportTiddlers macro
//--
config.macros.importTiddlers.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
if(readOnly) {
createTiddlyElement(place,"div",null,"marked",this.readOnlyWarning);
return;
}
var w = new Wizard();
w.createWizard(place,this.wizardTitle);
this.restart(w);
};
config.macros.importTiddlers.onCancel = function(e)
{
var wizard = new Wizard(this);
var place = wizard.clear();
config.macros.importTiddlers.restart(wizard);
return false;
};
config.macros.importTiddlers.restart = function(wizard)
{
wizard.addStep(this.step1Title,this.step1Html);
var s = wizard.getElement("selTypes");
for(var t in config.adaptors) {
var e = createTiddlyElement(s,"option",null,null,t);
e.value = t;
}
s = wizard.getElement("selFeeds");
var feeds = this.getFeeds();
for(t in feeds) {
e = createTiddlyElement(s,"option",null,null,t);
e.value = t;
}
wizard.setValue("feeds",feeds);
s.onchange = config.macros.importTiddlers.onFeedChange;
var fileInput = wizard.getElement("txtBrowse");
fileInput.onchange = config.macros.importTiddlers.onBrowseChange;
fileInput.onkeyup = config.macros.importTiddlers.onBrowseChange;
wizard.setButtons([{caption: this.openLabel, tooltip: this.openPrompt, onClick: config.macros.importTiddlers.onOpen}]);
};
config.macros.importTiddlers.getFeeds = function()
{
var feeds = {};
var tagged = store.getTaggedTiddlers("systemServer","title");
for(var t=0; t< tagged.length ; t + + ) {
var title = tagged[t].title;
var serverType = store.getTiddlerSlice(title,"Type");
if(!serverType)
serverType = "file";
feeds[title] = {title: title,
url: store.getTiddlerSlice(title,"URL"),
workspace: store.getTiddlerSlice(title,"Workspace"),
workspaceList: store.getTiddlerSlice(title,"WorkspaceList"),
tiddlerFilter: store.getTiddlerSlice(title,"TiddlerFilter"),
serverType: serverType,
description: store.getTiddlerSlice(title,"Description")};
}
return feeds;
};
config.macros.importTiddlers.onFeedChange = function(e)
{
var wizard = new Wizard(this);
var selTypes = wizard.getElement("selTypes");
var fileInput = wizard.getElement("txtPath");
var feeds = wizard.getValue("feeds");
var f = feeds[this.value];
if(f) {
selTypes.value = f.serverType;
fileInput.value = f.url;
this.selectedIndex = 0;
wizard.setValue("feedName",f.serverType);
wizard.setValue("feedHost",f.url);
wizard.setValue("feedWorkspace",f.workspace);
wizard.setValue("feedWorkspaceList",f.workspaceList);
wizard.setValue("feedTiddlerFilter",f.tiddlerFilter);
}
return false;
};
config.macros.importTiddlers.onBrowseChange = function(e)
{
var wizard = new Wizard(this);
var fileInput = wizard.getElement("txtPath");
fileInput.value = "file://" + this.value;
var serverType = wizard.getElement("selTypes");
serverType.value = "file";
return false;
};
config.macros.importTiddlers.onOpen = function(e)
{
var wizard = new Wizard(this);
var fileInput = wizard.getElement("txtPath");
var url = fileInput.value;
var serverType = wizard.getElement("selTypes").value;
var adaptor = new config.adaptors[serverType];
wizard.setValue("adaptor",adaptor);
wizard.setValue("serverType",serverType);
wizard.setValue("host",url);
var context = {};
var ret = adaptor.openHost(url,context,wizard,config.macros.importTiddlers.onOpenHost);
if(ret !== true)
displayMessage(ret);
wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusOpenHost);
return false;
};
config.macros.importTiddlers.onOpenHost = function(context,wizard)
{
var adaptor = wizard.getValue("adaptor");
if(context.status !== true)
displayMessage("Error in importTiddlers.onOpenHost: " + context.statusText);
var ret = adaptor.getWorkspaceList(context,wizard,config.macros.importTiddlers.onGetWorkspaceList);
if(ret !== true)
displayMessage(ret);
wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusGetWorkspaceList);
};
config.macros.importTiddlers.onGetWorkspaceList = function(context,wizard)
{
if(context.status !== true)
displayMessage("Error in importTiddlers.onGetWorkspaceList: " + context.statusText);
wizard.addStep(config.macros.importTiddlers.step2Title,config.macros.importTiddlers.step2Html);
var s = wizard.getElement("selWorkspace");
s.onchange = config.macros.importTiddlers.onWorkspaceChange;
for(var t=0; t< context.workspaces.length ; t + + ) {
var e = createTiddlyElement(s,"option",null,null,context.workspaces[t].title);
e.value = context.workspaces[t].title;
}
var workspaceList = wizard.getValue("feedWorkspaceList");
if(workspaceList) {
var list = workspaceList.parseParams("workspace",null,false,true);
for(var n=1; n< list.length ; n + + ) {
if(context.workspaces.findByField("title",list[n].value) == null) {
e = createTiddlyElement(s,"option",null,null,list[n].value);
e.value = list[n].value;
}
}
}
var workspace = wizard.getValue("feedWorkspace");
if(workspace) {
t = wizard.getElement("txtWorkspace");
t.value = workspace;
}
wizard.setButtons([{caption: config.macros.importTiddlers.openLabel, tooltip: config.macros.importTiddlers.openPrompt, onClick: config.macros.importTiddlers.onChooseWorkspace}]);
};
config.macros.importTiddlers.onWorkspaceChange = function(e)
{
var wizard = new Wizard(this);
var t = wizard.getElement("txtWorkspace");
t.value = this.value;
this.selectedIndex = 0;
return false;
};
config.macros.importTiddlers.onChooseWorkspace = function(e)
{
var wizard = new Wizard(this);
var adaptor = wizard.getValue("adaptor");
var workspace = wizard.getElement("txtWorkspace").value;
wizard.setValue("workspace",workspace);
var context = {};
var ret = adaptor.openWorkspace(workspace,context,wizard,config.macros.importTiddlers.onOpenWorkspace);
if(ret !== true)
displayMessage(ret);
wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusOpenWorkspace);
return false;
};
config.macros.importTiddlers.onOpenWorkspace = function(context,wizard)
{
if(context.status !== true)
displayMessage("Error in importTiddlers.onOpenWorkspace: " + context.statusText);
var adaptor = wizard.getValue("adaptor");
var ret = adaptor.getTiddlerList(context,wizard,config.macros.importTiddlers.onGetTiddlerList,wizard.getValue("feedTiddlerFilter"));
if(ret !== true)
displayMessage(ret);
wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusGetTiddlerList);
};
config.macros.importTiddlers.onGetTiddlerList = function(context,wizard)
{
if(context.status !== true)
displayMessage("Error in importTiddlers.onGetTiddlerList: " + context.statusText);
// Extract data for the listview
var listedTiddlers = [];
if(context.tiddlers) {
for(var n=0; n< context.tiddlers.length ; n + + ) {
var tiddler = context.tiddlers[n];
listedTiddlers.push({
title: tiddler.title,
modified: tiddler.modified,
modifier: tiddler.modifier,
text: tiddler.text ? wikifyPlainText(tiddler.text,100) : "",
tags: tiddler.tags,
size: tiddler.text ? tiddler.text.length : 0,
tiddler: tiddler
});
}
}
listedTiddlers.sort(function(a,b) {return a.title < b.title ? -1 : ( a . title = = b . title ? 0 : + 1 ) ; } ) ;
// Display the listview
wizard.addStep(config.macros.importTiddlers.step3Title,config.macros.importTiddlers.step3Html);
var markList = wizard.getElement("markList");
var listWrapper = document.createElement("div");
markList.parentNode.insertBefore(listWrapper,markList);
var listView = ListView.create(listWrapper,listedTiddlers,config.macros.importTiddlers.listViewTemplate);
wizard.setValue("listView",listView);
var txtSaveTiddler = wizard.getElement("txtSaveTiddler");
txtSaveTiddler.value = config.macros.importTiddlers.generateSystemServerName(wizard);
wizard.setButtons([
{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel},
{caption: config.macros.importTiddlers.importLabel, tooltip: config.macros.importTiddlers.importPrompt, onClick: config.macros.importTiddlers.doImport}
]);
};
config.macros.importTiddlers.generateSystemServerName = function(wizard)
{
var serverType = wizard.getValue("serverType");
var host = wizard.getValue("host");
var workspace = wizard.getValue("workspace");
var pattern = config.macros.importTiddlers[workspace ? "systemServerNamePattern" : "systemServerNamePatternNoWorkspace"];
return pattern.format([serverType,host,workspace]);
};
config.macros.importTiddlers.saveServerTiddler = function(wizard)
{
var txtSaveTiddler = wizard.getElement("txtSaveTiddler").value;
if(store.tiddlerExists(txtSaveTiddler)) {
if(!confirm(config.macros.importTiddlers.confirmOverwriteSaveTiddler.format([txtSaveTiddler])))
return;
store.suspendNotifications();
store.removeTiddler(txtSaveTiddler);
store.resumeNotifications();
}
var serverType = wizard.getValue("serverType");
var host = wizard.getValue("host");
var workspace = wizard.getValue("workspace");
var text = config.macros.importTiddlers.serverSaveTemplate.format([serverType,host,workspace]);
store.saveTiddler(txtSaveTiddler,txtSaveTiddler,text,config.macros.importTiddlers.serverSaveModifier,new Date(),["systemServer"]);
};
config.macros.importTiddlers.doImport = function(e)
{
var wizard = new Wizard(this);
if(wizard.getElement("chkSave").checked)
config.macros.importTiddlers.saveServerTiddler(wizard);
var chkSync = wizard.getElement("chkSync").checked;
wizard.setValue("sync",chkSync);
var listView = wizard.getValue("listView");
var rowNames = ListView.getSelectedRows(listView);
var adaptor = wizard.getValue("adaptor");
var overwrite = new Array();
var t;
for(t=0; t< rowNames.length ; t + + ) {
if(store.tiddlerExists(rowNames[t]))
overwrite.push(rowNames[t]);
}
if(overwrite.length > 0) {
if(!confirm(config.macros.importTiddlers.confirmOverwriteText.format([overwrite.join(", ")])))
return false;
}
wizard.addStep(config.macros.importTiddlers.step4Title.format([rowNames.length]),config.macros.importTiddlers.step4Html);
for(t=0; t< rowNames.length ; t + + ) {
var link = document.createElement("div");
createTiddlyLink(link,rowNames[t],true);
var place = wizard.getElement("markReport");
place.parentNode.insertBefore(link,place);
}
wizard.setValue("remainingImports",rowNames.length);
wizard.setButtons([
{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}
],config.macros.importTiddlers.statusDoingImport);
for(t=0; t< rowNames.length ; t + + ) {
var context = {};
context.allowSynchronous = true;
var inbound = adaptor.getTiddler(rowNames[t],context,wizard,config.macros.importTiddlers.onGetTiddler);
}
return false;
};
config.macros.importTiddlers.onGetTiddler = function(context,wizard)
{
if(!context.status)
displayMessage("Error in importTiddlers.onGetTiddler: " + context.statusText);
var tiddler = context.tiddler;
store.suspendNotifications();
store.saveTiddler(tiddler.title, tiddler.title, tiddler.text, tiddler.modifier, tiddler.modified, tiddler.tags, tiddler.fields, true, tiddler.created);
if(!wizard.getValue("sync")) {
store.setValue(tiddler.title,'server',null);
}
store.resumeNotifications();
if(!context.isSynchronous)
store.notify(tiddler.title,true);
var remainingImports = wizard.getValue("remainingImports")-1;
wizard.setValue("remainingImports",remainingImports);
if(remainingImports == 0) {
if(context.isSynchronous) {
store.notifyAll();
refreshDisplay();
}
wizard.setButtons([
{caption: config.macros.importTiddlers.doneLabel, tooltip: config.macros.importTiddlers.donePrompt, onClick: config.macros.importTiddlers.onCancel}
],config.macros.importTiddlers.statusDoneImport);
autoSaveChanges();
}
};
//--
//-- Sync macro
//--
// Synchronisation handlers
config.syncers = {};
// Sync state.
var currSync = null;
// sync macro
config.macros.sync.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
if(!wikifier.isStatic)
this.startSync(place);
};
config.macros.sync.startSync = function(place)
{
if(currSync)
config.macros.sync.cancelSync();
currSync = {};
currSync.syncList = this.getSyncableTiddlers();
this.createSyncTasks();
this.preProcessSyncableTiddlers();
var wizard = new Wizard();
currSync.wizard = wizard;
wizard.createWizard(place,this.wizardTitle);
wizard.addStep(this.step1Title,this.step1Html);
var markList = wizard.getElement("markList");
var listWrapper = document.createElement("div");
markList.parentNode.insertBefore(listWrapper,markList);
currSync.listView = ListView.create(listWrapper,currSync.syncList,this.listViewTemplate);
this.processSyncableTiddlers();
wizard.setButtons([
{caption: this.syncLabel, tooltip: this.syncPrompt, onClick: this.doSync}
]);
};
config.macros.sync.getSyncableTiddlers = function ()
{
var list = [];
store.forEachTiddler(function(title,tiddler) {
var syncItem = {};
syncItem.serverType = tiddler.getServerType();
syncItem.serverHost = tiddler.fields['server.host'];
syncItem.serverWorkspace = tiddler.fields['server.workspace'];
syncItem.tiddler = tiddler;
syncItem.title = tiddler.title;
syncItem.isTouched = tiddler.isTouched();
syncItem.selected = syncItem.isTouched;
syncItem.syncStatus = config.macros.sync.syncStatusList[syncItem.isTouched ? "changedLocally" : "none"];
syncItem.status = syncItem.syncStatus.text;
if(syncItem.serverType & & syncItem.serverHost)
list.push(syncItem);
});
list.sort(function(a,b) {return a.title < b.title ? -1 : ( a . title = = b . title ? 0 : + 1 ) ; } ) ;
return list;
};
config.macros.sync.preProcessSyncableTiddlers = function()
{
for(var t=0; t< currSync.syncList.length ; t + + ) {
si = currSync.syncList[t];
var ti = si.syncTask.syncMachine.generateTiddlerInfo(si.tiddler);
si.serverUrl = ti.uri;
}
};
config.macros.sync.processSyncableTiddlers = function()
{
for(var t=0; t< currSync.syncList.length ; t + + ) {
si = currSync.syncList[t];
si.rowElement.style.backgroundColor = si.syncStatus.color;
}
};
config.macros.sync.createSyncTasks = function()
{
currSync.syncTasks = [];
for(var t=0; t< currSync.syncList.length ; t + + ) {
var si = currSync.syncList[t];
var r = null;
for(var st=0; st< currSync.syncTasks.length ; st + + ) {
var cst = currSync.syncTasks[st];
if(si.serverType == cst.serverType & & si.serverHost == cst.serverHost & & si.serverWorkspace == cst.serverWorkspace)
r = cst;
}
if(r == null) {
si.syncTask = this.createSyncTask(si);
currSync.syncTasks.push(si.syncTask);
} else {
si.syncTask = r;
r.syncItems.push(si);
}
}
};
config.macros.sync.createSyncTask = function(syncItem)
{
var st = {};
st.serverType = syncItem.serverType;
st.serverHost = syncItem.serverHost;
st.serverWorkspace = syncItem.serverWorkspace;
st.syncItems = [syncItem];
st.syncMachine = new SyncMachine(st.serverType,{
start: function() {
return this.openHost(st.serverHost,"openWorkspace");
},
openWorkspace: function() {
return this.openWorkspace(st.serverWorkspace,"getTiddlerList");
},
getTiddlerList: function() {
return this.getTiddlerList("gotTiddlerList");
},
gotTiddlerList: function(tiddlers) {
for(var t=0; t< st.syncItems.length ; t + + ) {
var si = st.syncItems[t];
var f = tiddlers.findByField("title",si.title);
if(f !== null) {
if(tiddlers[f].fields['server.page.revision'] > si.tiddler.fields['server.page.revision']) {
si.syncStatus = config.macros.sync.syncStatusList[si.isTouched ? 'changedBoth' : 'changedServer'];
}
} else {
si.syncStatus = config.macros.sync.syncStatusList.notFound;
}
config.macros.sync.updateSyncStatus(si);
}
},
getTiddler: function(title) {
return this.getTiddler(title,"onGetTiddler");
},
onGetTiddler: function(tiddler) {
var syncItem = st.syncItems.findByField("title",tiddler.title);
if(syncItem !== null) {
syncItem = st.syncItems[syncItem];
store.saveTiddler(tiddler.title, tiddler.title, tiddler.text, tiddler.modifier, tiddler.modified, tiddler.tags, tiddler.fields, true, tiddler.created);
syncItem.syncStatus = config.macros.sync.syncStatusList.gotFromServer;
config.macros.sync.updateSyncStatus(syncItem);
}
},
putTiddler: function(tiddler) {
return this.putTiddler(tiddler,"onPutTiddler");
},
onPutTiddler: function(tiddler) {
var syncItem = st.syncItems.findByField("title",tiddler.title);
if(syncItem !== null) {
syncItem = st.syncItems[syncItem];
store.resetTiddler(tiddler.title);
syncItem.syncStatus = config.macros.sync.syncStatusList.putToServer;
config.macros.sync.updateSyncStatus(syncItem);
}
}
});
st.syncMachine.go();
return st;
};
config.macros.sync.updateSyncStatus = function(syncItem)
{
var e = syncItem.colElements["status"];
removeChildren(e);
createTiddlyText(e,syncItem.syncStatus.text);
syncItem.rowElement.style.backgroundColor = syncItem.syncStatus.color;
};
config.macros.sync.doSync = function(e)
{
var rowNames = ListView.getSelectedRows(currSync.listView);
for(var t=0; t< currSync.syncList.length ; t + + ) {
var si = currSync.syncList[t];
if(rowNames.indexOf(si.title) != -1) {
config.macros.sync.doSyncItem(si);
}
}
return false;
};
config.macros.sync.doSyncItem = function(syncItem)
{
var r = true;
var sl = config.macros.sync.syncStatusList;
switch(syncItem.syncStatus) {
case sl.changedServer:
r = syncItem.syncTask.syncMachine.go("getTiddler",syncItem.title);
break;
case sl.notFound:
case sl.changedLocally:
case sl.changedBoth:
r = syncItem.syncTask.syncMachine.go("putTiddler",syncItem.tiddler);
break;
default:
break;
}
if(r !== true)
displayMessage("Error in doSyncItem: " + r);
};
config.macros.sync.cancelSync = function()
{
currSync = null;
};
function SyncMachine(serverType,steps)
{
this.serverType = serverType;
this.adaptor = new config.adaptors[serverType];
this.steps = steps;
}
SyncMachine.prototype.go = function(step,varargs)
{
if(!step)
step = "start";
var h = this.steps[step];
if(!h)
return null;
var a = [];
for(var t=1; t< arguments.length ; t + + )
a.push(arguments[t]);
var r = h.apply(this,a);
if(typeof r == "string")
this.invokeError(r);
return r;
};
SyncMachine.prototype.invokeError = function(message)
{
if(this.steps.error)
this.steps.error(message);
};
SyncMachine.prototype.openHost = function(host,nextStep)
{
var me = this;
return me.adaptor.openHost(host,null,null,function(context) {
if(typeof context.status == "string")
me.invokeError(context.status);
else
me.go(nextStep);
});
};
SyncMachine.prototype.getWorkspaceList = function(nextStep)
{
var me = this;
return me.adaptor.getWorkspaceList(null,null,function(context) {
if(typeof context.status == "string")
me.invokeError(context.status);
else
me.go(nextStep,context.workspaces);
});
};
SyncMachine.prototype.openWorkspace = function(workspace,nextStep)
{
var me = this;
return me.adaptor.openWorkspace(workspace,null,null,function(context) {
if(typeof context.status == "string")
me.invokeError(context.status);
else
me.go(nextStep);
});
};
SyncMachine.prototype.getTiddlerList = function(nextStep)
{
var me = this;
return me.adaptor.getTiddlerList(null,null,function(context) {
if(typeof context.status == "string")
me.invokeError(context.status);
else
me.go(nextStep,context.tiddlers);
});
};
SyncMachine.prototype.generateTiddlerInfo = function(tiddler)
{
return this.adaptor.generateTiddlerInfo(tiddler);
};
SyncMachine.prototype.getTiddler = function(title,nextStep)
{
var me = this;
return me.adaptor.getTiddler(title,null,null,function(context) {
if(typeof context.status == "string")
me.invokeError(context.status);
else
me.go(nextStep,context.tiddler);
});
};
SyncMachine.prototype.putTiddler = function(tiddler,nextStep)
{
var me = this;
return me.adaptor.putTiddler(tiddler,null,null,function(context) {
if(typeof context.status == "string")
me.invokeError(context.status);
else
me.go(nextStep,tiddler);
});
};
//--
//-- Manager UI for groups of tiddlers
//--
config.macros.plugins.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
var wizard = new Wizard();
wizard.createWizard(place,this.wizardTitle);
wizard.addStep(this.step1Title,this.step1Html);
var markList = wizard.getElement("markList");
var listWrapper = document.createElement("div");
markList.parentNode.insertBefore(listWrapper,markList);
listWrapper.setAttribute("refresh","macro");
listWrapper.setAttribute("macroName","plugins");
listWrapper.setAttribute("params",paramString);
this.refresh(listWrapper,paramString);
};
config.macros.plugins.refresh = function(listWrapper,params)
{
var wizard = new Wizard(listWrapper);
var selectedRows = [];
ListView.forEachSelector(listWrapper,function(e,rowName) {
if(e.checked)
selectedRows.push(e.getAttribute("rowName"));
});
removeChildren(listWrapper);
params = params.parseParams("anon");
var plugins = installedPlugins.slice(0);
var t,tiddler,p;
var configTiddlers = store.getTaggedTiddlers("systemConfig");
for(t=0; t< configTiddlers.length ; t + + ) {
tiddler = configTiddlers[t];
if(plugins.findByField("title",tiddler.title) == null) {
p = getPluginInfo(tiddler);
p.executed = false;
p.log.splice(0,0,this.skippedText);
plugins.push(p);
}
}
for(t=0; t< plugins.length ; t + + ) {
p = plugins[t];
p.size = p.tiddler.text ? p.tiddler.text.length : 0;
p.forced = p.tiddler.isTagged("systemConfigForce");
p.disabled = p.tiddler.isTagged("systemConfigDisable");
p.Selected = selectedRows.indexOf(plugins[t].title) != -1;
}
if(plugins.length == 0) {
createTiddlyElement(listWrapper,"em",null,null,this.noPluginText);
wizard.setButtons([]);
} else {
var listView = ListView.create(listWrapper,plugins,this.listViewTemplate,this.onSelectCommand);
wizard.setValue("listView",listView);
wizard.setButtons([
{caption: config.macros.plugins.removeLabel, tooltip: config.macros.plugins.removePrompt, onClick: config.macros.plugins.doRemoveTag},
{caption: config.macros.plugins.deleteLabel, tooltip: config.macros.plugins.deletePrompt, onClick: config.macros.plugins.doDelete}
]);
}
};
config.macros.plugins.doRemoveTag = function(e)
{
var wizard = new Wizard(this);
var listView = wizard.getValue("listView");
var rowNames = ListView.getSelectedRows(listView);
if(rowNames.length == 0) {
alert(config.messages.nothingSelected);
} else {
for(var t=0; t< rowNames.length ; t + + )
store.setTiddlerTag(rowNames[t],false,"systemConfig");
}
};
config.macros.plugins.doDelete = function(e)
{
var wizard = new Wizard(this);
var listView = wizard.getValue("listView");
var rowNames = ListView.getSelectedRows(listView);
if(rowNames.length == 0) {
alert(config.messages.nothingSelected);
} else {
if(confirm(config.macros.plugins.confirmDeleteText.format([rowNames.join(", ")]))) {
for(t=0; t< rowNames.length ; t + + ) {
store.removeTiddler(rowNames[t]);
story.closeTiddler(rowNames[t],true);
}
}
}
};
//--
//-- Message area
//--
function getMessageDiv()
{
var msgArea = document.getElementById("messageArea");
if(!msgArea)
return null;
if(!msgArea.hasChildNodes())
createTiddlyButton(createTiddlyElement(msgArea,"div",null,"messageToolbar"),
config.messages.messageClose.text,
config.messages.messageClose.tooltip,
clearMessage);
msgArea.style.display = "block";
return createTiddlyElement(msgArea,"div");
}
function displayMessage(text,linkText)
{
var e = getMessageDiv();
if(!e) {
alert(text);
return;
}
if(linkText) {
var link = createTiddlyElement(e,"a",null,null,text);
link.href = linkText;
link.target = "_blank";
} else {
e.appendChild(document.createTextNode(text));
}
}
function clearMessage()
{
var msgArea = document.getElementById("messageArea");
if(msgArea) {
removeChildren(msgArea);
msgArea.style.display = "none";
}
return false;
}
//--
//-- Refresh mechanism
//--
config.refreshers = {
link: function(e,changeList)
{
var title = e.getAttribute("tiddlyLink");
refreshTiddlyLink(e,title);
return true;
},
tiddler: function(e,changeList)
{
var title = e.getAttribute("tiddler");
var template = e.getAttribute("template");
if(changeList & & changeList.indexOf(title) != -1 & & !story.isDirty(title))
story.refreshTiddler(title,template,true);
else
refreshElements(e,changeList);
return true;
},
content: function(e,changeList)
{
var title = e.getAttribute("tiddler");
var force = e.getAttribute("force");
if(force != null || changeList == null || changeList.indexOf(title) != -1) {
removeChildren(e);
wikify(store.getTiddlerText(title,title),e,null);
return true;
} else
return false;
},
macro: function(e,changeList)
{
var macro = e.getAttribute("macroName");
var params = e.getAttribute("params");
if(macro)
macro = config.macros[macro];
if(macro & & macro.refresh)
macro.refresh(e,params);
return true;
}
};
function refreshElements(root,changeList)
{
var nodes = root.childNodes;
for(var c=0; c< nodes.length ; c + + ) {
var e = nodes[c], type = null;
if(e.getAttribute & & (e.tagName ? e.tagName != "IFRAME" : true))
type = e.getAttribute("refresh");
var refresher = config.refreshers[type];
var refreshed = false;
if(refresher != undefined)
refreshed = refresher(e,changeList);
if(e.hasChildNodes() & & !refreshed)
refreshElements(e,changeList);
}
}
function applyHtmlMacros(root,tiddler)
{
var e = root.firstChild;
while(e) {
var nextChild = e.nextSibling;
if(e.getAttribute) {
var macro = e.getAttribute("macro");
if(macro) {
var params = "";
var p = macro.indexOf(" ");
if(p != -1) {
params = macro.substr(p+1);
macro = macro.substr(0,p);
}
invokeMacro(e,macro,params,null,tiddler);
}
}
if(e.hasChildNodes())
applyHtmlMacros(e,tiddler);
e = nextChild;
}
}
function refreshPageTemplate(title)
{
var stash = createTiddlyElement(document.body,"div");
stash.style.display = "none";
var display = document.getElementById("tiddlerDisplay");
var nodes,t;
if(display) {
nodes = display.childNodes;
for(t=nodes.length-1; t>=0; t--)
stash.appendChild(nodes[t]);
}
var wrapper = document.getElementById("contentWrapper");
if(!title)
title = "PageTemplate";
var html = store.getRecursiveTiddlerText(title,null,10);
wrapper.innerHTML = html;
applyHtmlMacros(wrapper);
refreshElements(wrapper);
display = document.getElementById("tiddlerDisplay");
removeChildren(display);
if(!display)
display = createTiddlyElement(wrapper,"div","tiddlerDisplay");
nodes = stash.childNodes;
for(t=nodes.length-1; t>=0; t--)
display.appendChild(nodes[t]);
removeNode(stash);
}
function refreshDisplay(hint)
{
if(typeof hint == "string")
hint = [hint];
var e = document.getElementById("contentWrapper");
refreshElements(e,hint);
if(backstage.isPanelVisible()) {
e = document.getElementById("backstage");
refreshElements(e,hint);
}
}
function refreshPageTitle()
{
document.title = getPageTitle();
}
function getPageTitle()
{
var st = wikifyPlain("SiteTitle");
var ss = wikifyPlain("SiteSubtitle");
return st + ((st == "" || ss == "") ? "" : " - ") + ss;
}
function refreshStyles(title,doc)
{
if(!doc)
doc = document;
setStylesheet(title == null ? "" : store.getRecursiveTiddlerText(title,"",10),title,doc);
}
function refreshColorPalette(title)
{
if(!startingUp)
refreshAll();
}
function refreshAll()
{
refreshPageTemplate();
refreshDisplay();
refreshStyles("StyleSheetLayout");
refreshStyles("StyleSheetColors");
refreshStyles("StyleSheet");
refreshStyles("StyleSheetPrint");
}
//--
//-- Options cookie stuff
//--
config.optionHandlers = {
'txt': {
get: function(name) {return encodeCookie(config.options[name].toString());},
set: function(name,value) {config.options[name] = decodeCookie(value);}
},
'chk': {
get: function(name) {return config.options[name] ? "true" : "false";},
set: function(name,value) {config.options[name] = value == "true";}
}
};
function loadOptionsCookie()
{
if(safeMode)
return;
var cookies = document.cookie.split(";");
for(var c=0; c< cookies.length ; c + + ) {
var p = cookies[c].indexOf("=");
if(p != -1) {
var name = cookies[c].substr(0,p).trim();
var value = cookies[c].substr(p+1).trim();
var optType = name.substr(0,3);
if(config.optionHandlers[optType] & & config.optionHandlers[optType].set)
config.optionHandlers[optType].set(name,value);
}
}
}
function saveOptionCookie(name)
{
if(safeMode)
return;
var c = name + "=";
var optType = name.substr(0,3);
if(config.optionHandlers[optType] & & config.optionHandlers[optType].get)
c += config.optionHandlers[optType].get(name);
c += "; expires=Fri, 1 Jan 2038 12:00:00 UTC; path=/";
document.cookie = c;
}
function encodeCookie(s)
{
return escape(manualConvertUnicodeToUTF8(s));
}
function decodeCookie(s)
{
s = unescape(s);
var re = /&#[0-9]{1,5}; /g;
return s.replace(re,function($0) {return String.fromCharCode(eval($0.replace(/[&#; ]/g,"")));});
}
//--
//-- Saving
//--
var saveUsingSafari = false;
var startSaveArea = '< div id = "' + 'storeArea" > '; // Split up into two so that indexOf() of this source doesn't find it
var endSaveArea = '< /d' + 'iv>';
// If there are unsaved changes, force the user to confirm before exitting
function confirmExit()
{
hadConfirmExit = true;
if((store & & store.isDirty & & store.isDirty()) || (story & & story.areAnyDirty & & story.areAnyDirty()))
return config.messages.confirmExit;
}
// Give the user a chance to save changes before exitting
function checkUnsavedChanges()
{
if(store & & store.isDirty & & store.isDirty() & & window.hadConfirmExit === false) {
if(confirm(config.messages.unsavedChangesWarning))
saveChanges();
}
}
function updateLanguageAttribute(s)
{
if(config.locale) {
var mRE = /(< html ( ? : . * ? ) ? ) ( ? : xml:lang \ = " ( [ a-z ] + ) " ) ? ( ? : lang \ = " ( [ a-z ] + ) " ) ? > /;
var m = mRE.exec(s);
if(m) {
var t = m[1];
if(m[2])
t += ' xml:lang="' + config.locale + '"';
if(m[3])
t += ' lang="' + config.locale + '"';
t += ">";
s = s.substr(0,m.index) + t + s.substr(m.index+m[0].length);
}
}
return s;
}
function updateMarkupBlock(s,blockName,tiddlerName)
{
return s.replaceChunk(
"<!-- %0 - START --> ".format([blockName]),
"<!-- %0 - END --> ".format([blockName]),
"\n" + store.getRecursiveTiddlerText(tiddlerName,"") + "\n");
}
function updateOriginal(original,posDiv)
{
if(!posDiv)
posDiv = locateStoreArea(original);
if(!posDiv) {
alert(config.messages.invalidFileError.format([localPath]));
return null;
}
var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n" +
convertUnicodeToUTF8(store.allTiddlersAsHtml()) + "\n" +
original.substr(posDiv[1]);
var newSiteTitle = convertUnicodeToUTF8(getPageTitle()).htmlEncode();
revised = revised.replaceChunk("< title " + " > ","< /title"+">"," " + newSiteTitle + " ");
revised = updateLanguageAttribute(revised);
revised = updateMarkupBlock(revised,"PRE-HEAD","MarkupPreHead");
revised = updateMarkupBlock(revised,"POST-HEAD","MarkupPostHead");
revised = updateMarkupBlock(revised,"PRE-BODY","MarkupPreBody");
revised = updateMarkupBlock(revised,"POST-SCRIPT","MarkupPostBody");
return revised;
}
function locateStoreArea(original)
{
// Locate the storeArea div's
var posOpeningDiv = original.indexOf(startSaveArea);
var limitClosingDiv = original.indexOf("< "+"!--POST-STOREAREA--"+">");
if(limitClosingDiv == -1)
limitClosingDiv = original.indexOf("< "+"!--POST-BODY-START--"+">");
var posClosingDiv = original.lastIndexOf(endSaveArea,limitClosingDiv == -1 ? original.length : limitClosingDiv);
return (posOpeningDiv != -1 & & posClosingDiv != -1) ? [posOpeningDiv,posClosingDiv] : null;
}
function autoSaveChanges(onlyIfDirty,tiddlers)
{
if(config.options.chkAutoSave)
saveChanges(onlyIfDirty,tiddlers);
}
// Save this tiddlywiki with the pending changes
function saveChanges(onlyIfDirty,tiddlers)
{
if(onlyIfDirty & & !store.isDirty())
return;
clearMessage();
// Get the URL of the document
var originalPath = document.location.toString();
// Check we were loaded from a file URL
if(originalPath.substr(0,5) != "file:") {
alert(config.messages.notFileUrlError);
if(store.tiddlerExists(config.messages.saveInstructions))
story.displayTiddler(null,config.messages.saveInstructions);
return;
}
var localPath = getLocalPath(originalPath);
// Load the original file
var original = loadFile(localPath);
if(original == null) {
alert(config.messages.cantSaveError);
if(store.tiddlerExists(config.messages.saveInstructions))
story.displayTiddler(null,config.messages.saveInstructions);
return;
}
// Locate the storeArea div's
var posDiv = locateStoreArea(original);
if(!posDiv) {
alert(config.messages.invalidFileError.format([localPath]));
return;
}
saveBackup(localPath,original);
saveRss(localPath);
saveEmpty(localPath,original,posDiv);
saveMain(localPath,original,posDiv);
}
function saveBackup(localPath,original)
{
// Save the backup
if(config.options.chkSaveBackups) {
var backupPath = getBackupPath(localPath);
var backup = config.browser.isIE ? ieCopyFile(backupPath,localPath) : saveFile(backupPath,original);
if(backup)
displayMessage(config.messages.backupSaved,"file://" + backupPath);
else
alert(config.messages.backupFailed);
}
}
function saveRss(localPath)
{
if(config.options.chkGenerateAnRssFeed) {
var rssPath = localPath.substr(0,localPath.lastIndexOf(".")) + ".xml";
var rssSave = saveFile(rssPath,convertUnicodeToUTF8(generateRss()));
if(rssSave)
displayMessage(config.messages.rssSaved,"file://" + rssPath);
else
alert(config.messages.rssFailed);
}
}
function saveEmpty(localPath,original,posDiv)
{
if(config.options.chkSaveEmptyTemplate) {
var emptyPath,p;
if((p = localPath.lastIndexOf("/")) != -1)
emptyPath = localPath.substr(0,p) + "/empty.html";
else if((p = localPath.lastIndexOf("\\")) != -1)
emptyPath = localPath.substr(0,p) + "\\empty.html";
else
emptyPath = localPath + ".empty.html";
var empty = original.substr(0,posDiv[0] + startSaveArea.length) + original.substr(posDiv[1]);
var emptySave = saveFile(emptyPath,empty);
if(emptySave)
displayMessage(config.messages.emptySaved,"file://" + emptyPath);
else
alert(config.messages.emptyFailed);
}
}
function saveMain(localPath,original,posDiv)
{
var save;
try {
var revised = updateOriginal(original,posDiv);
save = saveFile(localPath,revised);
} catch (ex) {
showException(ex);
}
if(save) {
displayMessage(config.messages.mainSaved,"file://" + localPath);
store.setDirty(false);
} else {
alert(config.messages.mainFailed);
}
}
function getLocalPath(origPath)
{
var originalPath = convertUriToUTF8(origPath,config.options.txtFileSystemCharSet);
// Remove any location or query part of the URL
var argPos = originalPath.indexOf("?");
if(argPos != -1)
originalPath = originalPath.substr(0,argPos);
var hashPos = originalPath.indexOf("#");
if(hashPos != -1)
originalPath = originalPath.substr(0,hashPos);
// Convert file://localhost/ to file:///
if(originalPath.indexOf("file://localhost/") == 0)
originalPath = "file://" + originalPath.substr(16);
// Convert to a native file format
var localPath;
if(originalPath.charAt(9) == ":") // pc local file
localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\");
else if(originalPath.indexOf("file://///") == 0) // FireFox pc network file
localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\");
else if(originalPath.indexOf("file:///") == 0) // mac/unix local file
localPath = unescape(originalPath.substr(7));
else if(originalPath.indexOf("file:/") == 0) // mac/unix local file
localPath = unescape(originalPath.substr(5));
else // pc network file
localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\");
return localPath;
}
function getBackupPath(localPath)
{
var backSlash = true;
var dirPathPos = localPath.lastIndexOf("\\");
if(dirPathPos == -1) {
dirPathPos = localPath.lastIndexOf("/");
backSlash = false;
}
var backupFolder = config.options.txtBackupFolder;
if(!backupFolder || backupFolder == "")
backupFolder = ".";
var backupPath = localPath.substr(0,dirPathPos) + (backSlash ? "\\" : "/") + backupFolder + localPath.substr(dirPathPos);
backupPath = backupPath.substr(0,backupPath.lastIndexOf(".")) + "." + (new Date()).convertToYYYYMMDDHHMMSSMMM() + ".html";
return backupPath;
}
function generateRss()
{
var s = [];
var d = new Date();
var u = store.getTiddlerText("SiteUrl");
// Assemble the header
s.push("< " + "?xml version=\"1.0\"?" + ">");
s.push("< rss version = \"2.0\" > ");
s.push("< channel > ");
s.push("< title " + " > " + wikifyPlain("SiteTitle").htmlEncode() + "< /title" + ">");
if(u)
s.push("< link > " + u.htmlEncode() + "< / link > ");
s.push("< description > " + wikifyPlain("SiteSubtitle").htmlEncode() + "< / description > ");
s.push("< language > en-us< / language > ");
s.push("< copyright > Copyright " + d.getFullYear() + " " + config.options.txtUserName.htmlEncode() + "< / copyright > ");
s.push("< pubDate > " + d.toGMTString() + "< / pubDate > ");
s.push("< lastBuildDate > " + d.toGMTString() + "< / lastBuildDate > ");
s.push("< docs > http://blogs.law.harvard.edu/tech/rss< / docs > ");
s.push("< generator > TiddlyWiki " + version.major + "." + version.minor + "." + version.revision + "< / generator > ");
// The body
var tiddlers = store.getTiddlers("modified","excludeLists");
var n = config.numRssItems > tiddlers.length ? 0 : tiddlers.length-config.numRssItems;
for (var t=tiddlers.length-1; t>=n; t--)
s.push(tiddlers[t].saveToRss(u));
// And footer
s.push("< / channel > ");
s.push("< / rss > ");
// Save it all
return s.join("\n");
}
//--
//-- Filesystem code
//--
function convertUTF8ToUnicode(u)
{
if(window.netscape == undefined)
return manualConvertUTF8ToUnicode(u);
else
return mozConvertUTF8ToUnicode(u);
}
function manualConvertUTF8ToUnicode(utf)
{
var uni = utf;
var src = 0;
var dst = 0;
var b1, b2, b3;
var c;
while(src < utf.length ) {
b1 = utf.charCodeAt(src++);
if(b1 < 0x80 ) {
dst++;
} else if(b1 < 0xE0 ) {
b2 = utf.charCodeAt(src++);
c = String.fromCharCode(((b1 & 0x1F) < < 6 ) | ( b2 & 0x3F ) ) ;
uni = uni.substring(0,dst++).concat(c,utf.substr(src));
} else {
b2 = utf.charCodeAt(src++);
b3 = utf.charCodeAt(src++);
c = String.fromCharCode(((b1 & 0xF) < < 12 ) | ( ( b2 & 0x3F ) < < 6 ) | ( b3 & 0x3F ) ) ;
uni = uni.substring(0,dst++).concat(c,utf.substr(src));
}
}
return uni;
}
function mozConvertUTF8ToUnicode(u)
{
try {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
} catch(ex) {
return manualConvertUTF8ToUnicode(u);
} // fallback
var s = converter.ConvertToUnicode(u);
var fin = converter.Finish();
return (fin.length > 0) ? s+fin : s;
}
function convertUnicodeToUTF8(s)
{
if(window.netscape == undefined)
return manualConvertUnicodeToUTF8(s);
else
return mozConvertUnicodeToUTF8(s);
}
function manualConvertUnicodeToUTF8(s)
{
var re = /[^\u0000-\u007F]/g ;
return s.replace(re,function($0) {return "& #" + $0.charCodeAt(0).toString() + ";";});
}
function mozConvertUnicodeToUTF8(s)
{
try {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
} catch(ex) {
return manualConvertUnicodeToUTF8(s);
} // fallback
var u = converter.ConvertFromUnicode(s);
var fin = converter.Finish();
if(fin.length > 0)
return u + fin;
else
return u;
}
function convertUriToUTF8(uri,charSet)
{
if(window.netscape == undefined || charSet == undefined || charSet == "")
return uri;
try {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var converter = Components.classes["@mozilla.org/intl/utf8converterservice;1"].getService(Components.interfaces.nsIUTF8ConverterService);
} catch(ex) {
return uri;
}
return converter.convertURISpecToUTF8(uri,charSet);
}
function saveFile(fileUrl,content)
{
var r = null;
if(!r)
r = mozillaSaveFile(fileUrl,content);
if(!r)
r = ieSaveFile(fileUrl,content);
if(!r)
r = javaSaveFile(fileUrl,content);
return r;
}
function loadFile(fileUrl)
{
var r = null;
if((r == null) || (r == false))
r = mozillaLoadFile(fileUrl);
if((r == null) || (r == false))
r = ieLoadFile(fileUrl);
if((r == null) || (r == false))
r = javaLoadFile(fileUrl);
return r;
}
// Returns null if it can't do it, false if there's an error, true if it saved OK
function ieSaveFile(filePath,content)
{
try {
var fso = new ActiveXObject("Scripting.FileSystemObject");
} catch(ex) {
return null;
}
var file = fso.OpenTextFile(filePath,2,-1,0);
file.Write(content);
file.Close();
return true;
}
// Returns null if it can't do it, false if there's an error, or a string of the content if successful
function ieLoadFile(filePath)
{
try {
var fso = new ActiveXObject("Scripting.FileSystemObject");
var file = fso.OpenTextFile(filePath,1);
var content = file.ReadAll();
file.Close();
} catch(ex) {
return null;
}
return content;
}
function ieCopyFile(dest,source)
{
try {
var fso = new ActiveXObject("Scripting.FileSystemObject");
fso.GetFile(source).Copy(dest);
} catch(ex) {
return false;
}
return true;
}
// Returns null if it can't do it, false if there's an error, true if it saved OK
function mozillaSaveFile(filePath,content)
{
if(window.Components) {
try {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
file.initWithPath(filePath);
if(!file.exists())
file.create(0,0664);
var out = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
out.init(file,0x20|0x02,00004,null);
out.write(content,content.length);
out.flush();
out.close();
return true;
} catch(ex) {
return false;
}
}
return null;
}
// Returns null if it can't do it, false if there's an error, or a string of the content if successful
function mozillaLoadFile(filePath)
{
if(window.Components) {
try {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
file.initWithPath(filePath);
if(!file.exists())
return null;
var inputStream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
inputStream.init(file,0x01,00004,null);
var sInputStream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
sInputStream.init(inputStream);
return sInputStream.read(sInputStream.available());
} catch(ex) {
return false;
}
}
return null;
}
function javaUrlToFilename(url)
{
var f = "//localhost";
if(url.indexOf(f) == 0)
return url.substring(f.length);
var i = url.indexOf(":");
if(i > 0)
return url.substring(i-1);
return url;
}
function javaSaveFile(filePath,content)
{
try {
if(document.applets["TiddlySaver"])
return document.applets["TiddlySaver"].saveFile(javaUrlToFilename(filePath),"UTF-8",content);
} catch(ex) {
}
try {
var s = new java.io.PrintStream(new java.io.FileOutputStream(javaUrlToFilename(filePath)));
s.print(content);
s.close();
} catch(ex) {
return null;
}
return true;
}
function javaLoadFile(filePath)
{
try {
if(document.applets["TiddlySaver"])
return String(document.applets["TiddlySaver"].loadFile(javaUrlToFilename(filePath),"UTF-8"));
} catch(ex) {
}
var content = [];
try {
var r = new java.io.BufferedReader(new java.io.FileReader(javaUrlToFilename(filePath)));
var line;
while((line = r.readLine()) != null)
content.push(new String(line));
r.close();
} catch(ex) {
return null;
}
return content.join("\n");
}
//--
//-- Server adaptor for talking to static files
//--
function FileAdaptor()
{
this.host = null;
this.store = null;
return this;
}
FileAdaptor.NotLoadedError = "TiddlyWiki file has not been loaded";
FileAdaptor.serverType = 'file';
// Open the specified host/server
FileAdaptor.prototype.openHost = function(host,context,userParams,callback)
{
this.host = host;
if(!context)
context = {};
context.adaptor = this;
context.callback = callback;
context.userParams = userParams;
var ret = loadRemoteFile(host,FileAdaptor.openHostCallback,context);
return typeof(ret) == "string" ? ret : true;
};
FileAdaptor.openHostCallback = function(status,context,responseText,url,xhr)
{
var adaptor = context.adaptor;
context.status = status;
if(!status) {
context.statusText = "Error reading file: " + xhr.statusText;
} else {
// Load the content into a TiddlyWiki() object
adaptor.store = new TiddlyWiki();
if(!adaptor.store.importTiddlyWiki(responseText))
context.statusText = config.messages.invalidFileError.format([url]);
}
context.callback(context,context.userParams);
};
// Gets the list of workspaces on a given server
FileAdaptor.prototype.getWorkspaceList = function(context,userParams,callback)
{
if(!context)
context = {};
context.workspaces = [{title:"(default)"}];
context.status = true;
window.setTimeout(function() {callback(context,userParams);},10);
return true;
};
// Open the specified workspace
FileAdaptor.prototype.openWorkspace = function(workspace,context,userParams,callback)
{
if(!context)
context = {};
context.status = true;
window.setTimeout(function() {callback(context,userParams);},10);
return true;
};
// Gets the list of tiddlers within a given workspace
FileAdaptor.prototype.getTiddlerList = function(context,userParams,callback)
{
if(!this.store)
return FileAdaptor.NotLoadedError;
if(!context)
context = {};
context.tiddlers = [];
this.store.forEachTiddler(function(title,tiddler)
{
var t = new Tiddler(title);
t.text = tiddler.text;
t.modified = tiddler.modified;
t.modifier = tiddler.modifier;
t.fields['server.page.revision'] = tiddler.modified.convertToYYYYMMDDHHMM();
t.tags = tiddler.tags;
context.tiddlers.push(t);
});
context.status = true;
window.setTimeout(function() {callback(context,userParams);},10);
return true;
};
FileAdaptor.prototype.generateTiddlerInfo = function(tiddler)
{
var info = {};
info.uri = tiddler.fields['server.host'] + "#" + tiddler.title;
return info;
};
// Retrieves a tiddler from a given workspace on a given server
FileAdaptor.prototype.getTiddler = function(title,context,userParams,callback)
{
if(!this.store)
return FileAdaptor.NotLoadedError;
if(!context)
context = {};
context.tiddler = this.store.fetchTiddler(title);
if(context.tiddler) {
context.tiddler.fields['server.type'] = FileAdaptor.serverType;
context.tiddler.fields['server.host'] = this.host;
context.tiddler.fields['server.page.revision'] = context.tiddler.modified.convertToYYYYMMDDHHMM();
}
context.status = true;
if(context.allowSynchronous) {
context.isSynchronous = true;
callback(context,userParams);
} else {
window.setTimeout(function() {callback(context,userParams);},10);
}
return true;
};
FileAdaptor.prototype.close = function()
{
delete this.store;
this.store = null;
};
config.adaptors[FileAdaptor.serverType] = FileAdaptor;
//--
//-- Remote HTTP requests
//--
function loadRemoteFile(url,callback,params)
{
return doHttp("GET",url,null,null,null,null,callback,params,null);
}
// HTTP status codes
var httpStatus = {
OK: 200,
ContentCreated: 201,
NoContent: 204,
Unauthorized: 401,
Forbidden: 403,
NotFound: 404,
MethodNotAllowed: 405
};
function doHttp(type,url,data,contentType,username,password,callback,params,headers)
{
// Get an xhr object
var x = getXMLHttpRequest();
if(!x)
return "Can't create XMLHttpRequest object";
// Install callback
x.onreadystatechange = function() {
if (x.readyState == 4 & & callback & & (x.status !== undefined)) {
if([0, httpStatus.OK, httpStatus.ContentCreated, httpStatus.NoContent].contains(x.status))
callback(true,params,x.responseText,url,x);
else
callback(false,params,null,url,x);
x.onreadystatechange = function(){};
x = null;
}
};
// Send request
if(window.Components & & window.netscape & & window.netscape.security & & document.location.protocol.indexOf("http") == -1)
window.netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
try {
url = url + (url.indexOf("?") < 0 ? " ? " : " & " ) + " nocache = " + Math . random ( ) ;
x.open(type,url,true,username,password);
if (data)
x.setRequestHeader("Content-Type", contentType ? contentType : "application/x-www-form-urlencoded");
if (x.overrideMimeType)
x.setRequestHeader("Connection", "close");
if(headers) {
for(n in headers)
x.setRequestHeader(n,headers[n]);
}
x.setRequestHeader("X-Requested-With", "TiddlyWiki " + version.major + "." + version.minor + "." + version.revision + (version.beta ? " (beta " + version.beta + ")" : ""));
x.send(data);
} catch (ex) {
return exceptionText(ex);
}
return x;
}
function getXMLHttpRequest()
{
try {
var x = new XMLHttpRequest(); // Modern
} catch(ex) {
try {
x = new ActiveXObject("Msxml2.XMLHTTP"); // IE 6
} catch (ex2) {
return null;
}
}
return x;
}
//--
//-- TiddlyWiki-specific utility functions
//--
function createTiddlyButton(theParent,theText,theTooltip,theAction,theClass,theId,theAccessKey)
{
var theButton = document.createElement("a");
if(theAction) {
theButton.onclick = theAction;
theButton.setAttribute("href","javascript:;");
}
if(theTooltip)
theButton.setAttribute("title",theTooltip);
if(theText)
theButton.appendChild(document.createTextNode(theText));
if(theClass)
theButton.className = theClass;
else
theButton.className = "button";
if(theId)
theButton.id = theId;
if(theParent)
theParent.appendChild(theButton);
if(theAccessKey)
theButton.setAttribute("accessKey",theAccessKey);
return theButton;
}
function createTiddlyLink(place,title,includeText,theClass,isStatic,linkedFromTiddler,noToggle)
{
var text = includeText ? title : null;
var i = getTiddlyLinkInfo(title,theClass);
var btn = isStatic ? createExternalLink(place,store.getTiddlerText("SiteUrl",null) + "#" + title) : createTiddlyButton(place,text,i.subTitle,onClickTiddlerLink,i.classes);
btn.setAttribute("refresh","link");
btn.setAttribute("tiddlyLink",title);
if(noToggle)
btn.setAttribute("noToggle","true");
if(linkedFromTiddler) {
var fields = linkedFromTiddler.getInheritedFields();
if(fields)
btn.setAttribute("tiddlyFields",fields);
}
return btn;
}
function refreshTiddlyLink(e,title)
{
var i = getTiddlyLinkInfo(title,e.className);
e.className = i.classes;
e.title = i.subTitle;
}
function getTiddlyLinkInfo(title,currClasses)
{
var classes = currClasses ? currClasses.split(" ") : [];
classes.pushUnique("tiddlyLink");
var tiddler = store.fetchTiddler(title);
var subTitle;
if(tiddler) {
subTitle = tiddler.getSubtitle();
classes.pushUnique("tiddlyLinkExisting");
classes.remove("tiddlyLinkNonExisting");
classes.remove("shadow");
} else {
classes.remove("tiddlyLinkExisting");
classes.pushUnique("tiddlyLinkNonExisting");
if(store.isShadowTiddler(title)) {
subTitle = config.messages.shadowedTiddlerToolTip.format([title]);
classes.pushUnique("shadow");
} else {
subTitle = config.messages.undefinedTiddlerToolTip.format([title]);
classes.remove("shadow");
}
}
if(config.annotations[title])
subTitle = config.annotations[title];
return {classes: classes.join(" "),subTitle: subTitle};
}
function createExternalLink(place,url)
{
var theLink = document.createElement("a");
theLink.className = "externalLink";
theLink.href = url;
theLink.title = config.messages.externalLinkTooltip.format([url]);
if(config.options.chkOpenInNewWindow)
theLink.target = "_blank";
place.appendChild(theLink);
return theLink;
}
// Event handler for clicking on a tiddly link
function onClickTiddlerLink(e)
{
if(!e) e = window.event;
var theTarget = resolveTarget(e);
var theLink = theTarget;
var title = null;
var fields = null;
var noToggle = null;
do {
title = theLink.getAttribute("tiddlyLink");
fields = theLink.getAttribute("tiddlyFields");
noToggle = theLink.getAttribute("noToggle");
theLink = theLink.parentNode;
} while(title == null & & theLink != null);
if(!fields & & !store.isShadowTiddler(title))
fields = String.encodeHashMap(config.defaultCustomFields);
if(title) {
var toggling = e.metaKey || e.ctrlKey;
if(config.options.chkToggleLinks)
toggling = !toggling;
if(noToggle)
toggling = false;
story.displayTiddler(theTarget,title,null,true,null,fields,toggling);
}
clearMessage();
return false;
}
// Create a button for a tag with a popup listing all the tiddlers that it tags
function createTagButton(place,tag,excludeTiddler)
{
var theTag = createTiddlyButton(place,tag,config.views.wikified.tag.tooltip.format([tag]),onClickTag);
theTag.setAttribute("tag",tag);
if(excludeTiddler)
theTag.setAttribute("tiddler",excludeTiddler);
return theTag;
}
// Event handler for clicking on a tiddler tag
function onClickTag(e)
{
if(!e) var e = window.event;
var theTarget = resolveTarget(e);
var popup = Popup.create(this);
var tag = this.getAttribute("tag");
var title = this.getAttribute("tiddler");
if(popup & & tag) {
var tagged = store.getTaggedTiddlers(tag);
var titles = [];
var li,r;
for(r=0;r< tagged.length ; r + + ) {
if(tagged[r].title != title)
titles.push(tagged[r].title);
}
var lingo = config.views.wikified.tag;
if(titles.length > 0) {
var openAll = createTiddlyButton(createTiddlyElement(popup,"li"),lingo.openAllText.format([tag]),lingo.openAllTooltip,onClickTagOpenAll);
openAll.setAttribute("tag",tag);
createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
for(r=0; r< titles.length ; r + + ) {
createTiddlyLink(createTiddlyElement(popup,"li"),titles[r],true);
}
} else {
createTiddlyText(createTiddlyElement(popup,"li",null,"disabled"),lingo.popupNone.format([tag]));
}
createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
var h = createTiddlyLink(createTiddlyElement(popup,"li"),tag,false);
createTiddlyText(h,lingo.openTag.format([tag]));
}
Popup.show();
e.cancelBubble = true;
if(e.stopPropagation) e.stopPropagation();
return false;
}
// Event handler for 'open all' on a tiddler popup
function onClickTagOpenAll(e)
{
if(!e) var e = window.event;
var tag = this.getAttribute("tag");
var tagged = store.getTaggedTiddlers(tag);
var titles = [];
for(var t=0; t< tagged.length ; t + + )
titles.push(tagged[t].title);
story.displayTiddlers(this,titles);
return false;
}
function onClickError(e)
{
if(!e) var e = window.event;
var popup = Popup.create(this);
var lines = this.getAttribute("errorText").split("\n");
for(var t=0; t< lines.length ; t + + )
createTiddlyElement(popup,"li",null,null,lines[t]);
Popup.show();
e.cancelBubble = true;
if(e.stopPropagation) e.stopPropagation();
return false;
}
function createTiddlyDropDown(place,onchange,options,defaultValue)
{
var sel = createTiddlyElement(place,"select");
sel.onchange = onchange;
for(var t=0; t< options.length ; t + + ) {
var e = createTiddlyElement(sel,"option",null,null,options[t].caption);
e.value = options[t].name;
if(options[t].name == defaultValue)
e.selected = true;
}
return sel;
}
function createTiddlyPopup(place,caption,tooltip,tiddler)
{
if(tiddler.text) {
createTiddlyLink(place,caption,true);
var btn = createTiddlyButton(place,glyph("downArrow"),tooltip,onClickTiddlyPopup,"tiddlerPopupButton");
btn.tiddler = tiddler;
} else {
createTiddlyText(place,caption);
}
}
function onClickTiddlyPopup(e)
{
if(!e) var e = window.event;
var tiddler = this.tiddler;
if(tiddler.text) {
var popup = Popup.create(this,"div","popupTiddler");
wikify(tiddler.text,popup,null,tiddler);
Popup.show();
}
if(e) e.cancelBubble = true;
if(e & & e.stopPropagation) e.stopPropagation();
return false;
}
function createTiddlyError(place,title,text)
{
var btn = createTiddlyButton(place,title,null,onClickError,"errorButton");
if(text) btn.setAttribute("errorText",text);
}
function merge(dst,src,preserveExisting)
{
for(p in src) {
if(!preserveExisting || dst[p] === undefined)
dst[p] = src[p];
}
return dst;
}
// Returns a string containing the description of an exception, optionally prepended by a message
function exceptionText(e,message)
{
var s = e.description ? e.description : e.toString();
return message ? "%0:\n%1".format([message,s]) : s;
}
// Displays an alert of an exception description with optional message
function showException(e,message)
{
alert(exceptionText(e,message));
}
function alertAndThrow(m)
{
alert(m);
throw(m);
}
function glyph(name)
{
var g = config.glyphs;
var b = g.currBrowser;
if(b == null) {
b = 0;
while(!g.browsers[b]() & & b < g.browsers.length-1 )
b++;
g.currBrowser = b;
}
if(!g.codes[name])
return "";
return g.codes[name][b];
}
//-
//- Animation engine
//-
function Animator()
{
this.running = 0; // Incremented at start of each animation, decremented afterwards. If zero, the interval timer is disabled
this.timerID = 0; // ID of the timer used for animating
this.animations = []; // List of animations in progress
return this;
}
// Start animation engine
Animator.prototype.startAnimating = function() // Variable number of arguments
{
for(var t=0; t< arguments.length ; t + + )
this.animations.push(arguments[t]);
if(this.running == 0) {
var me = this;
this.timerID = window.setInterval(function() {me.doAnimate(me);},10);
}
this.running += arguments.length;
};
// Perform an animation engine tick, calling each of the known animation modules
Animator.prototype.doAnimate = function(me)
{
var a = 0;
while(a < me.animations.length ) {
var animation = me.animations[a];
if(animation.tick()) {
a++;
} else {
me.animations.splice(a,1);
if(--me.running == 0)
window.clearInterval(me.timerID);
}
}
};
// Map a 0..1 value to 0..1, but slow down at the start and end
Animator.slowInSlowOut = function(progress)
{
return(1-((Math.cos(progress * Math.PI)+1)/2));
};
//--
//-- Morpher animation
//--
// Animate a set of properties of an element
function Morpher(element,duration,properties,callback)
{
this.element = element;
this.duration = duration;
this.properties = properties;
this.startTime = new Date();
this.endTime = Number(this.startTime) + duration;
this.callback = callback;
this.tick();
return this;
}
Morpher.prototype.assignStyle = function(element,style,value)
{
switch(style) {
case "-tw-vertScroll":
window.scrollTo(findScrollX(),value);
break;
case "-tw-horizScroll":
window.scrollTo(value,findScrollY());
break;
default:
element.style[style] = value;
break;
}
};
Morpher.prototype.stop = function()
{
for(var t=0; t< this.properties.length ; t + + ) {
var p = this.properties[t];
if(p.atEnd !== undefined) {
this.assignStyle(this.element,p.style,p.atEnd);
}
}
if(this.callback)
this.callback(this.element,this.properties);
};
Morpher.prototype.tick = function()
{
var currTime = Number(new Date());
progress = Animator.slowInSlowOut(Math.min(1,(currTime-this.startTime)/this.duration));
for(var t=0; t< this.properties.length ; t + + ) {
var p = this.properties[t];
if(p.start !== undefined & & p.end !== undefined) {
var template = p.template ? p.template : "%0";
switch(p.format) {
case undefined:
case "style":
var v = p.start + (p.end-p.start) * progress;
this.assignStyle(this.element,p.style,template.format([v]));
break;
case "color":
break;
}
}
}
if(currTime >= this.endTime) {
this.stop();
return false;
}
return true;
};
//--
//-- Zoomer animation
//--
function Zoomer(text,startElement,targetElement,unused)
{
var e = createTiddlyElement(document.body,"div",null,"zoomer");
createTiddlyElement(e,"div",null,null,text);
var winWidth = findWindowWidth();
var winHeight = findWindowHeight();
var p = [
{style: 'left', start: findPosX(startElement), end: findPosX(targetElement), template: '%0px'},
{style: 'top', start: findPosY(startElement), end: findPosY(targetElement), template: '%0px'},
{style: 'width', start: Math.min(startElement.scrollWidth,winWidth), end: Math.min(targetElement.scrollWidth,winWidth), template: '%0px', atEnd: 'auto'},
{style: 'height', start: Math.min(startElement.scrollHeight,winHeight), end: Math.min(targetElement.scrollHeight,winHeight), template: '%0px', atEnd: 'auto'},
{style: 'fontSize', start: 8, end: 24, template: '%0pt'}
];
var c = function(element,properties) {removeNode(element);};
return new Morpher(e,config.animDuration,p,c);
}
//--
//-- Scroller animation
//--
function Scroller(targetElement,unused)
{
var p = [
{style: '-tw-vertScroll', start: findScrollY(), end: ensureVisible(targetElement)}
];
return new Morpher(targetElement,config.animDuration,p);
}
//--
//-- Slider animation
//--
// deleteMode - "none", "all" [delete target element and it's children], [only] "children" [but not the target element]
function Slider(element,opening,unused,deleteMode)
{
element.style.overflow = 'hidden';
if(opening)
element.style.height = '0px'; // Resolves a Firefox flashing bug
element.style.display = 'block';
var left = findPosX(element);
var width = element.scrollWidth;
var height = element.scrollHeight;
var winWidth = findWindowWidth();
var p = [];
var c = null;
if(opening) {
p.push({style: 'height', start: 0, end: height, template: '%0px', atEnd: 'auto'});
p.push({style: 'opacity', start: 0, end: 1, template: '%0'});
p.push({style: 'filter', start: 0, end: 100, template: 'alpha(opacity:%0)'});
} else {
p.push({style: 'height', start: height, end: 0, template: '%0px'});
p.push({style: 'display', atEnd: 'none'});
p.push({style: 'opacity', start: 1, end: 0, template: '%0'});
p.push({style: 'filter', start: 100, end: 0, template: 'alpha(opacity:%0)'});
switch(deleteMode) {
case "all":
c = function(element,properties) {removeNode(element);};
break;
case "children":
c = function(element,properties) {removeChildren(element);};
break;
}
}
return new Morpher(element,config.animDuration,p,c);
}
//--
//-- Popup menu
//--
var Popup = {
stack: [] // Array of objects with members root: and popup:
};
Popup.create = function(root,elem,theClass)
{
Popup.remove();
var popup = createTiddlyElement(document.body,elem ? elem : "ol","popup",theClass ? theClass : "popup");
Popup.stack.push({root: root, popup: popup});
return popup;
};
Popup.onDocumentClick = function(e)
{
if (!e) var e = window.event;
var target = resolveTarget(e);
if(e.eventPhase == undefined)
Popup.remove();
else if(e.eventPhase == Event.BUBBLING_PHASE || e.eventPhase == Event.AT_TARGET)
Popup.remove();
return true;
};
Popup.show = function(unused1,unused2)
{
var curr = Popup.stack[Popup.stack.length-1];
this.place(curr.root,curr.popup);
addClass(curr.root,"highlight");
if(config.options.chkAnimate & & anim & & typeof Scroller == "function")
anim.startAnimating(new Scroller(curr.popup));
else
window.scrollTo(0,ensureVisible(curr.popup));
};
Popup.place = function(root,popup,offset)
{
if(!offset) var offset = {x:0, y:0};
var rootLeft = findPosX(root);
var rootTop = findPosY(root);
var rootHeight = root.offsetHeight;
var popupLeft = rootLeft + offset.x;
var popupTop = rootTop + rootHeight + offset.y;
var winWidth = findWindowWidth();
if(popup.offsetWidth > winWidth*0.75)
popup.style.width = winWidth*0.75 + "px";
var popupWidth = popup.offsetWidth;
if(popupLeft + popupWidth > winWidth)
popupLeft = winWidth - popupWidth;
popup.style.left = popupLeft + "px";
popup.style.top = popupTop + "px";
popup.style.display = "block";
}
Popup.remove = function()
{
if(Popup.stack.length > 0) {
Popup.removeFrom(0);
}
};
Popup.removeFrom = function(from)
{
for(var t=Popup.stack.length-1; t>=from; t--) {
var p = Popup.stack[t];
removeClass(p.root,"highlight");
removeNode(p.popup);
}
Popup.stack = Popup.stack.slice(0,from);
};
//--
//-- Wizard support
//--
function Wizard(elem)
{
if(elem) {
this.formElem = findRelated(elem,"wizard","className");
this.bodyElem = findRelated(this.formElem.firstChild,"wizardBody","className","nextSibling");
this.footElem = findRelated(this.formElem.firstChild,"wizardFooter","className","nextSibling");
} else {
this.formElem = null;
this.bodyElem = null;
this.footElem = null;
}
}
Wizard.prototype.setValue = function(name,value)
{
if(this.formElem)
this.formElem[name] = value;
};
Wizard.prototype.getValue = function(name)
{
return this.formElem ? this.formElem[name] : null;
};
Wizard.prototype.createWizard = function(place,title)
{
this.formElem = createTiddlyElement(place,"form",null,"wizard");
createTiddlyElement(this.formElem,"h1",null,null,title);
this.bodyElem = createTiddlyElement(this.formElem,"div",null,"wizardBody");
this.footElem = createTiddlyElement(this.formElem,"div",null,"wizardFooter");
};
Wizard.prototype.clear = function()
{
removeChildren(this.bodyElem);
};
Wizard.prototype.setButtons = function(buttonInfo,status)
{
removeChildren(this.footElem);
for(var t=0; t< buttonInfo.length ; t + + ) {
createTiddlyButton(this.footElem,buttonInfo[t].caption,buttonInfo[t].tooltip,buttonInfo[t].onClick);
insertSpacer(this.footElem);
}
if(typeof status == "string") {
createTiddlyElement(this.footElem,"span",null,"status",status);
}
};
Wizard.prototype.addStep = function(stepTitle,html)
{
removeChildren(this.bodyElem);
var w = createTiddlyElement(this.bodyElem,"div");
createTiddlyElement(w,"h2",null,null,stepTitle);
var step = createTiddlyElement(w,"div",null,"wizardStep");
step.innerHTML = html;
applyHtmlMacros(step,tiddler);
};
Wizard.prototype.getElement = function(name)
{
return this.formElem.elements[name];
};
//--
//-- ListView gadget
//--
var ListView = {};
// Create a listview
ListView.create = function(place,listObject,listTemplate,callback,className)
{
var table = createTiddlyElement(place,"table",null,className ? className : "listView twtable");
var thead = createTiddlyElement(table,"thead");
var r = createTiddlyElement(thead,"tr");
for(var t=0; t< listTemplate.columns.length ; t + + ) {
var columnTemplate = listTemplate.columns[t];
var c = createTiddlyElement(r,"th");
var colType = ListView.columnTypes[columnTemplate.type];
if(colType & & colType.createHeader)
colType.createHeader(c,columnTemplate,t);
}
var tbody = createTiddlyElement(table,"tbody");
for(var rc=0; rc< listObject.length ; rc + + ) {
rowObject = listObject[rc];
r = createTiddlyElement(tbody,"tr");
for(c=0; c< listTemplate.rowClasses.length ; c + + ) {
if(rowObject[listTemplate.rowClasses[c].field])
addClass(r,listTemplate.rowClasses[c].className);
}
rowObject.rowElement = r;
rowObject.colElements = {};
for(var cc=0; cc< listTemplate.columns.length ; cc + + ) {
c = createTiddlyElement(r,"td");
columnTemplate = listTemplate.columns[cc];
var field = columnTemplate.field;
colType = ListView.columnTypes[columnTemplate.type];
if(colType & & colType.createItem)
colType.createItem(c,rowObject,field,columnTemplate,cc,rc);
rowObject.colElements[field] = c;
}
}
if(callback & & listTemplate.actions)
createTiddlyDropDown(place,ListView.getCommandHandler(callback),listTemplate.actions);
if(callback & & listTemplate.buttons) {
for(t=0; t< listTemplate.buttons.length ; t + + ) {
var a = listTemplate.buttons[t];
if(a & & a.name != "")
createTiddlyButton(place,a.caption,null,ListView.getCommandHandler(callback,a.name,a.allowEmptySelection));
}
}
return table;
};
ListView.getCommandHandler = function(callback,name,allowEmptySelection)
{
return function(e) {
var view = findRelated(this,"TABLE",null,"previousSibling");
var tiddlers = [];
ListView.forEachSelector(view,function(e,rowName) {
if(e.checked)
tiddlers.push(rowName);
});
if(tiddlers.length == 0 & & !allowEmptySelection) {
alert(config.messages.nothingSelected);
} else {
if(this.nodeName.toLowerCase() == "select") {
callback(view,this.value,tiddlers);
this.selectedIndex = 0;
} else {
callback(view,name,tiddlers);
}
}
};
};
// Invoke a callback for each selector checkbox in the listview
ListView.forEachSelector = function(view,callback)
{
var checkboxes = view.getElementsByTagName("input");
var hadOne = false;
for(var t=0; t< checkboxes.length ; t + + ) {
var cb = checkboxes[t];
if(cb.getAttribute("type") == "checkbox") {
var rn = cb.getAttribute("rowName");
if(rn) {
callback(cb,rn);
hadOne = true;
}
}
}
return hadOne;
};
ListView.getSelectedRows = function(view)
{
var rowNames = [];
ListView.forEachSelector(view,function(e,rowName) {
if(e.checked)
rowNames.push(rowName);
});
return rowNames;
};
ListView.columnTypes = {};
ListView.columnTypes.String = {
createHeader: function(place,columnTemplate,col)
{
createTiddlyText(place,columnTemplate.title);
},
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var v = listObject[field];
if(v != undefined)
createTiddlyText(place,v);
}
};
ListView.columnTypes.WikiText = {
createHeader: ListView.columnTypes.String.createHeader,
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var v = listObject[field];
if(v != undefined)
wikify(v,place,null,null);
}
};
ListView.columnTypes.Tiddler = {
createHeader: ListView.columnTypes.String.createHeader,
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var v = listObject[field];
if(v != undefined & & v.title)
createTiddlyPopup(place,v.title,config.messages.listView.tiddlerTooltip,v);
}
};
ListView.columnTypes.Size = {
createHeader: ListView.columnTypes.String.createHeader,
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var v = listObject[field];
if(v != undefined) {
var t = 0;
while(t< config.messages.sizeTemplates.length-1 & & v < config . messages . sizeTemplates [ t ] . unit )
t++;
createTiddlyText(place,config.messages.sizeTemplates[t].template.format([Math.round(v/config.messages.sizeTemplates[t].unit)]));
}
}
};
ListView.columnTypes.Link = {
createHeader: ListView.columnTypes.String.createHeader,
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var v = listObject[field];
var c = columnTemplate.text;
if(v != undefined)
createTiddlyText(createExternalLink(place,v),c ? c : v);
}
};
ListView.columnTypes.Date = {
createHeader: ListView.columnTypes.String.createHeader,
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var v = listObject[field];
if(v != undefined)
createTiddlyText(place,v.formatString(columnTemplate.dateFormat));
}
};
ListView.columnTypes.StringList = {
createHeader: ListView.columnTypes.String.createHeader,
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var v = listObject[field];
if(v != undefined) {
for(var t=0; t< v.length ; t + + ) {
createTiddlyText(place,v[t]);
createTiddlyElement(place,"br");
}
}
}
};
ListView.columnTypes.Selector = {
createHeader: function(place,columnTemplate,col)
{
createTiddlyCheckbox(place,null,false,this.onHeaderChange);
},
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var e = createTiddlyCheckbox(place,null,listObject[field],null);
e.setAttribute("rowName",listObject[columnTemplate.rowName]);
},
onHeaderChange: function(e)
{
var state = this.checked;
var view = findRelated(this,"TABLE");
if(!view)
return;
ListView.forEachSelector(view,function(e,rowName) {
e.checked = state;
});
}
};
ListView.columnTypes.Tags = {
createHeader: ListView.columnTypes.String.createHeader,
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var tags = listObject[field];
createTiddlyText(place,String.encodeTiddlyLinkList(tags));
}
};
ListView.columnTypes.Boolean = {
createHeader: ListView.columnTypes.String.createHeader,
createItem: function(place,listObject,field,columnTemplate,col,row)
{
if(listObject[field] == true)
createTiddlyText(place,columnTemplate.trueText);
if(listObject[field] == false)
createTiddlyText(place,columnTemplate.falseText);
}
};
ListView.columnTypes.TagCheckbox = {
createHeader: ListView.columnTypes.String.createHeader,
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var e = createTiddlyCheckbox(place,null,listObject[field],this.onChange);
e.setAttribute("tiddler",listObject.title);
e.setAttribute("tag",columnTemplate.tag);
},
onChange : function(e)
{
var tag = this.getAttribute("tag");
var tiddler = this.getAttribute("tiddler");
store.setTiddlerTag(tiddler,this.checked,tag);
}
};
ListView.columnTypes.TiddlerLink = {
createHeader: ListView.columnTypes.String.createHeader,
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var v = listObject[field];
if(v != undefined) {
var link = createTiddlyLink(place,listObject[columnTemplate.tiddlerLink],false,null);
createTiddlyText(link,listObject[field]);
}
}
};
//--
//-- Augmented methods for the JavaScript Number(), Array(), String() and Date() objects
//--
// Clamp a number to a range
Number.prototype.clamp = function(min,max)
{
var c = this;
if(c < min )
c = min;
if(c > max)
c = max;
return c;
};
// Add indexOf function if browser does not support it
if(!Array.indexOf) {
Array.prototype.indexOf = function(item,from)
{
if(!from)
from = 0;
for(var i=from; i< this.length ; i + + ) {
if(this[i] === item)
return i;
}
return -1;
};}
// Find an entry in a given field of the members of an array
Array.prototype.findByField = function(field,value)
{
for(var t=0; t< this.length ; t + + ) {
if(this[t][field] == value)
return t;
}
return null;
};
// Return whether an entry exists in an array
Array.prototype.contains = function(item)
{
return this.indexOf(item) != -1;
};
// Adds, removes or toggles a particular value within an array
// value - value to add
// mode - +1 to add value, -1 to remove value, 0 to toggle it
Array.prototype.setItem = function(value,mode)
{
var p = this.indexOf(value);
if(mode == 0)
mode = (p == -1) ? +1 : -1;
if(mode == +1) {
if(p == -1)
this.push(value);
} else if(mode == -1) {
if(p != -1)
this.splice(p,1);
}
};
// Return whether one of a list of values exists in an array
Array.prototype.containsAny = function(items)
{
for(var i=0; i< items.length ; i + + ) {
if (this.indexOf(items[i]) != -1)
return true;
}
return false;
};
// Return whether all of a list of values exists in an array
Array.prototype.containsAll = function(items)
{
for (var i = 0; i< items.length ; i + + ) {
if (this.indexOf(items[i]) == -1)
return false;
}
return true;
};
// 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
Array.prototype.pushUnique = function(item,unique)
{
if(unique === false) {
this.push(item);
} else {
if(this.indexOf(item) == -1)
this.push(item);
}
};
Array.prototype.remove = function(item)
{
var p = this.indexOf(item);
if(p != -1)
this.splice(p,1);
};
// Get characters from the right end of a string
String.prototype.right = function(n)
{
return n < this.length ? this . slice ( this . length-n ) : this ;
};
// Trim whitespace from both ends of a string
String.prototype.trim = function()
{
return this.replace(/^\s*|\s*$/g,"");
};
// Convert a string from a CSS style property name to a JavaScript style name ("background-color" -> "backgroundColor")
String.prototype.unDash = function()
{
var s = this.split("-");
if(s.length > 1) {
for(var t=1; t< s.length ; t + + )
s[t] = s[t].substr(0,1).toUpperCase() + s[t].substr(1);
}
return s.join("");
};
// Substitute substrings from an array into a format string that includes '%1'-type specifiers
String.prototype.format = function(substrings)
{
var subRegExp = /(?:%(\d+))/mg;
var currPos = 0;
var r = [];
do {
var match = subRegExp.exec(this);
if(match & & match[1]) {
if(match.index > currPos)
r.push(this.substring(currPos,match.index));
r.push(substrings[parseInt(match[1])]);
currPos = subRegExp.lastIndex;
}
} while(match);
if(currPos < this.length )
r.push(this.substring(currPos,this.length));
return r.join("");
};
// Escape any special RegExp characters with that character preceded by a backslash
String.prototype.escapeRegExp = function()
{
var s = "\\^$*+?()=!|,{}[].";
var c = this;
for(var t=0; t< s.length ; t + + )
c = c.replace(new RegExp("\\" + s.substr(t,1),"g"),"\\" + s.substr(t,1));
return c;
};
// Convert "\" to "\s", newlines to "\n" (and remove carriage returns)
String.prototype.escapeLineBreaks = function()
{
return this.replace(/\\/mg,"\\s").replace(/\n/mg,"\\n").replace(/\r/mg,"");
};
// Convert "\n" to newlines, "\b" to " ", "\s" to "\" (and remove carriage returns)
String.prototype.unescapeLineBreaks = function()
{
return this.replace(/\\n/mg,"\n").replace(/\\b/mg," ").replace(/\\s/mg,"\\").replace(/\r/mg,"");
};
// Convert & to "& ", < to " & lt ; " , > to "> " and " to "" "
String.prototype.htmlEncode = function()
{
return this.replace(/&/mg,"& ").replace(/< /mg,"< ").replace(/>/mg,"> ").replace(/\"/mg,"" ");
};
// Convert "& " to & , "< " to < , "> " to > and "" " to "
String.prototype.htmlDecode = function()
{
return this.replace(/< /mg,"< ").replace(/> /mg,">").replace(/" /mg,"\"").replace(/& /mg,"&");
};
// Convert a string to it's JSON representation by encoding control characters, double quotes and backslash. See json.org
String.prototype.toJSONString = function()
{
var m = {
'\b': '\\b',
'\f': '\\f',
'\n': '\\n',
'\r': '\\r',
'\t': '\\t',
'"' : '\\"',
'\\': '\\\\'
};
var replaceFn = function(a,b) {
var c = m[b];
if(c)
return c;
c = b.charCodeAt();
return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
};
if(/["\\\x00-\x1f]/.test(this))
return '"' + this.replace(/([\x00-\x1f\\"])/g,replaceFn) + '"';
return '"' + this + '"';
};
// Parse a space-separated string of name:value parameters
// The result is an array of objects:
// result[0] = object with a member for each parameter name, value of that member being an array of values
// result[1..n] = one object for each parameter, with 'name' and 'value' members
String.prototype.parseParams = function(defaultName,defaultValue,allowEval,noNames,cascadeDefaults)
{
var parseToken = function(match,p) {
var n;
if(match[p]) // Double quoted
n = match[p];
else if(match[p+1]) // Single quoted
n = match[p+1];
else if(match[p+2]) // Double-square-bracket quoted
n = match[p+2];
else if(match[p+3]) // Double-brace quoted
try {
n = match[p+3];
if(allowEval)
n = window.eval(n);
} catch(ex) {
throw "Unable to evaluate {{" + match[p+3] + "}}: " + exceptionText(ex);
}
else if(match[p+4]) // Unquoted
n = match[p+4];
else if(match[p+5]) // empty quote
n = "";
return n;
};
var r = [{}];
var dblQuote = "(?:\"((?:(?:\\\\\")|[^\"])+)\")";
var sngQuote = "(?:'((?:(?:\\\\\')|[^'])+)')";
var dblSquare = "(?:\\[\\[((?:\\s|\\S)*?)\\]\\])";
var dblBrace = "(?:\\{\\{((?:\\s|\\S)*?)\\}\\})";
var unQuoted = noNames ? "([^\"'\\s]\\S*)" : "([^\"':\\s][^\\s:]*)";
var emptyQuote = "((?:\"\")|(?:''))";
var skipSpace = "(?:\\s*)";
var token = "(?:" + dblQuote + "|" + sngQuote + "|" + dblSquare + "|" + dblBrace + "|" + unQuoted + "|" + emptyQuote + ")";
var re = noNames ? new RegExp(token,"mg") : new RegExp(skipSpace + token + skipSpace + "(?:(\\:)" + skipSpace + token + ")?","mg");
var params = [];
do {
var match = re.exec(this);
if(match) {
var n = parseToken(match,1);
if(noNames) {
r.push({name:"",value:n});
} else {
var v = parseToken(match,8);
if(v == null & & defaultName) {
v = n;
n = defaultName;
} else if(v == null & & defaultValue) {
v = defaultValue;
}
r.push({name:n,value:v});
if(cascadeDefaults) {
defaultName = n;
defaultValue = v;
}
}
}
} while(match);
// Summarise parameters into first element
for(var t=1; t< r.length ; t + + ) {
if(r[0][r[t].name])
r[0][r[t].name].push(r[t].value);
else
r[0][r[t].name] = [r[t].value];
}
return r;
};
// Process a string list of macro parameters into an array. Parameters can be quoted with "", '',
// [[]], {{ }} or left unquoted (and therefore space-separated). Double-braces {{}} results in
// an *evaluated* parameter: e.g. {{config.options.txtUserName}} results in the current user's name.
String.prototype.readMacroParams = function()
{
var p = this.parseParams("list",null,true,true);
var n = [];
for(var t=1; t< p.length ; t + + )
n.push(p[t].value);
return n;
};
// Process a string list of unique tiddler names into an array. Tiddler names that have spaces in them must be [[bracketed]]
String.prototype.readBracketedList = function(unique)
{
var p = this.parseParams("list",null,false,true);
var n = [];
for(var t=1; t< p.length ; t + + )
n.pushUnique(p[t].value,unique);
return n;
};
// Returns array with start and end index of chunk between given start and end marker, or undefined.
String.prototype.getChunkRange = function(start,end)
{
var s = this.indexOf(start);
if(s != -1) {
s += start.length;
var e = this.indexOf(end,s);
if(e != -1)
return [s,e];
}
};
// Replace a chunk of a string given start and end markers
String.prototype.replaceChunk = function(start,end,sub)
{
var r = this.getChunkRange(start,end);
return r ? this.substring(0,r[0]) + sub + this.substring(r[1]) : this;
};
// Returns a chunk of a string between start and end markers, or undefined
String.prototype.getChunk = function(start,end)
{
var r = this.getChunkRange(start,end);
if(r)
return this.substring(r[0],r[1]);
};
// Static method to bracket a string with double square brackets if it contains a space
String.encodeTiddlyLink = function(title)
{
return title.indexOf(" ") == -1 ? title : "[[" + title + "]]";
};
// Static method to encodeTiddlyLink for every item in an array and join them with spaces
String.encodeTiddlyLinkList = function(list)
{
if(list) {
var results = [];
for(var t=0; t< list.length ; t + + )
results.push(String.encodeTiddlyLink(list[t]));
return results.join(" ");
} else {
return "";
}
};
// Convert a string as a sequence of name:"value" pairs into a hashmap
String.prototype.decodeHashMap = function()
{
var fields = this.parseParams("anon","",false);
var r = {};
for(var t=1; t< fields.length ; t + + )
r[fields[t].name] = fields[t].value;
return r;
};
// Static method to encode a hashmap into a name:"value"... string
String.encodeHashMap = function(hashmap)
{
var r = [];
for(var t in hashmap)
r.push(t + ':"' + hashmap[t] + '"');
return r.join(" ");
};
// Static method to left-pad a string with 0s to a certain width
String.zeroPad = function(n,d)
{
var s = n.toString();
if(s.length < d )
s = "000000000000000000000000000".substr(0,d-s.length) + s;
return s;
};
String.prototype.startsWith = function(prefix)
{
return !prefix || this.substring(0,prefix.length) == prefix;
};
// Returns the first value of the given named parameter.
function getParam(params,name,defaultValue)
{
if(!params)
return defaultValue;
var p = params[0][name];
return p ? p[0] : defaultValue;
}
// Returns the first value of the given boolean named parameter.
function getFlag(params,name,defaultValue)
{
return !!getParam(params,name,defaultValue);
}
// Substitute date components into a string
Date.prototype.formatString = function(template)
{
var t = template.replace(/0hh12/g,String.zeroPad(this.getHours12(),2));
t = t.replace(/hh12/g,this.getHours12());
t = t.replace(/0hh/g,String.zeroPad(this.getHours(),2));
t = t.replace(/hh/g,this.getHours());
t = t.replace(/mmm/g,config.messages.dates.shortMonths[this.getMonth()]);
t = t.replace(/0mm/g,String.zeroPad(this.getMinutes(),2));
t = t.replace(/mm/g,this.getMinutes());
t = t.replace(/0ss/g,String.zeroPad(this.getSeconds(),2));
t = t.replace(/ss/g,this.getSeconds());
t = t.replace(/[ap]m/g,this.getAmPm().toLowerCase());
t = t.replace(/[AP]M/g,this.getAmPm().toUpperCase());
t = t.replace(/wYYYY/g,this.getYearForWeekNo());
t = t.replace(/wYY/g,String.zeroPad(this.getYearForWeekNo()-2000,2));
t = t.replace(/YYYY/g,this.getFullYear());
t = t.replace(/YY/g,String.zeroPad(this.getFullYear()-2000,2));
t = t.replace(/MMM/g,config.messages.dates.months[this.getMonth()]);
t = t.replace(/0MM/g,String.zeroPad(this.getMonth()+1,2));
t = t.replace(/MM/g,this.getMonth()+1);
t = t.replace(/0WW/g,String.zeroPad(this.getWeek(),2));
t = t.replace(/WW/g,this.getWeek());
t = t.replace(/DDD/g,config.messages.dates.days[this.getDay()]);
t = t.replace(/ddd/g,config.messages.dates.shortDays[this.getDay()]);
t = t.replace(/0DD/g,String.zeroPad(this.getDate(),2));
t = t.replace(/DDth/g,this.getDate()+this.daySuffix());
t = t.replace(/DD/g,this.getDate());
return t;
};
Date.prototype.getWeek = function()
{
var dt = new Date(this.getTime());
var d = dt.getDay();
if (d==0) d=7;// JavaScript Sun=0, ISO Sun=7
dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week to calculate weekNo
var n = Math.floor((dt.getTime()-new Date(dt.getFullYear(),0,1)+3600000)/86400000);
return Math.floor(n/7)+1;
};
Date.prototype.getYearForWeekNo = function()
{
var dt = new Date(this.getTime());
var d = dt.getDay();
if (d==0) d=7;// JavaScript Sun=0, ISO Sun=7
dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week
return dt.getFullYear();
};
Date.prototype.getHours12 = function()
{
var h = this.getHours();
return h > 12 ? h-12 : ( h > 0 ? h : 12 );
};
Date.prototype.getAmPm = function()
{
return this.getHours() >= 12 ? config.messages.dates.pm : config.messages.dates.am;
};
Date.prototype.daySuffix = function()
{
return config.messages.dates.daySuffixes[this.getDate()-1];
};
// Convert a date to local YYYYMMDDHHMM string format
Date.prototype.convertToLocalYYYYMMDDHHMM = function()
{
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);
};
// Convert a date to UTC YYYYMMDDHHMM string format
Date.prototype.convertToYYYYMMDDHHMM = function()
{
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);
};
// Convert a date to UTC YYYYMMDD.HHMMSSMMM string format
Date.prototype.convertToYYYYMMDDHHMMSSMMM = function()
{
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);
};
// Static method to create a date from a UTC YYYYMMDDHHMM format string
Date.convertFromYYYYMMDDHHMM = function(d)
{
return new Date(Date.UTC(parseInt(d.substr(0,4),10),
parseInt(d.substr(4,2),10)-1,
parseInt(d.substr(6,2),10),
parseInt(d.substr(8,2),10),
parseInt(d.substr(10,2),10),0,0));
};
//--
//-- Crypto functions and associated conversion routines
//--
// Crypto "namespace"
function Crypto() {}
// Convert a string to an array of big-endian 32-bit words
Crypto.strToBe32s = function(str)
{
var be = Array();
var len = Math.floor(str.length/4);
var i, j;
for(i=0, j=0; i< len ; i + + , j + = 4 ) {
be[i] = ((str.charCodeAt(j)& 0xff) < < 24 ) | ( ( str . charCodeAt ( j + 1 ) & 0xff ) < < 16 ) | ( ( str . charCodeAt ( j + 2 ) & 0xff ) < < 8 ) | ( str . charCodeAt ( j + 3 ) & 0xff ) ;
}
while (j< str.length ) {
be[j>>2] |= (str.charCodeAt(j)&0xff)<<(24-(j*8)%32);
j++;
}
return be;
};
// Convert an array of big-endian 32-bit words to a string
Crypto.be32sToStr = function(be)
{
var str = "";
for(var i=0;i< be.length * 32 ; i + = 8 )
str += String.fromCharCode((be[i>>5]>>>(24-i%32)) & 0xff);
return str;
};
// Convert an array of big-endian 32-bit words to a hex string
Crypto.be32sToHex = function(be)
{
var hex = "0123456789ABCDEF";
var str = "";
for(var i=0;i< be.length * 4 ; i + + )
str += hex.charAt((be[i>>2]>>((3-i%4)*8+4))& 0xF) + hex.charAt((be[i>>2]>>((3-i%4)*8))&0xF);
return str;
};
// Return, in hex, the SHA-1 hash of a string
Crypto.hexSha1Str = function(str)
{
return Crypto.be32sToHex(Crypto.sha1Str(str));
};
// Return the SHA-1 hash of a string
Crypto.sha1Str = function(str)
{
return Crypto.sha1(Crypto.strToBe32s(str),str.length);
};
// Calculate the SHA-1 hash of an array of blen bytes of big-endian 32-bit words
Crypto.sha1 = function(x,blen)
{
// Add 32-bit integers, wrapping at 32 bits
add32 = function(a,b)
{
var lsw = (a&0xFFFF)+(b&0xFFFF);
var msw = (a>>16)+(b>>16)+(lsw>>16);
return (msw< < 16 ) | ( lsw & 0xFFFF ) ;
};
// Add five 32-bit integers, wrapping at 32 bits
add32x5 = function(a,b,c,d,e)
{
var lsw = (a&0xFFFF)+(b&0xFFFF)+(c&0xFFFF)+(d&0xFFFF)+(e&0xFFFF);
var msw = (a>>16)+(b>>16)+(c>>16)+(d>>16)+(e>>16)+(lsw>>16);
return (msw< < 16 ) | ( lsw & 0xFFFF ) ;
};
// Bitwise rotate left a 32-bit integer by 1 bit
rol32 = function(n)
{
return (n>>>31)|(n< < 1 ) ;
};
var len = blen*8;
// Append padding so length in bits is 448 mod 512
x[len>>5] |= 0x80 < < (24-len%32);
// Append length
x[((len+64>>9)< < 4 ) + 15 ] = len ;
var w = Array(80);
var k1 = 0x5A827999;
var k2 = 0x6ED9EBA1;
var k3 = 0x8F1BBCDC;
var k4 = 0xCA62C1D6;
var h0 = 0x67452301;
var h1 = 0xEFCDAB89;
var h2 = 0x98BADCFE;
var h3 = 0x10325476;
var h4 = 0xC3D2E1F0;
for(var i=0;i< x.length ; i + = 16 ) {
var j,t;
var a = h0;
var b = h1;
var c = h2;
var d = h3;
var e = h4;
for(j = 0;j< 16 ; j + + ) {
w[j] = x[i+j];
t = add32x5(e,(a>>>27)|(a< < 5 ) , d ^ ( b & ( c ^ d ) ) , w [ j ] , k1 ) ;
e=d; d=c; c=(b>>>2)|(b< < 30 ) ; b = a; a = t;
}
for(j=16;j< 20 ; j + + ) {
w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
t = add32x5(e,(a>>>27)|(a< < 5 ) , d ^ ( b & ( c ^ d ) ) , w [ j ] , k1 ) ;
e=d; d=c; c=(b>>>2)|(b< < 30 ) ; b = a; a = t;
}
for(j=20;j< 40 ; j + + ) {
w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
t = add32x5(e,(a>>>27)|(a< < 5 ) , b ^ c ^ d , w [ j ] , k2 ) ;
e=d; d=c; c=(b>>>2)|(b< < 30 ) ; b = a; a = t;
}
for(j=40;j< 60 ; j + + ) {
w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
t = add32x5(e,(a>>>27)|(a< < 5 ) , ( b & c ) | ( d & ( b | c ) ) , w [ j ] , k3 ) ;
e=d; d=c; c=(b>>>2)|(b< < 30 ) ; b = a; a = t;
}
for(j=60;j< 80 ; j + + ) {
w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
t = add32x5(e,(a>>>27)|(a< < 5 ) , b ^ c ^ d , w [ j ] , k4 ) ;
e=d; d=c; c=(b>>>2)|(b< < 30 ) ; b = a; a = t;
}
h0 = add32(h0,a);
h1 = add32(h1,b);
h2 = add32(h2,c);
h3 = add32(h3,d);
h4 = add32(h4,e);
}
return Array(h0,h1,h2,h3,h4);
};
//--
//-- RGB colour object
//--
// Construct an RGB colour object from a '#rrggbb', '#rgb' or 'rgb(n,n,n)' string or from separate r,g,b values
function RGB(r,g,b)
{
this.r = 0;
this.g = 0;
this.b = 0;
if(typeof r == "string") {
if(r.substr(0,1) == "#") {
if(r.length == 7) {
this.r = parseInt(r.substr(1,2),16)/255;
this.g = parseInt(r.substr(3,2),16)/255;
this.b = parseInt(r.substr(5,2),16)/255;
} else {
this.r = parseInt(r.substr(1,1),16)/15;
this.g = parseInt(r.substr(2,1),16)/15;
this.b = parseInt(r.substr(3,1),16)/15;
}
} else {
var rgbPattern = /rgb\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/;
var c = r.match(rgbPattern);
if(c) {
this.r = parseInt(c[1],10)/255;
this.g = parseInt(c[2],10)/255;
this.b = parseInt(c[3],10)/255;
}
}
} else {
this.r = r;
this.g = g;
this.b = b;
}
return this;
}
// Mixes this colour with another in a specified proportion
// c = other colour to mix
// f = 0..1 where 0 is this colour and 1 is the new colour
// Returns an RGB object
RGB.prototype.mix = function(c,f)
{
return new RGB(this.r + (c.r-this.r) * f,this.g + (c.g-this.g) * f,this.b + (c.b-this.b) * f);
};
// Return an rgb colour as a #rrggbb format hex string
RGB.prototype.toString = function()
{
return "#" + ("0" + Math.floor(this.r.clamp(0,1) * 255).toString(16)).right(2) +
("0" + Math.floor(this.g.clamp(0,1) * 255).toString(16)).right(2) +
("0" + Math.floor(this.b.clamp(0,1) * 255).toString(16)).right(2);
};
//--
//-- DOM utilities - many derived from www.quirksmode.org
//--
function drawGradient(place,horiz,colours)
{
for(var t=0; t< = 100; t+=2) {
var bar = document.createElement("div");
place.appendChild(bar);
bar.style.position = "absolute";
bar.style.left = horiz ? t + "%" : 0;
bar.style.top = horiz ? 0 : t + "%";
bar.style.width = horiz ? (101-t) + "%" : "100%";
bar.style.height = horiz ? "100%" : (101-t) + "%";
bar.style.zIndex = -1;
var f = t/100;
var p = f*(colours.length-1);
bar.style.backgroundColor = colours[Math.floor(p)].mix(colours[Math.ceil(p)],p-Math.floor(p)).toString();
}
}
function createTiddlyText(theParent,theText)
{
return theParent.appendChild(document.createTextNode(theText));
}
function createTiddlyCheckbox(theParent,caption,checked,onChange)
{
var cb = document.createElement("input");
cb.setAttribute("type","checkbox");
cb.onclick = onChange;
theParent.appendChild(cb);
cb.checked = checked;
cb.className = "chkOptionInput";
if(caption)
wikify(caption,theParent);
return cb;
}
function createTiddlyElement(theParent,theElement,theID,theClass,theText)
{
var e = document.createElement(theElement);
if(theClass != null)
e.className = theClass;
if(theID != null)
e.setAttribute("id",theID);
if(theText != null)
e.appendChild(document.createTextNode(theText));
if(theParent != null)
theParent.appendChild(e);
return e;
}
function addEvent(obj,type,fn)
{
if(obj.attachEvent) {
obj['e'+type+fn] = fn;
obj[type+fn] = function(){obj['e'+type+fn](window.event);};
obj.attachEvent('on'+type,obj[type+fn]);
} else {
obj.addEventListener(type,fn,false);
}
}
function removeEvent(obj,type,fn)
{
if(obj.detachEvent) {
obj.detachEvent('on'+type,obj[type+fn]);
obj[type+fn] = null;
} else {
obj.removeEventListener(type,fn,false);
}
}
function addClass(e,theClass)
{
var currClass = e.className.split(" ");
if(currClass.indexOf(theClass) == -1)
e.className += " " + theClass;
}
function removeClass(e,theClass)
{
var currClass = e.className.split(" ");
var i = currClass.indexOf(theClass);
while(i != -1) {
currClass.splice(i,1);
i = currClass.indexOf(theClass);
}
e.className = currClass.join(" ");
}
function hasClass(e,theClass)
{
if(e.className) {
if(e.className.split(" ").indexOf(theClass) != -1)
return true;
}
return false;
}
// Find the closest relative with a given property value (property defaults to tagName, relative defaults to parentNode)
function findRelated(e,value,name,relative)
{
name = name ? name : "tagName";
relative = relative ? relative : "parentNode";
if(name == "className") {
while(e & & !hasClass(e,value)) {
e = e[relative];
}
} else {
while(e & & e[name] != value) {
e = e[relative];
}
}
return e;
}
// Resolve the target object of an event
function resolveTarget(e)
{
var obj;
if(e.target)
obj = e.target;
else if(e.srcElement)
obj = e.srcElement;
if(obj.nodeType == 3) // defeat Safari bug
obj = obj.parentNode;
return obj;
}
// Return the content of an element as plain text with no formatting
function getPlainText(e)
{
var text = "";
if(e.innerText)
text = e.innerText;
else if(e.textContent)
text = e.textContent;
return text;
}
// Get the scroll position for window.scrollTo necessary to scroll a given element into view
function ensureVisible(e)
{
var posTop = findPosY(e);
var posBot = posTop + e.offsetHeight;
var winTop = findScrollY();
var winHeight = findWindowHeight();
var winBot = winTop + winHeight;
if(posTop < winTop ) {
return posTop;
} else if(posBot > winBot) {
if(e.offsetHeight < winHeight )
return posTop - (winHeight - e.offsetHeight);
else
return posTop;
} else {
return winTop;
}
}
// Get the current width of the display window
function findWindowWidth()
{
return window.innerWidth ? window.innerWidth : document.documentElement.clientWidth;
}
// Get the current height of the display window
function findWindowHeight()
{
return window.innerHeight ? window.innerHeight : document.documentElement.clientHeight;
}
// Get the current horizontal page scroll position
function findScrollX()
{
return window.scrollX ? window.scrollX : document.documentElement.scrollLeft;
}
// Get the current vertical page scroll position
function findScrollY()
{
return window.scrollY ? window.scrollY : document.documentElement.scrollTop;
}
function findPosX(obj)
{
var curleft = 0;
while(obj.offsetParent) {
curleft += obj.offsetLeft;
obj = obj.offsetParent;
}
return curleft;
}
function findPosY(obj)
{
var curtop = 0;
while(obj.offsetParent) {
curtop += obj.offsetTop;
obj = obj.offsetParent;
}
return curtop;
}
// Blur a particular element
function blurElement(e)
{
if(e != null & & e.focus & & e.blur) {
e.focus();
e.blur();
}
}
// Create a non-breaking space
function insertSpacer(place)
{
var e = document.createTextNode(String.fromCharCode(160));
if(place)
place.appendChild(e);
return e;
}
// Remove all children of a node
function removeChildren(e)
{
while(e & & e.hasChildNodes())
removeNode(e.firstChild);
}
// Remove a node and all it's children
function removeNode(e)
{
scrubNode(e);
e.parentNode.removeChild(e);
}
// Remove any event handlers or non-primitve custom attributes
function scrubNode(e)
{
var att = e.attributes;
if(att) {
for(var t=0; t< att.length ; t + + ) {
var n = att[t].name;
if(n !== 'style' & & (typeof e[n] === 'function' || (typeof e[n] === 'object' & & e[n] != null))) {
try {
e[n] = null;
} catch(ex) {
}
}
}
}
var c = e.firstChild;
while(c) {
scrubNode(c);
c = c.nextSibling;
}
}
// Add a stylesheet, replacing any previous custom stylesheet
function setStylesheet(s,id,doc)
{
if(!id)
id = "customStyleSheet";
if(!doc)
doc = document;
var n = doc.getElementById(id);
if(doc.createStyleSheet) {
// Test for IE's non-standard createStyleSheet method
if(n)
n.parentNode.removeChild(n);
// This failed without the
doc.getElementsByTagName("head")[0].insertAdjacentHTML("beforeEnd"," < style id = '" + id + "' > " + s + " < / style > ");
} else {
if(n) {
n.replaceChild(doc.createTextNode(s),n.firstChild);
} else {
n = doc.createElement("style");
n.type = "text/css";
n.id = id;
n.appendChild(doc.createTextNode(s));
doc.getElementsByTagName("head")[0].appendChild(n);
}
}
}
// Force the browser to do a document reflow when needed to workaround browser bugs
function forceReflow()
{
if(config.browser.isGecko) {
setStylesheet("body {top:-1em;margin-top:1em;}");
setStylesheet("");
}
}
// Replace the current selection of a textarea or text input and scroll it into view
function replaceSelection(e,text)
{
if(e.setSelectionRange) {
var oldpos = e.selectionStart;
var isRange = e.selectionEnd > e.selectionStart;
e.value = e.value.substr(0,e.selectionStart) + text + e.value.substr(e.selectionEnd);
e.setSelectionRange(isRange ? oldpos : oldpos + text.length,oldpos + text.length);
var linecount = e.value.split('\n').length;
var thisline = e.value.substr(0,e.selectionStart).split('\n').length-1;
e.scrollTop = Math.floor((thisline - e.rows / 2) * e.scrollHeight / linecount);
} else if(document.selection) {
var range = document.selection.createRange();
if(range.parentElement() == e) {
var isCollapsed = range.text == "";
range.text = text;
if(!isCollapsed) {
range.moveStart('character', -text.length);
range.select();
}
}
}
}
// Returns the text of the given (text) node, possibly merging subsequent text nodes
function getNodeText(e)
{
var t = "";
while(e & & e.nodeName == "#text") {
t += e.nodeValue;
e = e.nextSibling;
}
return t;
}
//--
//-- LoaderBase and SaverBase
//--
function LoaderBase() {}
LoaderBase.prototype.loadTiddler = function(store,node,tiddlers)
{
var title = this.getTitle(store,node);
if(title) {
var tiddler = store.createTiddler(title);
this.internalizeTiddler(store,tiddler,title,node);
tiddlers.push(tiddler);
}
};
LoaderBase.prototype.loadTiddlers = function(store,nodes)
{
var tiddlers = [];
for(var t = 0; t < nodes.length ; t + + ) {
try {
this.loadTiddler(store,nodes[t],tiddlers);
} catch(ex) {
showException(ex,config.messages.tiddlerLoadError.format([this.getTitle(store,nodes[t])]));
}
}
return tiddlers;
};
function SaverBase() {}
SaverBase.prototype.externalize = function(store)
{
var results = [];
var tiddlers = store.getTiddlers("title");
for(var t = 0; t < tiddlers.length ; t + + )
results.push(this.externalizeTiddler(store,tiddlers[t]));
return results.join("\n");
};
//--
//-- TW21Loader (inherits from LoaderBase)
//--
function TW21Loader() {}
TW21Loader.prototype = new LoaderBase();
TW21Loader.prototype.getTitle = function(store,node)
{
var title = null;
if(node.getAttribute) {
title = node.getAttribute("title");
if(!title)
title = node.getAttribute("tiddler");
}
if(!title & & node.id) {
var lenPrefix = store.idPrefix.length;
if (node.id.substr(0,lenPrefix) == store.idPrefix)
title = node.id.substr(lenPrefix);
}
return title;
};
TW21Loader.prototype.internalizeTiddler = function(store,tiddler,title,node)
{
var e = node.firstChild;
var text = null;
if(node.getAttribute("tiddler")) {
text = getNodeText(e).unescapeLineBreaks();
} else {
while(e.nodeName!="PRE" & & e.nodeName!="pre") {
e = e.nextSibling;
}
text = e.innerHTML.replace(/\r/mg,"").htmlDecode();
}
var modifier = node.getAttribute("modifier");
var c = node.getAttribute("created");
var m = node.getAttribute("modified");
var created = c ? Date.convertFromYYYYMMDDHHMM(c) : version.date;
var modified = m ? Date.convertFromYYYYMMDDHHMM(m) : created;
var tags = node.getAttribute("tags");
var fields = {};
var attrs = node.attributes;
for(var i = attrs.length-1; i >= 0; i--) {
var name = attrs[i].name;
if (attrs[i].specified & & !TiddlyWiki.isStandardField(name)) {
fields[name] = attrs[i].value.unescapeLineBreaks();
}
}
tiddler.assign(title,text,modifier,modified,tags,created,fields);
return tiddler;
};
//--
//-- TW21Saver (inherits from SaverBase)
//--
function TW21Saver() {}
TW21Saver.prototype = new SaverBase();
TW21Saver.prototype.externalizeTiddler = function(store,tiddler)
{
try {
var extendedAttributes = "";
var usePre = config.options.chkUsePreForStorage;
store.forEachField(tiddler,
function(tiddler,fieldName,value) {
// don't store stuff from the temp namespace
if(typeof value != "string")
value = "";
if (!fieldName.match(/^temp\./))
extendedAttributes += ' %0="%1"'.format([fieldName,value.escapeLineBreaks().htmlEncode()]);
},true);
var created = tiddler.created.convertToYYYYMMDDHHMM();
var modified = tiddler.modified.convertToYYYYMMDDHHMM();
var vdate = version.date.convertToYYYYMMDDHHMM();
var attributes = tiddler.modifier ? ' modifier="' + tiddler.modifier.htmlEncode() + '"' : "";
attributes += (usePre & & modified == created) ? "" : ' modified="' + modified +'"';
attributes += (usePre & & created == vdate) ? "" :' created="' + created + '"';
var tags = tiddler.getTags();
if(!usePre || tags)
attributes += ' tags="' + tags.htmlEncode() + '"';
return ('< div % 0 = "%1" % 2 % 3 > %4< /'+'div>').format([
usePre ? "title" : "tiddler",
tiddler.title.htmlEncode(),
attributes,
extendedAttributes,
usePre ? "\n< pre > " + tiddler.text.htmlEncode() + "< / pre > \n" : tiddler.text.escapeLineBreaks().htmlEncode()
]);
} catch (ex) {
throw exceptionText(ex,config.messages.tiddlerSaveError.format([tiddler.title]));
}
};
//--
//-- Deprecated code
//--
// @Deprecated: Use createElementAndWikify and this.termRegExp instead
config.formatterHelpers.charFormatHelper = function(w)
{
w.subWikify(createTiddlyElement(w.output,this.element),this.terminator);
};
// @Deprecated: Use enclosedTextHelper and this.lookaheadRegExp instead
config.formatterHelpers.monospacedByLineHelper = function(w)
{
var lookaheadRegExp = new RegExp(this.lookahead,"mg");
lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = lookaheadRegExp.exec(w.source);
if(lookaheadMatch & & lookaheadMatch.index == w.matchStart) {
var text = lookaheadMatch[1];
if(config.browser.isIE)
text = text.replace(/\n/g,"\r");
createTiddlyElement(w.output,"pre",null,null,text);
w.nextMatch = lookaheadRegExp.lastIndex;
}
};
// @Deprecated: Use < br > or < br / > instead of < < br > >
config.macros.br.handler = function(place)
{
createTiddlyElement(place,"br");
};
// Find an entry in an array. Returns the array index or null
// @Deprecated: Use indexOf instead
Array.prototype.find = function(item)
{
var i = this.indexOf(item);
return i == -1 ? null : i;
};
// Load a tiddler from an HTML DIV. The caller should make sure to later call Tiddler.changed()
// @Deprecated: Use store.getLoader().internalizeTiddler instead
Tiddler.prototype.loadFromDiv = function(divRef,title)
{
return store.getLoader().internalizeTiddler(store,this,title,divRef);
};
// Format the text for storage in an HTML DIV
// @Deprecated Use store.getSaver().externalizeTiddler instead.
Tiddler.prototype.saveToDiv = function()
{
return store.getSaver().externalizeTiddler(store,this);
};
// @Deprecated: Use store.allTiddlersAsHtml() instead
function allTiddlersAsHtml()
{
return store.allTiddlersAsHtml();
}
// @Deprecated: Use refreshPageTemplate instead
function applyPageTemplate(title)
{
refreshPageTemplate(title);
}
// @Deprecated: Use story.displayTiddlers instead
function displayTiddlers(srcElement,titles,template,unused1,unused2,animate,unused3)
{
story.displayTiddlers(srcElement,titles,template,animate);
}
// @Deprecated: Use story.displayTiddler instead
function displayTiddler(srcElement,title,template,unused1,unused2,animate,unused3)
{
story.displayTiddler(srcElement,title,template,animate);
}
// @Deprecated: Use functions on right hand side directly instead
var createTiddlerPopup = Popup.create;
var scrollToTiddlerPopup = Popup.show;
var hideTiddlerPopup = Popup.remove;
// @Deprecated: Use right hand side directly instead
var regexpBackSlashEn = new RegExp("\\\\n","mg");
var regexpBackSlash = new RegExp("\\\\","mg");
var regexpBackSlashEss = new RegExp("\\\\s","mg");
var regexpNewLine = new RegExp("\n","mg");
var regexpCarriageReturn = new RegExp("\r","mg");
//--
//-- End of scripts
//--
//]]>
< / script >
< script type = "text/javascript" >
//< ![CDATA[
if(useJavaSaver)
document.write("< applet style = 'position:absolute;left:-1px' name = 'TiddlySaver' code = 'TiddlySaver.class' archive = 'TiddlySaver.jar' width = '1' height = '1' > < / applet > ");
//]]>
< / script >
<!-- POST - SCRIPT - START -->
<!-- POST - SCRIPT - END -->
< / body >
< / html >