<!--{{{-->
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>
<!--}}}-->
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
/*{{{*/
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)';}
/*}}}*/
/*{{{*/
* 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;}
/*}}}*/
/***
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 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;}
/*}}}*/
/*{{{*/
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {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;}
}
/*}}}*/
<!--{{{-->
<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>
<!--}}}-->
<!--{{{-->
<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>
<!--}}}-->
<!--{{{-->
<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>
<!--}}}-->
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>>
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
<<importTiddlers>>
This game was not created in a vacuum.  All that I am and all that I have, I owe to God and my family, and to many great teachers, employers, and friends.  I've read and played a lot of great games, both role-playing and non-, which have both inspired and entertained me and irritated me enough to try to make a better one.

''Historical and Geographic Consulting''
Daniel Johnston
Debbie Sandburg

''Earliest Playtesters''
Joshua Chong
Angela Fortunato
Kevin Garlick
Christian Hiedeker
Heath Hindman
Janelle Hindman
Nicole Hochstrasser
Laura Johnston
Kayleena Richins
[[Acknowledgements]]
[[Creator Philosophy]]
[[Suggested Reading]]
Air is the [[Mental|Mental Attributes]] [[External|External Attributes]] Attribute - the Attribute that defines reason and intellect.  as the power to cause change in the outside world through mental activity, it also euqates with eloquence, personal magnetism, and fine-motor dexterity.

People with high Air learn quickly and readily demonstrate their ability.  They are frequently artists, scientists, politicians, teachers and leaders.  In both physical and social confrontations they tend to study their opponents and strike with speed, precision, and efficiency, often completely disabling their opponent with a single, seemingly effortless, expertly timed attack at their most vulnerble spot.  They are generally pragmatic and while not necessarily either good or evil, are frequently cunning and duplicitous.
''Appearance:'' The Aralei are tall, lean people.  Their hair ranges from tightly curled to wavy in blacks, browns, and a few dark auburn and gold-colored.  They are hairy, but both men and women shave most of their bodies for hygenic purposes.  Men of the warior caste typically shave their heads or leave only a small topknot.  Most people have dark skin and dark eyes.

In personal combat, Aralei hold nothing back.  A duel between Aralei is a vicious flurry of knees, elbows, headbutts, teeth, and jagged blades.
Athletics is the skill that governs movement skills, body control, and coordination.  It is especially useful for climbing, jumping, balancing, swimming, acrobatics, and many sports.

Athletics+[[Air]]
-Throwing or kicking accuracy
-Footwork

Athletics+[[Earth]]
-Balance
-Endurance
-Flexibility

Athletics+[[Fire]]
-Climbing
-Jumping
-Lifting

Athletics+[[Water]]
-Swimming
Attributes are the qualities that define a character's most fundamental abilities and some aspects of their personality.  The four Attributes are [[Air]], [[Earth]], [[Fire]], and [[Water]].

1 – Terrible
2 – Bad
3 – Poor
4 – Average
5 – Good
6 – Great
7 – Excellent
8 – Legendary
Bargain is the skill for making good use of money.  It includes experience in appraising goods, business management, and bribery.  It is almost always used with [[Air]].
The game uses four normal six-sided dice for most actions.  Read a 1 or a 2 as '-1,' a 3 or a 4 as '0,' and a 5 or a 6 as '+1.'  This gives a result ranging from -4 to +4.  Any time this system is used, it is abbreviated as 'D.'

For most actions, players will add an [[Attribute|Attributes]] to a [[Skill|Skills]] and add D.  If the total is equal to or higher than a difficulty number set by the [[GM|Gamemaster]], the character succeeds.

''Modifiers''
[[Synergy Bonus]]
[[Unfamiliarity Penalty]]
[[Description Bonus]]
1. [[Creation: Concept]]

2. [[Creation: Attributes]]

3. [[Creation: Skills and Languages]]

4. [[Creation: Bonus Points]]

5. [[Creation: Miscellaneous Traits]]

[[Creation: Example]]
Charisma is the skill of making friends and influencing others' behavior.  It covers persuasion, intimidation, and seduction.

It is usually used with either [[Air]] or [[Fire]].  Use Air for making logical arguments and making believable threats to distant or abstract targets (Do what I want or I'll burn down the library vital to your cousin's life work!).  Use Fire for making passionate arguments or making threats of immediate, physical violence (Do what I want or I'll tear your thumbnails out!).  Fire is usually used for seduction, but Air could be under the right circumstances.  [[Water]] might reasonably used to convince someone to talk who isn't comfortable doing so by dint of being sincere and a good listener.

Charisma is not used for lying (that's [[Deception]]) or bribery (that's [[Bargain]]), but it can certainly provide a [[Synergy Bonus]].
Attack: Air + Combat Skill + D
Defense: Water + //either// Combat Skill + D

Defense > Attack = Miss
Defense = Attack = Hit //but// does a maximum of one point of damage.
Attack > Defense = Hit

Damage: Fire + Weapon Damage Bonus + Margin of Success + D
Absorbtion: Earth + Armor Bonus + D
'Craft' is not a single skill, but a class of skills used for making tangible artworks and practical items.  Therefore, a character doesn't learn 'the' Craft skill, he learns 'a' Craft skill.  When learning a Craft skill, the player must define which Craft his character is learning.  A character may know any number of Crafts.

Usually used with [[Air]] or [[Fire]]

Examples: Architecture, Basketweaving, Blacksmithing, Cooking, Gunsmithing, Leatherworking, Painting, Pottery, Sculpture, Weaving
All characters start with 20 points for [[Attributes]].  Players must place at least one point in each Attribute and may not exceed the maximum of eight points in any Attribute.  Each character has one 'favored' Attribute, which must always be at least one point higher than any other Attribute and may affect the way the character uses [[Sorcery]].
Bonus Points add a little flexibility to [[Character Creation]].

A Characterization is a small bit of information about a character that makes him a little bit more of a real person and not just a group of numbers with a name at the top.  Characterizations might include preferences (doesn't like milk), political views (Aralean nationalist), interesting mannerisms (always winks when he says 'Thank you'), or inconsequential abilities (can spit 20 yards).  Every Characterization is worth one Bonus Point and should have some behavioral manifestation that players must roleplay or be deprived of the benefit.  A player may take up to four Characterizations.  Players are, of course, encouraged to develop their characters as much as they like, but further character development is not worth additional Bonus Points.

Flaws are problems or limitations that a character has that defy description by Attributes or Skills.  If a character isn't a good marksman, he can just have a low Ranged Skill; if he's not too bright, he might have a low Air Attribute; but if he's missing an arm, there's no "having two arms" Skill, so having one arm is a Flaw.  Minor Flaws are worth two Bonus Points, Major Flaws are worth four.  Flaws might be physical dysfunctions (having one arm), social disadvantages (wanted dead by a powerful enemy), psychological problems (fear of cats), or even supernatural effects (can only use sorcery at night).  Rather than present a list of every possible kind of flaw, use this rule of thumb: if it has a dramatic or overpowering impact on a character's life all or almost all of the time, it's Major; if it makes a significant but manageable impact on his life some of the time, it's Minor.  Character Concept plays into it as well.  Having one arm would be a Major Flaw for an Aralei Warrior, but probably only a Minor Flaw for a Colonial Diplomat. 

The difference between a Characterization, Minor Flaw, and Major Flaw is one of degree.  If a character dislikes cats and thinks they're eerie, that's a Characterization.  If he has an actual fear of cats, causing visible emotional distress and inability to concentrate when a cat is nearby, that’s probably a Minor Flaw.  If he has pathological ailurophobia and goes catatonic (no pun intended) anytime he sees a picture of or hears the word 'cat,' that's a Major Flaw.

So now the character is a walking disaster with a heap of Bonus Points.  It is the time to spend some of those Bonus Points on Gifts.  Gifts are exactly like Flaws, except good.  The same rule of thumb for Major and Minor applies.  Gifts and Flaws can be combined in the case of abilities that both give benefits and create problems.  Exceptional Night Vision (M, no penalties in darkness) might be combined with Light Sensitivity (m, must wear eye protection in sunlight or be at -1 to all actions) to balance out as a Minor Gift.

The GM has the right to veto any Gift or Flaw she believes will be unbalancing, disruptive, or out of genre, and may wish to place a maximum limit on the number of BP available from Flaws.

Players can also spend Bonus Points to further improve Skills and Attributes.  Additional points in Attributes cost 4 Bonus Points, Skills cost one.  This is the only way for a character to have a skill higher than 4 at the start of an adventure.

Generally, Bonus Points can only be earned and spent during character creation.  Alternatively (and at the GM's sole discretion), players may continue to gain and use Bonus Points by further developing their characters' back stories, creating illustrations or props for the game, or bringing snacks for everyone at the session (perhaps more appropriately termed 'Brownie Points').
A character concept is a very short description of who a character is.  It's the quick answer if somebody asks, "What kind of character are you playing?"  This first step is actually quite important, because placing [[Attribute|Attributes]] and [[Skill|Skills]] points will be much easier if you first have a clear idea of your character's type in mind.  Character Concepts frequently include a job, a social role, or a personality modifier.

Examples: Bounty Hunter, Colonial Diplomat, Escaped Convict, Nehom Explorer, Tribal Seer,
Pete is about to create a character for a game.  First, he talk swith Sharon, the [[GM|Gamemaster]] about what kind of adventure she is planning.  She tells him her adventure is going to deal with exploration, so as long as his character has some travel and survival skills, she can work just about anyone in.

Pete is interested in the Taksans, so he decides to play a young soldier sent to evaluate.
All characters start with 4+D [[Luck]].

Characters start with the average of their [[Attributes]] (rounded down) in [[Quintessence]] (probably 5).

Characters start with 0 [[Karma]].

Characters start with 0 [[Experience]].

Players should define their chracters' gender, age, height, weight, and ethnicity during character creation.

Optional rule: You can generate a random height by using the average height for your race and gender and adding D inches or Dx2 centimeters.

Taksan: Men cm (5'6"), Women cm (5'2")
Aralei: Men 6'6" (cm), Women 5'10" (cm)
Retulian: Men 6'2" (cm), Women 5'8" (cm)
Nehom: Men 5'4" (cm), Women 4'10" (cm)
 
Characters start with 16 points to invest in [[Skills]] and foreign [[Languages]].  They cannot be raised higher than 4 at this phase of Character creation.
This game began with a few questions that had far-reaching consequences: Why had the Europeans developed and acquired so much technology by the time of Columbus, while the Native Americans had developed so little?  Why did the Europeans colonize the Americas and not the Chinese?  And since swords are so cool and guns are so cool, but guns seem to have completely displaced swords, so how can you have a game setting that allows both to be viable options?
/***
|''Name:''|CryptoFunctionsPlugin|
|''Description:''|Support for cryptographic functions|
***/
//{{{
if(!version.extensions.CryptoFunctionsPlugin) {
version.extensions.CryptoFunctionsPlugin = {installed:true};

//--
//-- 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);
};


}
//}}}
Deception is used for lying to and misleading people, and for making things seem to be what they are not - including forgery (with [[Air]]) and disguise (with [[Earth]]).

Lying can be done offensively (to create reactions and control situations) or defensively (to resist giving away information).  Use it with [[Air]] for offensive and [[Water]] for defensive lying.
[[Introduction]]
MainMenu
/***
|''Name:''|DeprecatedFunctionsPlugin|
|''Description:''|Support for deprecated functions removed from core|
***/
//{{{
if(!version.extensions.DeprecatedFunctionsPlugin) {
version.extensions.DeprecatedFunctionsPlugin = {installed:true};

//--
//-- 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 = {};
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");

}
//}}}
From the point of view of the rules, things are left rather abstract and vague.  One melee attack is pretty much like another as far as the system is concerned.  Players are encouraged to describe exactly how they are doing a particular action.

[[GMs|Gamemaster]] should reward good descriptions with a +1 bonus, and excellent descriptions with a +2 bonus.
Earth is the [[Physical|Physical Attributes]] [[Internal|Internal Attributes]] Attribute - the Attribute of health and stamina.  Characters use Earth to resist and survive damage, poison, and disease, exert themselves while lacking water, food, or sleep.  It is also used to resist intimidation or seduction and to maintain balance and calm.

Individuals with high Earth are typically calm, stoic, and confident.  They trust simply in their ability to endure, and therefore take life as it comes without much complaining or commentary.  Not usually ambitious, many work as simple farmers, miners, or laborers, but they form the backbone and bedrock of many organizations through their patience, reliability, and perseverance.  They are rooted in the world of the senses and love their creature comforts, especially food and drink, often to excess, and can be relatively indifferent of others' problems as long as their own security is not threatened.
Experience Points (XP) are a measure of how much a character has learned in the course of his adventures.  Players can spend XP to improve their characters' abilities.

[[Attributes]] cost four times their current value to improve.

//A character with Air 4 needs 16 XP to improve to Air 5.//

[[Skills]] cost their current value to improve.

//A character with Charisma 3 needs 3 XP to improve to Charisma 4.//

[[Specializations]] cost 1/2 their current value (rounded down) to improve.

//A character with //

Other: Players can buy some Gifts or pay off some Flaws using XP with GM permission.  It's probably too late to develop amazing night vision or regrow a missing limb, but psychological, social, and supernatural Gifts and Flaws are fair game.
External Attributes are used to interact with and create change in the world outside of the individual.  [[Air]] and [[Fire]] are External Attributes.
Fire is the [[Physical|Physical Attributes]] [[External|External Attributes]] Attribute - the Attribute of strength and power.  Characters use Fire to push, pull, lift, carry, create and destroy physical objects.  It is also a general measure of a character's attractiveness and sex appeal.

A dominant Fire attribute indicates tremendous vigor and dynamic energy.  These are the doers in the world.  They often find work as soldiers and hunters, artisans, and performers.  Others find their calling as reformers and revolutionaries, especially when alloyed with [[Air]].  Universally they are driven by passion - be it for justice, excellence, vengeance, or domination.  In confrontations they
The gamemaster or GM is a very special player.  She is the one who plans and presents the adventure to the players, describes the setting, and represents the players' allies and antagonists.

[[NPCs]]
[[WT]] takes place in a relatively small area around a large, elongated gulf on the southeast coast of a major continent.  The gulf is warm and shallow, only partially salty because of a constricted connection to the ocean and the large amounts of fresh water flowing into it.

The area to the north of the gulf is largely grassland with occasional patches of sparse forest and scrub.  This savannah fades into desert and steppe to the east and north.  South of the gulf the land is low and marshy, fragmented by twisting waterways and bayous.  Still farther south lies a dense rainforest.

To the northwest is a range of rugged mountains and occasionally-active volcanoes separated by broad valleys.  Ample rainfall in this region collects into a large river that flows south and empties into the gulf.
Attribute
Gamemaster
GM
NPC
PC - Player Character
Player
Player Character
Skill
Trait
Internal Attributes are used to perceive and control forces within an individual, and resist changes from without. [[Earth]] and [[Water]] are Internal Attributes.
Karma is a measure of how good or evil a character is.  It is an arbitrary force at work in the universe.

When a player spends [[Luck]] or [[Quintessence]], the GM makes a Karma check.  She rolls one die.  If it is equal to or lower than the number of the character's Karma, a Karma Effect occurs instead of the Luck or Quintessence effect.
'Knowledge' is not a single skill, but a class of skills to measure book learning and ability to do research.  Therefore, a character doesn't learn 'the' Knowledge skill, he learns 'a' Knowledge skill.  When learning a Knowledge skill, the player must define which Knowledge skill his character is learning.  A character may know any number of Knowledge skills.


Almost always used with [[Air]].
Examples: Astronomy & Astrology, Biology, Folklore, Geography, Geology, History, Religion & Mythology, Physics
Each culture has its own language.  Each character starts out a native speaker of his home country's language.

Languages work just like [[Skills]] except they only go up to 4.  They can be purchased with starting [[Skill Points|Creation: Skills and Languages]] and [[Bonus Ponts|Creation: Bonus Points]] just like skills or improved later in play.

0 - No training.  Can only communicate through gestures and drawings.
1 - Beginner.  Can make very simple statements, questions, and requests.  May pick up a few key words when listening to a native.
2 - Intermidiate.  Can communicate slowly and simply.
3 - Fluent.  The character can speak freely, has a distinctive accent, and may be momentarily puzzled by specialized vocabulary, slang, and idioms.
4 - Native.  The character has no discernable accent, knows any specialized vocabulary, slang, and idioms any native speaker would.
/***
|''Name:''|LegacyStrikeThroughPlugin|
|''Description:''|Support for legacy (pre 2.1) strike through formatting|
|''Version:''|1.0.2|
|''Date:''|Jul 21, 2006|
|''Source:''|http://www.tiddlywiki.com/#LegacyStrikeThroughPlugin|
|''Author:''|MartinBudden (mjbudden (at) gmail (dot) com)|
|''License:''|[[BSD open source license]]|
|''CoreVersion:''|2.1.0|
***/

//{{{
// Ensure that the LegacyStrikeThrough Plugin is only installed once.
if(!version.extensions.LegacyStrikeThroughPlugin) {
version.extensions.LegacyStrikeThroughPlugin = {installed:true};

config.formatters.push(
{
	name: "legacyStrikeByChar",
	match: "==",
	termRegExp: /(==)/mg,
	element: "strike",
	handler: config.formatterHelpers.createElementAndWikify
});

} //# end of "install only once"
//}}}
The Laryllian Empire is a nation far to the east.  The [[Nehom]] separated themselves from Llaryll long ago.  The Llaryllians have since developed advanced technology and magic along somewhat different lines than other civilizations.

They have gunpowder and other explosive and incendiary compounds, but not firearms.  They use crossbows with exploding arrows (elite soldiers use longbows and a unique martial art that enables them to use the longbow as a melee weapon without damaging it) and 'flamethrowers'.
Luck is a stat that defines how lucky a character is.  All characters start with 4+D Luck.

Chracters gain additional Luck every time the player rolls a critical success.

A player can spend a Luck point at any time to reroll a single die roll.  The player must keep the second result.  If the second result is a critical success, the player does not earn a replacement Luck point.

Optional Rule: Luck is a secret stat, kept by the GM for each character.  If a player attempts to //push their Luck// by spending a Luck point when they don't have any, attempt to invoke a [[Karma Effect|Karma]].  If no Karma Effect occurs, they are simply //out of Luck//.
[[Introduction]]

Setting
[[Geography]]
[[Taksan]]
[[Aralei]]
[[Retulia]]
[[Nehom]]
[[Other Peoples]]

System
[[Character Creation]]
[[Traits]]
[[Basic Mechanics]]
[[Combat]]
[[Sorcery]]

[[Afterwords]]
[[Glossary]]
[[Taksans|Taksan]] use a metric system of measurement.  For the most part, this uses the same meters, liters, and grams as in the real world.

Taksan Time
1 day = 8 watches
1 watch = 100 X
1 X = 100 nonces

Taksan/Real World Time Conversion
1 watch = 3 hours
1 X = 1.8 minutes (1m48s)
1 nonce = 1.08 seconds

Taksan Calendar
13 months of 27 days (three 9-day weeks) and a 13-day festival week that occurs outside the normal months (14 days in a leap year)
Medicine is the skill used to treat injured characters
Melee Combat is used for fighting at close range either unarmed or with hand weapons.  Used with Air for attacks and Water for defense.  See [[Combat]].
Mental Attribtes refer to the mind and spirit, the realm of ideas and the imagination.  [[Air]] and [[Water]] are Mental Attributes.
Soldier
A4, E5, F5, W4
Melee Combat 4
Ranged Combat 4
Strategy 2
The desert teems with life; it is simply quieter than most places.  The holdings of the Nehom are not vast expanses of sand dunes, as many would imagine, but basins of scrubland and cacti – even stands or trees around oases – divided by canyons, crags, and salt flats.  Each tribe has control of an area of ‘good’ land sufficient for their needs, with ‘difficult’ land forming natural boundaries between them.  Each tribe is fiercely protective of its territory, especially the few wells and storage caches.  Trespassers are judged by a seeress; thieves are attacked without hesitation.

Each tribe is led by a matriarch, usually the eldest and most powerful sorceress in the tribe.  The other adult women with magical ability are seeresses and act as the matriarch’s deputies.  All tribal affairs are conducted by the matriarch and seeresses.  Adult women with no significant magical ability my speak in the tribal council, but have no decision-making power.  Men may not even address the council except at the council’s request.  Individuals are considered adult at 16.

Almost all of the Nehom are nomadic herders and gatherers, though a few oases are rich enough to support a small permanent settlement of gardeners, weavers, and traders.

In their own tongue, ‘Nehom’ means ‘the weeping people.’  Many generations ago they were exiled from a nation far to the east and came to dwell in the desert to await the fulfillment of a prophecy.

Nehom are without exception small, slender, and fair, most with straight white-blonde hair and large pale blue eyes.  Both men and women wear their hair long, often elaborately knotted or braided.  In order to survive in the desert sun, they have adopted a largely nocturnal existance, arising the hour before sunset and retiring before midday.  Any time they are out in the sun they cover themselves head to toe in flowing robes of woven wool and goat hair, cowling their heads and veiling their faces, leather boots and gloves, and protecting their eyes with bone goggles.  Each tribe and family had a distinctive pattern of weaving and every individual has some unique embroidery they apply to all their garments, so that even completely covered, one can easily identify everyone by name, family, and tribe.
The people described so far make up only a small part of the world.  There are many more nations in the west on the Taksan's home continent, some of which may begin competing for colonial empires of their own.  In the jungles to the south, there are fierce, inhospitable tribes and perhaps more.  To the north and east, an alien civilization as advanced and powerful as the Taksans' is begining to emerge.
Perception is used to gain information about the world, it is used to resist stealth and surprise, locate hidden or lost objects, detect lies.  It is almost always used with [[Water]], though occasionaly with [[Earth]].  It can also be used with [[Air]] when conducting bureaucratic investigations and some kinds of research.
'Performance' is not a single skill, but a class of skills used for non-tangible arts.  Therefore, a character doesn't learn 'the' Performance skill, he learns 'a' Performance skill.  When learning a Performance skill, the player must define which Performance skill his character is learning.  A character may know any number of Performance skills.

All Performance skills include the ability to compose new works in the selected medium.

Examples: Acting, Dance, Musical Instrument (must specify), Singing, Writing
Physical Attributes refer to the body and the tangible world, the realm of matter and physical force.  [[Earth]] and [[Fire]] are Physical Attributes.
Quintessence is a mysterious fifth element at work in the world that creates magical effects.

A player can spend a Quintessence point at any time to double his character's Attributes for all actions in one round or for one extended action.  The Character need not know about, believe in, or have training in [[Sorcery]] - all people have access to this power instinctively, but most only use it in times of extreme stress.  Some sorcerous effects require Quintessence.

A character's maximum Quintessence is the average of his [[Attributes]], rounded down.  Characters regain one Quintessence every time they get a full-night's sleep (8 hours).
Ranged Combat is used for attacking targets out of reach with either thrown objects, slings, bows, or firearms.  Artillery is used with the [[Strategy]] Skill.  Ranged Combat is used with Air for attacks and Water for defense.  See [[Combat]].
The Retulians are big.
A [[Role-Playing Game]] of Cultures in Collision
[[WT]]
Skills are broad areas of training that characters acquire through study and practice.

0 - No training
1 - novice
2 - apprentice
3 - journeyman
4 - average professional
5 - 'master,' best in a small city
6 - Among the best in a large city
7 - Among the best in a nation
8 - Among the best in the world


[[Athletics]]
[[Bargain]]
[[Charisma]] 
[[Craft]]
[[Deception]]
[[Knowledge]]
[[Medicine]]
[[Melee Combat]]
[[Perception]]
[[Performance]]
[[Ranged Combat]]
[[Sorcery]]
[[Strategy]]
[[Subtlety]]
[[Survival]]
[[Travel]]
The world is alive with magic

Focus
Meditation

''Battle Meditation''
Sorcery+Water
It seems the times you really need a magical boost are the times when you really can't meditate or sit still with your focus.  Battle Meditation is essentially a refined application of Elemental Control that allows a character to use sorcery to an advantage in combat.

A character must make a Sorcery+Water check to enter Battle Meditation.  This is Moderate if done before combat, Difficult if attempted during.

//Air//
Use control of winds to redirect projectiles.  Can double their Air for rolling the accuracy of thrown or bowshot projectiles (1.5xAir for firearms).  Can substitute Air for Water when avoiding incoming projectiles.

//Earth//
Use their control of the element present in their own bodies to resist massive amounts of damage.  Double Earth for the purpose of absorbing damage.

//Fire//
Manipulate fire within the chamber of a firearm.  Can add their Fire to the Fire of the weapon.

//Water//
Manipulate fluids within opponent''Beast Communion''
Water+Sorcery

''Body Mastery''
Earth+Sorcery

''Concentration''
Water+Sorcery

''Dreamsight''
Water+Sorcery

''Dreamwalking''
Water+Sorcery

''Elemental Control''
Sorcery + Favored
Elemental Control is a limited telekinetic control of one's Favored Element.
//Air//
Create and control a wind of a force corresponding to the power of the caster

//Earth//

//Fire//
Become immune to damage by fire.  May not create fire, but can strengthen, subdue, and control existing fires.

//Water//

''Heal Other''
Fire+Sorcery

''Heal Self''
Earth+Sorcery

''Hibernation''
Earth+Sorcery

''Mind Call''
Air+Sorcery

''Mind Trick''
Air+Sorcery
/***
|''Name:''|SparklinePlugin|
|''Description:''|Sparklines macro|
***/
//{{{
if(!version.extensions.SparklinePlugin) {
version.extensions.SparklinePlugin = {installed:true};

//--
//-- Sparklines
//--

config.macros.sparkline = {};
config.macros.sparkline.handler = function(place,macroName,params)
{
	var data = [];
	var min = 0;
	var max = 0;
	var v;
	for(var t=0; t<params.length; t++) {
		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";
		v = Math.floor(((data[d] - min)/(max-min)) * h);
		tick.style.top = (h-v) + "px";
		tick.style.height = v + "px";
		box.appendChild(tick);
	}
};


}
//}}}
Specializations a specific areas of a skill which a character has focused his training.
Strategy is used to effectively lead military operations.  It can be used to direct troops in battle (though not neccessarily inspire them, that's [[Charisma]]), evaluate defenses, operate artillery and siege devices, and play certain games.
Subtlety is the skill used to remain unnoticed.  It is used for sneaking (with [[Water]]), covering tracks, and concealing items (with [[Air]]).
The 13th Warrior
Avatar: The Last Airbender
Brotherhood of the Wolf
Dances With Wolves
The Four Feathers
The Last Samurai
Lawrence of Arabia
The League of Extraordinary Gentlemen
The Mummy
The Mummy Returns
Mushishi
Pathfinder
Princess Mononoke
The Scorpion King
Whale Rider
Survival is used to find food and shelter in the wilderness, track, and predict weather.
If a character has a skill related to the primary skill that could in some conceivable way offer some kind of advantage, a player may add 1/2 of the supporting skill (rounded down) to the one he is using.  For example, an elite healer (Medicine 8) would be able to use his advanced knowledge of human anatomy to target vital arteries or clusters of nerves, giving him a +4 to combat rolls.

If a character has multiple skills that could contribute a Synergy Bonus, use only the highest one.
The Taksans are a large, technologically advanced land empire based on the western continent.  Forty years ago they discovered the eastern continent by accident.

The Taksans are 

fond of hats
have big noses
architecture uses mostly golden rectangles, ellipses, and parabolic arches
love singing
love plants, gardens, and open spaces - urban design balances tall buildings with large parks rather than uninterrupted short buildings

''Taksan Colony''

See
[[Measuring Systems]]
[[Taksan Technology]]
[[Taksan Military Forces]]
The Taksan Military's charter includes domestic peacetime law enforcement, disaster relief, and civil engineering.

Classification, Assignment, and Rotation
Every soldier starts as a Trooper.  Through advanced elective training and competitive exams, any trooper can obtain a specialized classification.  Within a given classification there are several possible assignments that may be performed for a period of one to five years.  Assignments usually require on-the-job training for members of the same classification.  By periodically rotating assignments and even classifications, soldiers maintain proficiency in a variety of skills.

A soldier is identified with his or her classification and assigment when adressed officially or introduced for the first time, and usaully referred to solely by his classification thereafter.

''Troopers''
Troopers are regular infantry and the bulk of the Taksan military.  They are responsible for day-to-day patrol duty (Trooper-Regulars), camp construction, sanitation, and maintenance (Trooper-Quartermasters), foraging, supply, logistics (Trooper-Commisary), administrative work and line officers.

Troopers are usually equipped with rifle, pistol, and bayonette.

''Artillery''
Artillery soldiers are in charge of cannons and mortars, the manufacture of munitions.  Since they have little to do during peacetime besides make bullets and put on occasional fireworks shows, this classification is seen as a dead end to contain embarassing failures.  They are frequently caricatured as slow-witted and hard of hearing.

''Medics''
 
Medics are usually equipped with pistols for self-defense.

''Dragoons''
Dragoons are hybrid mounted infantry and light cavalry.  They are responsible for scouting and reconnaissance (Dragoon-Scouts), policing wildlands, rapid trasport, escort duties, and fugitive capture (Dragoon-Rangers), intelligence and detective work, skirmishing and harrassment.

Dragoons are issued rifles, sabers, pistols, and bayonettes.

''Lancers''
Lancers are the special forces of the Taksan Army.  Originally armored heavy cavalry shock troops.  They are used for riot control and counter-insurgency, siege-breaking, and hard targets.

Lancer-Snipers cross-train with Dragoons (particularly Dragoon-Scouts and -Rangers)

Lancer-Grenadiers are Lancers cross-trained as Artillery.  They operate light, mobile artillery with Lancer units, act as sappers and demolition units, combat and civil engineering, and make up the bulk of the Army's research and development unit.  They are equppied with a variety of grenades and explosives, depending on their mission.
The [[Taksan Empire|Taksan]] is among the most technologically advanced in the world.  They have steam power, single shot breech-loading firearms, interchangeable parts, and lighter-than-air flight.

They do not have internal combustion, repeating firearms.

As a general rule of thumb, if it was available in late 1800's Europe and North America, the Taksans have got it.  But just because it exists doesn't mean it's widespread or even readily available, especially in the Colony.
[[Attributes]]
[[Skills]]
[[Quintessence]]
[[Luck]]
[[Karma]]
[[Experience]]
[[Limit?]]
'Travel' is not a single skill, but a class of skills used for going places.  Therefore, a character doesn't learn 'the' Travel skill, he learns 'a' Travel skill.  When learning a Travel skill, the player must define which Travel skill his character is learning.  A character may know any number of Travel skills.

All Travel skills involve training in navigation and vehicle maintenance (or mount care).

Examples: Balooning, Cart & Wagon Driving, Riding, Sailing, Steam Car Driving, Train Operation
Skills are very broad descriptions of training and ability; a character may easily be well-trained in a skill and still come across and application of the skill he is wholly unfamiliar with.  For example, an Aralei archer would have no idea how to use a Taksan rifle, even though both rely on similar principles of leading targets and aiming and use the same Skill.

The first time a character tries something he is unfamiliar with, he can only use his Attributes, not his Skill.  The second time he encounters the situation, he can use 1/4 of his Skill, 1/2 on the third try, and so on, until he can apply his whole skill from the fifth attempt on.
Water is the [[Mental|Mental Attributes]] [[Internal|Internal Attributes]] Attribute - the Attribute of wisdom and sensitivity.  Characters may use Water to resist persuasion, bribery, seduction, or intimidation, detect danger, read others' emotions, and work with animals.  It also helps a character prevent magical intrusions into his mind, sense magical emanations, and work his own sorcery with subtlety.

"Still waters run deep," accurately describes people with a powerful Water element.  They are quiet, and often seem to be lost in a world of dreams, but through keen perception and natural intuition they often understand more than they let on.  People with high water find their niche as healers and caregivers, mystics and spiritual leaders, and advisors.  They tend to avoid direct confronatations.
section of the TiddlyWiki HTML file", MarkupPostBody: "This tiddler is inserted at the end of the
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", StyleSheetColors: "This shadow tiddler contains CSS definitions related to the color of page elements. ''DO NOT EDIT THIS TIDDLER'', instead make your changes in the StyleSheet shadow tiddler.", StyleSheet: "This tiddler can contain custom CSS definitions", StyleSheetLayout: "This shadow tiddler contains CSS definitions related to the layout of page elements. ''DO NOT EDIT THIS TIDDLER'', instead make your changes in the StyleSheet shadow tiddler.", 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 = typeof Animator == "function" ? new Animator() : null; // 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 showBackstage; // Whether to include the backstage area 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 t10,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 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") { // 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, baseType; 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"; } if(!baseType) baseType = listType; listLevel = lookaheadMatch[0].length; w.nextMatch += lookaheadMatch[0].length; var t; if(listLevel > currLevel) { for(t=currLevel; tlistLevel; 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; tnewLevel; 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", element: "pre", handler: function(w) { switch(w.matchText) { case "/*{{{*/\n": // CSS this.lookaheadRegExp = /\/\*\{\{\{\*\/\n*((?:^[^\n]*\n)+?)(\n*^\/\*\}\}\}\*\/$\n?)/mg; break; case "{{{\n": // monospaced block this.lookaheadRegExp = /^\{\{\{\n((?:^[^\n]*\n)+?)(^\}\}\}$\n?)/mg; break; case "//{{{\n": // plugin this.lookaheadRegExp = /^\/\/\{\{\{\n\n*((?:^[^\n]*\n)+?)(\n*^\/\/\}\}\}$\n?)/mg; break; case "\n": //template this.lookaheadRegExp = /\n*((?:^[^\n]*\n)+?)(\n*^$\n?)/mg; break; default: break; } config.formatterHelpers.enclosedTextHelper.call(this,w); } }, { name: "wikifyComment", match: "^(?:/\\*\\*\\*|\n)/mg); w.subWikifyTerm(w.output,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: "wikiLink", match: config.textPrimitives.unWikiLink+"?"+config.textPrimitives.wikiLink, handler: function(w) { if(w.matchText.substr(0,1) == config.textPrimitives.unWikiLink) { w.outputText(w.output,w.matchStart+1,w.nextMatch); return; } 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 || 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: "characterFormat", match: "''|//|__|\\^\\^|~~|--(?!\\s|$)|\\{\\{\\{", handler: function(w) { switch(w.matchText) { case "''": w.subWikifyTerm(w.output.appendChild(document.createElement("strong")),/('')/mg); break; case "//": w.subWikifyTerm(createTiddlyElement(w.output,"em"),/(\/\/)/mg); break; case "__": w.subWikifyTerm(createTiddlyElement(w.output,"u"),/(__)/mg); break; case "^^": w.subWikifyTerm(createTiddlyElement(w.output,"sup"),/(\^\^)/mg); break; case "~~": w.subWikifyTerm(createTiddlyElement(w.output,"sub"),/(~~)/mg); break; case "--": w.subWikifyTerm(createTiddlyElement(w.output,"strike"),/(--)/mg); break; case "{{{": var lookaheadRegExp = /\{\{\{((?:.|\n)*?)\}\}\}/mg; lookaheadRegExp.lastIndex = w.matchStart; var lookaheadMatch = lookaheadRegExp.exec(w.source); if(lookaheadMatch && lookaheadMatch.index == w.matchStart) { createTiddlyElement(w.output,"code",null,null,lookaheadMatch[1]); w.nextMatch = lookaheadRegExp.lastIndex; } break; } } }, { name: "customFormat", match: "@@|\\{\\{", handler: function(w) { switch(w.matchText) { case "@@": 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,/(@@)/mg); break; case "{{": lookaheadRegExp = /\{\{[\s]*([\w]+[\s\w]*)[\s]*\{(\n?)/mg; lookaheadRegExp.lastIndex = w.matchStart; lookaheadMatch = lookaheadRegExp.exec(w.source); if(lookaheadMatch) { w.nextMatch = lookaheadRegExp.lastIndex; e = createTiddlyElement(w.output,lookaheadMatch[2] == "\n" ? "div" : "span",null,lookaheadMatch[1]); w.subWikifyTerm(e,/(\}\}\})/mg); } break; } } }, { name: "mdash", match: "--", handler: function(w) { createTiddlyElement(w.output,"span").innerHTML = "—"; } }, { name: "lineBreak", match: "\\n|
", handler: function(w) { createTiddlyElement(w.output,"br"); } }, { name: "rawText", match: "\\\"{3}|", lookaheadRegExp: /(?:\"{3}|)((?:.|\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: "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; } } ]; //-- //-- Wikifier //-- function getParser(tiddler,format) { if(tiddler) { if(!format) format = tiddler.fields["wikiformat"]; var i; if(format) { for(i in config.parsers) { if(format == config.parsers[i].format) return config.parsers[i]; } } else { for(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) { 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 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 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 = params[0] ? now.formatString(params[0].trim()) : 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.list.filter.handler = function(params) { var filter = params[1]; var results = []; if(filter) { var tiddlers = store.filterTiddlers(filter); for(var t=0; t=last; t--) { var tiddler = tiddlers[t]; var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8); if(theDay != lastDay) { var ul = document.createElement("ul"); place.appendChild(ul); createTiddlyElement(ul,"li",null,"listTitle",tiddler[field].formatString(dateFormat)); lastDay = theDay; } createTiddlyElement(ul,"li",null,"listLink").appendChild(createTiddlyLink(place,tiddler.title,true)); } }; 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> config.macros.gradient.handler = function(place,macroName,params,wikifier) { var panel = wikifier ? createTiddlyElement(place,"div",null,"gradient") : place; panel.style.position = "relative"; panel.style.overflow = "hidden"; panel.style.zIndex = "0"; if(wikifier) { var styles = config.formatterHelpers.inlineCssHelper(wikifier); config.formatterHelpers.applyCssHelper(panel,styles); } var colours = []; for(var t=1; t>"); 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 rows) rows = lines.length + 5; rows = Math.min(rows,maxLines); e.setAttribute("rows",rows); e.setAttribute("edit",field); place.appendChild(wrapper1); } return e; } }; config.macros.tagChooser.onClick = function(ev) { var e = ev ? ev : 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 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); if(customFields) 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 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(ev) { var e = ev ? ev : 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(); }; //-- //-- Tabs macro //-- 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': 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; } } }; //-- //-- 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 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; }; // Return the tiddler as an RSS item Tiddler.prototype.toRssItem = function(uri) { var s = []; s.push("" + this.title.htmlEncode() + ""); s.push("" + wikifyStatic(this.text,null,this).htmlEncode() + ""); for(var t=0; t" + this.tags[t] + ""); s.push("" + uri + "#" + encodeURIComponent(String.encodeTiddlyLink(this.title)) + ""); s.push("" + this.modified.toGMTString() + ""); return s.join("\n"); }; // Format the text for storage in an RSS item Tiddler.prototype.saveToRss = function(uri) { return "\n" + this.toRssItem(uri) + "\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 modifier = this.modifier; if(!modifier) modifier = config.messages.subtitleUnknown; var modified = this.modified; if(modified) modified = modified.toLocaleString(); else modified = config.messages.subtitleUnknown; return config.messages.tiddlerLinkTooltip.format([this.title,modifier,modified]); }; 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(); return serverType ? new config.adaptors[serverType] : 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) { var t = tiddlers[title]; return t instanceof Tiddler ? t : null; }; 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<" + "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,match) { var candidates = this.reverseLookup("tags",excludeTag,!!match); var results = []; for(var t=0; tString 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; var n,result; for(n in t.fields) { result = callback(t,n,t.fields[n]); if(result) return result; } if(onlyExtendedFields) return undefined; for(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; 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,tiddler,template,animate,unused,customFields,toggle) { var title = (tiddler instanceof Tiddler)? tiddler.title : tiddler; 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,"onGetTiddler"); }, onGetTiddler: function(context) { var tiddler = context.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(force) { var place = document.getElementById(this.container); var e = place.firstChild; if(!e) return; this.refreshTiddler(e.getAttribute("tiddler"),force ? null : e.getAttribute("template"),true); while((e = e.nextSibling) != null) this.refreshTiddler(e.getAttribute("tiddler"),force ? null : 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(ev) { var e = ev ? ev : 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(ev) { var e = ev ? ev : 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 0) displayMessage(config.macros.search.successMsg.format([matches.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 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 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(context) { var tiddler = context.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(context) { var title = context.title; var syncItem = st.syncItems.findByField("title",title); if(syncItem !== null) { syncItem = st.syncItems[syncItem]; store.resetTiddler(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=0; t--) stash.appendChild(nodes[t]); } var wrapper = document.getElementById("contentWrapper"); isAvailable = function(title) { var s = title ? title.indexOf(config.textPrimitives.sectionSeparator) : -1; if(s!=-1) title = title.substr(0,s); return store.tiddlerExists(title) || store.isShadowTiddler(title); }; if(!title || !isAvailable(title)) title = config.refreshers.pageTemplate; if(!isAvailable(title)) title = config.refreshers.defaultPageTemplate; //# this one is always avaialable 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) { setStylesheet(title == null ? "" : store.getRecursiveTiddlerText(title,"",10),title,doc ? doc : document); } function refreshColorPalette(title) { if(!startingUp) refreshAll(); } function refreshAll() { refreshPageTemplate(); refreshDisplay(); refreshStyles("StyleSheetLayout"); refreshStyles("StyleSheetColors"); refreshStyles(config.refreshers.styleSheet); refreshStyles("StyleSheetPrint"); } //-- //-- Options 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'; // Split up into two so that indexOf() of this source doesn't find it var endSaveArea = ''; // 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 = /(/; 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( "".format([blockName]), "".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("",""," " + 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) { 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("http://www.geocities.com/")) != -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("http://www.geocities.com/","g"),"\\"); else if(originalPath.indexOf("file://///") == 0) // FireFox pc network file localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("http://www.geocities.com/","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("http://www.geocities.com/","g"),"\\"); return localPath; } function getBackupPath(localPath,title,extension) { var slash = "\\"; var dirPathPos = localPath.lastIndexOf("\\"); if(dirPathPos == -1) { dirPathPos = localPath.lastIndexOf("http://www.geocities.com/"); slash = "http://www.geocities.com/"; } var backupFolder = config.options.txtBackupFolder; if(!backupFolder || backupFolder == "") backupFolder = "."; var backupPath = localPath.substr(0,dirPathPos) + slash + backupFolder + localPath.substr(dirPathPos); backupPath = backupPath.substr(0,backupPath.lastIndexOf(".")) + "."; if(title) backupPath += title.replace(/[\\\/\*\?\":<> ]/g,"_") + "."; backupPath += (new Date()).convertToYYYYMMDDHHMMSSMMM() + "." + (extension ? extension : "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(""); s.push(""); s.push("" + wikifyPlain("SiteTitle").htmlEncode() + ""); if(u) s.push("" + u.htmlEncode() + ""); s.push("" + wikifyPlain("SiteSubtitle").htmlEncode() + ""); s.push("en-us"); s.push("Copyright " + d.getFullYear() + " " + config.options.txtUserName.htmlEncode() + ""); s.push("" + d.toGMTString() + ""); s.push("" + d.toGMTString() + ""); s.push("http://blogs.law.harvard.edu/tech/rss"); s.push("TiddlyWiki " + version.major + "." + version.minor + "." + version.revision + ""); // 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("\n" + tiddlers[t].toRssItem(u) + "\n"); } // And footer s.push(""); s.push(""); // Save it all return s.join("\n"); } //-- //-- Filesystem code //-- function convertUTF8ToUnicode(u) { return window.netscape == undefined ? manualConvertUTF8ToUnicode(u) : 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(); return fin.length > 0 ? u + fin : 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 = mozillaSaveFile(fileUrl,content); if(!r) r = ieSaveFile(fileUrl,content); if(!r) r = javaSaveFile(fileUrl,content); return r; } function loadFile(fileUrl) { var r = mozillaLoadFile(fileUrl); if((r == null) || (r == false)) r = ieLoadFile(fileUrl); if((r == null) || (r == false)) r = javaLoadFile(fileUrl); return r; } function ieCreatePath(path) { try { var fso = new ActiveXObject("Scripting.FileSystemObject"); } catch(ex) { return null; } var pos = path.lastIndexOf("\\"); if(pos!=-1) path = path.substring(0, pos+1); var scan = []; scan.push(path); var i = 0; do { var parent = fso.GetParentFolderName(scan[i++]); if (fso.FolderExists(parent)) break; scan.push(parent); } while(true); for(i=scan.length-1;i>=0;i--) { if (!fso.FolderExists(scan[i])) fso.CreateFolder(scan[i]); } return true; } // Returns null if it can't do it, false if there's an error, true if it saved OK function ieSaveFile(filePath,content) { ieCreatePath(filePath); 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) { ieCreatePath(dest); 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 TiddlyWiki files //-- function FileAdaptor() { this.host = null; this.store = null; return this; } FileAdaptor.serverType = 'file'; FileAdaptor.prototype.setContext = function(context,userParams,callback) { if(!context) context = {}; context.userParams = userParams; if(callback) context.callback = callback; context.adaptor = this; if(!context.host) context.host = this.host; context.host = FileAdaptor.fullHostName(context.host); if(!context.workspace) context.workspace = this.workspace; return context; }; FileAdaptor.fullHostName = function(host) { if(!host) return ''; if(!host.match(/:\/\//)) host = 'http://' + host; return host; }; FileAdaptor.minHostName = function(host) { return host ? host.replace(/^http:\/\//,'').replace(/\/$/,'') : ''; }; // Open the specified host FileAdaptor.prototype.openHost = function(host,context,userParams,callback) { this.host = host; context = this.setContext(context,userParams,callback); context.status = true; if(callback) window.setTimeout(function() {callback(context,userParams);},10); return true; }; FileAdaptor.loadTiddlyWikiCallback = function(status,context,responseText,url,xhr) { context.status = status; if(!status) { context.statusText = "Error reading file: " + xhr.statusText; } else { context.adaptor.store = new TiddlyWiki(); if(!context.adaptor.store.importTiddlyWiki(responseText)) context.statusText = config.messages.invalidFileError.format([url]); } context.complete(context,context.userParams); }; // Get the list of workspaces on a given server FileAdaptor.prototype.getWorkspaceList = function(context,userParams,callback) { context = this.setContext(context,userParams,callback); context.workspaces = [{title:"(default)"}]; context.status = true; if(callback) window.setTimeout(function() {callback(context,userParams);},10); return true; }; // Open the specified workspace FileAdaptor.prototype.openWorkspace = function(workspace,context,userParams,callback) { this.workspace = workspace; context = this.setContext(context,userParams,callback); context.status = true; if(callback) 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,filter) { context = this.setContext(context,userParams,callback); if(!context.filter) context.filter = filter; context.complete = FileAdaptor.getTiddlerListComplete; if(this.store) { var ret = context.complete(context,context.userParams); } else { ret = loadRemoteFile(context.host,FileAdaptor.loadTiddlyWikiCallback,context); if(typeof ret != "string") ret = true; } return ret; }; FileAdaptor.getTiddlerListComplete = function(context,userParams) { if(context.filter) { context.tiddlers = context.adaptor.store.filterTiddlers(context.filter); } else { context.tiddlers = []; context.adaptor.store.forEachTiddler(function(title,tiddler) {context.tiddlers.push(tiddler);}); } for(var i=0; i 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= 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(ev) { var e = ev ? ev : window.event; 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 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 "backgroundColor") String.prototype.unDash = function() { var s = this.split("-"); if(s.length > 1) { for(var t=1; t 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 to ">" and " to """ String.prototype.htmlEncode = function() { return this.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 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)); }; //-- //-- 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,attribs) { 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(attribs){ for(var n in attribs){ e.setAttribute(n,attribs[n]); } } 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; } // Prevent an event from bubbling function stopEvent(e){ var ev = e? e : window.event; ev.cancelBubble = true; if (ev.stopPropagation) ev.stopPropagation(); return false; } // 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) { if(!config.browser.isIE) return; var att = e.attributes; if(att) { for(var t=0; t" + s + ""); } 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); } } } function removeStyleSheet(id) { var e = document.getElementById(id); if(e) e.parentNode.removeChild(e); } // 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 ('
%4').format([ usePre ? "title" : "tiddler", tiddler.title.htmlEncode(), attributes, extendedAttributes, usePre ? "\n
" + tiddler.text.htmlEncode() + "
\n" : tiddler.text.escapeLineBreaks().htmlEncode() ]); } catch (ex) { throw exceptionText(ex,config.messages.tiddlerSaveError.format([tiddler.title])); } }; //-- //-- End of scripts //-- //]]>
Hosted by www.Geocities.ws

1