<!--{{{-->
<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]];}

.wizard .notChanged {background:transparent;}
.wizard .changedLocally {background:#80ff80;}
.wizard .changedServer {background:#8080ff;}
.wizard .changedBoth {background:#ff8080;}
.wizard .notFound {background:#ffff80;}
.wizard .putToServer {background:#ff80ff;}
.wizard .gotFromServer {background:#80ffff;}

#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; width:90%; 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 [[ToolbarCommands::ViewToolbar]]'></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 [[ToolbarCommands::EditToolbar]]'></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 excludeLists'></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>>

/***

!Metadata:

|''Name:''|ArchivedTimeline|

|''Description:''|Timeline archived monthly.|

|''Version:''|0.7.0|

|''Date:''|Aug 25, 2007|

|''Source:''|http://sourceforge.net/project/showfiles.php?group_id=150646|

|''Author:''|BramChen (bram.chen (at) gmail (dot) com)|

|''License:''|[[Creative Commons Attribution-ShareAlike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]]|

|''~CoreVersion:''|2.0.11|

|''Browser:''|Firefox 1.5+; InternetExplorer 6.0|



!Syntax:

{{{<<timeline [modified|created [maxentries [dateFormate]]]>>}}}

!Examples:

{{{<<timeline>>}}}

{{{<<timeline created 10>>}}}

{{{<<timeline modified 10 "MMM DD, YYYY">>}}}



!Revision History:

|''Version''|''Date''|''Note''|

|0.7.0|Jul 25, 2006|Accept a date format parameter|

|0.6.3|Jan 14, 2007|Cleaned codes, Removed config.macros.timeline.slider and config.macros.timeline.onClickSlider|

|0.6.2|Dec 10, 2006|Add monthFormat to display month format for Chinese|

|0.6.1|Aug 12, 2006|A great effect on config.macros.timeline.slider for Firefox, thanks Bob McElrath|

|0.6.0|Jul 25, 2006|Runs compatibly with TW 2.1.0 (rev #403+)|

|0.5.2|Jun 21, 2006|Fixed bugs for dateFormat of TW 2.1|

|~|~|Change default dateFormat to "0DD MMM, YYYY"|

|0.5.1|Jun 04, 2006|Added config.macros.archivedTimeline.orderBy for localization|

|0.5.0|Apr 19, 2006|Fixed bug for twice records of the same date ()|

|~|~|Added Date.prototype.convertToLocalYYYYMMDDHHMM<<br>>in order to backward compatible with 2.0.6-|

|0.4.0|Apr 03, 2006|Added new parameter, {{{<<timeline [sortfield] [maxentries]>>}}}|

|~|~|Added config.options.txtTimelineMaxentries|

|0.3.1|Feb 04, 2006|JSLint checked|

|0.3.0|Feb 04, 2006|Fixed several missing variable declarations|

|0.2.0|Dec 26, 2005|changed for the new feature of Macro timeline of TW 2.0.0 beta 6|

|0.1.0|Nov 3, 2005|Initial release|



!Code section:

***/

//{{{

version.extensions.archivedTimeline = {major: 0, minor: 7, revision: 0,

	date: new Date("Aug 26, 2007"),

	name: "ArchivedTimeline",

	type: "Macro",

	author: "BramChen",

	source: "http://sourceforge.net/project/showfiles.php?group_id=150646"

};

config.options.txtTimelineMaxentries=0;

config.macros.archivedTimeline = {

	tooltips: "Arkiveret efter  ",

	orderBy:{modified: "ændret", created: "lavet"},

	monthFormat: "0DD MMM YYYY",

	dateFormat: "0DD MMM YYYY"

};

config.macros.timeline = config.macros.archivedTimeline;



config.macros.timeline.handler = function(place,macroName,params) {

	var field = params[0] ? params[0] : "modified";



	place.appendChild(document.createTextNode(this.tooltips + this.orderBy[field]));

	var tiddlers = store.reverseLookup("tags","excludeLists",false,field);

	var lastMonth = ""; var lastDay = ""; var theText = "----\n"; var i = 0;

	var last = (params[1])?params[1]:config.options.txtTimelineMaxentries;

		last = (isNaN(last)||last<1) ? 0:tiddlers.length-Math.min(tiddlers.length,parseInt(last));

	var dateFormat = params[2] ? params[2] : this.dateFormat;

	var cookie; var archives;

	for (var t=tiddlers.length-1; t>=last; t--) {

		var tiddler = tiddlers[t];

		var theMonth = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,6);

		var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);

		if(theMonth != lastMonth) {

			if (lastMonth === "") {

				lastMonth = theMonth;

				}

			else {

				place.appendChild(document.createElement('hr'));

				cookie = 'chktimeline'+(i++);

				archives = this.formatString(this.monthFormat, lastMonth);

				var panel = config.macros.slider.createSlider(place,cookie,archives,this.tooltips + archives);

				wikify(theText,panel);

				lastMonth = theMonth; theText = '----\n';

			}

		}

		if(theDay != lastDay){

			theText +=  tiddler[field].formatString(dateFormat) + '\n';

			lastDay = theDay; 

		}

		theText += '* [[' + tiddler.title + ']]\n';

	}

	place.appendChild(document.createElement('hr'));

	cookie = 'chktimeline'+(i++);

	archives = this.formatString(this.monthFormat, lastMonth);

	var panel = config.macros.slider.createSlider(place,cookie,archives,this.tooltips + archives);

	wikify(theText,panel);

	place.appendChild(document.createElement('hr'));

};



config.macros.timeline.formatString = function(template, yyyymm)

{

	var dateString = new Date(yyyymm.substr(0,4)+'/'+yyyymm.substr(4,2)+'/01');

	template = template.replace(/DDD|0DD|DD/g,'');

	return dateString.formatString(template);

};

if (!Date.prototype.convertToLocalYYYYMMDDHHMM){

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

	}

}

//}}}
/***
|''Name:''|BackstageSidebarPlugin|
|''Description:''|Moves the sidebar to the backstage, as suggested at http://www.tiddlywiki.org/wiki/Dev:Backstage#Customization|
|''Author''|JonathanLister|
|''CodeRepository:''|n/a |
|''Version:''|0.1|
|''Comments:''|Please make comments at http://groups.google.co.uk/group/TiddlyWikiDev |
|''License''|[[BSD License|http://www.opensource.org/licenses/bsd-license.php]] |
|''~CoreVersion:''|2.4|

***/

//{{{
if(!version.extensions.BackstageSidebarPlugin) {
version.extensions.BackstageSidebarPlugin = {installed:true};

config.tasks.sidebar = {
	text: "sidebar",
	tooltip: "sidebar options",
	content: "<<tiddler SideBarOptions>><<tiddler SideBarTabs>>"
};
config.backstageTasks.push("sidebar");

config.macros.BackstageSidebarPlugin = {
	tiddler:tiddler
};

config.macros.BackstageSidebarPlugin.init = function() {
	var tiddler = this.tiddler;
	setStylesheet(store.getTiddlerText(tiddler.title+'##Stylesheet'),'BackstageSidebarPlugin');
};

} //# end of 'install only once'
//}}}

/***
!Stylesheet

#sidebar {
	display:none;
}

!(end of Stylesheet)
***/
[[Link:|http://behovsanalyse.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://behovsanalyse.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
[[Links:|http://maans.newp.dk/TiddlyHome/BibliografiskArkiv/]]
<html><div align="center"><iframe src="http://maans.newp.dk/TiddlyHome/BibliografiskArkiv/" frameborder="2" width="100%" height="800"></iframe></div></html>
[[Links:|http://blog-hu.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://blog-hu.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
[[Link:|http://buksetrolden.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://buksetrolden.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
/***
|Name|CoreTweaks444|
|Source|http://www.TiddlyTools.com/#CoreTweaks|
|Version|n/a|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.2.0|
|Type|plugin|
|Requires||
|Overrides|various|
|Description|a small collection of overrides to TW core functions   |
This tiddler contains changes TW core functions to provide minor changes in standard features or behavior.  It is hoped that some of these tweaks may someday be added into the TW core, so that these adjustments will be available without needing these add-on definitions.
>''Note: the changes contained in this tiddler are generally applicable for version 2.4.1 of TiddlyWiki. Please view [[CoreTweaksArchive]] for tweaks that may be used with earlier versions of TiddlyWiki.''
----
***/
// // {{block{
/***
!!!444 'tiddler' and 'place' - global variables for use in computed macro parameters
***/
// // {{groupbox small{
/***
http://trac.tiddlywiki.org/ticket/444 - OPEN
When invoking a macro, this tweak makes the current containing tiddler object and DOM rendering location available as global variables (window.tiddler and window.place, respectively).  These globals can then be used within "computed macro parameters" to retrieve tiddler-relative and/or DOM-relative values or perform tiddler-specific side-effect functionality.
***/
//{{{
window.coreTweaks_invokeMacro = window.invokeMacro;
window.invokeMacro = function(place,macro,params,wikifier,tiddler) {
	var here=story.findContainingTiddler(place);
	window.tiddler=here?store.getTiddler(here.getAttribute("tiddler")):tiddler;
	window.place=place;
	window.coreTweaks_invokeMacro.apply(this,arguments);
}
//}}}
// // }}}}}}
[[Link:|http://dagbog.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://dagbog.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>


/***
|''Navn:''|DanishTranslationPlugin|
|''Beskrivelse:''|Translation of TiddlyWiki into Danish|
|''Forfatter:''|MartinBudden (mjbudden (at) gmail (dot) com)|
|''Kilde:''|www.example.com |
|''CodeRepository:''|http://svn.tiddlywiki.org/Trunk/association/locales/core/en/locale.en.js |
|''Version:''|0.3.7|
|''Dato:''|Jul 6, 2007|
|''Kommentarer:''|Please make comments at http://groups.google.co.uk/group/TiddlyWikiDev |
|''Licens:''|[[Creative Commons Attribution-ShareAlike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]] |
|''~CoreVersion:''|2.4|
***/

//{{{
//--
//-- Translateable strings
//--

// Strings in "double quotes" should be translated; strings in 'single quotes' should be left alone

config.locale = "da"; // W3C language tag

if (config.options.txtUserName == 'YourName') // do not translate this line, but do translate the next line
	merge(config.options,{txtUserName: "DitNavn"});

merge(config.tasks,{
	save: {text: "gem", tooltip: "Gem dine ændringer til denne TiddlyWiki", action: saveChanges},
	sync: {text: "synk", tooltip: "Synkronisér ændringer med andre TiddlyWiki filer og servere", content: '<<sync>>'},
	importTask: {text: "importér", tooltip: "Importér tiddlers og plugins fra andre TiddlyWiki filer og servere", content: '<<importTiddlers>>'},
	tweak: {text: "Tilpas", tooltip: "Tilpas TiddlyWikis udseende og opførsel", content: '<<options>>'},
	upgrade: {text: "upgradér", tooltip: "Upgrader TiddlyWikis kerne kode", content: '<<upgrade>>'},
	plugins: {text: "udvidelser", tooltip: "Administrér installerede udvidelser", content: '<<plugins>>'}
});

// Options that can be set in the options panel and/or cookies
merge(config.optionsDesc,{
	txtUserName: "Brugernavn til signering af dine ændringer",
	chkRegExpSearch: "Avend almindelige udtryk til søgninger",
	chkCaseSensitiveSearch: "Forskel på store og små bogstaver",
	chkIncrementalSearch: "Bogstav for bogstav-søgning",
	chkAnimate: "Anvend animationer",
	chkSaveBackups: "Gem en backupfil når der gemmes ændringer",
	chkAutoSave: "Gem automatisk ændringer",
	chkGenerateAnRssFeed: "Lav et RSS feed når der gemmes ændringer",
	chkSaveEmptyTemplate: "Lav en tom skabelon når der gemmes ændringer",
	chkOpenInNewWindow: "Ã…ben internet links i et nyt vindue",
	chkToggleLinks: "Når man klikker på et link i åbne tiddlers lukkes de",
	chkHttpReadOnly: "Skjul redigeringsværktøjer når den vises over HTTP",
	chkForceMinorUpdate: "Opdatér ikke brugernavn og dato når tiddlers bliver ændrede",
	chkConfirmDelete: "Bed om bekræftelse før tiddlers slettes",
	chkInsertTabs: "Brug tab tasten til at indsætte tab tegn istedet for at hoppe imellem felter",
	txtBackupFolder: "Navn på mappe til brug for backups",
	txtMaxEditRows: "Maximum antal af rækker i edit bokse",
	txtFileSystemCharSet: "Default tegnsæt til at gemme ændringer (Kun i Firefox/Mozilla)"});

merge(config.messages,{
	customConfigError: "Der opstod problemer ved loading af udvidelser. Se PluginManager for detaljer",
	pluginError: "Fejl: %0",
	pluginDisabled: "Ikke udført fordi det er slået fra via 'systemConfigDisable' tag",
	pluginForced: "Udført fordi det er tvunget via 'systemConfigForce' tag",
	pluginVersionError: "Ikke udført fordi denne udvidelse kræver en nyere udgave af TiddlyWiki",
	nothingSelected: "Intet er valgt. Du er nødt til at vælge en eller flere ting først",
	savedSnapshotError: "Det ser ud som om denne TiddlyWiki er blevet gemt forkert. Se venligst http://www.tiddlywiki.com/#DownloadSoftware for details",
	subtitleUnknown: "(ukendt)",
	undefinedTiddlerToolTip: "Tiddleren '%0' findes ikke endnu",
	shadowedTiddlerToolTip: "Tiddleren '%0' findes ikke endnu, men har en foruddefineret skygge værdi",
	tiddlerLinkTooltip: "%0 - %1, %2",
	externalLinkTooltip: "Internet link til %0",
	noTags: "Der er ingen taggede tiddlere",
	notFileUrlError: "Du er nødt til at gemme denne TiddlyWiki til en fil før du kan gemme ændringer",
	cantSaveError: "Det er ikke muligt at gemme ændringer. Mulige grunde indbefatter:\n- din browser understøtter det ikke (Firefox, Internet Explorer, Safari og Opera virker alle fint hvis de er konfigurerede korrekt)\n- stien til din TiddlyWiki fil indeholder ulovlige tegn\n- TiddlyWiki HTML filen er blevet flyttet eller omdøbt",
	invalidFileError: "Den originale fil '%0' lader ikke til at være en rigtig TiddlyWiki",
	backupSaved: "Backup gemt",
	backupFailed: "Det lykkedes IKKE at gemme en backup fil",
	rssSaved: "RSS feed gemt",
	rssFailed: "Det lykkedes IKKE at gemme et RSS feed",
	emptySaved: "Tom skabelon gemt",
	emptyFailed: "Det lykkedes IKKE at gemme en tom skabelon",
	mainSaved: "Hoved TiddlyWiki fil gemt",
	mainFailed: "Det lykkedes IKKE at gemme hoved TiddlyWiki filen. Dine ændringer er IKKE blevet gemt",
	macroError: "Fejl i makro <<\%0>>",
	macroErrorDetails: "Fejl ved udførsel af makro <<\%0>>:\n%1",
	missingMacro: "Ingen sådan makro",
	overwriteWarning: "En tiddler med navnet '%0' findes allerede. Vælg OK for at overskrive den",
	unsavedChangesWarning: "ADVARSEL! Der er ugemte æmdringer i TiddlyWikien\n\nVælg OK for at gemme\nVælg FORTRYD for at afvise",
	confirmExit: "--------------------------------\n\nDer er ugemte ændringer i TiddlyWikien. Hvis du fortsætter vil du miste disse ændringer\n\n--------------------------------",
	saveInstructions: "GemÆndringer",
	unsupportedTWFormat: "Ikke understøttet TiddlyWiki format '%0'",
	tiddlerSaveError: "Fejl ved forsøg på at gemme tiddler '%0'",
	tiddlerLoadError: "Fejl ved load af tiddler '%0'",
	wrongSaveFormat: "Kan ikke gemme med formatet '%0'. Bruger standard format til at gemme.",
	invalidFieldName: "Ikke tilladt feltnavn %0",
	fieldCannotBeChanged: "Felt '%0' kan ikke ændres",
	loadingMissingTiddler: "Forsøger at hente tiddleren '%0' fra '%1' serveren ved:\n\n'%2' i arbejdsområdet '%3'",
	upgradeDone: "Opgradering til version %0 er nu fuldført\n\nKlik 'OK' for at genopfriske den nyligt opgraderede TiddlyWiki"});

merge(config.messages.messageClose,{
	text: "luk",
	tooltip: "luk dette meddelelsesområde"});

config.messages.backstage = {
	open: {text: "bagscenen", tooltip: "Åben bagsceneområdet for at ændre på nogle grundlæggende indstillinger"},
	close: {text: "luk", tooltip: "Luk bagsceneområdet"},
	prompt: "bagscenen: ",
	decal: {
		edit: {text: "edit", tooltip: "Redigér tiddleren '%0'"}
	}
};

config.messages.listView = {
	tiddlerTooltip: "Klik for at se hele denne tiddlers tekst",
	previewUnavailable: "(forhåndsvisning er ikke tilgængelig)"
};

config.messages.dates.months = ["Januar", "Februar", "Marts", "April", "Maj", "Juni", "Juli", "August", "September", "Oktober", "November","December"];
config.messages.dates.days = ["Søndag", "Mandag", "Tirsdag", "Onsdag", "Torsdag", "Fredag", "Lørdag"];
config.messages.dates.shortMonths = ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"];
config.messages.dates.shortDays = ["Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør"];
// suffixes for dates, eg "1ste","2den","3die"..."30te","31te"
config.messages.dates.daySuffixes = ["ste","den","die","te","te","te","te","te","te","te",
		"te","te","te","te","te","te","te","te","te","te",
		"ste","den","die","te","te","te","te","te","te","te",
		"te"];
config.messages.dates.am = "formiddag";
config.messages.dates.pm = "eftermiddag";

merge(config.messages.tiddlerPopup,{
	});

merge(config.views.wikified.tag,{
	labelNoTags: "ingen tags",
	labelTags: "tags: ",
	openTag: "Ã…ben tag '%0'",
	tooltip: "Vis tiddlere der er taggede med '%0'",
	openAllText: "Ã…ben alle",
	openAllTooltip: "Ã…ben alle disse tiddlere",
	popupNone: "Ingen andre tiddlere er taggede med '%0'"});

merge(config.views.wikified,{
	defaultText: "Tiddleren '%0' findes ikke endnu. Dobbelt-klik for at lave den",
	defaultModifier: "(mangler)",
	shadowModifier: "(indbygget skygge tiddler)",
	dateFormat: "DD MMM YYYY", // use this to change the date format for your locale, eg "YYYY MMM DD", do not translate the Y, M or D
	createdPrompt: "lavet"});

merge(config.views.editor,{
	tagPrompt: "Skriv tags delt med mellemrum, [[brug 2 dobbelte firkantede klammer]] om nødvendigt, eller tilføj allerede eksisterende",
	defaultText: "Skriv teksten til '%0'"});

merge(config.views.editor.tagChooser,{
	text: "tags",
	tooltip: "Vælg eksisterende tags som tilføjelse til denne tiddler",
	popupNone: "Der er ikke defineret nogen tags",
	tagTooltip: "Tilføj tagget '%0'"});

merge(config.messages,{
	sizeTemplates:
		[
		{unit: 1024*1024*1024, template: "%0\u00a0GB"},
		{unit: 1024*1024, template: "%0\u00a0MB"},
		{unit: 1024, template: "%0\u00a0KB"},
		{unit: 1, template: "%0\u00a0B"}
		]});

merge(config.macros.search,{
	label: "søg",
	prompt: "Søg i denne TiddlyWiki",
	accessKey: "F",
	successMsg: "Der er fundet %0 tiddlere som matcher %1",
	failureMsg: "Der er ikke fundet nogen tiddlere som matcher %0"});

merge(config.macros.tagging,{
	label: "tagger: ",
	labelNotTag: "tagger ikke",
	tooltip: "Liste over tiddlere der er taggede med '%0'"});

merge(config.macros.timeline,{
	dateFormat: "DD MMM YYYY"});// use this to change the date format for your locale, eg "YYYY MMM DD", do not translate the Y, M or D

merge(config.macros.allTags,{
	tooltip: "Vis tiddlere der er taggede med '%0'",
	noTags: "Der er ingen taggede tiddlere"});

config.macros.list.all.prompt = "Alle tiddlere i alfabetisk orden";
config.macros.list.missing.prompt = "Tiddlere der linkes til men som ikke er definerede";
config.macros.list.orphans.prompt = "Tiddlere som der ikke linkes til fra nogen andre tiddlere";
config.macros.list.shadowed.prompt = "Tiddlere som er skyggede med grundlæggende indhold";
config.macros.list.touched.prompt = "Tiddlere som er blevet ændret lokalt ";

merge(config.macros.closeAll,{
	label: "luk alle",
	prompt: "Luk alle viste tiddlere (untaget dem som er ved at blive redigerede)"});

merge(config.macros.permaview,{
	label: "vis permalink",
	prompt: "Lav et link til en URL som henter alle de netop nu synlige tiddlere"});

merge(config.macros.saveChanges,{
	label: "gem ændringer",
	prompt: "Gem alle tiddlere for at lave en ny TiddlyWiki",
	accessKey: "S"});

merge(config.macros.newTiddler,{
	label: "ny tiddler",
	prompt: "Lav en ny tiddler",
	title: "Ny Tiddler",
	accessKey: "N"});

merge(config.macros.newJournal,{
	label: "ny journal",
	prompt: "Lav en ny tiddler ud fra nuværende dato og tid",
	accessKey: "J"});

merge(config.macros.options,{
	wizardTitle: "Tilpas avancerede muligheder",
	step1Title: "Disse muligheder gemmes i cookies i din browser",
	step1Html: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='false' name='chkUnknown'>Show unknown options</input>",
	unknownDescription: "//(ukendt)//",
	listViewTemplate: {
		columns: [
			{name: 'Option', field: 'option', title: "Option", type: 'String'},
			{name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
			{name: 'Name', field: 'name', title: "Name", type: 'String'}
			],
		rowClasses: [
			{className: 'lowlight', field: 'lowlight'}
			]}
	});

merge(config.macros.plugins,{
	wizardTitle: "Administrer udvidelser",
	step1Title: "Aktive udvidelser",
	step1Html: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
	skippedText: "(Denne udvidelse er ikke blevet aktiveret fordi den først er blevet tilføjet efter start)",
	noPluginText: "Der er ikke installeret nogen udvidelser",
	confirmDeleteText: "Er du sikker på at du vil slette disse udvidelser:\n\n%0",
	removeLabel: "Fjern systemConfig tag",
	removePrompt: "Fjern systemConfig tag",
	deleteLabel: "slet",
	deletePrompt: "Slet disse tiddlere permanent",
	listViewTemplate: {
		columns: [
			{name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
			{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
			{name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'},
			{name: 'Forced', field: 'forced', title: "Forced", tag: 'systemConfigForce', type: 'TagCheckbox'},
			{name: 'Disabled', field: 'disabled', title: "Disabled", tag: 'systemConfigDisable', type: 'TagCheckbox'},
			{name: 'Executed', field: 'executed', title: "Loaded", type: 'Boolean', trueText: "Yes", falseText: "No"},
			{name: 'Startup Time', field: 'startupTime', title: "Startup Time", type: 'String'},
			{name: 'Error', field: 'error', title: "Status", type: 'Boolean', trueText: "Error", falseText: "OK"},
			{name: 'Log', field: 'log', title: "Log", type: 'StringList'}
			],
		rowClasses: [
			{className: 'error', field: 'error'},
			{className: 'warning', field: 'warning'}
			]}
	});

merge(config.macros.toolbar,{
	moreLabel: "mere",
	morePrompt: "Vis flere muligheder"
	});

merge(config.macros.refreshDisplay,{
	label: "genopfrisk",
	prompt: "Genopfrisk hele TiddlyWikiens udseende"
	});

merge(config.macros.importTiddlers,{
	readOnlyWarning: "Du kan ikke importere til en låst TiddlyWiki fil. Prøv at åbne den fra en fil:// URL",
	wizardTitle: "Importer tiddlere fra en anden fil eller server",
	step1Title: "Trin 1: Find serveren eller TiddlyWiki filen",
	step1Html: "Vælg servertypen: <select name='selTypes'><option value=''>Choose...</option></select><br>Enter the URL or pathname here: <input type='text' size=50 name='txtPath'><br>...or browse for a file: <input type='file' size=50 name='txtBrowse'><br><hr>...or select a pre-defined feed: <select name='selFeeds'><option value=''>Choose...</option></select>",
	openLabel: "open",
	openPrompt: "Ã…ben forbindelsen til denne fil eller server",
	openError: "Der var problemer med at hente tiddlywiki filen",
	statusOpenHost: "Forbinder til hosten",
	statusGetWorkspaceList: "Henter en liste over tilgængelige arbejdsområder",
	step2Title: "Trin 2: Vælg arbejdsområde",
	step2Html: "Indskriv et navn på arbejdsområdet: <input type='text' size=50 name='txtWorkspace'><br>...eller vælg et der allerede er der: <select name='selWorkspace'><option value=''>Choose...</option></select>",
	cancelLabel: "fortryd",
	cancelPrompt: "Fortryd denne import",
	statusOpenWorkspace: "Åben arbejdsområdet",
	statusGetTiddlerList: "Henter listen over tilgængelige tiddlere",
	errorGettingTiddlerList: "Fejl ved hentning af liste over tiddlere, klik Fortryd for at prøve igen",
	step3Title: "Trin 3: Vælg hvilke tiddlere der skal importeres",
	step3Html: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='true' name='chkSync'>Keep these tiddlers linked to this server so that you can synchronise subsequent changes</input><br><input type='checkbox' name='chkSave'>Save the details of this server in a 'systemServer' tiddler called:</input> <input type='text' size=25 name='txtSaveTiddler'>",
	importLabel: "importer",
	importPrompt: "Importer disse tiddlere",
	confirmOverwriteText: "Er du sikker på at du vil overskrive disse tiddlere:\n\n%0",
	step4Title: "Trin 4: Importerer %0 tiddler(e)",
	step4Html: "<input type='hidden' name='markReport'></input>", // DO NOT TRANSLATE
	doneLabel: "udført",
	donePrompt: "Luk denne wizard",
	statusDoingImport: "Importerer tiddlere",
	statusDoneImport: "Alle tiddlere er importede",
	systemServerNamePattern: "%2 on %1",
	systemServerNamePatternNoWorkspace: "%1",
	confirmOverwriteSaveTiddler: "Tiddleren '%0' findes allerede. Klik 'OK' for at overskrive den med detaljerne fra denne server, eller 'Fortryd' for at efterlade uændret",
	serverSaveTemplate: "|''Type:''|%0|\n|''URL:''|%1|\n|''Workspace:''|%2|\n\nDenne tiddler blev lavet automatisk for at skrive denne servers detaljer",
	serverSaveModifier: "(System)",
	listViewTemplate: {
		columns: [
			{name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
			{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
			{name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'},
			{name: 'Tags', field: 'tags', title: "Tags", type: 'Tags'}
			],
		rowClasses: [
			]}
	});

merge(config.macros.upgrade,{
	wizardTitle: "Opgrader TiddlyWikis kerne kode",
	step1Title: "Opdater eller reparer denne TiddlyWiki til sidste nye udgivelse",
	step1Html: "Du er ved at opgradere til sidste nye udgave af TiddlyWikis kerne kode (from <a href='%0' class='externalLink' target='_blank'>%1</a>). Dit indhold vil blive bibeholdt under opgraderinen.<br><br>Bemærk at opgraderinger kan konfikte med gamle udvidelser. Hvis du får problemer med den opgraderede fil se her <a href='http://www.tiddlywiki.org/wiki/CoreUpgrades' class='externalLink' target='_blank'>http://www.tiddlywiki.org/wiki/CoreUpgrades</a>",
	errorCantUpgrade: "Kan ikke opgradere denne TiddlyWiki. Du kan kun opgradere en TiddlyWiki fil som er gemt lokalt på en pc",
	errorNotSaved: "Du skal gemme ændringer før du kan gennemføre en opgradering",
	step2Title: "Bekræft opgraderingsdetaljer",
	step2Html_downgrade: "Du er ved at nedgradere til TiddlyWiki version %0 fra %1.<br><br>Nedgradering til en ældre udgave af kerne koden er IKKE tilrådeligt",
	step2Html_restore: "Denne tiddlyWike bruger allerede den sidste nye kerne kode (%0).<br><br>Du kan fortsætte med opgraderingen for at sikre dig at koden ikke er blevet ødelagt",
	step2Html_upgrade: "Du er ved at opgradere til TiddlyWiki version %0 fra %1",
	upgradeLabel: "opgrader",
	upgradePrompt: "Forbered opgraderingsprocessen",
	statusPreparingBackup: "Forbereder backup",
	statusSavingBackup: "Gemmer backup fil",
	errorSavingBackup: "Der var problemer med at gemme backup filen",
	statusLoadingCore: "Loader kernekoden",
	errorLoadingCore: "Fejl ved load af kernekoden",
	errorCoreFormat: "Fejl ved den nye kernekode",
	statusSavingCore: "Gemmer den nye kernekode",
	statusReloadingCore: "Genloader den nye kernekode",
	startLabel: "start",
	startPrompt: "Start opgraderingsprocessen",
	cancelLabel: "fortryd",
	cancelPrompt: "Fortryd opgraderingsprocessen",
	step3Title: "Opgradering afbrudt",
	step3Html: "Du har afbrudt opgraderingsprocessen"
	});

merge(config.macros.sync,{
	listViewTemplate: {
		columns: [
			{name: 'Selected', field: 'selected', rowName: 'title', type: 'Selector'},
			{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
			{name: 'Server Type', field: 'serverType', title: "Server type", type: 'String'},
			{name: 'Server Host', field: 'serverHost', title: "Server host", type: 'String'},
			{name: 'Server Workspace', field: 'serverWorkspace', title: "Server workspace", type: 'String'},
			{name: 'Status', field: 'status', title: "Synchronisation status", type: 'String'},
			{name: 'Server URL', field: 'serverUrl', title: "Server URL", text: "View", type: 'Link'}
			],
		rowClasses: [
			],
		buttons: [
			{caption: "Synkronisér disse tiddlere", name: 'sync'}
			]},
	wizardTitle: "Synkroniser med internet servere og filer",
	step1Title: "Vælg hvilke tiddlere du vil synkronisere",
	step1Html: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
	syncLabel: "synk",
	syncPrompt: "Synkronisér disse tiddlere",
	hasChanged: "Ændret imens den var koblet fra",
	hasNotChanged: "Uændret imens den var koblet fra",
	syncStatusList: {
		none: {text: "...", color: "gennemsigtig", display:null},
		changedServer: {text: "Ændret på serveren", color: '#8080ff', display:null},
		changedLocally: {text: "Ændret imens den var koblet fra", color: '#80ff80', display:null},
		changedBoth: {text: "ændret imens den var koblet fra også på serveren", color: '#ff8080', display:null},
		notFound: {text: "Ikke fundet på serveren", color: '#ffff80', display:null},
		putToServer: {text: "Gemt update på serveren", color: '#ff80ff', display:null},
		gotFromServer: {text: "Hentet update fra serveren", color: '#80ffff', display:null}
		}
	});

merge(config.commands.closeTiddler,{
	text: "luk",
	tooltip: "Luk denne tiddler"});

merge(config.commands.closeOthers,{
	text: "luk andre",
	tooltip: "Luk alle andre tiddlere"});

merge(config.commands.editTiddler,{
	text: "redigér",
	tooltip: "Redigér denne tiddler",
	readOnlyText: "se",
	readOnlyTooltip: "Se denne tiddlers kilde"});

merge(config.commands.saveTiddler,{
	text: "færdig",
	tooltip: "Gem ændringer til denne tiddler"});

merge(config.commands.cancelTiddler,{
	text: "fortryd",
	tooltip: "Fortryd ændringer til denne tiddler",
	warning: "Er du sikker på at du vil fortryde dine ændringer til  '%0'?",
	readOnlyText: "færdig",
	readOnlyTooltip: "Se tiddlere normalt"});

merge(config.commands.deleteTiddler,{
	text: "slet",
	tooltip: "Slet denne tiddler",
	warning: "Er du sikker på at du vil slette '%0'?"});

merge(config.commands.permalink,{
	text: "permalink",
	tooltip: "Permalink til denne tiddler"});

merge(config.commands.references,{
	text: "referencer",
	tooltip: "Vis tiddlere som linker til denne tiddler",
	popupNone: "Ingen referencer"});

merge(config.commands.jump,{
	text: "spring",
	tooltip: "Spring til en anden tiddler"});

merge(config.commands.syncing,{
	text: "synkroniserer",
	tooltip: "Kontroller synkronisering af denne tiddler med en server eller en fil",
	currentlySyncing: "<div>Currently syncing via <span class='popupHighlight'>'%0'</span> to:</"+"div><div>host: <span class='popupHighlight'>%1</span></"+"div><div>workspace: <span class='popupHighlight'>%2</span></"+"div>", // Note escaping of closing <div> tag
	notCurrentlySyncing: "Sykroniserer ikke lige nu",
	captionUnSync: "Stop synkronisering af denne tiddler",
	chooseServer: "Synkronisér denne tiddler med en anden server:",
	currServerMarker: "\u25cf ",
	notCurrServerMarker: "  "});

merge(config.commands.fields,{
	text: "felter",
	tooltip: "Vis denne tiddlers udvidede felter",
	emptyText: "Der er ingen udvidede felter til rådighed for denne tiddler",
	listViewTemplate: {
		columns: [
			{name: 'Field', field: 'field', title: "Field", type: 'String'},
			{name: 'Value', field: 'value', title: "Value", type: 'String'}
			],
		rowClasses: [
			],
		buttons: [
			]}});

merge(config.shadowTiddlers,{
	DefaultTiddlers: "[[TranslatedGettingStarted]]",
	MainMenu: "[[TranslatedGettingStarted]]\n\n\n^^~TiddlyWiki version <<version>>\n© 2007 [[UnaMesa|http://www.unamesa.org/]]^^",
	TranslatedGettingStarted: "For at komme i gang med denne tomme tiddlywiki, skal du ændre på de følgende tiddlere:\n* SiteTitle & SiteSubtitle: Sidens titel og undertitel, som vist øverst (efter de er gemt, vil de også vise sig i browserens titelmenu)\n* MainMenu: er hovedmenuen (er oftest placeret til venstre)\n* DefaultTiddlers: Indeholder navnene på de tiddlere du vilhave skal starte op når du åbner TiddlyWiki\nDu skal også skrive dit brugernavn for at signere dine redigeringer: <<option txtUserName>>",
	SiteTitle: "Min TiddlyWiki",
	SiteSubtitle: "en genbrugelig ikke-liniær personlig web notesbog",
	SiteUrl: "http://www.tiddlywiki.com/",
	OptionsPanel: "Disse muligheder for at ændre på TiddlyWiki bliver gemt i din browser\n\nDit brugernavn til at signere dine ændringer. Skriv det som et WikiOrd (f.eks. PerPoulsen)\n<<option txtUserName>>\n\n<<option chkSaveBackups>> Save backups\n<<option chkAutoSave>> Auto save\n<<option chkRegExpSearch>> Regexp search\n<<option chkCaseSensitiveSearch>> Case sensitive search\n<<option chkAnimate>> Enable animations\n\n----\nAlso see [[TranslatedAdvancedOptions|AdvancedOptions]]",
	SideBarOptions: '<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal "DD MMM YYYY" "journal">><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel "muligheder \u00bb" "Ændre på TiddlyWikis avancerede muligheder">>',
	SideBarTabs: '<<tabs txtMainTab "Tidslinie" "Tidslinie" TabTimeline "Alle" "Alle tiddlere" TabAll "Tags" "Alle tags" TabTags "Flere" "Flere lister" TabMore>>',
	TabMore: '<<tabs txtMoreTab "Manglende" "Manglende tiddlere" TabMoreMissing "Uden tilknytning" "Tiddlere" TabMoreOrphans "Skyggede" "Skyggede tiddlere" TabMoreShadowed>>'
	});

merge(config.annotations,{
	AdvancedOptions: "Denne skygge tiddler giver adgang til flere avancerede muligheder",
	ColorPalette: "Disse værdier i denne skyggetiddler bestemmer hvilket farveskema, der bliver brugt til ~TiddlyWikis brugerflade",
	DefaultTiddlers: "Tiddlere som er listede i denne skyggetiddler vil automatisk blive vist når ~TiddlyWiki starter op",
	EditTemplate: "HTML skabelonen i denne skyggetiddler bestemmer hvordan tiddlere ser ud når de bliver redigerede",
	GettingStarted: "Denne skyggetiddler giver instruktioner om grundlæggende anvendelse",
	ImportTiddlers: "Denne skyggetiddler giver mulighed for at importere tiddlere",
	MainMenu: "Denne tiddler bliver brugt til at definere indholdet af hoved menuen i venstre side af skærmen",
	MarkupPreHead: "Denne tiddler bliver indsat i toppen af <head> sektionen på TiddlyWiki HTML filen",
	MarkupPostHead: "Denne tiddler bliver indsat i bunden af <head> sektionen på TiddlyWiki HTML filen",
	MarkupPreBody: "Denne tiddler bliver indsat i toppen af<body> sektionen på TiddlyWiki HTML filen",
	MarkupPostBody: "Denne tiddler bliver indsat i slutningen af  <body> sektionen på TiddlyWiki HTML filen umiddelbart efter script blokken",
	OptionsPanel: "Denne skyggetiddler bliver brugt til indholdet af muligheder skydepanelet i højre side",
	PageTemplate: "HTML skabelonen i denne skyggetiddler bestemmer det overordnede ~TiddlyWiki layout",
	PluginManager: "Denne skyggetiddler giver adgang til udvidelsesadministrationen",
	SideBarOptions: "Denne skyggetiddler bruges til indholdet af muligheder panelet i højre sidemenu",
	SideBarTabs: "Denne skyggetiddler bruges til indholdet af fanebladspanelet i højre sidemenu",
	SiteSubtitle: "Denne skyggetiddler bruges som anden del af sidens titel",
	SiteTitle: "Denne skyggetiddler bruges som første del af sidens titel",
	SiteUrl: "Denne skyggetiddler bør sættes til den fulde mål-URL til publikation",
	StyleSheetColors: "Denne skyggetiddler indeholder CSS definitionerne der bestemmer farverne på side elementerne. ''REDIGÉR IKKE DENNE TIDDLER'', lav i stedet dine ændringer i StyleSheet skyggetiddleren",
	StyleSheet: "Denne tiddler kan indeholde specialle CSS definitioner",
	StyleSheetLayout: "Denne skyggetiddler indeholder CSS definitioner der bestemmer layoutet på side elementer. ''REDIGÉR IKKE DENNE TIDDLER'', lav i stedet dine ændringer i StyleSheet skyggetiddleren",
	StyleSheetLocale: "Denne skyggetiddler indeholder CSS definitioner relateret til lokale oversættelser",
	StyleSheetPrint: "Denne skyggetiddler indeholder CSS definitioner til print",
	TabAll: "Denne skyggetiddler indeholder hvad der er i 'Alle' fanen i højre sidemenu",
	TabMore: "Denne skyggetiddler indeholder  hvad der er i 'Flere' fanen i højre sidemenu",
	TabMoreMissing: "Denne skyggetiddler indeholder  hvad der er i 'Mangler' fanen i højre sidemenu",
	TabMoreOrphans: "Denne skyggetiddler indeholder  hvad der er i  'Mangler tilknytning' fanen i højre sidemenu",
	TabMoreShadowed: "Denne skyggetiddler indeholder  hvad der er i 'Skyggede' fanen i højre sidemenu",
	TabTags: "Denne skyggetiddler indeholder  hvad der er i 'Tags' fanen i højre sidemenu",
	TabTimeline: "Denne skyggetiddler indeholder hvad der er i 'Tidslinie' fanen i højre sidemenu",
	ToolbarCommands: "Denne skyggetiddler bestemmer hvilke værktøjer der vises i tiddleres værktøjslinier",
	ViewTemplate: "HTML skabelonen i denne skyggetiddler bestemmer hvordan tiddlere ser ud"
	});

//}}}
[[Link:|http://dansk.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://dansk.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
<br><br>Velkommen til [[MÃ¥ns' TiddlyWiki samling|MÃ¥ns MÃ¥rtensson]]!
Hvis du ikke vil se dig omkring, men hellere telefonere eller maile? så klik [[her|Kontakt]] for mere info.
| !panelname|   !x |   !y |   !w |   !h |   !z | !fold | !hover |h
|       tabs|   -50|   285|      |      |    12|       |        |
|     header|   986|   406|   251|    53|    44|       |        |
|   mainmenu|   226|   499|  auto|  auto|    45|       |        |
|       Davs|   508|   241|   240|  auto|    46|       |        |
[[Davs]]
[[Links:|http://dendybetallerken.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://dendybetallerken.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
[[Link:|http://desktree.tiddlyspot.com/index.html]][[Download:|http://desktree.tiddlyspot.com/download]]
<html><div align="center"><iframe src="http://desktree.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
/***
|Name|[[DragScrollPlugin]]|
|Source|http://www.TiddlyTools.com/#DragScrollPlugin|
|Documentation|http://www.TiddlyTools.com/#DragScrollPlugin|
|Version|1.0.0|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Description|use SHIFT-DRAG to scroll the browser window|
DragScrollPlugin allows you to use your mouse to scroll the browser window by grabbing anywhere on the background of the document while holding the SHIFT key.
!!!!!Documentation
<<<
Whenever your page contains extra-wide content that does not 'wrap' onto extra lines, in can result in both horizontal and vertical scrollbars.  Unfortunately, using each scrollbar separately to navigate across the page can become very tedious and makes it more difficult to interact with your content in a flexible and effective manner.

''Drag-scrolling allows you to move in both the horizontal and vertical direction at the same time, simply by holding SHIFT while clicking and dragging the mouse across the page.''
<<<
!!!!!Configuration
<<<
<<option chkDragScroll>> enable //drag-scrolling//
<<<
!!!!!Revisions
<<<
2008.12.01 [1.0.0] Initial public release
<<<
!!!!!Code
***/
//{{{
version.extensions.DragScrollPlugin= {major: 1, minor: 0, revision: 0, date: new Date(2008,12,01)};

if (!config.dragscroll) { // only once
	config.dragscroll={
		// use to end event handling for processed events
		processed: function(ev) {
			var ev=ev||window.event;
			if (ev) { ev.cancelBubble=true; if (ev.stopPropagation) ev.stopPropagation(); }
			return false;
		},
		// MOUSE DOWN - SET CURSOR, SAVE SCROLL DATA
		savedMouseDown: document.onmousedown,
		mousedown: function(ev) {
			var ev=ev||window.event; var target=resolveTarget(ev); var d=config.dragscroll;
			var scroll=ev.shiftKey&&config.options.chkDragScroll;
			var skip=['input','option','textarea'].contains(target.nodeName.toLowerCase());
			if (!scroll||skip) { // handle event normally
				if (d.savedMouseDown!=undefined) return d.savedMouseDown.apply(target,arguments);
				else return;
			}
			d.mX=!config.browser.isIE?ev.pageX:ev.clientX; d.sX=findScrollX();
			d.mY=!config.browser.isIE?ev.pageY:ev.clientY; d.sY=findScrollY();
			d.scrolling=true; document.body.style.cursor='move';
			return config.dragscroll.processed(ev);
		},
		// MOUSE MOVE - UPDATE SCROLL DATA AND SCROLL THE WINDOW 
		savedMouseMove: document.onmousemove,
		mousemove: function(ev) {
			var ev=ev||window.event; var target=resolveTarget(ev); var d=config.dragscroll;
			if (!d.scrolling || !ev.shiftKey) { // NOT SCROLLING
				if (d.savedMouseMove!=undefined) return d.savedMouseMove.apply(target,arguments);
				else return;
			}
			// set scroll pos based on diff between new and old (x,y)
			var mx=!config.browser.isIE?ev.pageX:ev.clientX;
			var my=!config.browser.isIE?ev.pageY:ev.clientY;
			var sx=!config.browser.isIE?findScrollX():d.sX;
			var sy=!config.browser.isIE?findScrollY():d.sY;
			window.scrollTo(sx-mx+d.mX,sy-my+d.mY);
			return config.dragscroll.processed(ev);
		},
		// MOUSEUP - CLEAR THE DRAG DATA, RESET THE CURSOR
		savedMouseUp: document.onmouseup,
		mouseup: function(ev) {
			var ev=ev||window.event; var target=resolveTarget(ev); var d=config.dragscroll;
			var wasScrolling=d.scrolling; d.scrolling=false; document.body.style.cursor='auto';
			if (d.savedMouseUp!=undefined) return d.savedMouseUp.apply(target,arguments);
			if (wasScrolling) return config.dragscroll.processed(ev);
			return;
		}
	}
	// DEFAULT SETTING (ENABLED)
	if (config.options.chkDragScroll===undefined) config.options.chkDragScroll=true;
	// HIJACK MOUSE HANDLERS
	document.onmousedown=config.dragscroll.mousedown;
	document.onmousemove=config.dragscroll.mousemove;
	document.onmouseup  =config.dragscroll.mouseup;
}
//}}}
[[Link:|http://edb.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://edb.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
<!--{{{-->
<div class='moveablePanel'>
<div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='viewer'>
	<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>
</div>
<div class='subtitle'>TiddlyTagMindMap Node Settings</div>
<hr>
<div class='editor' macro='edit nodetooltip'></div><div class='editorFooter'>set the tooltip shown for this node</div>
<div class='editor' macro='edit nodelabel'></div><div class='editorFooter'>set alternative label for node instead of node name (this will be wikified)</div>
<div class='editor' macro='edit nodeprefix'></div><div class='editorFooter'>this will appear wikified and before the node name on the label</div>
<div class='editor' macro='edit nodesuffix'></div><div class='editorFooter'>this will appear wikified and after the node name on the label</div>
<div class='editor' macro='edit nodeColor'></div><div class='editorFooter'>set color of this node</div>
<div class='editor' macro='edit parentColor'></div><div class='editorFooter'>set color of all parents of this node</div>
<div class='editor' macro='edit childrenColor'></div><div class='editorFooter'>set color of all children of this node</div>
<div macro='resizeEditor'></div>
<div macro='moveablePanel name:{{story.findContainingTiddler(place).getAttribute("tiddler")}} undocked fold hover height:auto'></div>
</div>
<!--}}}-->
[[Link:|http://www.app.dropmind.com/web/PublishUrl.aspx?id=vy4rSyeCWk7y8j8d0+pS0efkgNUIv4nLF3T8EcINxn956eTfPRCFSA==]]
<html><img src="http://www.app.dropmind.com web/Published.aspx?id=vy4rSyeCWk7y8j8d0+pS0efkgNUIv4nLF3T8EcINxn956eTfPRCFSA==" /></html>
[[Link:|http://tomtw.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://tomtw.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
[[Link:|http://grundlaget.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://grundlaget.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
/*{{{*/
/* GENERAL STYLE TWEAKS */
body { background:#ffe; }
.headerForeground, .headerShadow
	{ padding:.5em; }
.headerShadow
	{ color:[[ColorPalette::Background]]; }
.headerShadow a
	{ color:[[ColorPalette::PrimaryPale]]; }
.headerForeground
	{ display:none; }
.undocked .headerShadow
	{ color:black; }
.undocked .headerShadow a
	{ color:green; }
.undocked .headerForeground
	{ display:block; }
#messageArea
	{  }
.popup
	{  }
.popup li a
	{ padding:2px; }
#mainMenu
	{ margin-left:1em; width:auto; text-align:center; background:#fff;
	border:1px solid; padding:1em; }
#sidebarOptions
	{ padding-left:1em; margin-left:-1em; border-left:1px solid transparent; }
#sidebarOptions:hover
	{ border-left:1px dotted; }
.editor textarea
	{ font-family:monospace; line-height:110%; }
.tagged, .tagging
	{  }
.selected .tagged, .selected .tagging
	{ border:1px solid; }
.tagging
	{ margin-left:1em; }

/*}}}*/
[[Link:|http://etik.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://etik.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
[[Link:|http://fagplan.tiddlyspot.com/index.html]][[Download:|http://fagplan.tiddlyspot.com/download]]
<html><div align="center"><iframe src="http://fagplan.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
[[Links:|http://fleks.tiddlyspot.com/index.html]]  [[Hent:|http://fleks.tiddlyspot.com/download]]
<html><div align="center"><iframe src="http://fleks.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
[[Link:|http://flytbartw.tiddlyspot.com/index.html]]  [[Download:|http://flytbartw.tiddlyspot.com/download]]
<html><div align="center"><iframe src="http://flytbartw.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
[[Link:|http://geografiwiki.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://geografiwiki.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
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>>
[[Links:|http://glas.tiddlyspot.com/index.html]]  [[Hent:|http://glas.tiddlyspot.com/download]]
<html><div align="center"><iframe src="http://glas.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
[[Link:|http://måns.dk/index.html]] [[Glas på tiddlyspot - download|http://glas.tiddlyspot.com/download]]
<html><div align="center"><iframe src="http://måns.dk/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
/***
|Name|GotoPlugin|
|Source|http://www.TiddlyTools.com/#GotoPlugin|
|Documentation|http://www.TiddlyTools.com/#GotoPluginInfo|
|Version|1.7.1|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Description|view any tiddler by entering it's title - displays list of possible matches|
''View a tiddler by typing its title and pressing //enter//.''  As you type, a list of possible matches is displayed.  You can scroll-and-click (or use arrows+enter) to select/view a tiddler, or press //escape// to close the listbox to resume typing.  When the listbox is ''//not//'' being displayed, press //escape// to clear the current text input and start over.
!!!!!Documentation
>see [[GotoPluginInfo]]
!!!!!Revisions
<<<
2008.12.15 [1.7.1] up arrow from input field now moves to end of droplist (search for input).  Also, shift+enter cam now be used to quickly invoke search for text.
|please see [[GotoPluginInfo]] for additional revision details|
2006.05.05 [0.0.0] started
<<<
!!!!!Code
***/
//{{{
version.extensions.GotoPlugin= {major: 1, minor: 7, revision: 1, date: new Date(2008,12,15)};

// automatically tweak shadow SideBarOptions to add <<gotoTiddler>> macro above <<search>>
config.shadowTiddlers.SideBarOptions=config.shadowTiddlers.SideBarOptions.replace(/<<search>>/,"{{button{goto}}}\n<<gotoTiddler>><<search>>");

config.macros.gotoTiddler= { 
	listMaxSize: 10,
	listHeading: 'Found %0 matching title%1...',
	searchItem: "Search for '%0'...",
	handler:
	function(place,macroName,params,wikifier,paramString,tiddler) {

		var quiet	=params.contains("quiet");
		var search	=params.contains("search");
		params = paramString.parseParams("anon",null,true,false,false);
		var instyle	=getParam(params,"inputstyle","");
		var liststyle	=getParam(params,"liststyle","");
		var filter	=getParam(params,"filter","");

		var html=this.html;
		var keyevent=window.event?"onkeydown":"onkeypress"; // IE event fixup for ESC handling
		html=html.replace(/%keyevent%/g,keyevent);
		html=html.replace(/%search%/g,search);
		html=html.replace(/%quiet%/g,quiet);
		html=html.replace(/%instyle%/g,instyle);
		html=html.replace(/%liststyle%/g,liststyle);
		html=html.replace(/%filter%/g,filter);
		if (config.browser.isIE) html=this.IEtableFixup.format([html]);
		createTiddlyElement(place,"span").innerHTML=html;
	},

	html:
	'<form onsubmit="return false" style="display:inline;margin:0;padding:0">\
		<input name=gotoTiddler type=text autocomplete="off" accesskey="G" style="%instyle%"\
			title="Enter title text... DOWN=select from list, ENTER=open/create tiddler, SHIFT-ENTER=search for text"\
			onclick="this.form.list.style.display=\'none\';"\
			onfocus="this.select(); this.setAttribute(\'accesskey\',\'G\');"\
			%keyevent%="return config.macros.gotoTiddler.inputEscKeyHandler(event,this,this.form.list);"\
			onkeyup="return config.macros.gotoTiddler.inputKeyHandler(event,this,%quiet%,%search%);">\
		<select name=list style="%liststyle%;display:none;position:absolute"\
			onchange="if (!this.selectedIndex) this.selectedIndex=1;"\
			onblur="this.style.display=\'none\';"\
			%keyevent%="return config.macros.gotoTiddler.selectKeyHandler(event,this,this.form.gotoTiddler);"\
			onclick="return config.macros.gotoTiddler.processItem(this.value,this.form.gotoTiddler,this);">\
		</select><input name="filter" type="hidden" value="%filter%">\
	</form>',

	IEtableFixup:
	"<table style='width:100%;display:inline;padding:0;margin:0;border:0;'>\
		<tr style='padding:0;margin:0;border:0;'><td style='padding:0;margin:0;border:0;'>\
		%0</td></tr></table>",

	getItems:
	function(val,filter) {
		if (!this.items.length || val.length<2) { // starting new search, refresh cached list of tiddlers/shadows/tags
			this.items=new Array();
			if (filter.length) {
				var fn=store.getMatchingTiddlers||store.getTaggedTiddlers;
				var tiddlers=store.sortTiddlers(fn.apply(store,[filter]),'title');
			} else 
				var tiddlers=store.getTiddlers("title","excludeLists");
			for(var t=0; t<tiddlers.length; t++) this.items.push(tiddlers[t].title);
			if (!filter.length) {
				for (var t in config.shadowTiddlers) this.items.pushUnique(t);
				var tags=store.getTags();
				for(var t=0; t<tags.length; t++) this.items.pushUnique(tags[t][0]);
			}
		}
		var found = [];
		var match=val.toLowerCase();
		for(var i=0; i<this.items.length; i++)
			if (this.items[i].toLowerCase().indexOf(match)!=-1) found.push(this.items[i]);
		return found;
	},
	items: [], // cached list of tiddlers/shadows/tags

	getItemSuffix:
	function(t) {
		if (store.tiddlerExists(t)) return "";  // tiddler
		if (store.isShadowTiddler(t)) return " (shadow)"; // shadow
		return " (tag)"; // tag 
	},

	keyProcessed:
	function(ev) { // utility function: exits handler and prevents browser from processing the keystroke
		ev.cancelBubble=true; // IE4+
		try{event.keyCode=0;}catch(e){}; // IE5
		if (window.event) ev.returnValue=false; // IE6
		if (ev.preventDefault) ev.preventDefault(); // moz/opera/konqueror
		if (ev.stopPropagation) ev.stopPropagation(); // all
		return false;
	},

	inputEscKeyHandler:
	function(event,here,list) {
		var key=event.keyCode;
		// escape... hide list (2nd esc=clears input)
		if (key==27) {
			if (list.style.display=="none")
				here.value=here.defaultValue;
			list.style.display="none";
			return this.keyProcessed(event);
		}
		return true; // key bubbles up
	},

	inputKeyHandler:
	function(event,here,quiet,search) {
		var key=event.keyCode;
		var list=here.form.list;
		var filter=here.form.filter;
		// non-printing chars bubble up, except for a few:
		if (key<48) switch(key) {
			// backspace=8, enter=13, space=32, up=38, down=40, delete=46
			case 8: case 13: case 32: case 38: case 40: case 46: break; default: return true;
		}
		// blank input... if down/enter... fall through (list all)... else, and hide list
		if (!here.value.length && !(key==40 || key==13))
			{ list.style.display="none"; return this.keyProcessed(event); }
		// make sure list is shown (unless quiet option)
		list.style.display=!quiet?"block":"none";
		// non-blank input... enter=show/create tiddler, SHIFT-enter=search for text
		if (key==13) return this.processItem(event.shiftKey?'*':here.value,here,list);
		// up or down key... shows and moves to list...
		if (key==38 || key==40) { list.style.display="block"; list.focus(); }
		// if list is showing, fill it with found results...
		if (list.style.display!="none") {
			var indent='\xa0\xa0\xa0';
			var found = this.getItems(here.value,filter.value); // find matching items...
			found.sort(); // alpha by title
			while (list.length > 0) list.options[0]=null; // clear list
			var hdr=this.listHeading.format([found.length,found.length==1?"":"s"]);
			list.options[0]=new Option(hdr,"",false,false);
			for (var t=0; t<found.length; t++) list.options[list.length]=
				new Option(indent+found[t]+this.getItemSuffix(found[t]),found[t],false,false);
			if (search)
				list.options[list.length]=new Option(this.searchItem.format([here.value]),"*",false,false);
			list.size=(list.length<this.listMaxSize?list.length:this.listMaxSize); // resize list...
			list.selectedIndex=key==38?list.length-1:key==40?1:0;
		}
		return true; // key bubbles up
	},

	selectKeyHandler:
	function(event,list,editfield) {
		if (event.keyCode==27) // escape... hide list, move to edit field
			{ editfield.focus(); list.style.display="none"; return this.keyProcessed(event); }
		if (event.keyCode==13 && list.value.length) // enter... view selected item
			{ this.processItem(list.value,editfield,list); return this.keyProcessed(event); }
		return true; // key bubbles up
	},

	processItem:
	function(title,here,list) {
		if (!title.length) return;
		list.style.display='none';
		if (title=="*")	{ story.search(here.value); return false; } // do full-text search
		here.value=title;
		story.displayTiddler(null,title); // show selected tiddler
		return false;
	}
}
//}}}
[[Link:|http://twgroup.tiddlyspot.com/index.html]] [[Download:|http://twgroup.tiddlyspot.com/download]]
<html><div align="center"><iframe src="http://twgroup.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
[[Link:|http://guitar.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://guitar.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
[[Links:|http://hu0809.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://hu0809.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
Denne hjemmeside er bygget på Jeremy Rustons utrolige, [[Tiddlywiki|http://www.tiddlywiki.com]]. Den betjenes bedst med [[Firefox|http://www.mozilla.com/firefox/]]. Denne variant er designet af MitchkeLeemans og genbrugt af [[Måns Mårtensson]].

Note for the real geeks: If you'd like to achieve the same effect, you could just save this page (as plain html) and adjust to your own taste, but it would be much better to get the unaldulterated versions of [[Tiddlywiki|http://www.tiddlywiki.com]], [[Moveablepanelplugin|http://www.TiddlyTools.com/quickstart/moveable.html]] and [[Tiddlytagmindmap|http://tiddlytagmindmap.tiddlyspot.com/]]. Have fun!
/***
|Name|ImageSizePlugin|
|Source|http://www.TiddlyTools.com/#ImageSizePlugin|
|Version|1.2.1|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin,formatter|
|Requires||
|Overrides|'image' formatter|
|Description|adds support for resizing images|
This plugin adds optional syntax to scale an image to a specified width and height and/or interactively resize the image with the mouse.
!!!!!Usage
<<<
The extended image syntax is:
{{{
[img(w+,h+)[...][...]]
}}}
where ''(w,h)'' indicates the desired width and height (in CSS units, e.g., px, em, cm, in, or %). Use ''auto'' (or a blank value) for either dimension to scale that dimension proportionally (i.e., maintain the aspect ratio). You can also calculate a CSS value 'on-the-fly' by using a //javascript expression// enclosed between """{{""" and """}}""". Appending a plus sign (+) to a dimension enables interactive resizing in that dimension (by dragging the mouse inside the image). Use ~SHIFT-click to show the full-sized (un-scaled) image. Use ~CTRL-click to restore the starting size (either scaled or full-sized).
<<<
!!!!!Examples
<<<
{{{
[img(100px+,75px+)[images/meow2.jpg]]
}}}
[img(100px+,75px+)[images/meow2.jpg]]
{{{
[<img(34%+,+)[images/meow.gif]]
[<img(21% ,+)[images/meow.gif]]
[<img(13%+, )[images/meow.gif]]
[<img( 8%+, )[images/meow.gif]]
[<img( 5% , )[images/meow.gif]]
[<img( 3% , )[images/meow.gif]]
[<img( 2% , )[images/meow.gif]]
[img(  1%+,+)[images/meow.gif]]
}}}
[<img(34%+,+)[images/meow.gif]]
[<img(21% ,+)[images/meow.gif]]
[<img(13%+, )[images/meow.gif]]
[<img( 8%+, )[images/meow.gif]]
[<img( 5% , )[images/meow.gif]]
[<img( 3% , )[images/meow.gif]]
[<img( 2% , )[images/meow.gif]]
[img(  1%+,+)[images/meow.gif]]
{{tagClear{
}}}
<<<
!!!!!Revisions
<<<
2009.02.24 [1.2.1] cleanup width/height regexp, use '+' suffix for resizing
2009.02.22 [1.2.0] added stretchable images
2008.01.19 [1.1.0] added evaluated width/height values
2008.01.18 [1.0.1] regexp for "(width,height)" now passes all CSS values to browser for validation
2008.01.17 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.ImageSizePlugin= {major: 1, minor: 2, revision: 1, date: new Date(2009,2,24)};
//}}}
//{{{
var f=config.formatters[config.formatters.findByField("name","image")];
f.match="\\[[<>]?[Ii][Mm][Gg](?:\\([^,]*,[^\\)]*\\))?\\[";
f.lookaheadRegExp=/\[([<]?)(>?)[Ii][Mm][Gg](?:\(([^,]*),([^\)]*)\))?\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg;
f.handler=function(w) {
	this.lookaheadRegExp.lastIndex = w.matchStart;
	var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
	if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
		var floatLeft=lookaheadMatch[1];
		var floatRight=lookaheadMatch[2];
		var width=lookaheadMatch[3];
		var height=lookaheadMatch[4];
		var tooltip=lookaheadMatch[5];
		var src=lookaheadMatch[6];
		var link=lookaheadMatch[7];

		// Simple bracketted link
		var e = w.output;
		if(link) { // LINKED IMAGE
			if (config.formatterHelpers.isExternalLink(link)) {
				if (config.macros.attach && config.macros.attach.isAttachment(link)) {
					// see [[AttachFilePluginFormatters]]
					e = createExternalLink(w.output,link);
					e.href=config.macros.attach.getAttachment(link);
					e.title = config.macros.attach.linkTooltip + link;
				} else
					e = createExternalLink(w.output,link);
			} else 
				e = createTiddlyLink(w.output,link,false,null,w.isStatic);
			addClass(e,"imageLink");
		}

		var img = createTiddlyElement(e,"img");
		if(floatLeft) img.align="left"; else if(floatRight) img.align="right";
		if(width||height) {
			var x=width.trim(); var y=height.trim();
			var stretchW=(x.substr(x.length-1,1)=='+'); if (stretchW) x=x.substr(0,x.length-1);
			var stretchH=(y.substr(y.length-1,1)=='+'); if (stretchH) y=y.substr(0,y.length-1);
			if (x.substr(0,2)=="{{")
				{ try{x=eval(x.substr(2,x.length-4))} catch(e){displayMessage(e.description||e.toString())} }
			if (y.substr(0,2)=="{{")
				{ try{y=eval(y.substr(2,y.length-4))} catch(e){displayMessage(e.description||e.toString())} }
			img.style.width=x.trim(); img.style.height=y.trim();
			config.formatterHelpers.addStretchHandlers(img,stretchW,stretchH);
		}
		if(tooltip) img.title = tooltip;

		// GET IMAGE SOURCE
		if (config.macros.attach && config.macros.attach.isAttachment(src))
			src=config.macros.attach.getAttachment(src); // see [[AttachFilePluginFormatters]]
		else if (config.formatterHelpers.resolvePath) { // see [[ImagePathPlugin]]
			if (config.browser.isIE || config.browser.isSafari) {
				img.onerror=(function(){
					this.src=config.formatterHelpers.resolvePath(this.src,false);
					return false;
				});
			} else
				src=config.formatterHelpers.resolvePath(src,true);
		}
		img.src=src;
		w.nextMatch = this.lookaheadRegExp.lastIndex;
	}
}

config.formatterHelpers.addStretchHandlers=function(e,stretchW,stretchH) {
	e.title=((stretchW||stretchH)?'DRAG=stretch/shrink, ':'')
		+'SHIFT-CLICK=show full size, CTRL-CLICK=restore initial size';
	e.statusMsg='width=%0, height=%1';
	e.style.cursor='move';
	e.originalW=e.style.width;
	e.originalH=e.style.height;
	e.minW=Math.max(e.offsetWidth/20,10);
	e.minH=Math.max(e.offsetHeight/20,10);
	e.stretchW=stretchW;
	e.stretchH=stretchH;
	e.onmousedown=function(ev) { var ev=ev||window.event;
		this.sizing=true;
		this.startX=!config.browser.isIE?ev.pageX:(ev.clientX+findScrollX());
		this.startY=!config.browser.isIE?ev.pageY:(ev.clientY+findScrollY());
		this.startW=this.offsetWidth;
		this.startH=this.offsetHeight;
		return false;
	};
	e.onmousemove=function(ev) { var ev=ev||window.event;
		if (this.sizing) {
			var s=this.style;
			var currX=!config.browser.isIE?ev.pageX:(ev.clientX+findScrollX());
			var currY=!config.browser.isIE?ev.pageY:(ev.clientY+findScrollY());
			var newW=(currX-this.offsetLeft)/(this.startX-this.offsetLeft)*this.startW;
			var newH=(currY-this.offsetTop )/(this.startY-this.offsetTop )*this.startH;
			if (this.stretchW) s.width =Math.floor(Math.max(newW,this.minW))+'px';
			if (this.stretchH) s.height=Math.floor(Math.max(newH,this.minH))+'px';
			clearMessage(); displayMessage(this.statusMsg.format([s.width,s.height]));
		}
		return false;
	};
	e.onmouseup=function(ev) { var ev=ev||window.event;
		if (ev.shiftKey) { this.style.width=this.style.height=''; }
		if (ev.ctrlKey)  { this.style.width=this.originalW; this.style.height=this.originalH; }
		this.sizing=false;
		clearMessage();
		return false;
	};
	e.onmouseout=function(ev) { var ev=ev||window.event;
		this.sizing=false;
		clearMessage();
		return false;
	};
}
//}}}
/***
|Name|InlineJavascriptPlugin|
|Source|http://www.TiddlyTools.com/#InlineJavascriptPlugin|
|Documentation|http://www.TiddlyTools.com/#InlineJavascriptPluginInfo|
|Version|1.9.3|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Description|Insert Javascript executable code directly into your tiddler content.|
''Call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
!!!!!Documentation
>see [[InlineJavascriptPluginInfo]]
!!!!!Revisions
<<<
2008.06.11 [1.9.3] added $(...) function as 'shorthand' convenience syntax for document.getElementById()
2008.03.03 [1.9.2] corrected declaration of wikifyPlainText() for 'TW 2.1.x compatibility fallback' (fixes Safari "parse error")
2008.02.23 [1.9.1] in onclick function, use string instead of array for 'bufferedHTML' attribute on link element (fixes IE errors)
2008.02.21 [1.9.0] 'onclick' scripts now allow returned text (or document.write() calls) to be wikified into a span that immediately follows the onclick link.  Also, added default 'return false' handling if no return value provided (prevents HREF from being triggered -- return TRUE to allow HREF to be processed).  Thanks to Xavier Verges for suggestion and preliminary code.
|please see [[InlineJavascriptPluginInfo]] for additional revision details|
2005.11.08 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.InlineJavascriptPlugin= {major: 1, minor: 9, revision: 3, date: new Date(2008,6,11)};

config.formatters.push( {
	name: "inlineJavascript",
	match: "\\<script",
	lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?(?: title=\\\"((?:.|\\n)*?)\\\")?(?: key=\\\"((?:.|\\n)*?)\\\")?( show)?\\>((?:.|\\n)*?)\\</script\\>",

	handler: function(w) {
		var lookaheadRegExp = new RegExp(this.lookahead,"mg");
		lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = lookaheadRegExp.exec(w.source)
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
			var src=lookaheadMatch[1];
			var label=lookaheadMatch[2];
			var tip=lookaheadMatch[3];
			var key=lookaheadMatch[4];
			var show=lookaheadMatch[5];
			var code=lookaheadMatch[6];
			if (src) { // load a script library
				// make script tag, set src, add to body to execute, then remove for cleanup
				var script = document.createElement("script"); script.src = src;
				document.body.appendChild(script); document.body.removeChild(script);
			}
			if (code) { // there is script code
				if (show) // show inline script code in tiddler output
					wikify("{{{\n"+lookaheadMatch[0]+"\n}}}\n",w.output);
				if (label) { // create a link to an 'onclick' script
					// add a link, define click handler, save code in link (pass 'place'), set link attributes
					var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",wikifyPlainText(label));
					var fixup=code.replace(/document.write\s*\(/gi,'place.bufferedHTML+=(');
					link.code="function _out(place){"+fixup+"\n};_out(this);"
					link.tiddler=w.tiddler;
					link.onclick=function(){
						this.bufferedHTML="";
						try{ var r=eval(this.code);
							if(this.bufferedHTML.length || (typeof(r)==="string")&&r.length)
								var s=this.parentNode.insertBefore(document.createElement("span"),this.nextSibling);
							if(this.bufferedHTML.length)
								s.innerHTML=this.bufferedHTML;
							if((typeof(r)==="string")&&r.length) {
								wikify(r,s,null,this.tiddler);
								return false;
							} else return r!==undefined?r:false;
						} catch(e){alert(e.description||e.toString());return false;}
					};
					link.setAttribute("title",tip||"");
					var URIcode='javascript:void(eval(decodeURIComponent(%22(function(){try{';
					URIcode+=encodeURIComponent(encodeURIComponent(code.replace(/\n/g,' ')));
					URIcode+='}catch(e){alert(e.description||e.toString())}})()%22)))';
					link.setAttribute("href",URIcode);
					link.style.cursor="pointer";
					if (key) link.accessKey=key.substr(0,1); // single character only
				}
				else { // run inline script code
					var fixup=code.replace(/document.write\s*\(/gi,'place.innerHTML+=(');
					var c="function _out(place){"+fixup+"\n};_out(w.output);";
					try	 { var out=eval(c); }
					catch(e) { out=e.description?e.description:e.toString(); }
					if (out && out.length) wikify(out,w.output,w.highlightRegExp,w.tiddler);
				}
			}
			w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
		}
	}
} )
//}}}

// // Backward-compatibility for TW2.1.x and earlier
//{{{
if (typeof(wikifyPlainText)=="undefined") window.wikifyPlainText=function(text,limit,tiddler) {
	if(limit > 0) text = text.substr(0,limit);
	var wikifier = new Wikifier(text,formatter,null,tiddler);
	return wikifier.wikifyPlain();
}
//}}}

// // $(...) function: 'shorthand' convenience syntax for document.getElementById()
//{{{
if (typeof($)=="undefined") { // avoid redefinition
function $() {
	var elements=new Array();
	for (var i=0; i<arguments.length; i++) {
		var element=arguments[i];
		if (typeof element=='string') element=document.getElementById(element);
		if (arguments.length==1) return element;
		elements.push(element);
	}
	return elements;
}
}
//}}}
[[Link:|http://kaffe.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://kaffe.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
[[Link:|http://klient.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://klient.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
[[Link:|http://gdsskole.tiddlyspot.com]]
<html><div align="center"><iframe src="http://gdsskole.tiddlyspot.com" frameborder="0" width="100%" height="600"></iframe></div></html>
 pr telefon: +45 25344884
 pr mail:  [[maans snabela newp punktum dk|mailto:"maans@newp.dk"]]
 pr skrift:  Elevvej 11 / 9600 / Aars
 pr visit:  Elevvej 11 / 9600 / Aars
 Hjemmeside: [[MÃ¥ns.dk|Glass - Min version og hjemmeside]]
[[Link:|http://kristendom.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://kristendom.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
[[Link:|http://twligninger.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://twligninger.tiddlyspot.com/index.html" frameborder="2" width="100%" height="600"></iframe></div></html>
/***
|''Name:''|LoadRemoteFileThroughProxy (previous LoadRemoteFileHijack)|
|''Description:''|When the TiddlyWiki file is located on the web (view over http) the content of [[SiteProxy]] tiddler is added in front of the file url. If [[SiteProxy]] does not exist "/proxy/" is added. |
|''Version:''|1.1.0|
|''Date:''|mar 17, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#LoadRemoteFileHijack|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0|
***/
//{{{
version.extensions.LoadRemoteFileThroughProxy = {
 major: 1, minor: 1, revision: 0, 
 date: new Date("mar 17, 2007"), 
 source: "http://tiddlywiki.bidix.info/#LoadRemoteFileThroughProxy"};

if (!window.bidix) window.bidix = {}; // bidix namespace
if (!bidix.core) bidix.core = {};

bidix.core.loadRemoteFile = loadRemoteFile;
loadRemoteFile = function(url,callback,params)
{
 if ((document.location.toString().substr(0,4) == "http") && (url.substr(0,4) == "http")){ 
 url = store.getTiddlerText("SiteProxy", "/proxy/") + url;
 }
 return bidix.core.loadRemoteFile(url,callback,params);
}
//}}}
[[Link:|http://mmftg.tiddlyspot.com/index.html]] [[download|http://mmftg.tiddlyspot.com/download]]
<html><div align="center"><iframe src="http://mmftg.tiddlyspot.com/index.html" frameborder="2" width="900em" height="800"></iframe></div></html>
[[MÃ¥ns MÃ¥rtensson]]
[[Kontakt]]
[[cv|Mit CV]]
<<tiddlytagmindmap id:main width:550 height:450 startState:all exclude:["excludeLists"]>>
[[Link:|http://mmscv.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://mmscv.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
Designer af [[doordouwe.nl|http://doordouwe.nl]] Den originale
hjemmeside, denne side er bygget på. 
Kijk ook naar een kort [[filmpje|MobilFilmpje]]
[[Link:|http://måns.dk/weekend/index.html]]
<<moveablePanel name:ElevListen>><html><div align="center"><iframe src="http://måns.dk/weekend/index.html" frameborder="2" width="50%" height="800"></iframe></div></html>
[[Link:|http://skrivebord.tiddlyspot.com/index.html]]  [[Download:|http://skrivebord.tiddlyspot.com/download]]
<html><div align="center"><iframe src="http://skrivebord.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
<!--{{{-->
<!--
|Name|MoveableEditTemplate|
|Source|http://www.TiddlyTools.com/#MoveableEditTemplate|
|Version||
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|template|
|Requires|MoveablePanelPlugin, EditTemplate, TaggedTemplateTweak|
|Overrides||
|Description|add moveablePanel macro to tiddlers tagged with 'moveable'|
-->
<div class='moveablePanel'>
	[[EditTemplate]]
	<div macro='moveablePanel name:{{tiddler?tiddler.title:""}} height:auto'></div>
</div>
<!--}}}-->
<html><style>
#tiddlerMoveablePanelPackage .tagged { display:none; }
#tiddlerMoveablePanelPackage .viewer .content { max-height:999999em; height:auto; overflow:visible; }
</style></html>@@font-size:150%;''"free-range" tiddlers with room to roam...''@@
<<<
@@font-size:90%;line-height:120%;text-align:justify;display:block;<<tiddler QuickStartBadge with: moveable.html>>[[MoveablePanelPlugin]] turns tiddlers and other document content into  '''moveable panels' that can be dragged with the mouse to  //undock// them from their default //anchor points// and reposition them to almost any location on the page -- even far off screen!''  Using moveable panels, your document content can now be spread out over a wide (actually, //infinite//) area, both horizontally and vertically, allowing you to ''quickly create a complete 'web desktop' with different groupings of moveable tiddlers and menus''... all within a single TiddlyWiki document!

No matter where you place the panels, the document extents will ''automatically grow (or shrink) as needed to contain all moveable panels''.  Unfortunately, ''you can only scroll in one direction at a time when using the browser window's separate horizontal and vertical scrollbars'', making navigation between far-flung panels extremely tedious and awkward.  To address this, [[DragScrollPlugin]] allows you to ''scroll the browser window in both the horizontal and vertical directions at the same time, simply by holding down the SHIFT key while dragging the mouse across the page'' until the desired content is scrolled into view.

Of course, once you can move things around, you will want them to //stay there//.  By itself, [[MoveablePanelPlugin]] has no memory and all panels are rendered at their original, docked anchor points each time the document is loaded into the browser.  [[PanelManagerPlugin]] adds the ability to automatically ''track and record each panel's current position and size in a //panel map//, using simple wiki-formatted tables stored in tiddlers''.  The current panel map is also automatically stored as a browser-cookie, so that ''wherever you place a panel, it stays there until you move it again'', even in between document sessions.

[[PanelManagerPlugin]] also adds a popup menu with lots of functions for managing individual moveable panels (e.g., 'bring to front', 'jump to panel', 'dock/undock', etc.) plus a ''powerful, graphical panel map viewer and interactive page navigator'' that allows you to load, save, edit and view //named// panel maps stored in your document.  You can also scroll directly to any panel or other selected page location, anywhere on the page, by using the 'jump to panel' command or ''//compass navigation tool//'' from the Panel Manager popup menu.

You can access the Panel Manager popup map viewer at any time from the <<moveablePanel menu label:[[Panel Manager button (&#x2261;)]]>> that is added to the upper right corner of each undocked panel.  You can also ''open the popup menu from //anywhere// on the page simply by using ALT+CLICK on the //background// of the page'' (i.e., not on a link or other //active// element that could respond to the click!).  This permits access to the Panel Manager commands, even when there are no moveable panels visible on the screen.
@@
<<<
/***
|Name|[[MoveablePanelPlugin]]|
|Source|http://www.TiddlyTools.com/#MoveablePanelPlugin|
|Documentation|http://www.TiddlyTools.com/#MoveablePanelPluginInfo|
|Version|3.0.3|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Description|move/size any tiddler or page element|
Use the mouse to move/resize any specific tiddler content, page element, or [[floating slider panel|NestedSlidersPlugin]].
!!!!!Documentation
>see [[MoveablePanelPluginInfo]]
!!!!!Configuration
<<<
<<option chkMoveablePanelShowStatus>> show position/size while moving/resizing a panel
<<option chkMoveablePanelShowManager>> automatically add Panel Manager button to undocked panels (see [[PanelManagerPlugin]])
<<<
!!!!!Revisions
<<<
2008.12.24 [3.0.3] added ESC key handling to cancel panel move/size (restores previous panel state)
|please see [[MoveablePanelPluginInfo]] for additional revision details|
2006.03.04 [1.0.0] Initial public release
<<<
!!!!!Code
***/
//{{{
version.extensions.MoveablePanelPlugin= {major: 3, minor: 0, revision: 3, date: new Date(2008,12,24)};
if (config.macros.moveablePanel===undefined) config.macros.moveablePanel={};
//}}}
// // translate
//{{{
// TRANSLATORS: copy this section to MoveablePanelPluginLingoXX (where 'XX' is a language/country code)
if (config.macros.moveablePanel===undefined) config.macros.moveablePanel={};
merge(config.macros.moveablePanel,{

	foldLabel:	'\u2212', // minus
	foldTip:	'FOLD=reduce panel size',
	unfoldLabel:	'+',
	unfoldTip:	'UNFOLD=restore panel size',
	hoverLabel:	'^',
	hoverTip:	'HOVER=keep panel in view when scrolling',
	scrollLabel:	'\u2248', // asymp
	scrollTip:	'SCROLL=allow panel to move with page',
	closeLabel:	'X',
	closeTip:	'CLOSE=hide this panel',
	dockLabel:	'\u221A', // radic
	dockTip:	'DOCK=reset size/position',

	noPid:		'unnamed panel',

	statusMsg:	'%0: pos=(%1,%2)%3 size=(%4,%5) z=%6',
	hoveredMsg:	'[hovering]',
	dockedTip:	'%0: docked',
	scrollMsg:	'%0: pos=(%1,%2)',
	msgDuration:	3000,

	moveTip:	'%0DRAG EDGE=move',
	sizeTip:	'(SHIFT=resize)',
	sizeWidthTip:	'(SHIFT=resize width)',
	sizeHeightTip:	'(SHIFT=resize height)',
	clickTip:	  'CLICK=bring to front, SHIFT-CLICK=send to back',
	dblclickdockTip:  'DOUBLE-CLICK=dock',
	dblclickunfoldTip:'DOUBLE-CLICK=unfold',

	foldParam:	'fold',
	hoverParam:	'hover',
	nocloseParam:	'noclose',
	nodockParam:	'nodock',
	undockedParam:	'undocked',

	jumpParam:	'jump',
	dockParam:	'dock',
	moveParam:	'move',
	labelParam:	'label',
	promptParam:	'prompt',

	allParam:	'all',
	nameParam:	'name',
	topParam:	'top',
	leftParam:	'left',
	widthParam:	'width',
	heightParam:	'height',

	managerParam:	'manager'
});
//}}}
// // global functions (general utilities)
//{{{
// if removeCookie() function is not defined by TW core, define it here (for <TW2.5)
if (window.removeCookie===undefined) {
	window.removeCookie=function(name) {
		document.cookie = name+'=; expires=Thu, 01-Jan-1970 00:00:01 UTC; path=/;'; 
	}
}
if (window.copyObject===undefined) {
	window.copyObject=function(src)	{
		for (var i in src) this[i]=typeof src[i]!='object'?src[i]:new copyObject(src[i]);
	}
}
if (window.compareObjects===undefined) {
	window.compareObjects=function(a,b) {
		if (a===b) return true;
		if (a==undefined||b==undefined) return false;
		for (var i in a) if (typeof a[i]!='object'?a[i]!==b[i]:!compareObjects(a[i],b[i])) return false;
		return true;
	}
}
if (window.isEmptyObject===undefined) {
	window.isEmptyObject=function(src) { for (var i in src) return false; return true; }
}

// cross-browser metrics
window.findMouseX=function(ev)
	{ if (!ev) return 0; return !config.browser.isIE?ev.pageX:(ev.clientX+findScrollX()); }
window.findMouseY=function(ev)
	{ if (!ev) return 0; return !config.browser.isIE?ev.pageY:(ev.clientY+findScrollY()); }
//}}}
// // macro
//{{{
merge(config.macros.moveablePanel,{
	handler: function(place,macroName,params,wikifier,paramString,tiddler) {

		// ALTERNATIVE OUTPUT: Panel Manager macro extensions...
		if (this.manager && this.manager.handler(place,macroName,params,wikifier,paramString,tiddler))
			return; // processed by PanelManager

		// UNPACK KEYWORD PARAMS
		var showfold	 =params.contains(this.foldParam);
		var showhover	 =params.contains(this.hoverParam);
		var showclose	 =!params.contains(this.nocloseParam);
		var showdock	 =!params.contains(this.nodockParam);
		var showmanager  =params.contains(this.managerParam);
		var startundocked=params.contains(this.undockedParam);
		var jump	 =params.contains(this.jumpParam);
		var dock	 =params.contains(this.dockParam);
		var move	 =params.contains(this.moveParam);
		var all		 =params.contains(this.allParam);

		// UNPACK VALUE PARAMS
		params=paramString.parseParams('anon',null,true,false,false);
		var label =getParam(params,this.labelParam,null);
		var prompt=getParam(params,this.promptParam,null);
		var name  =getParam(params,this.nameParam,null);
		var top   =getParam(params,this.topParam,null);
		var left  =getParam(params,this.leftParam,null);
		var width =getParam(params,this.widthParam,null);
		var height=getParam(params,this.heightParam,null);

		// COMMANDS: JUMP, MOVE, DOCK
		if (jump||move||dock) {
			if (!label||!label.length) {
				var p=this.findPanel(name);
				if (jump) { if (p) this.scrollToPanel(p,true); else window.scrollTo(left,top); }
				if (move) { this.movePanel(p,left,top,true,true); }
				if (dock) { if (all) this.forAllPanels(this.dockPanel); else if (p) this.dockPanel(p); }
				return;
			}
			var tip=(jump?this.jumpParam:move?this.moveParam:dock?this.dockParam:'')+': '+name;
			var b=createTiddlyButton(place,label,prompt||tip, function(ev) {
				var cmm=config.macros.moveablePanel; var p=cmm.findPanel(this.name);
				if (this.jump) { if (p) cmm.scrollToPanel(p,true); else window.scrollTo(this.left,this.top); }
				if (this.move) { if (p) cmm.movePanel(p,this.left,this.top,true,true); }
				if (this.dock) { if (p) cmm.dockPanel(p); else if (this.all) cmm.forAllPanels(cmm.dockPanel); }
				return cmm.processed(ev)
			},'button');
			b.jump=jump; b.move=move; b.dock=dock;
			b.name=name; b.all=all;   b.left=left; b.top=top;
			return;
		}

		// PANEL SETUP
		var p=this.getPanel(place);
		this.cachePanel(p);
		addClass(p,'moveablePanel');
		p.pid=name;
		p.showmanager=showmanager;
		p.fixedheight=height||undefined;
		p.fixedwidth=width||undefined;
		this.addPanelButtons(p,showfold,showhover,showclose,showdock,showmanager);
		this.addMouseHandlers(p);
		if (startundocked) {
			this.undockPanel(p);
			if (!startingUp) { this.bringPanelToFront(p); this.scrollToPanel(p); }
		}
		if (this.manager) { this.manager.applyMap(p); this.manager.trackMap(p); }
		this.notify(p);
	},
//}}}
// // notifications
//{{{
	quiet: 0, // flag to suspend/resume notifications
	notify: function(p) { // notify others of panel changes
		if (this.quiet) return;
		if (this.manager) this.manager.notify(p); // pass notices to manager (updates viewers)
	},
//}}}
// // general panel utilities
//{{{
	getPanel: function(place) { // find containing panel or floating slider (use current element as fallback)
		var p=place;
		while (p && !(hasClass(p,'moveablePanel')||hasClass(p,'floatingPanel'))) p=p.parentNode;
		return p||place;
	},
	getAllPanels: function(zSort) { // find 'moveablePanel' elements (optionally sort by zIndex)
		var panels=[];
		var sortByZindex=function(a,b){
			var v1=parseInt(a.style.zIndex); if (isNaN(v1)) v1=0;
			var v2=parseInt(b.style.zIndex); if (isNaN(v2)) v2=0;
			return(v1==v2)?0:(v1>v2?1:-1);
		}
		// if native browser fn is defined, use it (*much* more efficient!)
		if (document.getElementsByClassName) { 
			var elems=document.getElementsByClassName('moveablePanel');
			for (var i=0; i<elems.length; i++) panels.push(elems[i]);
			return zSort?panels.sort(sortByZindex):panels;
		}
		// otherwise, find all DIVs and SPANs with the right class
		// NOTE: IE requires use of Enumerator() to iterate over elements, or it FREEZES UP COMPLETELY!!
		var isIE=config.browser.isIE;
		var elems=document.getElementsByTagName('DIV');
		for (var i=isIE?new Enumerator(elems):0; isIE?!i.atEnd():(i<elems.length); isIE?i.moveNext():i++) {
			var panel=isIE?i.item():elems[i];
			if (hasClass(panel,'moveablePanel')) panels.push(panel);
		}
		var elems=document.getElementsByTagName('SPAN');
		for (var i=isIE?new Enumerator(elems):0; isIE?!i.atEnd():(i<elems.length); isIE?i.moveNext():i++) {
			var panel=isIE?i.item():elems[i];
			if (hasClass(panel,'moveablePanel')) panels.push(panel);
		}
		return zSort?panels.sort(sortByZindex):panels;
	},
	findPanel: function(pid) { // find a named panel
		var p=this.getAllPanels();
		for (var i=0; i<p.length; i++) { if (pid && p[i].pid==pid) return p[i]; }
		return undefined;
	},
	forAllPanels: function(callback) { // invoke a function on each panel
		var panels=this.getAllPanels();
		this.quiet++;
		for (var i=0; i<panels.length; i++) callback.apply(this,[panels[i]]);
		this.quiet--;
		this.notify('all');
	},
	cachePanel: function(p) { // save original styles and handlers
		if (!p.saved) p.saved={ 
			x:p.style.left||'', y:p.style.top||'', w:p.style.width||'', h:p.style.height||'',
			z:p.style.zIndex||'', pos:p.style.position||'', title: p.title,
			mouseover:p.onmouseover, mouseout:p.onmouseout,
			mousedown:p.onmousedown, mousemove:p.onmousemove, dblclick:p.ondblclick
		};
	},
	restorePanel: function(p) { // restore original styles
		if (!p.saved) return;
		p.style.left=p.saved.x; p.style.top=p.saved.y; p.style.width=p.saved.w; p.style.height=p.saved.h;
		p.style.zIndex=p.saved.z; p.style.position=p.saved.pos; p.title=p.saved.title;
		removeClass(p,'folded'); removeClass(p,'hover'); removeClass(p,'undocked');
	},
//}}}
// // panel metrics
//{{{
	getPanelOffset: function(p) { // adjustment for child elements inside relative/floatingPanel containers
		var r=new Object(); r.x=0; r.y=0; if (!p) return r;
		var pp=p.parentNode; while (pp && !(pp.style&&pp.style.position=='relative')) pp=pp.parentNode;
		if (pp) { r.x+=findPosX(pp); r.y+=findPosY(pp); }
		var pp=p.parentNode; while (pp && !hasClass(pp,'floatingPanel')) pp=pp.parentNode;
		if (pp) { r.x+=findPosX(pp); r.y+=findPosY(pp); }
		return r;
	},
	// PROBLEM: the offsetWidth/offsetHeight do not seem to account for padding or borders
	// WORKAROUND: subtract padding and border (in px) from width and height
	// ISSUE: I still don't understand why this is needed...
	// TBD: get padding/border values from p.style and convert to px
	// NOTE: 10.6667 seems to be about 1em...
	getPanelEdgeWidth:
	  	function(p) { return 10.6667; },
	getPanelEdgeHeight:
		function(p) { return 10.6667; },
	getPanelHeight:
		function(p) { var pad=10.6667; var border=1; return p.offsetHeight-(pad*2+border*2); },
	getPanelWidth:
		function(p) { var pad=10.6667; var border=1; return p.offsetWidth -(pad*2+border*2); },
//}}}
// // panel stacking (zIndex)
//{{{
	isStackable: function(p) { // zIndex is only effective with absolute or fixed elements
		return (['absolute','fixed'].contains(p.style.position)&&!hasClass(p,'popup'));
	},
	normalizeStack: function(panels) { // set zIndex to correspond to stack order
		for (var i=0; i<panels.length; i++) {var z=panels[i].style.zIndex;
			if (z==0||z=='auto') continue; // if not stacking (e.g., 'auto', '', or null)
			if (z<10000 || z>10000) continue; // use large values for "always in front/back"
			if (z!=i+2) panels[i].style.zIndex=i+2;
			if (this.manager) this.manager.trackMap(panels[i]);
		}
		return panels;
	},
	bringPanelToFront: function(p) { if (!p) return;
		if (!this.isStackable(p)) return; // can't be stacked
		var panels=this.getAllPanels(true);
// WFFL - normalizing every time works, but takes too long
//		if (p.style.zIndex>panels.length+2) return; // stay in front
//		this.normalizeStack(panels);
//		p.style.zIndex=panels.length+2;
// WFFL - for now, just bump up the max (ignore z>10000) and normalize much less often
		if (p.style.zIndex>1000) this.normalizeStack(panels);
		var zMax=0; if (panels.length) {
			var i=panels.length-1; zMax=parseInt(panels[i].style.zIndex);
			while (zMax>10000 && i>=0) zMax=parseInt(panels[--i].style.zIndex);
			if (p==panels[i]) return; // already in front
			if (isNaN(zMax)) zMax=0;
		}
		p.style.zIndex=zMax+1;
		this.notify(p);
	},
	sendPanelToBack: function(p) { if (!p) return;
		if (!this.isStackable(p)) return; // can't be stacked
		var panels=this.getAllPanels(true);
// WFFL - normalizing every time works, but takes too long
//		if (p.style.zIndex<2) return; // stay in back
//		this.normalizeStack(panels);
//		p.style.zIndex=1;
// WFFL - for now, just bump down the min (ignore z<10000) and normalize much less often
		if (p.style.zIndex<1000) this.normalizeStack(panels);
		var zMin=0; if (panels.length) {
			var i=0; zMin=parseInt(panels[i].style.zIndex);
			while (zMin<10000 && i<panels.length-1) zMin=parseInt(panels[++i].style.zIndex);
			if (p==panels[i]) return; // already in back
			if (isNaN(zMin)) zMin=0;
		}
		p.style.zIndex=zMin-1;
		this.notify(p);
	},
	returnPanelToStack: function(p) { if (!p) return;
		p.style.zIndex=p.saved?p.saved.zIndex:'';
		this.notify(p);
	},
//}}}
// // panel scrolling 
//{{{
	noScrollX: 0, // flags to disable TW built-in scrolling behavior
	noScrollY: 0, // set by hijacks, cleared by ensurePanelVisible(), below
	// scroll view to show panel along nearest edge of window or centered (optional)
	scrollToPanel: function(p,center) { if (!p) return;
		if (hasClass(p,'popup')) return; // popup=let core scrolling handle it
		if (hasClass(p,'hover')) return; // hover=always in view=don't scroll
		var scrollSize=findWindowWidth()-document.body.offsetWidth; // width of scrollbar
		var sx=findScrollX();	var ww=findWindowWidth()-scrollSize;
		var sy=findScrollY();	var wh=findWindowHeight()-scrollSize;
		var px=findPosX(p);	var pw=p.offsetWidth;
		var py=findPosY(p);	var ph=p.offsetHeight;
		var nx=sx; var ny=sy; // assume no scrolling is needed
		// if BR is not in view, scroll to show BR
		if (px+pw>sx+ww) nx=px+pw-ww;
		if (py+ph>sy+wh) ny=py+ph-wh;
		// if TL not in view or too big... scroll to show TL
		if (px<nx || px>nx+ww || px+pw>nx+ww) nx=px;
		if (py<ny || py>ny+wh || py+ph>ny+wh) ny=py;
		// optionally, center in view (if panel fits)
		if (center && pw<ww) nx-=(ww-pw)/2;
		if (center && ph<wh) ny-=(wh-ph)/2;
		if (nx!=sx||ny!=sy) { // if we need to scroll...
			window.scrollTo(nx,ny);
			if (config.options.chkMoveablePanelShowStatus && !startingUp) {
				var id=hasClass(p,'tiddler')?p.getAttribute('tiddler'):p.pid;
				this.timedMessage(this.scrollMsg.format([id||this.noPid,px,py]),this.msgDuration);
			}
			this.notify(p);
		}
	},
	// bring to front and scroll into view (with optional ASYNC)
	ensurePanelVisible: function(p,delay) { if (!p) return;
		if (delay && !startingUp) { // wait for core animation to complete...
			if (hasClass(p,'tiddler'))
				p=config.macros.moveablePanel.findPanel(p.getAttribute('tiddler'))||p;
			if (!p.id) p.id=new Date().getTime()+Math.random(); // unique ID
			var code='config.macros.moveablePanel.ensurePanelVisible(document.getElementById("%0"));';
			setTimeout(code.format([p.id]),delay);
			return;
		}
		// unblock scrolling and bring the panel into view
		if (this.noScrollX) this.noScrollX--; if (this.noScrollY) this.noScrollY--;
		if (hasClass(p,'popup')) return; // leave popups alone!
		this.bringPanelToFront(p);
		if (this.noScrollX+this.noScrollY==0 && !startingUp) // no scroll during document startup
			this.scrollToPanel(p);
	},
//}}}
// // panel status
//{{{
	formatPanelStatus: function(p) {
		var s=p.style; var msg=this.statusMsg.format([p.pid||this.noPid,
			s.left,s.top,hasClass(p,'hover')?this.hoveredMsg:'',s.width,s.height,s.zIndex]);
		return msg.replace(/(\.[0-9]+)|px/g,''); // remove decimals and 'px'
	},
	showPanelStatus: function(p,show) { // display panel info in titlebar while moving/sizing
		if (!config.options.chkMoveablePanelShowStatus) return;
		if (show) document.title=this.formatPanelStatus(p)
		else refreshPageTitle();
	},
	timedMessage: function(msg,duration) {
		document.title=msg; setTimeout('refreshPageTitle()',duration);
	},
	getPanelTooltip: function(p) {
		return hasClass(p,'undocked')?this.formatPanelStatus(p):this.dockedTip.format([p.pid||this.noPid]);
	},
//}}}
// // panel actions
//{{{
	undockPanel: function(p,front) { // undocked with default pos/size
		if (hasClass(p,'undocked')) return; // already undocked
		// get size BEFORE undocking
		p.style.width=p.fixedwidth  ||(this.getPanelWidth(p)+'px');
		p.style.height=p.fixedheight||(this.getPanelHeight(p)+'px');
		addClass(p,'undocked');	if (!this.isStackable(p)) p.style.position='absolute'; // UNDOCK it
		// set position AFTER undocking
		var offset=this.getPanelOffset(p);
		p.style.left=findPosX(p)-offset.x+'px'; p.style.top=findPosY(p)-offset.y+'px';
		if (front) this.bringPanelToFront(p);
		this.notify(p);
	},
	dockPanel: function(p) { // reset to docked pos/size
		if (!hasClass(p,'undocked')) return; // already docked
		this.restorePanel(p); // reset panel
		// FOR FLOATING SLIDERS: trigger slider adjustment handler (if any)
		if (hasClass(p,'floatingPanel') && window.adjustSliderPos)
			window.adjustSliderPos(p.parentNode,p.button,p);
		this.quiet++; if (this.manager) this.manager.trackMap(p); this.quiet--;
		this.notify(p)
	},
	closePanel: function(p) { // dock panel, then close (for tiddlers and floating sliders)
		var t=story.findContainingTiddler(p);
		var isTiddler=t&&this.findPanel(t.getAttribute('tiddler'));
		var isFloating=hasClass(p,'floatingPanel');
		if (!isTiddler) // when closing TIDDLERS, leave them undocked (keeps size/pos)
			this.dockPanel(p);
		// FOR FLOATING SLIDERS: set focus and do a fake click on slider button
		if (isFloating) { p.button.focus(); onClickNestedSlider({target:p.button}); }
		// FOR TIDDLERS: call story.closeTiddler()
		if (isTiddler) { story.closeTiddler(t.getAttribute('tiddler')); }
	},
	movePanel: function(p,x,y,show,centered) { if (!p) return;
		this.quiet++;
		this.undockPanel(p);
		// adjust for child elements inside relative/floatingPanel containers
		var offset=this.getPanelOffset(p);
		p.style.left=x-offset.x+'px'; p.style.top=y-offset.y+'px';
		if (show) { this.bringPanelToFront(p); this.scrollToPanel(p,centered); }
		this.quiet--;
		this.showPanelStatus(p,true);
		if (this.manager) this.manager.trackMap(p);
	},
	foldPanel: function(p) { // toggle panel height
		if (hasClass(p,'folded')) removeClass(p,'folded'); else addClass(p,'folded');
		if (this.manager) this.manager.trackMap(p);
		this.notify(p);
	},
	hoverPanel: function(p) { // toggle fixed position
		if (hasClass(p,'hover')) {
			removeClass(p,'hover');
			var offset=this.getPanelOffset(p);
			p.style.left=p.offsetLeft+findScrollX()-offset.x+'px';
			p.style.top=p.offsetTop+findScrollY()-offset.y+'px';
		} else {
			var offset=this.getPanelOffset(p);
			var ww=findWindowWidth(); var wh=findWindowHeight();
			p.style.left=(p.offsetLeft-findScrollX()+offset.x)%ww+'px';
			p.style.top =(p.offsetTop -findScrollY()+offset.y)%wh+'px';
			addClass(p,'hover'); 
		}
		if (this.manager) this.manager.trackMap(p);
		this.notify(p);
	},
	resetPanel: function(p) { // reset to session starting pos/size
		if (this.manager) this.manager.resetPanel(p); else this.dockPanel(p);
	},
//}}}
// // menu buttons
//{{{
	processed: function(ev) { var ev=ev||window.event; // use to end event handling for menus and mouse actions
		if (ev) { ev.cancelBubble=true; if (ev.stopPropagation) ev.stopPropagation(); } return false;
	},
	addPanelButtons: function(p,showfold,showhover,showclose,showdock,showmanager) {
		if (p.menu) return; // only once per panel
		function cmd(menu,label,tip,callback,show,arg) {
			var fn=function(ev){return this.callback.apply(config.macros.moveablePanel,[this,ev,this.arg]);}
			var b=createTiddlyButton(menu,label,tip,fn,'moveablePanelButton');
			b.style.display=show?'inline':'none'; b.callback=callback; b.arg=arg;
			return b;
		}
		var m=createTiddlyElement(p,'div',null,'moveablePanelMenu');
		p.showfold=showfold;
		p.foldbutton= cmd(m,this.foldLabel,this.foldTip,this.foldHandler,showfold);
		p.unfoldbutton= cmd(m,this.unfoldLabel,this.unfoldTip,this.foldHandler,false);
		p.showhover=showhover;
		p.hoverbutton=cmd(m,this.hoverLabel,this.hoverTip,this.hoverHandler,showhover);
		p.scrollbutton=cmd(m,this.scrollLabel,this.scrollTip,this.hoverHandler,false);
		p.showdock=showdock;
		p.dockbutton= cmd(m,this.dockLabel,this.dockTip,this.dockHandler,showdock);
		p.showclose=showclose;
		p.closebutton=cmd(m,this.closeLabel,this.closeTip,this.closeHandler,showclose);
		p.showmanager=showmanager;
		if (this.manager) p.managerbutton=cmd(m,this.manager.buttonLabel,this.manager.buttonTip,
			this.manager.popup,showmanager,p.pid);
		p.menu=m;
	},
	togglePanelButtons: function(p,show) { if (!p||!p.menu) return;
		var undocked=hasClass(p,'undocked');
		var floating=hasClass(p,'floatingPanel');
		var hover=hasClass(p,'hover');
		var folded=hasClass(p,'folded');
		var t=story.findContainingTiddler(p);
		var tiddler=t&&this.findPanel(t.getAttribute('tiddler'));
		var show=show&&(undocked||floating);
		p.menu.style.display=show?'inline':'none';
		if (p.showfold)  p.foldbutton.style.display  =!folded?'inline':'none';
		if (p.showfold)  p.unfoldbutton.style.display= folded?'inline':'none';
		if (p.showhover) p.hoverbutton.style.display =!hover?'inline':'none';
		if (p.showhover) p.scrollbutton.style.display= hover?'inline':'none';
		if (p.showdock)  p.dockbutton.style.display =undocked?'inline':'none';
		if (p.showclose) p.closebutton.style.display=floating||(tiddler&&undocked)?'inline':'none';
		if (p.managerbutton) { // see [[PanelManagerPlugin]]
			var show=p.showmanager||config.options.chkMoveablePanelShowManager;
			p.managerbutton.style.display=show?'inline':'none';
		}
	},
	foldHandler: function(place,ev){ var p=this.getPanel(place);
		this.foldPanel(p); this.togglePanelButtons(p,true); return this.processed(ev); },
	hoverHandler: function(place,ev){ var p=this.getPanel(place);
		this.hoverPanel(p); this.togglePanelButtons(p,true); return this.processed(ev); },
	dockHandler: function(place,ev){ var p=this.getPanel(place);
		this.dockPanel(p); this.togglePanelButtons(p,true); return this.processed(ev); },
	closeHandler: function(place,ev){ var p=this.getPanel(place);
		this.closePanel(p); this.togglePanelButtons(p,true); return this.processed(ev); },
//}}}
// // mouse handlers
//{{{
	addMouseHandlers: function(p) {
		if (p.handlers) return true; // only add handlers ONCE
		p.onmouseover=function(ev) { var ev=ev||window.event;
			var r=config.macros.moveablePanel.mouseover(this,ev);
			return r&&this.saved.mouseover?this.saved.mouseover.apply(this,arguments):true;
		};
		p.onmouseout=function(ev) { var ev=ev||window.event;
			var r=config.macros.moveablePanel.mouseout(this,ev);
			return r&&this.saved.mouseout?this.saved.mouseout.apply(this,arguments):true;
		};
		p.onmousemove=function(ev) { var ev=ev||window.event;
			var r=config.macros.moveablePanel.mousemove(this,ev);
			return r&&this.saved.mousemove?this.saved.mousemove.apply(this,arguments):true;
		};
		p.ondblclick=function(ev) { var ev=ev||window.event;
			var r=config.macros.moveablePanel.dblclick(this,ev);
			return r&&this.saved.dblclick?this.saved.dblclick.apply(this,arguments):r;
		};
		p.onmousedown=function(ev) { var ev=ev||window.event;
			var r=config.macros.moveablePanel.mousedown(this,ev);
			return r&&this.saved.mousedown?this.saved.mousedown.apply(this,arguments):r;
		};
		p.handlers=true;
	},
	isEdge: function(p,ev) { // near 'edge' of panel (or child element)?
		var ev=ev||window.event; var target=resolveTarget(ev);
		if (!p) return false;
		// ignore form input fields
		if (['input','select','option','textarea'].contains(target.nodeName.toLowerCase())) return false;
		var left=findPosX(p); var top=findPosY(p);
		var width=p.offsetWidth; var height=p.offsetHeight;
		var x=findMouseX(ev); var y=findMouseY(ev);
		if (hasClass(p,'hover')) { x-=findScrollX(); y-=findScrollY(); } // window-relative panel
		if (x<left||y<top||x>=left+width||y>=top+height) { // outside of panel
			if (p==target || p!=this.getPanel(target)) return false;
			return this.isEdge(target,ev); // check target child element
		}
		var edgeW=this.getPanelEdgeWidth(p); var edgeH=this.getPanelEdgeHeight(p);
		var isT=(y-top<edgeH); var isL=(x-left<edgeW);
		var isB=(top+height-y<edgeH); var isR=(left+width-x<edgeW);
		return isT||isL||isB||isR;
	},
	// temporary element during move/size keeps document from shrinking 
	addGhost: function(p) {
		var g=document.getElementById('moveablePanelGhost');
		if (!g) g=createTiddlyElement(document.body,'div','moveablePanelGhost','moveablePanelGhost');
		var border=1; // note: must match CSS for 'moveablePanelGhost' WFFL-HACK
		g.style.left=findPosX(p)+'px';
		g.style.top=findPosY(p)+'px';
		g.style.width=((p.offsetWidth-border*2)||0)+'px';
		g.style.height=((p.offsetHeight-border*2)||0)+'px';
	},
	clearGhost: function() {
		var e=document.getElementById('moveablePanelGhost');
		if (e) e.parentNode.removeChild(e);
	},
	// MOUSEOVER=SHOW MENU
	mouseover: function(place,ev) { var ev=ev||window.event;
		var p=this.getPanel(place);
		addClass(p,'selected'); // shows toolbar-classed items
		this.togglePanelButtons(p,true);
		return true;
	},
	// MOUSEOUT=HIDE MENU
	mouseout: function(place,ev) { var ev=ev||window.event;
		var p=this.getPanel(place);
		removeClass(p,'selected'); // hides toolbar-classed items
		this.togglePanelButtons(p,false);
		return true;
	},
	// MOUSEMOVE=SHOW MENU AND SET CURSOR/TIP
	mousemove: function(place,ev) { var ev=ev||window.event;
		var p=this.getPanel(place);
		p.style.cursor='auto'; p.title=p.saved?p.saved.title:'';
		if (!this.isEdge(p,ev)) return true;
		var fw=p.fixedwidth;  if (fw==null) fw=undefined;
		var fh=p.fixedheight; if (fh==null) fh=undefined;

		p.title=this.moveTip.format([p.pid?p.pid+': ':'']);
		if (fw===undefined&&fh===undefined) p.title+=' '+this.sizeTip;
		else if  (fw===undefined) p.title+=' '+this.sizeWidthTip;
		else if  (fh===undefined) p.title+=' '+this.sizeHeightTip;
		if (hasClass(p,'undocked')) {
			p.title+=', '+this.clickTip+', ';
			p.title+=hasClass(p,'folded')?this.dblclickunfoldTip:this.dblclickdockTip;
		}
		p.style.cursor='move';
		if (ev.shiftKey&&!(fw&&fh)) { // set resizing cursor (if not fixed width/height)
			var left=findPosX(p); var top=findPosY(p);
			var width=p.offsetWidth; var height=p.offsetHeight;
			var x=findMouseX(ev); var y=findMouseY(ev);
			if (hasClass(p,'hover')) { x-=findScrollX(); y-=findScrollY(); } // window-relative panel
			var edgeW=this.getPanelEdgeWidth(p); var edgeH=this.getPanelEdgeHeight(p);
			var isT=(y-top<edgeH); var isL=(x-left<edgeW);
			var isB=(top+height-y<edgeH); var isR=(left+width-x<edgeW);
			p.style.cursor=(fh===undefined?(isT?'n':(isB?'s':'')):'')
				+(fw===undefined?(isL?'w':(isR?'e':'')):'')+'-resize';
		}
		return true;
	},
	// DOUBLE-CLICK=DOCK OR UNFOLD
	dblclick: function(place,ev) { var ev=ev||window.event;
		var p=this.getPanel(place);
		if (!this.isEdge(p,ev)) return true;
		// if folded... unfold, otherwise... undock
		if (hasClass(p,'folded')) this.foldPanel(p); else this.dockPanel(p);
		this.togglePanelButtons(p,false);
		return this.processed(ev);
	},
	// MOUSEDOWN=START MOVE/SIZE, CLICK=BRING TO FRONT, SHIFT-CLICK=SEND TO BACK
	mousedown: function(place,ev) { var ev=ev||window.event;
		var p=this.getPanel(place);

		// CLICK ALWAYS BRINGS TO FRONT
		this.quiet++;
		this.bringPanelToFront(p);
		if (this.manager) this.manager.trackMap(p);
		this.quiet--;
		if (!this.isEdge(p,ev)) return true;

		// start capturing mouse events and set mouse/key handlers
		var target=p; // if 'capture' not supported, track in panel only
		if (document.body.setCapture) // IE
			{ document.body.setCapture(); var target=document.body; }
		if (window.captureEvents) // moz
			{ window.captureEvents(Event.MouseMove|Event.MouseUp,true); var target=window; }
 		if (target.onmousemove!=undefined) target.saved_mousemove=target.onmousemove;
		target.onmousemove=this.dragmove;
		if (target.onmouseup!=undefined) target.saved_mouseup=target.onmouseup;
		target.onmouseup=this.dragstop;
 		if (target.onkeydown!=undefined) target.saved_keydown=target.onkeydown;
		target.onkeydown=this.dragkey;

		// calculate and save drag data in target element
		var x=findMouseX(ev); var left=findPosX(p); var width =p.offsetWidth;
		var y=findMouseY(ev); var top =findPosY(p); var height=p.offsetHeight;
		var sizing=ev.shiftKey;
		var edgeW=this.getPanelEdgeWidth(p); var edgeH=this.getPanelEdgeHeight(p);
		var isT=(y-top<edgeH); var isL=(x-left<edgeW);
		var isB=(top+height-y<edgeH); var isR=(left+width-x<edgeW);
		var d=new Object();
		d.panel=p; d.left=left; d.top=top;
		d.width=this.getPanelWidth(p); d.height=this.getPanelHeight(p);
		d.sizing=sizing; d.edgeW=edgeW; d.edgeH=edgeH;
		d.isT=isT; d.isL=isL; d.isB=isB; d.isR=isR; d.offset=this.getPanelOffset(p);
		d.saved={ x:p.style.left, y:p.style.top, w:p.style.width, h:p.style.height,
			z:p.style.zIndex, pos:p.style.position, classname:p.className };
		target.data=d;
		this.addGhost(p); // keep document from shrinking during move/size
		return this.processed(ev);
	},
	// MOUSEMOVE (during drag)=move/size panel
	dragmove: function(ev){ var ev=ev||window.event; var cmm=config.macros.moveablePanel;
		var d=this.data; var p=d.panel;
		if (!p) { this.onmousemove=this.saved_mousemove?this.saved_mousemove:null; return; }

		cmm.quiet++; // save all notifications until the end...

		// ensure panel is undocked and scrolled into view, THEN get starting mouse and scroll positions
		if (!hasClass(p,'undocked'))
			{ cmm.undockPanel(p,true); if (this.manager) this.manager.trackMap(p); }
		if (d.x===undefined) // first move event only
			{ cmm.scrollToPanel(p); d.x=findMouseX(ev); d.y=findMouseY(ev); }

		// get current mouse pos
		var newX=findMouseX(ev); var newY=findMouseY(ev);

		// calculate new TLWH (start with current panel pos/size)
		var startX=d.x; var startY=d.y; var offsetX=d.offset.x; var offsetY=d.offset.y;
		var L=d.left; var T=d.top; var W=d.width; var H=d.height;
		var newL=L; var newT=T; var newW=p.fixedwidth||W; var newH=p.fixedheight||H;
		if (d.sizing) { // resize panel
			var minW=d.edgeW*2; var minH=d.edgeH*2; // stay bigger than edge areas
			if (hasClass(p,'folded')) this.fold(p.foldButton,ev); // un-fold first!
			if (d.isT) newH=H-newY+startY+1;
			if (d.isB) newH=H+newY-startY+1;
			if (d.isL) newW=W-newX+startX+1;
			if (d.isR) newW=W+newX-startX+1;
			if (d.isT) newT=T-offsetY+newY-startY+1; else newT=T-offsetY; 
			if (d.isL) newL=L-offsetX+newX-startX+1; else newL=L-offsetX; 
			if ((d.isL||d.isR)&&!p.fixedwidth)  newW=(newW>minW?newW:minW);
			if ((d.isT||d.isB)&&!p.fixedheight) newH=(newH>minH?newH:minH);
		} else { // move panel
			newL=L-offsetX+newX-startX+1;
			newT=T-offsetY+newY-startY+1;
		}
		if (hasClass(p,'hover')) { // hover=stay on first screen
			var ww=findWindowWidth(); var wh=findWindowHeight();
			newL+=offsetX; newT+=offsetY; // hover=no relative offset (window-relative)
			// WFFL lower right is off... a bit too far (perhaps scrollwidth?)
			if (newL+newW>ww) newL=ww-newW; if (newT+newH>wh) newT=wh-newH; // limit lower right
			if (newL<0) newL=0; if (newT<0) newT=0; // limit upper left
		} else { // normal floating panel=limit upper left (stay on page)
			if (newL+offsetX<0) newL=0-offsetX; if (newT+offsetY<0) newT=0-offsetY;
		}

		// move the panel and scroll into view as needed
		p.style.left=newL.toString()+'px';
		p.style.top=newT.toString()+'px';
		if (d.sizing) p.style.width=newW.toString()+'px';
		if (d.sizing) p.style.height=newH.toString()+'px';
		cmm.scrollToPanel(p);

		// report new position and notify panel manager... done!
		cmm.quiet--; cmm.showPanelStatus(p,true); cmm.notify(p);
		return cmm.processed(ev);
	},
	dragkey: function(ev){ var ev=ev||window.event;
		var d=this.data; var p=d.panel;
		if (ev.keyCode==27) { // ESC=CANCEL... restore panel to previous pos/size
			p.style.left =d.saved.x; p.style.top   =d.saved.y;
			p.style.width=d.saved.w; p.style.height=d.saved.h;
			p.style.zIndex=d.saved.z;
			p.style.position=d.saved.pos;
			p.className=d.saved.classname;
			return this.onmouseup(ev);
		}
		if (this.saved_keydown) return this.saved_keydown(ev);
	},
	// MOUSEUP: END MOVE/SIZE, SHIFT-CLICK=SEND TO BACK
	dragstop: function(ev){ var ev=ev||window.event; var cmm=config.macros.moveablePanel;
		var newX=findMouseX(ev); var newY=findMouseY(ev);
		if (this.releaseCapture) this.releaseCapture(); // IE
		if (this.releaseEvents) this.releaseEvents(Event.MouseMove|Event.MouseUp); // moz
		this.onmousemove=this.saved_mousemove?this.saved_mousemove:null;
		this.onmouseup=this.saved_mouseup?this.saved_mouseup:null;
		this.onkeydown=this.saved_keydown?this.saved_keydown:null;
		var d=this.data; var p=d.panel;
		if (ev.shiftKey && d.x==newX && d.y==newY && cmm.isEdge(p,ev))
			cmm.sendPanelToBack(p); // SHIFT-CLICK *EDGE*
		cmm.togglePanelButtons(p,true);
		cmm.quiet++; if (cmm.manager) cmm.manager.trackMap(p); cmm.quiet--;
		cmm.clearGhost(); // allow document to adjust extents (if needed)
		cmm.showPanelStatus(p,false); cmm.timedMessage(cmm.formatPanelStatus(p),cmm.msgDuration);
		return cmm.processed(ev);
	},
//}}}
// // CSS definitions
//{{{
	css: '/*{{{*/\n'
		+'.moveablePanelMenu\n'
			+'\t{ display:none; position:absolute; right:.5em; top:-1em; }\n'
		+'.undocked .selected.moveablePanelMenu\n'
			+'\t{ display:inline; }\n'
		+'.floatingPanel .selected .moveablePanelMenu\n'
			+'\t{ display:inline; }\n'
		+'.hover\n'
			+'\t{ position:fixed !important; }\n'
		+'.folded\n'
			+'\t{ height:1.5em !important; overflow:hidden !important; }\n'
		+'.tiddler .folded\n'
			+'\t{ height:2em !important; }\n'
		+'.folded  .moveablePanelMenu\n'
			+'\t{ top:.5em; }	/* buttons fit in folded panel */\n'
		+'.tiddler .moveablePanelMenu\n'
			+'\t{ top:.2em; }	/* buttons fit in tiddler title */\n'
		+'.undocked .toolbar\n'
			+'\t{ padding-right:7.5em; }	/* make room for buttons next to toolbar */\n'
		+'.floatingPanel .moveablePanelMenu\n'
			+'\t{ right:1em;top:1em; } /* buttons fit in floating panel */\n'
		+'.moveablePanelButton\n {'
			+'\tbackground:#ccc !important; color:#000 !important;\n'
			+'\tborder:1px solid #666; padding:0 .25em; margin:0px 1px;\n'
			+'}\n'
		+'.moveablePanelButton:hover\n'
			+'\t{ background:#fff !important; color:#000 !important; }\n'
		+'.popup\n'
			+'\t{ z-index:9999999 !important; } /* popups MUST always be on top!  */\n'
		+'.moveablePanelGhost\n'
			+'\t{ position:absolute; border:1px dotted #999; }\n'
		+'/*}}}*/'
});
//}}}
// // load time initialization
//{{{
// defaults for options
if (config.options.txtMoveablePanelMapName===undefined)
	config.options.txtMoveablePanelMapName='DefaultMap';
if (config.options.chkMoveablePanelShowStatus===undefined)
	config.options.chkMoveablePanelShowStatus=true;
if (config.options.chkMoveablePanelShowManager===undefined)
	config.options.chkMoveablePanelShowManager=true;

// set up shadow stylesheet, then load styles (might be customized)
config.shadowTiddlers.MoveablePanelStyles=config.macros.moveablePanel.css;
var css=store.getRecursiveTiddlerText('MoveablePanelStyles',config.macros.moveablePanel.css,10);
setStylesheet(css,'moveablePanelStyles');
//}}}
// // hijacks
//{{{
// adjust popup placement to account for the current horizontal scrollbar
// offset (if any), so that popups will appear in the correct location, even
// when their 'root' element is scrolled far from the page origin.
var fn=Popup.place; fn=fn.toString(); if (fn.indexOf('findScrollX')==-1) { // only once
	fn=fn.replace(/winWidth\s*-\s*scrollWidth\s*-\s*1/,
		'findScrollX() + winWidth - scrollWidth - 1');
	fn=fn.replace(/winWidth\s*-\s*popupWidth\s*-\s*scrollWidth\s*-\s*1/,
		'findScrollX() + winWidth - popupWidth - scrollWidth - 1');
	eval('Popup.place='+fn);
}
//}}}
//{{{
// window.scrollTo() is used throughout the core (and plugins) to scroll to a *vertical*
// position as computed by ensureVisible(), in order to bring a tiddler into view.
// Unfortunately, the *horizontal* scroll position is almost always hard-coded to 0
// (i.e. a return to the left edge of the page).  Normally, this is not a problem,
// since a page is rarely scrolled horizontally.  However, when there are moveable
// panels, they can appear far from the left edge, so always scrolling to the left
// edge is very disruptive.  In addition, unwanted scrolling can occur as a side
// effect when displaying or refreshing a tiddler (e.g., switching between
// view/edit templates).  These hijacks adds control flags ('noScrollX' and 'noscrollY')
// that can be used to temporarily disable scrolling within the document.
if (window.scrollTo_moveablePanel==undefined) { // only once
window.scrollTo_moveablePanel=window.scrollTo;
	window.scrollTo=function(x,y) {
		var cmm=config.macros.moveablePanel;
		if (cmm.noScrollX&&cmm.noScrollY) return;
		x=cmm.noScrollX?findScrollX():x;
		y=cmm.noScrollY?findScrollY():y;
		window.scrollTo_moveablePanel(x,y);
	}
}
// ensureVisible() is used to calculate the y-offset of a tiddler, just before scrolling
// This tweak sets up an ASYNC timer to invoke the 'bring to front/scroll into view'
// function for 'tiddler' and 'popup' classes.  This allows the function
// to be triggered *after* core scrolling occurs, so the fixups are not
// stomped on by the core's normal scroll handling
if (window.ensureVisible_moveablePanel==undefined) { // only once
	window.ensureVisible_moveablePanel=window.ensureVisible;
	window.ensureVisible=function(e) {
		var ny=ensureVisible_moveablePanel.apply(this,arguments); // get core value

		// fixup height to account for horizontal scrollbar (if present)
		var atBottom=findPosY(e)+e.offsetHeight>=findScrollY()+findWindowHeight();
		var hasHScroll=document.documentElement.scrollWidth>findWindowWidth();
		var hScrollSize=findWindowWidth()-document.body.offsetWidth;
		if (atBottom && hasHScroll) ny+=hScrollSize;

		// defer scrolling for tiddlers and popups (except during startup)
		if (!startingUp && (hasClass(e,'tiddler')||hasClass(e,'popup'))) {
			var cmm=config.macros.moveablePanel;
			cmm.noScrollX++; if (hasClass(e,'tiddler')) cmm.noScrollY++;
			var delay=config.options.chkAnimate?config.animDuration+50:50;
			cmm.ensurePanelVisible(e,delay); // ASYNC SCROLL
		}
		return ny;
	}
}

// story.refreshTiddler()
if (Story.prototype.refreshTiddler_moveablePanel==undefined) { // only once
	Story.prototype.refreshTiddler_moveablePanel=Story.prototype.refreshTiddler;
	Story.prototype.refreshTiddler=function() {
		var cmm=config.macros.moveablePanel;
		cmm.noScrollX++; cmm.noScrollY++; // DON'T SCROLL AT ALL
		var r=this.refreshTiddler_moveablePanel.apply(this,arguments);
		cmm.noScrollX--; cmm.noScrollY--;
		return r;

	}
}
// story.displayTiddler()
if (Story.prototype.displayTiddler_moveablePanel==undefined) { // only once
	Story.prototype.displayTiddler_moveablePanel=Story.prototype.displayTiddler;
	Story.prototype.displayTiddler=function(srcElement,tiddler) {
		var cmm=config.macros.moveablePanel;
//WFFL		cmm.noScrollX++; cmm.noScrollY++;
		var r=this.displayTiddler_moveablePanel.apply(this,arguments);
		var title=(tiddler instanceof Tiddler)?tiddler.title:tiddler;
		var panel=cmm.findPanel(title); // if moveable... unfold panel (but not during startup)
		if (panel&&hasClass(panel,'folded')&&!startingUp) cmm.foldPanel(panel);
		var delay=config.options.chkAnimate?config.animDuration+50:50;
		cmm.ensurePanelVisible(this.getTiddler(title),delay); // ASYNC SCROLL
		return r;

	}
}
//}}}
//{{{
// Zoomer() displays an animated bounding box when showing a tiddler.  But this box 'zooms' to the tiddler's 'anchor point', not the current panel position, which can cause a 'scroll blink'.  This hijack redirects the zoomer's target directly to the undocked panel (if any)
if (window.Zoomer_moveablePanel==undefined) { // only once
	window.Zoomer_moveablePanel=window.Zoomer;
	window.Zoomer=function(text,startElement,targetElement,unused) {
		if (hasClass(targetElement,'tiddler')) {
			var tid=targetElement.getAttribute('tiddler');
			arguments[2]=config.macros.moveablePanel.findPanel(tid)||targetElement;			
		}
		return window.Zoomer_moveablePanel.apply(this,arguments);
	}
}
//}}}
/***
|Name|MoveablePanelPluginInfo|
|Source|http://www.TiddlyTools.com/#MoveablePanelPlugin|
|Documentation|http://www.TiddlyTools.com/#MoveablePanelPluginInfo|
|Version|3.0.3|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Description|documentation for MoveablePanelPlugin|
Adds move/resize mouse handling plus fold/unfold, hover/scroll, and close/dock buttons to any rendered tiddler content, page element, or [[floating slider panel|NestedSlidersPlugin]].
!!!!!Usage
<<<
Please see [[MoveablePanelPackage]] for a general overview of features as well as information about other plugins and tiddlers associated with installation and use of moveable panels.
{{{
<<moveablePanel>>
<<moveablePanel undocked fold hover noclose nodock manager name:... width:... height:...>>

<<moveablePanel move label:... prompt:... name:... top:... left:...>>

<<moveablePanel jump label:... prompt:... name:... >>
<<moveablePanel jump label:... prompt:... top:... left:... >>

<<moveablePanel dock label:... prompt:... name:... >>
<<moveablePanel dock label:... prompt:... all>>
}}}
Without any parameters, the {{{<<moveablePanel>>}}} simply adds the 'moveable panel' mouse handling and button commands to a containing element that has been marekd with a CSS class of 'moveablePanel' (e.g., """{{moveablePanel{...}}}""") or 'floatingPanel' (created by [[NestedSlidersPlugin]]) .  If no containing 'moveablePanel' or 'floatingPanel' element is located, the panel handlers are added directly to the element in which the macro is contained.

__Optional macro parameters are:__
*''undocked''<br>by default, a moveable panel is rendered inline ('docked'), and only floats in front of other content ('undocked') after the user has moved it from it's 'anchor point'.  The ''undocked'' keyword indicates that the panel should be be immediately 'undocked' and brought to the front of the 'stack' of undocked panels as soon as it is rendered.
*''fold''<br>adds the fold/unfold (-/+) button
*''hover''<br>adds the hover/scroll (^/=) button
*''noclose''<br>supresses the close (X) button
*''nodock''<br>supresses the dock (&radic;) button
*''manager''<br>forces the Panel Manger button to always appear in the panel, regardless of the option setting (see below)
*''name:...''<br>specifies a unique identifier for the panel, to be used by the ''menu'', ''move'', ''jump'', or ''dock'' functions described below, as well as by the (optional) [[PanelManagerPlugin]] to create //panel maps// that track and record the size and position of named panels, so that they can remain where you put them, even in between browser sessions.
*''width:...''<br>specifies a fixed-width for the panel (using CSS dimensions).  This prevents horizontal resizing.
*''height:...''<br>specifies a fixed-height for the panel (using CSS dimensions).  This prevents vertical resizing.

In addition to adding panel handlers to a containing element, the macro can also be used to invoke several panel-specific actions, either immediately, or by clicking on an embedded command link:
*''dock''<br>returns a named panel to it's original anchor point.
*''jump''<br>scrolls the window to view a named panel or go to the specified (top,left) location on the page.  To dock all panels at once, the ''all'' keyword can be used instead of specifying a ''name:...'' parameter.
*''move''<br>moves the named panel to the specified (top,left) location on the page.
Note: when using ''dock'', ''jump'', or ''move'', if the ''label:...'' parameter is omitted, the action is performed immediately upon rendering the macro.  Otherwise, a command link is created using the specified ''label:...'' and (optional) ''prompt:...'' values.

__Mouse handling:__
When the mouse is just inside the edges of a moveable panel, the cursor will change to 'crossed-arrows'.  Grab (click-hold) the panel anywhere in the edge area and then drag the mouse to reposition it.  To resize the panel, hold the ''shift'' key before grabbing the panel: the cursor will change to a 'double-arrow' resizing symbol.  Drag a side edge of the panel to stretch horizontally or vertically, or drag a corner of the panel to stretch in both dimensions at once.  Double-clicking the edge of a panel resets it to its original 'docked' size and location.  Clicking (anywhere) in a moveable panel brings it to the top of the 'zIndex' stack (if overlapping with other panels).  Shift-click (along an edge) sends the panel to the back.

__Panel buttons:__
When the mouse is anywhere over a panel (not just near the edge), a special 'panel menu' appears in the ''upper right corner'' of the panel, with the following command buttons:
* &minus;/+ (fold/unfold): ''fold'' temporarily reduces the panel height to just one line.  ''unfold'' restores the panel height.
*^/= (hover/scroll): ''hover'' causes the panel to remain in view (i.e., 'fixed position') even when scrolling the rest of the page content.  ''scroll'' allows the panel to revert to scrolling with the page content (the usual behavior)
*X (close): ''close'' hides a panel from the page display.  If you have moved/resized a panel, closing it restores its default position and size.
*&radic; (dock): When a moveable panel has actually been moved from its default position, the ''close'' command is replaced with ''dock'', which restores the tiddler to its default //non-floating// location on the page.
*&#x2261; (manager): If [[PanelManagerPlugin]] is installed, this button provides instant access to the entire Panel Manager popup menu and interactive, graphical panel map viewer.

__Adding panel handlers to all tiddlers__
You can apply {{{<<moveablePanel ...>>}}} to //all// tiddlers by customizing the [[ViewTemplate]] definition to wrap the entire tiddler layout within a span that invokes the macro.  To uniquely name each moveable tiddler panel (i.e., so its position/size will be remembered by the Panel Manager), you can use a 'computed parameter' to dynamically assign the tiddler's title as the value of the 'name:...' parameter, like this:
{{{
<div class='moveablePanel'>
	... rest of tiddler layout template (i.e., everything from the normal ViewTemplate)
	<div macro='moveablePanel name:{{tiddler?tiddler.title:""}} height:auto'></div>
</div>
}}}
//Note: reference to 'tiddler.title' in this way requires installation of TiddlyTools'// [[CoreTweaks##444]] or use of TW2.5 (not yet available)
<<<
!!!!!Configuration
<<<
<<option chkMoveablePanelShowManager>> automatically add<<moveablePanel menu label:[[Panel Manager Popup Menu]]>>in undocked panels
{{{<<option chkMoveablePanelShowManager>>}}}
<<option chkMoveablePanelShowStatus>> show position/size while moving/resizing a panel
{{{<<option chkMoveablePanelShowStatus>>}}}
<<<
!!!!!Revisions
<<<
2008.12.24 [3.0.3] added ESC key handling to cancel panel move/size (restores previous panel state)
2008.12.20 [3.0.2] addGhost()/clearGhost(): shows panel outline during move/size (prevents document from shrinking until move/size is done)
2008.12.15 [3.0.1] handling for 'hovered' elements: adjust for fixed vs. absolute (no relative offsets, no scroll offsets), translate movement to top-left screen, restrict movement within screen bounds
2008.12.10 [3.0.0] total rewrite: extensive code refactoring and improved event handling for cross-browser compatibility, manage zIndex 'stacking' of panels, added shadow tiddler for customizable CSS, named panels, macro commands for embedding jump, dock, or move command links, complete I18N/L10N-readiness, {{{ensureVisible()}}} 'fixups' for horizontal scrolling, and hooks for optional [[PanelManagerPlugin]] (panel map 'memory' and interactive graphical viewers)
2008.11.17 [2.6.0] added optional 'height:...' and 'width:...' macro params for fixed size dimensions (use 'height:auto' for moveable tiddlers).  Added CSS for moveablePanelMenu and moveablePanelButton styles (for easier customization)
2008.11.16 [2.5.2] small fixes to mouseover/isEdge() handling.  some code cleanup as well
2008.11.15 [2.5.1] changed stored panel data format to be compatible with Project Cecily map format (space-separated, strip decimals and 'px').  Also, moved slider-specific adjustPanel() logic into [[NestedSlidersPlugin]].
2008.11.12 [2.5.0] more major code changes (lots of event handling fixes and code refactoring)
2008.11.09 [2.4.0] major re-write to fix mouse event handling issues and isEdge() logic for nested moveable panels
2008.11.06 [2.3.0] added CLICK/SHIFT-CLICK for 'move to top / return to stack' panel zIndex handling
2008.11.03 [2.2.0] automatically store/recall position/size of named panels using a cookie and/or tiddler
2008.09.11 [2.1.2] corrected caching of transient attribute (use =='true' to convert string to boolean)
2008.01.08 [*.*.*] plugin size reduction: documentation moved to ...Info tiddler
2007.12.30 [2.1.0] added 'noedges' option for alternative 'grab handles' (top=move, bottom-right=resize)
2007.12.17 [2.0.0] code reduction and feature cleanup: in getPanel(), removed handling for returning a tiddler element.  Instead, when the macro is not in a floating panel, it will make it's containing element moveable.  Removed 'maximize' functionality (which was badly broken), and replaced it with 'double-click=DOCK' (resets size/position without hiding panel). Changed 'float/close' menu style to use absolute positioned elements instead of using 'float:right' so panels don't suddenly stretch when the menu items are displayed.  Also, throughout code, replaced direct comparison for className=='floatingPanel' with hasClass() test function.  This allows additional classes to be used with moveable floating panels to apply custom CSS styles.
2007.06.10 [1.3.7] in handler(), mouse event handlers now use apply() to correctly invoke any previous mouse handler functions.  This allows *moveable tiddlers* to still process the TW standard handlers for mouseover/out that toggle the 'selected' class used to highlight the toolbar items when the mouse is over a tiddler.  Also, when setting up a moveable 'floatingPanel', clear the existing 'snap panel back to button location' mouseover/out event handlers (defined by NestedSlidersPlugin when the panels are created).  This extends the fix from v1.3.5 (see below). 
2007.06.08 [1.3.6] in getPanel(), remove unneeded check for 'moveable' tag.  Also, added support for 'noclose' macro param, and updated documentation accordingly.
2007.06.02 [1.3.5] in handler(), if floating panel has a corresponding button element (i.e., a NestedSlider), then remove onmouseover handler from that button element, to prevent automatic 'snap to original location' behavior.  This allows *moveable* floating panels to maintain their placement when they have been manually re-positioned.  This change is made for compatibility with [[NestedSlidersPlugin]] use of onmouseover (see entry for version 2.0.4)
2006.10.17 [1.3.4] when moving panel, adjust position for relative containing DIV
2006.05.25 [1.3.3] in closePanel(), use p.button.onclick() so that normal processing (updating slider button tooltip, access key, etc.) is performed
2006.05.11 [1.3.2] doc update
2006.05.11 [1.3.1] re-define all functions within moveablePanel object (eliminate global window.* function definitions (and some 'leaky closures' in IE)
2006.05.11 [1.3.0] converted from inline javascript to true plugin
2006.05.09 [1.2.3] in closePanel(), set focus to sliderpanel button (if any)
2006.05.02 [1.2.2] in MoveOrSizePanel(), calculate adjustments for top and left when inside nested floating panels
2006.04.06 [1.2.1] in getPanel(), allow redefinition or bypass of 'moveable' tag (changed from hard-coded 'tearoff')
2006.03.29 [1.2.0] in getPanel(), require 'tearoff' tag to enable floating tiddlers
2006.03.13 [1.1.0] added handling for floating tiddlers and conditional menu display
2006.03.06 [1.0.2] set move or resize cursor during mousetracking
2006.03.05 [1.0.1] use 'window' vs 'document.body' so mousetracking in FF doesn't drop the panel when moving too quickly
2006.03.04 [1.0.0] Initial public release
<<<
/***
This stylesheet contains CSS definitions for extra styling of tiddlers (and other standard page content) for use with MoveablePanelPlugin
***/
/*{{{*/
/* UNDOCKED STYLES */
.tiddler
	{ margin:0 !important; padding:0 !important; overflow:visible !important;
}
.undocked .header {
	border:1px solid; 
}
.undocked #sidebarOptions { 
	width:auto; border:1px solid; padding:1em; margin:0; background:#fff;
	
}
.undocked #sidebarTabs { 
	width:auto; border:1px solid; padding:1.0em; background:#fff;
	
}
.undocked #sidebarTabs .tabContents
	{ width:17em; max-height:30em; overflow:auto; }
.undocked #sidebarTabs .tabContents .tabContents
	{ width:16em; max-height:24em; overflow:auto; }

/* TIDDLER 'TITLEBAR' */
.title {
	font-size:120%;
	line-height:150%;
	background-color:#ace;
	color:[[ColorPalette::Background]];
	border:1px solid [[ColorPalette::Foreground]]; border-bottom:0;
	
	padding-left:.5em;
}
.selected .title {
	background-color:#def;
	color:[[ColorPalette::Foreground]];
}
.subtitle { display:none; }

/* TIDDLER TOOLBAR */
.tiddler .folded
	{ height:2em !important; }
.tiddler .folded .title
	{  border:1px solid #000; }
.tiddler .moveablePanelMenu
	{ top:.4em !important }	/* shift buttons to fit in titlebar */
.undocked .toolbar
	{ padding-right:8em !important; } /* make room for buttons next to toolbar */
.toolbar
	{ float:right; visibility:hidden; margin-top:.5em; margin-right:.5em; }
.toolbar .button
	{ padding:0px .5em; }
.selected .toolbar 
	{ visibility:visible; }
.selected .toolbar .button {
	background:#fff; color:black; border:1px solid black; margin:0 1px;
	 }
.selected .toolbar .button:hover
	{ background:#ace; }

/* TIDDLER BODY */
.viewer {
	border:1px solid; padding:1em; background:#fff;
	
}
.viewer .content
	{ overflow:auto; } /* limit tiddler height */

/* ADJUST 'TAGGED' DISPLAY FOR UNDOCKED TIDDLERS */
.undocked .tagged
	{ position:absolute; right:2.5em; }
.selected .undocked .tagged
	{ opacity:0.1 !important; filter:'alpha(opacity:10)' !important; }
.selected .undocked .tagged:hover
	{ opacity:1 !important; filter:'alpha(opacity:100)' !important; }

/* DOTTED FOCUS AROUND CURRENT TIDDLER */
.moveablePanel
	{ margin:1px; }
.selected .moveablePanel
	{ margin:0px; border:1px dotted;
	 }

/* ELIMINATE SCROLLBARS (and TAGS) IN PanelViewer */
#tiddlerPanelViewer .viewer .content
	{ max-height:999999em; }
#tiddlerPanelViewer .panelManagerMapViewer
	{ margin-right:1em; }
#tiddlerPanelViewer .tagged
	{ display:none; }

/*}}}*/
<!--{{{-->
<!--
|Name|MoveableViewTemplate|
|Source|http://www.TiddlyTools.com/#MoveableViewTemplate|
|Version||
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|template|
|Requires|MoveablePanelPlugin, ViewTemplate, TaggedTemplateTweak|
|Overrides||
|Description|add moveablePanel macro to tiddlers tagged with 'moveable'|
-->
<div class='moveablePanel'>
	[[ViewTemplate]]
	<div macro='moveablePanel name:{{tiddler?tiddler.title:""}}  height:auto'></div>
</div>
<!--}}}-->
[>img(80px+,)[Dette er mig|http://dl.dropbox.com/u/1064531/CsvTest/fra%20Anders%20009ed.gif][cv]] 
Jeg har oversat/designet og tilpasset wikierne på denne side. 
Alle er baseret på Jeremy Rustons oprindelige [[TiddlyWiki|http://tiddlywiki.com]]. 
De fleste bliver "hostet" - gratis - på  [[TiddlySpot|http://tiddlyspot.com]]. 
Du kan gøre det samme!!
[[Link:|http://notabene.tiddlyspot.com/index.html]] [[download|http://notabene.tiddlyspot.com/download]]
<html><div align="center"><iframe src="http://notabene.tiddlyspot.com/index.html" frameborder="2" width="600em" height="800"></iframe></div></html>
[[Link:|http://notebrise.tiddlyspot.com/index.html]] [[download|http://notebrise.tiddlyspot.com/download]]
<html><div align="center"><iframe src="http://notebrise.tiddlyspot.com/index.html" frameborder="2" width="600em" height="800"></iframe></div></html>
[[Link:|http://noter3.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://noter3.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
/%
|Name|OpenTaggedTiddlers|
|Source|http://www.TiddlyTools.com/#OpenTaggedTiddlers|
|Version|1.2.1|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.3|
|Type|script|
|Requires|InlineJavascriptPlugin|
|Overrides||
|Description|open multiple tagged tiddlers with a single click|

Usage: <<tiddler OpenTaggedTiddlers with: "label" "tagToMatch" "sortBy" "reverse" "close">>

if MatchTagsPlugin is installed, "tagToMatch" can be a boolean tag expression
"sortBy" is an optional tiddler fieldname, and defaults to "title" (use "modified" or "created" for dates)
"reverse" is an optional KEYWORD, and reverses the order of display of the matched tiddler (i.e., descending vs. ascending)
"close" is an optional KEYWORD, that closes all open tiddlers before opening the tagged tiddlers

Note: use "" as placeholders when omitting optional keywords

%/<script label="$1">
	var list=[];
	var match="$2";
	var sortBy="$3"; if ((sortBy=="$"+"3")||(sortBy=="")) sortBy="title";
	var filter="[tag[%0]][sort[%1]]".format([match,sortBy]);
	var tids=store.filterTiddlers(filter);
	if ("$4"=="reverse") tids=tids.reverse();
	for (var t=0;t<tids.length;t++) list.push(tids[t].title);
	if ("$5"=="close") story.closeAllTiddlers();
	var here=story.findContainingTiddler(place);
	story.displayTiddlers(here,list);
	if (here && list.length) { // scroll to top of newly displayed tiddlers
		var cmd='window.scrollTo(0,'+(here.offsetTop+here.offsetHeight)+')';
		var delay=config.options.chkAnimate?config.animDuration+100:0;
		setTimeout(cmd,delay);
	}
	return false;
</script>
[[Link:|http://opgivelser.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://opgivelser.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
<!--{{{-->
<div id="tagmindmap" parameters="width:1024 height:768 id:main startState:{{['info','TiddlyWiki','2009']}} zoom:80 exclude: ['excludeLists','systemTiddler','systemConfig'] ignoreLoneNodes:true breadcrumbs:true" </div>
<div macro='moveablePanel name:header'>
	<div class='header' >
				
			<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
			<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
		
	</div>
</div>
<div id='mainMenu' class='moveablePanel'>
	<div refresh='content' tiddler='MainMenu'></div>
	<div macro='moveablePanel name:mainmenu width:auto height:auto'></div>
</div>
<div id='sidebar'>
	<span  style='position:relative'>
	<div macro='moveablePanel name:options width:16em height:auto'>
		<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
	</div>
	<div macro='moveablePanel name:tabs width:16em height:auto'>
		<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
	</div>
	</span>
</div>
<div id='displayArea'>
	<div id='messageArea'></div>
	<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
/***
|''Name''|PaletteViewMacro|
|''Version''|0.2|
|''Author''|FND|
|''Source''|[[FND's DevPad|http://devpad.tiddlyspot.com/#PaletteViewMacro]]|
|''License''|[[Creative Commons Attribution-ShareAlike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]]|
|''~CoreVersion''|2.1|
|''Type''|macro|
|''Requires''|N/A|
|''Overrides''|N/A|
|''Description''|Displays color palettes.|
!Notes
There is also [[ViewPalettePlugin|http://simon.tiddlyspot.com/#ViewPalettePlugin]], which currently does not work with TiddlyWiki v2.2 though.
!Usage
{{{
<<paletteView [tiddler name]>>
}}}
!!Example
<<paletteView [[ColorPalette]]>>
!Revision History
!!v0.1 (2007-11-18)
* initial release
!!v0.2 (2007-11-20)
* limited processing to slices containing [[actual color values|http://www.w3.org/TR/CSS21/syndata.html#color-units]]
* changed fallback value to the tiddler the macro is called from (instead of using [[ColorPalette]])
!To Do
* selection list for all available palettes (tag-based)
* parameter for custom table class
* customizable column order
* documentation (e.g. using from within [[ViewTemplate]])
!Code
***/
//{{{
config.macros.paletteView = {};

config.macros.paletteView.handler = function(place, macroName, params, wikifier, paramString, tiddler) {
	var title = params[0] || tiddler.title;
	//var palettes = store.getTaggedTiddlers(params[0]); // DEBUG: yet to be implemented
	var colors = store.calcAllSlices(title);
	var labels = [];
	for(var c in colors) {
		if(this.isColor(colors[c])) {
			labels.push(c);
		}
	}
	if(labels.length > 0) {
		var output = "|!Sample|!Value|!Name|h\n";
		for(var i = 0; i < labels.length; i++) {
			output += "|padding:0 4em;background-color:" + colors[labels[i]] + ";&nbsp;|"
				+ "{{{" + colors[labels[i]] + "}}}|"
				+ "[[" + labels[i] + "|" + title + "]]|\n";
		}
		wikify(output, place);
	}
};

config.macros.paletteView.isColor = function(s) {
	var colors = ["Black", "Green", "Silver", "Lime", "Gray", "Olive", "White", "Yellow",
		"Maroon", "Navy", "Red", "Blue", "Purple", "Teal", "Fuchsia", "Aqua", "Orange"];
	var match = s.match(/^#[0-9A-F]{3}$|^#[0-9A-F]{6}$|^RGB\([\d,\s]{5,}\)$/i);
	if(match) return true;
	if(colors.contains(s)) return true;
	return false;
};
//}}}
/***
|Name|[[PanelManagerPlugin]]|
|Source|http://www.TiddlyTools.com/#PanelManagerPlugin|
|Documentation|http://www.TiddlyTools.com/#PanelManagerPlugin|
|Version|1.0.1|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires|MoveablePanelPlugin|
|Overrides||
|Description|Add-on for [[MoveablePanelPlugin]]: Panel Manager Menu, Control Panel, and Map Viewer |
Track position/size of moveable panels using named //panel maps//.  Interactive graphical map viewer provides "bird's eye" view of entire document for quick navigation between panels and management of panel layouts.
!!!//''PLEASE NOTE: this is an experimental addition to MoveablePanelPlugin.  It is currently a __BETA release for testing and review purposes only__, and is subject to change without notice or regard for backward-compatibility with this or other versions of this plugin.  Do not rely on this release for production-ready purposes!''//
!!!!!Documentation
<<<
see [[PanelManagerPluginInfo]] (pending)
{{{
<<moveablePanel menu label:... prompt:...>>
<<moveablePanel menu label:... prompt:... name:...>>
<<moveablePanel maps label:... prompt:...>>
<<moveablePanel load label:... prompt:... name:...>>
<<moveablePanel viewer size:... >>
<<moveablePanel table>>
<<moveablePanel commands>>
}}}
*''menu''<br>instead of adding the mouse handling to the containing panel, the macro will render just the Panel Manager menu button.  This allows you to embed the button anywhere in your document (e.g., in the main menu or sidebar) to provide a fixed location for always accessing the current panel layout.  When ''menu'' is specified, you can use ''label:...'' and ''prompt:...'' to override the default button text (&#x2261;) and tooltip to suit your purposes.  If you provide a ''name:...'' parameter along with ''menu'', then only the section of the Panel Manager menu that applies to that named panel will be included in the resulting menu (to control a single, specific panel).
*''maps''<br>embeds a popup list of all panel maps stored in the document, permitting you to quickly switch between panel maps just by selecting a map form the popup list.
*''load''<br>embeds a command link that loads the panel map specified by the ''name:...'' parameter.
*''viewer''<br>embeds a graphical, interactive panel map viewer and page navigator in your tiddler content.  You can specify the maximum width and height of the embedded viewer using the ''size:...'' parameter with CSS units of measure (e.g., px, em, cm, in, %).  If the size is not specified, the default is for the viewer to fit the element in which it rendered (i.e., using the 'auto' or '100%' CSS value).  The ''viewer'' display is updated //live// as panels are docked/undocked, moved, size, folded, etc.
*''table''<br>embeds a panel map data table viewer in your tiddler content.  This table shows the x, y, w, h, and z, values associated with each panel stored in the current map.  As with the ''viewer'', the ''table'' data is automatically updated when panels are changed.
*''commands''<br>embeds the panel map management commands (i.e., ''new'', ''load'', ''edit'', ''save'', and ''view table...'').

// more documentation pending... //
<<<
!!!!!Open issues
<<<
Known problems:
* IE: Popups appear as a vertical line when X > window width (i.e., the core assumes left side of page)... maybe a CSS clipping issue?
* IE: 'zoomed in' mapsize calculation is way off.  These equations need to be re-examined for all browsers.
Additional features (for later):
* Track hover/docked states (in addition to x,y,w,h,z,folded)
* Drag outline in map to scroll page
* Option to normalize z-range when saving maps
<<<
!!!!!Configuration
<<<
<<option chkPanelManagerUseCookies>> remember panel maps between sessions (enables cookies)
<<option chkMoveablePanelShowStatus>> show position/size while moving/resizing a panel
<<option chkMoveablePanelShowManager>> add Panel Manager button to all undocked panels
<<option chkPanelManagerAutoMap>> automatically show map viewer as soon as popup menu is opened
<<option chkPanelManagerMapFullPage>> show full page (zoom out) in map viewer (no scrollbars)
Popup map viewer display size (maximum width and height): {{fourchar{<<option txtPanelManagerPopupMapSize>>}}}
^^//(use CSS dimensions, leave blank or use 'auto' to fit to container)//^^
<<<
!!!!!Examples
<<<
popup menu:
>{{{<<moveablePanel menu label:panels>>}}}
><<moveablePanel menu label:panels>>
map viewer control panel
>{{{<<moveablePanel commands>>}}}
><<moveablePanel commands>>
map viewer display
>{{{<<moveablePanel viewer size:400px>>}}}
>{{groupbox floatleft center{<<moveablePanel viewer size:400px>>}}}{{clear block{}}}
<<<
!!!!!Revisions
<<<
2008.12.15 [1.0.1] handling for 'hovered' elements: adjust for fixed vs. absolute (no relative offsets, no scroll offsets), translate movements to top-left screen, restrict movements within screen bounds
2008.11.26 [1.0.0] initial release - use with [[MoveablePanelPlugin]] v3.0.0 or above
|please see [[MoveablePanelPluginInfo]] for additional information|
<<<
!!!!!Code
***/
//{{{
version.extensions.PanelManagerPlugin= {major: 1, minor: 0, revision: 1, date: new Date(2008,12,15)};
//}}}
// // defaults for options
//{{{
if (config.options.txtMoveablePanelMapName===undefined)
	config.options.txtMoveablePanelMapName='DefaultMap';
if (config.options.chkMoveablePanelShowStatus===undefined)
	config.options.chkMoveablePanelShowStatus=true;
if (config.options.chkMoveablePanelShowManager===undefined)
	config.options.chkMoveablePanelShowManager=true;
if (config.options.chkPanelManagerAutoMap===undefined)
	config.options.chkPanelManagerAutoMap=true;
if (config.options.chkPanelManagerMapFullPage===undefined)
	config.options.chkPanelManagerMapFullPage=true;
if (config.options.txtPanelManagerPopupMapSize===undefined)
	config.options.txtPanelManagerPopupMapSize='auto';
if (config.options.chkPanelManagerUseCookies===undefined)
	config.options.chkPanelManagerUseCookies=true;
//}}}
// // shadow tiddlers (for displaying interfaces inside sliders, tabs, etc)
//{{{
config.shadowTiddlers.PanelViewer='<<moveablePanel viewer>>';
config.shadowTiddlers.PanelTable='<<moveablePanel table>>';
config.shadowTiddlers.PanelCommands='<<moveablePanel commands>>';
//}}}
// // translate
//{{{
// TRANSLATORS: copy this section to PanelManagerPluginLingoXX
if (config.macros.moveablePanel===undefined) config.macros.moveablePanel={};
if (config.macros.moveablePanel.manager===undefined) config.macros.moveablePanel.manager={};
merge(config.macros.moveablePanel.manager,{

	buttonLabel:	'\u2261', // equiv
	buttonTip:	'Panel Manager',

	panelCmd:	"panel: '%0'\xa0",
	jumpToPanelCmd:	'jump to panel',
	jumpToPanelTip:	"bring '%0' into view",
	frontCmd:	'bring to front',
	frontTip:	"bring '%0' to front of stack",
	backCmd:	'send to back',
	backTip:	"send '%0' to back of stack",
	stackCmd:	'return to stack',
	stackTip:	"return '%0' to it's default stack order (zIndex)",
	moveCmd:	'move panel',
	moveTip:	"move '%0' to another location on the page",
	foldCmd:	'fold panel',
	foldTip:	"reduce the height of '%0'",
	unfoldCmd:	'unfold panel',
	unfoldTip:	"restore the height of '%0'",
	hoverCmd:	'hover panel',
	hoverTip:	"keep '%0' in view when scrolling",
	scrollCmd:	'scroll panel',
	scrollTip:	"allow '%0' to move with page",
	dockCmd:	'dock panel',
	dockTip:	"attach '%0' to it's default anchor point",
	undockCmd:	'undock panel',
	undockTip:	"detach '%0' from it's default anchor point",
	closeCmd:	'close panel',
	closeTip:	"hide/close '%0'",
	openCmd:	'open panel',
	openTip:	"show/open '%0'",
	resetCmd:	'reset panel',
	resetTip:	"return '%0' to it's starting size/position for this session",

	tiddlerCmd:	"tiddler: '%0'",
	tiddlerDirtyMsg:"'%0' is currently being edited. Unsaved changes will be discarded.",

	selectPanelCmd:	'panels...',
	selectPanelTip:	'select and navigate to other panels',
	selectPanelMsg:	'select a panel:',

	selectMapCmd:	'maps...',
	selectMapTip:	'Select a stored panel layout',
	selectMapMsg:	'select a map:',

	viewMapCmd:	"map: '%0'\xa0",
	viewMapTip:	'view, load, edit and save panel layouts',
	viewMapHeader:	"__//current map:// %0 %1__\n",
	viewMapEmpty:	'| there are currently no //undocked// panels |>|>|>|>|>|',
	viewMapUnsaved:	'(unsaved)',
	newMapCmd:	'new',
	newMapTip:	"Dock all panels and start a new map",
	newMapPrompt:	'Create a new panel map:',
	newMapName:	'NewMap',
	newMapErr:	"A panel map named '%0' already exists.  Unsaved changes in '%0' will be discarded.",
	loadMapCmd:	'load',
	loadThisMapTip:	"Apply the panel layout from '%0'",
	switchMapMsg:	"Now using panel map: '%0'",
	editMapCmd:	"edit",
	editMapTip:	'Edit the stored panel layout',
	saveMapCmd:	'save',
	saveMapTip:	'Save the current panel layout',
	saveMapPrompt:	'Save the current panel map to a tiddler:',
	saveMapMsg:	"Panel layout saved to '%0'",
	unsavedMapErr:	"Unsaved changes to the current panel map, '%0', will be discarded.",

	optionsCmd:	'options...',
	optionsTip:	'set MoveablePanel options',
	useCookiesCmd:	'remember panel maps between sessions\xa0',
	useCookiesTip:	'remember panel maps between sessions (uses cookies)',
	showManagerCmd:	'add PanelManager button to all panels\xa0',
	showManagerTip:	'add PanelManager button to all panels',
	autoMapCmd:	'show map viewer when popup menu is opened\xa0',
	autoMapTip:	'show map viewer when popup menu is opened',
	showStatusCmd:	'show panel info while moving/sizing\xa0',
	showStatusTip:	'show panel info while moving/sizing',
	mapFullPageCmd:	'zoom out (fullpage)',
	mapFullPageTip:	'view the entire panel map scaled to fit\xa0',
	mapScrollPageCmd:'zoom in (scroll)',
	mapScrollPageTip:'view a portion of the panel map with scrolling',
	mapSizeCmd:	'viewer size:\xa0',
	mapSizeTip:	'set the map viewer display (use CSS measurements: px, em, in, cm, %)',

	dockAllCmd:	'dock all panels',
	dockAllTip:	'Return all panels to their default anchor points',
	resetAllCmd:	'reset all panels',
	resetAllTip:	'Reset all panels to their starting size/position for this session',

	noPid:		'unnamed panel',
	noPanels:	'\xa0no active panels\xa0',
	notAPanel:	"\xa0has not been displayed yet\xa0",
	noMaps:		'\xa0no saved maps\xa0',
	thisPanel:	'this panel',
	notMoveableMsg:	"'%0' is not a moveable panel",
	viewerMapStatsMsg:
		 "| document size:&nbsp;|''%0 x %1'' |\n"
		+"| window size:&nbsp;|''%2 x %3'' |\n"
		+"| window view:&nbsp;|''(%4-%5) x (%6-%7)'' |\n",
	viewerTableCmd:	'show table...',
	viewerTableTip:	'show/hide current map data table',
	viewerBackgroundTip:'click for display options...',
	refreshMapCmd:	'refresh viewer',
	refreshMapTip:	'redraw map viewer display image',

	viewerMapTip:	'click to scroll...',
	XYJumpCmd:	'scroll window to:',
	XYJumpTip:	'scroll to %0(%1,%2)',
	XYMoveCmd:	"move '%0' to:",
	XYMoveTip:	'move panel to %0(%1,%2)',
	jumpHereCmd:	'scroll here (%0,%1)\xa0',
	moveHereCmd:	'move here (%0,%1)\xa0',
	compassJumpCmd:	'or, scroll to:',
	compassMoveCmd:	'or, move to:',
	centerJumpCmd:	'center on panel',
	centerJumpTip:	'view panel in center of window ',
	centerMoveCmd:	'center in view',
	centerMoveTip:	'center of current window view ',
	compassTL:	'\u25E4', compassT: '\u25B2', compassTR: '\u25E5',
	compassL:	'\u25C4', compassC: '\u25CA', compassR:  '\u25BA',
	compassBL:	'\u25E3', compassB: '\u25BC', compassBR: '\u25E2',
	compassTLTip:	'top left corner of page ',
   	compassTTip:	'top edge of page ',
	compassTRTip:	'top right corner of page ',
	compassLTip:	'left edge of page ',
	compassCTip:	'center of page ',
	compassRTip:	'right edge of page ',
	compassBLTip:	'bottom left corner of page ',
	compassBTip:	'bottom edge of page ',
	compassBRTip:	'bottom right corner of page ',

	mapTags:	['panelmap'], // default tags - 1st tag used to find panelmaps - can be customized
	mapTag:		'panelmap', // fallback default - DO NOT CHANGE
	mapHeader:	'| %0!panelname|   !x |   !y |   !w |   !h |   !z | !fold | !hover |h', // CHANGE HEADINGS ONLY
	mapFormat:	'| %0| %1| %2| %3| %4| %5|   %6   |    %7   |', // DO NOT CHANGE
	checkmark:	'\u221A', // DO NOT CHANGE

	// DO NOT TRANSLATE PARAMETERS (BREAKS PORTABILITY OF CONTENT ACROSS DOCUMENTS)
	nameParam:	'name',
	menuParam:	'menu',
	mapsParam:	'maps',
	labelParam:	'label',
	promptParam:	'prompt',
	commandsParam:	'commands',
	viewerParam:	'viewer',
	tableParam:	'table',
	sizeParam:	'size',
	loadParam:	'load'
});
//}}}
// // general utilities (global)
//{{{
// if removeCookie() function is not defined by TW core, define it here (for <TW2.5)
if (window.removeCookie===undefined) {
	window.removeCookie=function(name) {
		document.cookie = name+'=; expires=Thu, 01-Jan-1970 00:00:01 UTC; path=/;'; 
	}
}
if (window.copyObject===undefined) {
	window.copyObject=function(src)	{
		for (var i in src) this[i]=typeof src[i]!='object'?src[i]:new copyObject(src[i]);
	}
}
if (window.compareObjects===undefined) {
	window.compareObjects=function(a,b) {
		if (a===b) return true;
		if (a==undefined||b==undefined) return false;
		for (var i in a) if (typeof a[i]!='object'?a[i]!==b[i]:!compareObjects(a[i],b[i])) return false;
		return true;
	}
}
if (window.isEmptyObject===undefined) {
	window.isEmptyObject=function(src) { for (var i in src) return false; return true; }
}

// cross-browser metrics
window.findMouseX=function(ev)
	{ if (!ev) return 0; return !config.browser.isIE?ev.pageX:(ev.clientX+findScrollX()); }
window.findMouseY=function(ev)
	{ if (!ev) return 0; return !config.browser.isIE?ev.pageY:(ev.clientY+findScrollY()); }
// NOTE: WEBKIT uses document.width/height, MOZ uses the 'documentElement.scrollWidth/Height'
window.findDocumentWidth=function()
	{ var dw=document.documentElement.scrollWidth; if (document.width>dw) dw=document.width; return dw; }
window.findDocumentHeight=function()
	{ var dh=document.documentElement.scrollHeight; if (document.height>dh) dh=document.height; return dh; }

// abbreviations for adding menu elements
window.addLI=function(place)
	{return createTiddlyElement(place,'li');};
window.addBR=function(place)
	{return createTiddlyElement(place,'br');};
window.addHR=function(place)
	{return createTiddlyElement(createTiddlyElement(place,'li',null,'listBreak'),'div');};
window.addSEP=function(place)
	{return createTiddlyText(place,'\xa0|\xa0');};
window.addTXT=function(place,txt)
	{return createTiddlyText(addLI(place),txt)};
window.addBTN=function(place,label,tip,fn)
	{return createTiddlyButton(place,label,tip,fn,'button')};
window.addCMD=function(place,label,tip,fn)
	{return createTiddlyButton(addLI(place),label,tip,fn,'button')};
window.addPOP=function(place,className)
	{return Popup.create(place,null,'popup '+className)};
window.addCHK=function(place,label,tip,opt,hidechk) { // option checkbox AND text toggle config.options[chk...]
	if (!hidechk) config.macros.option.genericCreate(place,'chk',opt,null,'no');
	var b=addBTN(place,label,tip,function(ev){
		var ev=ev||window.event; var cmm=config.macros.moveablePanel;
		config.options[this.opt]=!config.options[this.opt];
		config.macros.option.propagateOption(this.opt,'checked',config.options[this.opt],'input');
		saveOptionCookie(this.opt); cmm.manager.notify('option:'+this.opt);
		Popup.remove(Popup.find(this)); return cmm.processed(ev);
	}); b.opt=opt; b.innerHTML=label;
};

// open popup at current mouse position
Popup.showHere=function(place,ev) {
	var x=findMouseX(ev)-findPosX(place);
	var y=findMouseY(ev)-findPosY(place);
	Popup.show('top','left',{x:x,y:y});
}
//}}}
// // macro
//{{{
if (config.macros.moveablePanel===undefined) config.macros.moveablePanel={};
if (config.macros.moveablePanel.manager===undefined) config.macros.moveablePanel.manager={};
merge(config.macros.moveablePanel.manager,{
	handler: function(place,macroName,params,wikifier,paramString,tiddler) {

		var showmenu	=params.contains(this.menuParam);
		var showcommands=params.contains(this.commandsParam);
		var showtable	=params.contains(this.tableParam);
		var showviewer	=params.contains(this.viewerParam);
		var showmaps	=params.contains(this.mapsParam);

		params=paramString.parseParams('anon',null,true,false,false);
		var load	=getParam(params,this.loadParam,null);
		var name	=getParam(params,this.nameParam,null);
		var label	=getParam(params,this.labelParam,null);
		var prompt	=getParam(params,this.promptParam,null);
		var size	=getParam(params,this.sizeParam,null);

		if (load) addBTN(place,label||load,prompt||this.loadThisMapTip.format([load]),function(ev){
			config.macros.moveablePanel.manager.loadMap(this.map,ev)}).map=load;
		if (showmenu) 	  this.menu(place,name,label||this.buttonLabel,prompt||this.buttonTip);
		if (showcommands) this.viewer_commands(createTiddlyElement(place,'div'));
		if (showtable)	  this.viewer_table(createTiddlyElement(place,'div'));
		if (showviewer)	  this.viewer_map(createTiddlyElement(place,'div'),false,size);
		if (showmaps)	  this.menu_loadMap(place,label||this.selectMapCmd,prompt||this.selectMapTip,'bottom','left');

		return load||showmenu||showcommands||showtable||showviewer||showmaps; // handled==TRUE
	},
//}}}
// // notifications
//{{{
	notify: function(p) { // p=panel that was changed (or a text message if refresh/reload event)
		if (config.macros.moveablePanel.quiet) return;
		// for now, just a general refresh of all currently display viewers
		this.refreshAllViewers(p);
	},
//}}}
// // panel maps
//{{{
	map: undefined,
	startingMap: undefined,
	trackMap: function(p) {
		if (!p||!p.pid||!p.pid.length) return;
		this.readMap(config.options.txtMoveablePanelMapName);
		var re=/(\.[0-9]*px)|px/g; // removes decimals and 'px' from CSS
		if (!hasClass(p,'undocked'))
			delete this.map[p.pid];
		else this.map[p.pid]={ pid:p.pid, 
			x:p.style.left.replace(re,''),  y:p.style.top.replace(re,''),
			w:p.style.width.replace(re,''),	h:p.style.height.replace(re,''),
			z:p.style.zIndex, folded:hasClass(p,'folded'), hover:hasClass(p,'hover')  };
		this.setMapCookie(config.options.txtMoveablePanelMapName);
		this.notify(p);
	},
	applyMap: function(p) {
		var cmm=config.macros.moveablePanel;
		if (!p||!p.pid||!p.pid.length) return;
		this.readMap(config.options.txtMoveablePanelMapName);
		var d=this.map[p.pid]; if (!d) return; // panel is not mapped... do nothing
		if (!cmm.isStackable(p)) p.style.position='absolute';
		addClass(p,'undocked');
		if (d.folded) addClass(p,'folded'); else removeClass(p,'folded');
		if (d.hover)  addClass(p,'hover');  else removeClass(p,'hover');
		function addPX(v) { return v&&v.length?v+(!isNaN(v)?'px':''):''; }
		p.style.left  =addPX(d.x); p.style.top   =addPX(d.y);
		p.style.width =addPX(d.w); p.style.height=addPX(d.h);
		p.style.zIndex=d.z&&d.z.length?d.z:'';
		this.notify(p);
	},
	formatMap: function(includeHeading) {
		var cmm=config.macros.moveablePanel;
		function pad(t,maxlen) {
			var spaces='                                                  '; // 50 spaces
			return t.toString().length>=maxlen?'':spaces.substr(0,maxlen-t.toString().length);
		}
		var panels=cmm.getAllPanels(true); // sorted by zIndex
		var maxlen=0; for (var i=0; i<panels.length; i++)
			if (panels[i].pid && panels[i].pid.length>maxlen) maxlen=panels[i].pid.length;
		var panelHeader=this.mapHeader.split('|')[1].trim().format(['']);
		if (maxlen<panelHeader.length) maxlen=panelHeader.length;
		var out=[]; 
		if (includeHeading) out.push(this.mapHeader.format([pad(panelHeader,maxlen)]));
		for (var i=0; i<panels.length; i++) {
			var pid=panels[i].pid; var d=this.map[pid]; if (!d) continue;
			out.push(this.mapFormat.format([pad(pid,maxlen)+pid,
				pad(d.x,5)+d.x, pad(d.y,5)+d.y,	pad(d.w,5)+d.w, pad(d.h,5)+d.h,
				pad(d.z,5)+d.z, d.folded?this.checkmark:' ', d.hover?this.checkmark:' ' ]));
		}
		return out.join('\n');
	},
	setMapCookie: function(map) {
		if (!config.options.chkPanelManagerUseCookies) return;
		var opt='txt'+map;
		config.options[opt]=this.formatMap();
		if (config.options[opt].length) saveOptionCookie(opt); else removeCookie(opt);
	},
	readMap: function(map,force) { // get map from tiddler+cookie (cookie takes precedence)
		if (this.map && !force) return; // CACHED or LOAD ON DEMAND
		delete this.map; this.map=new Object();
		var t=store.getTiddlerText(map);
		if (config.options.chkPanelManagerUseCookies) var c=config.options['txt'+map];
		var m=(t||'')+(t&&c?'\n':'')+(c||'');
		if (!m||!m.length) return false; // NO MAP
		var items=m.split('\n');
		for (var i=0; i<items.length; i++) {
			// skip non-data table rows (|h, |c, or |k syntax)
			if (items[i].substr(items[i].length-1,1)!='|') continue;
			var d=items[i].split('|');
			for (var j=0;j<d.length;j++) d[j]=d[j]?d[j].trim():'';
			if (d[1]&&d[1].length) { 
				var m=this.map[d[1]]=new Object();
				m.pid=d[1]; m.x=d[2]; m.y=d[3]; m.w=d[4]; m.h=d[5]; m.z=d[6];
				m.folded=(d[7]&&d[7].length>0); m.hover=(d[8]&&d[8].length>0);
			}
		}
		if (!force) this.startingMap=new copyObject(this.map); // DEEP COPY TO CACHE
	},
	writeMap: function(map) {
		this.readMap(map);
		var t=store.getTiddler(map);
		var who=t&&config.options.chkForceMinorUpdate?t.modifier:config.options.txtUserName;
		var when=t&&config.options.chkForceMinorUpdate?t.modified:new Date();
		var tags=t?t.tags:this.mapTags; tags.pushUnique(this.mapTags[0]||this.mapTag);
		var fields=t?t.fields:{};
		store.saveTiddler(map,map,this.formatMap(true),who,when,tags,fields);
		story.refreshTiddler(map,null,true);
	},
	newMap: function(ev) { // clear map and docked all panels
		var cmm=config.macros.moveablePanel;
		var map=config.options.txtMoveablePanelMapName;
		var newname=prompt(this.newMapPrompt,this.newMapName);
		while (newname && newname.trim().length && newname!=map && newname!=this.newMapName
			&& (config.options['txt'+newname]||store.tiddlerExists(newname)) ) {
			if (confirm(this.newMapErr.format([newname]))) break;  // CANCELLED
			newname=prompt(this.newMapPrompt,newname);
		}
		if (!newname || !newname.trim().length) return true; // CANCELLED
		if (this.isMapChanged(map)&&!confirm(this.unsavedMapErr.format([map]))) return true;
		delete this.map; this.map=new Object();
		config.options['txt'+newname]=''; removeCookie('txt'+newname); // flush new map cookie (if any)
		var panels=cmm.getAllPanels();
		cmm.quiet++; for (var i=0; i<panels.length; i++) cmm.restorePanel(panels[i]); cmm.quiet--;
		config.options.txtMoveablePanelMapName=newname;
		saveOptionCookie('txtMoveablePanelMapName');
		this.notify('new map');
		return cmm.processed(ev);
	},
	loadMap: function(map,ev) { // *adds* entries to existing map data
		var cmm=config.macros.moveablePanel;
		var currmap=config.options.txtMoveablePanelMapName;
		if (this.isMapChanged(currmap)&&!confirm(this.unsavedMapErr.format([currmap]))) return true;
		config.options['txt'+map]=''; removeCookie('txt'+map);
		this.readMap(map,true);	// FORCE RELOAD
		cmm.quiet++;
		var panels=cmm.getAllPanels();
		for (var i=0; i<panels.length; i++) {
			if (hasClass(panels[i],'undocked')) cmm.restorePanel(panels[i]);
			this.applyMap(panels[i]);
		}
		cmm.quiet--;
		config.options.txtMoveablePanelMapName=map;
		saveOptionCookie('txtMoveablePanelMapName')
		this.setMapCookie(map);
		this.notify('load map');
		return cmm.processed(ev);
	},
	saveMap: function(map,ev) {
		var cmm=config.macros.moveablePanel;
		var map=prompt(this.saveMapPrompt,map);
		while (map && map.trim().length && store.tiddlerExists(map)) {
			var msg=story.isDirty(map)?this.tiddlerDirtyMsg:config.messages.overwriteWarning;
			if (confirm(msg.format([map]))) break;  // CANCELLED
			map=prompt(this.saveMapPrompt,map);
		}
		if (!map || !map.trim().length) return true; // CANCELLED
		if (story.isDirty(map)) { story.closeTiddler(map); story.displayTiddler(null,map); }
		this.writeMap(map);
		displayMessage(this.saveMapMsg.format([map]));
		config.options.txtMoveablePanelMapName=map; saveOptionCookie('txtMoveablePanelMapName');
		return cmm.processed(ev);
	},
	isPanelMapped: function(pid) { // is panel ID in the map?
		return this.map && this.map[pid];
	},
	isPanelChanged: function(p) { // compare current and starting map values
		var now=this.map?this.map[p.pid]:undefined;
		var then=this.startingMap?this.startingMap[p.pid]:undefined;
		if (!now&&!then) return false;
		if (!now&&then || now&&!then) return true;
		return (now.x!=then.x || now.y!=then.y || now.w!=then.w || now.h!=then.h || now.z!=then.z);
	},
	resetPanel: function(p) { // restore panel from starting map (if any)
		var cmm=config.macros.moveablePanel;
		if (!this.startingMap || !this.startingMap[p.pid]) { cmm.dockPanel(p); return; }
		cmm.quiet++;
		if (hasClass(p,'folded')) cmm.foldPanel(p);  // un-fold
		if (hasClass(p,'hover')) cmm.hoverPanel(p);  // un-hover
		this.map[p.pid]=new copyObject(this.startingMap[p.pid]);
		this.setMapCookie(config.options.txtMoveablePanelMapName);
		cmm.quiet--;
		this.applyMap(p);
	},
	isMapChanged: function(map) { // compare with saved map or starting map
		var currMap=this.formatMap(true);
		var savedMap=store.getTiddlerText(map);
		if (isEmptyObject(this.map)&&(!savedMap||(map==this.newMapName))) return false;
		return savedMap?currMap!=savedMap:!compareObjects(this.map,this.startingMap);
	},
//}}}
// // menu button and popup
//{{{
	menu: function(place,name,label,prompt) {
		if (name) { // show only the submenu for the named panel
			var b=addBTN(place,label,prompt,function(ev){
				var ev=ev||window.event; var cmm=config.macros.moveablePanel;
				var popup=addPOP(this,'panelManagerPopup'); if (!popup) return false;
				var docX=findMouseX(ev)+findScrollX(); var docY=findMouseY(ev)+findScrollY();
				cmm.manager.menu_panel(popup,cmm.findPanel(this.pid),this.pid,Popup.find(this)+1,docX,docY);
				Popup.show(); return cmm.processed(ev);
			}); b.innerHTML=label; b.pid=name;
		} else { // show entire manager menu
			var b=addBTN(place,label,prompt,function(ev){
				return config.macros.moveablePanel.manager.popup(this,ev,null,true);
			}); b.innerHTML=label;
		}
	},
	popup: function(place,ev,pid,nopanel) {
		var ev=ev||window.event; var cmm=config.macros.moveablePanel; var mgr=cmm.manager;
		var popup=addPOP(place,'sticky panelManagerPopup'); if (!popup) return cmm.processed(ev);
		popup.onclick=function(ev) { var ev=ev||window.event; var cmm=config.macros.moveablePanel;
			var lvl=Popup.find(this); if (lvl<Popup.stack.length-1) // toggle child popups
				{ Popup.remove(lvl+1); return cmm.processed(ev); }
		}
		var panel=cmm.findPanel(pid)||cmm.getPanel(place);
		var showPanelMenu=hasClass(panel,'moveablePanel')&&!nopanel;
		mgr.menu_map(popup,config.options.chkPanelManagerAutoMap);
		if (showPanelMenu) { // FOR THIS PANEL
			var b=addCMD(popup,mgr.panelCmd.format([pid]),cmm.getPanelTooltip(panel),function(ev){
				var ev=ev||window.event; var cmm=config.macros.moveablePanel;
				var popup=addPOP(this,'panelManagerPopup'); if (!popup) return false;
				var docX=findMouseX(ev)+findScrollX(); var docY=findMouseY(ev)+findScrollY();
				cmm.manager.menu_panel(popup,panel,this.panel.pid,Popup.find(this)+1,docX,docY);
				Popup.show('top','right'); return cmm.processed(ev);
			}); b.panel=panel;
		}
		addHR(popup);
		mgr.menu_forAll(popup);
		addHR(popup);
		mgr.menu_selectMap(popup);
		mgr.menu_selectPanel(popup);
		mgr.menu_options(popup);
		addHR(popup);
		addTXT(popup,mgr.XYJumpCmd);
		mgr.menu_compass(popup,showPanelMenu?panel:null,findMouseX(ev),findMouseY(ev)); // scroll
		Popup.showHere(place,ev)
		return cmm.processed(ev);
	},
//}}}
// // manager menu
//{{{
	menu_panel: function(place,p,pid,remove,x,y) {
		var cmm=config.macros.moveablePanel;
		// commands FOR ONE PANEL
		// p=panel, pid=requested panel ID, remove=popup level to close afterwards
		if (!p){addTXT(place,this.panelCmd.format([pid]));addTXT(place,this.notAPanel.format([pid]));return;}
		function cmd(place,label,tip,callback,p,arg) { // buttons invoke 'callback(p,arg)'
			var b=addCMD(place,label,tip,function(ev){
				var ev=ev||window.event; var cmm=config.macros.moveablePanel;
				this.callback.apply(cmm,[this.panel,this.arg]);
				cmm.manager.trackMap(this.panel);
				cmm.manager.refreshAllViewers();
				Popup.remove(this.remove);
				return cmm.processed(ev);
			}); b.panel=p; b.callback=callback; b.arg=arg; b.remove=remove;
		}
		var pid=p.pid||this.thisPanel;
		var u=hasClass(p,'undocked');
		var f=hasClass(p,'floatingPanel');
		var folded=hasClass(p,'folded');
		var hover=hasClass(p,'hover');
		var v=p.style.display!='none'; 
		var here=story.findContainingTiddler(p);
		var t=here&&cmm.findPanel(here.getAttribute('tiddler'));
		cmd(place,this.jumpToPanelCmd,this.jumpToPanelTip.format([pid]),cmm.ensurePanelVisible, p);
		if (u) cmd(place,this.frontCmd,this.frontTip.format([pid]),cmm.bringPanelToFront, p);
		if (u) cmd(place,this.backCmd, this.backTip.format( [pid]),cmm.sendPanelToBack,   p);
		if (u) cmd(place,this.stackCmd,this.stackTip.format([pid]),cmm.returnPanelToStack,p);
		if (p.showfold && (u||f)) {
			if (!folded) cmd(place,this.foldCmd,  this.foldTip.format(  [pid]),cmm.foldPanel, p);
			if ( folded) cmd(place,this.unfoldCmd,this.unfoldTip.format([pid]),cmm.foldPanel, p);
		}
		if (p.showhover && (u||f)) {
			if (!hover) cmd(place,this.hoverCmd, this.hoverTip.format( [pid]),cmm.hoverPanel,p);
			if ( hover) cmd(place,this.scrollCmd,this.scrollTip.format([pid]),cmm.hoverPanel,p);
		}
		if (cmm.manager.isPanelChanged(p))
			cmd(place,this.resetCmd,this.resetTip.format([pid]),cmm.resetPanel,p);
		if (t)		cmd(place,this.closeCmd,this.closeTip.format([pid]),cmm.closePanel,p);
		if (f&&v)	cmd(place,this.closeCmd,this.closeTip.format([pid]),cmm.closePanel,p);
		if (f&&!v)	cmd(place,this.openCmd, this.openTip.format( [pid]),cmm.closePanel,p);
		if (u)  cmd(place,this.dockCmd,  this.dockTip.format(  [pid]),cmm.dockPanel,  p);
		if (!u) cmd(place,this.undockCmd,this.undockTip.format([pid]),cmm.undockPanel,p,true);
		if (u||f) { // move panel
			addHR(place); addTXT(place,this.XYMoveCmd.format([pid]));
			this.menu_compass(place,p,x,y,true); // move
		}
	},
	menu_compass: function(place,p,x,y,move) { // scroll page or move panel using 'compass' buttons
		function cmd(place,label,tip,isTD,p,x,y,move) {
			var b=createTiddlyButton(isTD?createTiddlyElement(place,'TD'):addLI(place),label,tip,function(ev){
				var ev=ev||window.event; var cmm=config.macros.moveablePanel;
				if (this.move && this.p) cmm.movePanel(this.p,this.x,this.y,true,true);
				else window.scrollTo(this.x,this.y);
				cmm.manager.refreshAllViewers(); Popup.remove(Popup.find(this)); return cmm.processed(ev);
			},isTD?'panelManagerPopupCompassButton':'button'); b.p=p; b.x=x; b.y=y; b.move=move;
		}
		var ww=findWindowWidth();  var dw=findDocumentWidth();  var sx=findScrollX();
		var wh=findWindowHeight(); var dh=findDocumentHeight(); var sy=findScrollY();
		var cx=Math.floor(dw/2); var cy=Math.floor(dh/2);
		var nx=sx; var ny=sy; // assume scrolling
		move=move&&p; // only if a valid panel
		var tip=move?this.XYMoveTip:this.XYJumpTip;
		if (p) { // if panel, calc window center position for center on panel / center in view
			var px=p.offsetLeft; var py=p.offsetTop; var pw=p.offsetWidth; var ph=p.offsetHeight;
			if (move) { // adjust document width/centering to account for panel width/height
				dw-=pw+2; cx-=pw/2; var nx=px;
				dh-=ph+2; cy-=ph/2; var ny=py;
				var wcx=Math.floor(sx+ww/2-pw/2);
				var wcy=Math.floor(sy+wh/2-ph/2);
			} else {
				var offset=config.macros.moveablePanel.getPanelOffset(p); // adjust for relative elements
				var wcx=Math.max(Math.floor(px+offset.x-ww/2+pw/2),0);
				var wcy=Math.max(Math.floor(py+offset.y-wh/2+ph/2),0);
			}
		}
		var indent='\xa0\xa0';
		// PANEL
		if (p) {
			var label=move?this.centerMoveCmd:this.centerJumpCmd;
			var prompt=tip.format([move?this.centerMoveTip:this.centerJumpTip,wcx,wcy]);
			cmd(place,indent+label,prompt,false,p,wcx,wcy,move);
		}
		// HERE
		var label=move?this.moveHereCmd:this.jumpHereCmd;
		cmd(place,indent+label.format([x,y]),tip.format(['',x,y]),false,p,x,y,move);
		addTXT(place,indent+(move?this.compassMoveCmd:this.compassJumpCmd));
		// COMPASS
		var tbl=createTiddlyElement(place,'table',null,'panelManagerPopupCompass');
		var tbody=createTiddlyElement(tbl,'tbody');
		var tr=createTiddlyElement(tbody,'tr');
		cmd(tr,this.compassTL,tip.format([this.compassTLTip, 0,0]),true,p, 0,0,move);
		cmd(tr,this.compassT, tip.format([this.compassTTip ,nx,0]),true,p,nx,0,move);
		cmd(tr,this.compassTR,tip.format([this.compassTRTip,dw,0]),true,p,dw,0,move);
		var tr=createTiddlyElement(tbody,'tr');
		cmd(tr,this.compassL, tip.format([this.compassLTip, 0,ny]),true,p, 0,ny,move);
		cmd(tr,this.compassC, tip.format([this.compassCTip,cx,cy]),true,p,cx,cy,move);
		cmd(tr,this.compassR, tip.format([this.compassRTip,dw,ny]),true,p,dw,ny,move);
		var tr=createTiddlyElement(tbody,'tr');
		cmd(tr,this.compassBL,tip.format([this.compassBLTip, 0,dh]),true,p, 0,dh,move);
		cmd(tr,this.compassB, tip.format([this.compassBTip ,nx,dh]),true,p,nx,dh,move);
		cmd(tr,this.compassBR,tip.format([this.compassBRTip,dw,dh]),true,p,dw,dh,move);
	},
	menu_map: function(place,autoclick) {
		var map=config.options.txtMoveablePanelMapName;
		var b=addCMD(place,this.viewMapCmd.format([map]),this.viewMapTip,function(ev){
			var ev=ev||window.event; var cmm=config.macros.moveablePanel;
			var popup=addPOP(this,'sticky panelManagerMapPopup'); if (!popup) return false;
			cmm.manager.viewer_commands(popup);
			addHR(popup);
			cmm.manager.viewer_map(popup);
			popup.onclick=function(ev) {
				var ev=ev||window.event; var cmm=config.macros.moveablePanel;
				var lvl=Popup.find(this); if (lvl<Popup.stack.length-1) // toggle child popup
					{ Popup.remove(lvl+1); return cmm.processed(ev); }
				var popup=addPOP(this,'sticky panelManagerPopup'); if(!popup)return false;
				cmm.manager.menu_mapBackground(popup);
				Popup.showHere(this,ev); return cmm.processed(ev);
			}
			popup.title=cmm.manager.viewerBackgroundTip;
			Popup.show('top','right');
			return cmm.processed(ev);
		});
		// autoclick on initial mouseover
		if (autoclick) b.onmouseover=function(ev) { this.onmouseover=null; return this.onclick.apply(this,arguments); };
	},
	menu_forAll: function(place) {
		var cmm=config.macros.moveablePanel;
		// commands FOR ALL PANELS
		function cmd(label,tip,callback) {
			var b=addCMD(place,label,tip,function(ev){
				var ev=ev||window.event; var cmm=config.macros.moveablePanel;
				if (!confirm(this.title+'?')) return false;
				var panels=cmm.forAllPanels(this.callback);
				cmm.manager.refreshAllViewers();
				Popup.remove(Popup.find(this)); return cmm.processed(ev);
			}); b.callback=callback;
		};
		cmd(this.resetAllCmd,this.resetAllTip,cmm.resetPanel);
		cmd(this.dockAllCmd,this.dockAllTip,cmm.dockPanel);
	},
	menu_selectPanel: function(place){
		// LIST OF PANELS with PANEL SUBMENUS
		addCMD(place,this.selectPanelCmd,this.selectPanelTip,function(ev){
			var ev=ev||window.event; var cmm=config.macros.moveablePanel;
			var popup=addPOP(this,'panelManagerPopup'); if (!popup) return false;
			var panels=cmm.getAllPanels();
			addTXT(popup,panels.length?cmm.manager.selectPanelMsg:cmm.manager.noPanels);
			for (var i=0; i<panels.length; i++) { var p=panels[i];
				var b=addCMD(popup,p.pid||cmm.manager.noPid,cmm.getPanelTooltip(p),function(ev){
					var ev=ev||window.event; var cmm=config.macros.moveablePanel;
					var popup=addPOP(this,'panelManagerPopup');
					if(!popup)return false;
					var docX=findMouseX(ev)+findScrollX(); var docY=findMouseY(ev)+findScrollY();
					cmm.manager.menu_panel(popup,this.p,
						this.p.pid||cmm.manager.thisPanel,Popup.find(this)+1,docX,docY);
					Popup.show('top','right'); return cmm.processed(ev);
				}); b.p=p; b.onmouseover=b.onclick; // ALWAYS autoclick on mouseover
			}
			Popup.show('top','right'); return cmm.processed(ev);
		});
	},
	menu_selectMap: function(place){
		// same as LOAD COMMAND IN VIEWER (with different label/tip and popup alignment)
		this.menu_loadMap(addLI(place),this.selectMapCmd,this.selectMapTip,'top','right');
	},
	menu_options: function(place) {
		var on='<input type="checkbox" checked>'; var off='<input type="checkbox">';
		addCMD(place,this.optionsCmd,this.optionsTip,function(ev){
			var ev=ev||window.event; var cmm=config.macros.moveablePanel; var mgr=cmm.manager;
			var popup=addPOP(this,'sticky panelManagerPopup'); if (!popup) return false;
			addCHK(popup,mgr.useCookiesCmd,mgr.useCookiesTip,'chkPanelManagerUseCookies');
			addBR(popup);
			addCHK(popup,mgr.showManagerCmd,mgr.showManagerTip,'chkMoveablePanelShowManager');
			addBR(popup);
			addCHK(popup,mgr.autoMapCmd,mgr.autoMapTip,'chkPanelManagerAutoMap');
			addBR(popup);
			addCHK(popup,mgr.showStatusCmd,mgr.showStatusTip,'chkMoveablePanelShowStatus');
			Popup.show('top','right'); return cmm.processed(ev);
		});
	},
//}}}
// // panel map viewers
//{{{
	// MAP MANAGEMENT COMMANDS
	viewer_commands: function(place,refresh) {
		if (refresh) removeChildren(place);
		else place=createTiddlyElement(place,'div',null,'panelManagerMapCommands');
		var map=config.options.txtMoveablePanelMapName;
		var unsaved=this.isMapChanged(map)?this.viewMapUnsaved:'';
		wikify(this.viewMapHeader.format([map,unsaved]),place);
				this.command_newMap(place);
		addSEP(place); 	this.menu_loadMap(place,this.loadMapCmd,this.selectMapTip);
		addSEP(place); 	this.command_editMap(place);
		addSEP(place); 	this.command_saveMap(place);
		addSEP(place); 	this.command_viewerTable(place);
	},
//}}}
//{{{
	// TABLE VIEW - ALL MAP ENTRIES
	viewer_table: function(place,refresh) {
		var cmm=config.macros.moveablePanel;
		if (refresh) removeChildren(place);
		else place=createTiddlyElement(place,"div",null,"panelManagerMapTable");
		place.onclick=function(ev){ var cmm=config.macros.moveablePanel;
			var lvl=Popup.find(this); if (lvl!=-1) Popup.remove(lvl+1);
			cmm.manager.refreshAllViewers(); return cmm.processed(ev); }
		var link='[[%0]]'; var cmd='<<moveablePanel %2 %3:[[%0]] %4:[[%0]] %5:[[%1]]>>';
		cmd=cmd.format(['%0','%1',this.menuParam,this.nameParam,this.labelParam,this.promptParam]);
		var sortByZ=function(a,b){ var v1=parseInt(a.z); var v2=parseInt(b.z); return(v1==v2)?0:(v1>v2?1:-1); }
		var map=[]; for (var pid in this.map) map.push(this.map[pid]); map=map.sort(sortByZ);
		var rows=[]; for (var i=0; i<map.length; i++) { var m=map[i];
			var isPanel=cmm.findPanel(m.pid);
			var isTiddler=store.tiddlerExists(m.pid)||store.isShadowTiddler(m.pid);
			var fmt=isPanel?cmd:(isTiddler?link:cmd);
			var lbl=fmt.format([m.pid,this.panelCmd.format([m.pid])]);
			rows.push(this.mapFormat.format([lbl,m.x,m.y,m.w,m.h,m.z,
				m.folded?this.checkmark:' ', m.hover?this.checkmark:' ']));
		}
		var table=this.mapHeader.format([''])+'\n'+rows.join('\n')+(!rows.length?this.viewMapEmpty:'');
		wikify(table,place);
	},
//}}}
//{{{
	// GRAPHICAL VIEWER - ACTIVE PANELS AND TIDDLERS
	mapXtoDocX: function(e,ev,scale,scroller) { // convert mouse click in map panel to equivalent document location
		var mouseX=findMouseX(ev);
		var mapX=findPosX(e.parentNode)-scroller.scrollLeft;
		var docX=Math.floor((mouseX-mapX)/scale)-Math.floor((mouseX-mapX)*scale);
		return docX;
	},
	mapYtoDocY: function(e,ev,scale,scroller) { // convert mouse click in map panel to equivalent document location
		var mouseY=findMouseY(ev);
		var mapY=findPosY(e.parentNode)-scroller.scrollTop;
		var docY=Math.floor((mouseY-mapY)/scale)-Math.floor((mouseY-mapY)*scale);
		return docY;
	},
	viewer_map: function(place,refresh,mapSize){
		var cmm=config.macros.moveablePanel;
		if (!refresh) {
			place=createTiddlyElement(place,'div',null,'panelManagerMapViewer');
			place.mapSize=mapSize; // save for use with refresh
		} else {
			var mapSize=place.mapSize; // refresh... use saved map size
			removeChildren(place); // NOTE: ASSUMES CONTAINER HAS NO OTHER CONTENT
		}

		// METRICS
		var dw=findDocumentWidth();  var ww=findWindowWidth();  if (dw<ww) dw=ww; var sx=findScrollX();
		var dh=findDocumentHeight(); var wh=findWindowHeight(); if (dh<wh) dh=wh; var sy=findScrollY();

		// SET MAP MAXSIZE
		var wrapper=createTiddlyElement(place,'div');
		if (Popup.find(place)!=-1) mapSize=config.options.txtPanelManagerPopupMapSize; // IF POPUP
		wrapper.style.width=mapSize||''; mapSize=wrapper.offsetWidth; // APPLY CSS THEN GET PIXELS

		// SET SCROLLING/SCALING
		var scroll=!config.options.chkPanelManagerMapFullPage;
		// default to fit entire page in viewer
		if (dw>dh) { var w=mapSize; var h=dh/dw*mapSize; var scale=w/dw; }
		else 	   { var h=mapSize; var w=dw/dh*mapSize; var scale=h/dh; }
		if (scroll) { // set smaller dimension to fixed value, scroll the other
			wrapper.style.width=mapSize+'px'; wrapper.style.height=wh/ww*mapSize+'px';  
			wrapper.style.overflow='auto'; // make it's contents scrollable
			var scrollsize=findWindowWidth()-document.body.offsetWidth+2;
			if (dw<=ww&&dh<=wh) { // smaller than window... enlarge to fit width
				w=mapSize; h=dh/dw*w; scale=w/dw;
				wrapper.style.overflow='visible'; // no scrollbars
			} else if (dw>dh) { // wide... add hScroll
				h=wh/ww*mapSize; w=dw/dh*h; scale=h/dh;
				wrapper.style.height=h+scrollsize+'px';  
			} else { // tall... add vScroll
				w=mapSize-scrollsize; h=dh/dw*w; scale=w/dw;
			}
		}

		// CREATE DOCUMENT BACKGROUND
		var doc=createTiddlyElement(wrapper,'div',null,'map');
		doc.style.width=w+'px'; doc.style.height=h+'px';
		doc.onclick=function(ev){ // BACKGROUND POPUP: SCROLL+OPTIONS
			var ev=ev||window.event; var cmm=config.macros.moveablePanel;
			var lvl=Popup.find(this); if (lvl<Popup.stack.length-1) // toggle child popup
				{ Popup.remove(lvl+1); return cmm.processed(ev); }
			var popup=addPOP(this,'sticky panelManagerPopup'); if(!popup)return false;
			var dx=cmm.manager.mapXtoDocX(this,ev,scale,this.parentNode);
			var dy=cmm.manager.mapYtoDocY(this,ev,scale,this.parentNode);
			addTXT(popup,cmm.manager.XYJumpCmd); cmm.manager.menu_compass(popup,null,dx,dy);
			addHR(popup); cmm.manager.menu_mapBackground(popup);
			Popup.showHere(this,ev); return cmm.processed(ev);
		};
		doc.scale=scale; doc.title=this.viewerMapTip; doc.style.cursor='crosshair';

		// SHOW VIEWPORT (CURRENT WINDOW POS)
		var currview=createTiddlyElement(doc,'div');
		var s=currview.style; s.border='1px dotted'; s.position='absolute';
		s.left=sx*scale+'px'; s.top=sy*scale+'px'; s.width=(ww-2)*scale+'px'; s.height=(wh-2)*scale+'px';

		// GET ALL PANELS AND FIND BASELINE Z FOR RENDERING MAP ON TOP OF POPUPS/PANELS
		var panels=cmm.getAllPanels(); var allPids=[]; var minZ=0; var viewerZ=0;
		for (var i=0; i<panels.length; i++) { var p=panels[i]; allPids.push(p.pid); 
			if (p.style.zIndex<minZ) minZ=p.style.zIndex;
		}
		if (Popup.find(place)!=-1) viewerZ=Popup.stack[Popup.find(place)].popup.style.zIndex;
		else if (cmm.getPanel(place)) viewerZ=cmm.getPanel(place).style.zIndex;
		var baseZ=viewerZ-minZ+1;

		// DRAW PANEL BOXES
		for (var i=0; i<panels.length; i++) {
			var p=panels[i];
			var d=cmm.manager.viewer_mapbox_draw(doc,p,scale,baseZ);
			d.title=cmm.getPanelTooltip(p);
		}

		// DRAW TIDDLER BOXES
		story.forEachTiddler(function(t,e){
			if (allPids.contains(t)) return; // TIDDLER IS ALSO MOVEABLE PANEL... SKIP IT
			var d=cmm.manager.viewer_mapbox_draw(doc,e,scale,baseZ);
			d.tid=t; var tiddler=store.getTiddler(t);
 			d.title=tiddler?tiddler.getSubtitle():config.macros.moveablePanel.manager.tiddlerCmd.format([t]);
		});

		// SHOW DOC/WINDOW SIZE/VIEWPORT
		var span=createTiddlyElement(place,'span',null,'panelManagerMapStats');
		var msg=this.viewerMapStatsMsg.format([dw,dh,ww,wh,sx,sx+ww,sy,sy+wh]);
		wikify(msg,span);

		// SET MAP SCROLLPOS TO MATCH PAGE SCROLLPOS
		// NOTE: must be done *after* all content has been rendered or scrollbar will jump to zero
		if (scroll) { wrapper.scrollTop=sy*scale; wrapper.scrollLeft=sx*scale; }

	},
	// draw one map box with borders, mouseover shading and drag handling for moving
	viewer_mapbox_draw: function(doc,p,scale,baseZ) {
		var x=findPosX(p); var w=p.offsetWidth; var y=findPosY(p); var h=p.offsetHeight;
		if (hasClass(p,'hover')) { x+=findScrollX(); y+=findScrollY(); } // hover=always in view
		var db=createTiddlyElement(doc,'div',null,'panelManagerViewerMapBox');
		db.panel=p; 		db.scale=scale; 	var s=db.style;
		s.border="1px solid";	s.position='absolute';	s.cursor='crosshair';	s.zIndex=baseZ+p.style.zIndex;
		s.top=y*scale+'px';	s.left=x*scale+'px'; 	s.width=w*scale+'px';	s.height=h*scale+'px';
		s.background='#eee';	s.opacity='0.6';	s.filter='alpha(opacity:60)';
		db.onmouseover=function(ev)
			{ var s=this.style; s.background='#999';s.opacity='1';s.filter='alpha(opacity:100)'; }
		db.onmouseout=function(ev)
			{ var s=this.style; s.background='#eee';s.opacity='0.5';s.filter='alpha(opacity:50)'; }
		db.onmousedown=this.viewer_mapbox_dragstart;
		db.onclick=this.viewer_mapbox_popup;
		return db;
	},
	viewer_mapbox_dragstart: function(ev) { var ev=ev||window.event; var cmm=config.macros.moveablePanel;
		// capture mouse events and set drag handlers on target (body, window, or this panel)
		var target=this; // fallback to this panel if 'capture' not supported
		if (document.body.setCapture) // IE
			{ document.body.setCapture(); var target=document.body; }
		if (window.captureEvents) // moz
			{ window.captureEvents(Event.MouseMove|Event.MouseUp,true); var target=window; }
		// save drag data in target element
		if (!target.dragData) target.dragData=new Object();
		var d=target.dragData;
		d.box=this; d.scale=this.scale;	d.map=this.parentNode; d.scroller=this.parentNode.parentNode;
		d.startX=findMouseX(ev); d.startScrollX=d.scroller.scrollLeft; d.grabX=findMouseX(ev)-findPosX(this);
		d.startY=findMouseY(ev); d.startScrollY=d.scroller.scrollTop;  d.grabY=findMouseY(ev)-findPosY(this);
		d.offset=cmm.getPanelOffset(d.box.panel);
		d.dragging=true; this.style.cursor='move';
		d.savedonmousemove=target.onmousemove;
		target.onmousemove=cmm.manager.viewer_mapbox_dragmove;
		d.savedonmouseup=target.onmouseup;
		target.onmouseup=cmm.manager.viewer_mapbox_dragstop;
		cmm.addGhost(d.box.panel); // keep document from shrinking during move/size
		cmm.noScrollX++; cmm.noScrollY++; // prevent document from scrolling during move/size
		return cmm.processed(ev);
	},
	viewer_mapbox_dragmove: function(ev) { var ev=ev||window.event; var cmm=config.macros.moveablePanel;
		var d=this.dragData; if (!d || !d.dragging) return; // NOT DRAGGING
		if (!hasClass(d.box.panel,'moveablePanel')) { // NOT MOVEABLE
			clearMessage();
			displayMessage(cmm.manager.notMoveableMsg.format([d.box.panel.pid||d.box.tid]));
			return this.onmouseup(ev);
		}
		cmm.quiet++; cmm.undockPanel(d.box.panel,true); cmm.quiet--; // GET READY TO MOVE
		var mouseX=!config.browser.isIE?ev.pageX:ev.clientX;
		var mouseY=!config.browser.isIE?ev.pageY:ev.clientY;
		var mapX=findPosX(d.map)+d.startScrollX; var mapW=d.map.offsetWidth;
		var mapY=findPosY(d.map)+d.startScrollY; var mapH=d.map.offsetHeight;
		var scrollX=d.scroller.scrollLeft;	 var scrollW=d.scroller.offsetWidth;
		var scrollY=d.scroller.scrollTop;	 var scrollH=d.scroller.offsetHeight;
		var boxW=d.box.offsetWidth;		 var boxH=d.box.offsetHeight;
		var boxX=findMouseX(ev)-mapX-d.grabX+scrollX;
		var boxY=findMouseY(ev)-mapY-d.grabY+scrollY;
		if (boxX<0) boxX=0; if (boxY<0) boxY=0; // limit upper left=stay on page
		if (hasClass(d.box.panel,'hover')) { // hover=limit bottom right (stay in screen)
			if (boxX+boxW>scrollW) boxX=scrollW-boxW; if (boxY+boxH>scrollH) boxY=scrollH-boxH;
			if (boxX<scrollX) boxX=scrollX; if (boxY<scrollY) boxY=scrollY;
		}
		var docX=Math.floor(boxX/d.scale)-d.offset.x;
		var docY=Math.floor(boxY/d.scale)-d.offset.y;
		if (hasClass(d.box.panel,'hover')) { // window-relative placement
			var ww=findWindowWidth();  var sx=findScrollX();
			var wh=findWindowHeight(); var sy=findScrollY();
			docX-=sx-d.offset.x; docY-=sy-d.offset.y;
			if (docX+d.box.panel.offsetWidth >ww) docX=ww-d.box.panel.offsetWidth;
			if (docY+d.box.panel.offsetHeight>wh) docY=wh-d.box.panel.offsetHeight;
			if (docX<0) docX=0; if (docY<0) docY=0;
		}
		// update box AND panel positions
		d.box.style.left=boxX+'px';	d.box.panel.style.left=docX+'px';
		d.box.style.top =boxY+'px';	d.box.panel.style.top =docY+'px';
		// resize map/scroll viewer as needed
		if (boxX<scrollX) d.scroller.scrollLeft=boxX;
		if (boxX+boxW>scrollX+scrollW || boxX+boxW>d.map.offsetWidth) {
			d.map.style.width=Math.max(boxX+boxW,mapW)+'px';
			d.scroller.scrollLeft=boxX+boxW-scrollW;
		}
		if (boxY<scrollY) d.scroller.scrollTop=boxY;
		if (boxY+boxH>scrollY+scrollH || boxY+boxH>d.map.offsetHeight) {
			d.map.style.height=Math.max(boxY+boxH,mapH)+'px';
			d.scroller.scrollTop=boxY+boxH-scrollH;
		}
		cmm.showPanelStatus(d.box.panel,true);
		return cmm.processed(ev);
	},
	viewer_mapbox_dragstop: function(ev) { var ev=ev||window.event; var cmm=config.macros.moveablePanel;
		var d=this.dragData; if (!d || !d.dragging) return; // NOT DRAGGING
		if (this.releaseCapture) this.releaseCapture(); // IE
		if (this.releaseEvents) this.releaseEvents(Event.MouseMove|Event.MouseUp); // moz
		this.onmousemove=d.savedonmousemove; this.onmouseup=d.savedonmouseup;
		cmm.noScrollX--; cmm.noScrollY--; // allow document to scroll
		cmm.clearGhost(); // allow document to adjust extents (if needed)
		var moved=findMouseX(ev)!=d.startX || findMouseY(ev)!=d.startY;
		if (moved) { cmm.manager.trackMap(d.box.panel); cmm.manager.refreshAllViewers(); }
		d.dragging=false; d.box.style.cursor='pointer';
		cmm.showPanelStatus(d.box.panel,false);
		cmm.timedMessage(cmm.formatPanelStatus(d.box.panel),cmm.msgDuration);
		// HACK: ignore next click to prevent webkit from closing popup after dragging
		d.box.ignoreClick=moved&&config.browser.isSafari;
		return cmm.processed(ev);
	},
	viewer_mapbox_popup: function(ev) {
		var ev=ev||window.event; var cmm=config.macros.moveablePanel; var mgr=cmm.manager;
		if (this.ignoreClick) { this.ignoreClick=false; return cmm.processed(ev); } // HACK
		var lvl=Popup.find(this); if (lvl<Popup.stack.length-1) // toggle child popup
			{ Popup.remove(lvl+1); return cmm.processed(ev); }
		var popup=addPOP(this,'sticky panelManagerPopup'); if (!popup) return false;
		var dx=cmm.manager.mapXtoDocX(this,ev,this.scale,this.parentNode.parentNode);
		var dy=cmm.manager.mapYtoDocY(this,ev,this.scale,this.parentNode.parentNode);
		if (this.tid)	cmm.manager.menu_mapTiddler(popup,this.tid,this.panel,dx,dy);
		else		cmm.manager.menu_mapPanel(popup,this.panel,dx,dy);
		Popup.showHere(this,ev); return cmm.processed(ev);
	},

//}}}
//{{{
	refreshAllViewers: function(){
		var elems=document.getElementsByTagName("DIV");
		for (var i=0; i<elems.length; i++) {
			if (hasClass(elems[i],'panelManagerMapViewer'))   this.viewer_map(elems[i],true);
			if (hasClass(elems[i],'panelManagerMapTable'))	  this.viewer_table(elems[i],true);
			if (hasClass(elems[i],'panelManagerMapCommands')) this.viewer_commands(elems[i],true);
		}
	},
//}}}
// // map viewer commands
//{{{
	menu_mapBackground: function(place) {
		var centered=createTiddlyElement(place,'div'); centered.style.textAlign='center';
		if (Popup.find(place)>0) { // POPUP VIEWER PERMITS RESIZING
			addBTN(centered,'\xa0'+this.mapSizeCmd,this.refreshMapTip,function(ev){
				var ev=ev||window.event; var cmm=config.macros.moveablePanel;
				cmm.manager.refreshAllViewers();
				Popup.remove(Popup.find(this));	return cmm.processed(ev);
			});
			wikify('{{panelManagerMapPopupEdit{<<option txtPanelManagerPopupMapSize>>}}}\xa0',centered);
		}
		var opt='chkPanelManagerMapFullPage'; // toggle label...
		var label=config.options[opt]?this.mapScrollPageCmd:this.mapFullPageCmd;
		var tip=config.options[opt]?this.mapScrollPageTip:this.mapFullPageTip;
		addCHK(addLI(centered),label,tip,opt,true);
		addHR(centered); addCMD(centered,this.refreshMapCmd,this.refreshMapTip,function(ev){
			var ev=ev||window.event; var cmm=config.macros.moveablePanel;
			cmm.manager.refreshAllViewers();
			Popup.remove(Popup.find(this));	return cmm.processed(ev);
		});
	},
	menu_mapPanel: function(place,panel,docX,docY) {
		var cmm=config.macros.moveablePanel;
		var b=addCMD(place,this.panelCmd.format([panel.pid]),cmm.getPanelTooltip(panel),function(ev){
			var ev=ev||window.event; var cmm=config.macros.moveablePanel;
			var popup=addPOP(this,'panelManagerPopup'); if (!popup) return false;
			cmm.manager.menu_panel(popup,panel,this.panel.pid||this.thisPanel,Popup.find(this)+1,docX,docY);
			Popup.show('top','right'); return cmm.processed(ev);
		}); b.panel=panel;
		// autoclick on initial mouseover
		b.onmouseover=function(ev) { this.onmouseover=null; return this.onclick.apply(this,arguments); };
		addHR(place); addTXT(place,this.XYJumpCmd); this.menu_compass(place,panel,docX,docY);
		addHR(place); this.menu_mapBackground(place);
	},
	menu_mapTiddler: function(place,tid,tiddlerElem,docX,docY) {
		var cmm=config.macros.moveablePanel;
		var b=addCMD(place,this.tiddlerCmd.format([tid]),'',function(ev){
			var ev=ev||window.event; var cmm=config.macros.moveablePanel; var mgr=cmm.manager;
			var popup=addPOP(this,'panelManagerPopup'); if (!popup) return false;
			var b=addCMD(popup,mgr.jumpToPanelCmd,mgr.jumpToPanelTip.format([tid]),function(ev){
				var ev=ev||window.event; var cmm=config.macros.moveablePanel;
				cmm.scrollToPanel(this.tiddlerElem,true); cmm.manager.refreshAllViewers();
				Popup.remove(Popup.find(this)); return cmm.processed(ev);
			}); b.tid=this.tid; b.tiddlerElem=this.tiddlerElem;
			var b=addCMD(popup,mgr.closeCmd,mgr.closeTip.format([tid]),function(ev){
				var ev=ev||window.event; var cmm=config.macros.moveablePanel;
				var OK=!story.isDirty(this.tid)||confirm(cmm.manager.tiddlerDirtyMsg.format([this.tid]));
				if (OK) { story.closeTiddler(this.tid); cmm.manager.refreshAllViewers(); }
				Popup.remove(Popup.find(this)); return cmm.processed(ev);
			}); b.tid=this.tid;
			Popup.show('top','right'); return cmm.processed(ev);
		}); b.tid=tid; b.tiddlerElem=tiddlerElem;
		// autoclick on initial mouseover
		b.onmouseover=function(ev) { this.onmouseover=null; return this.onclick.apply(this,arguments); };
		addHR(place); addTXT(place,this.XYJumpCmd); this.menu_compass(place,tiddlerElem,docX,docY);
		addHR(place); this.menu_mapBackground(place);
	},
	command_newMap: function(place){
		addBTN(place,this.newMapCmd,this.newMapTip,function(ev){
			var ev=ev||window.event; var cmm=config.macros.moveablePanel;
			if (!cmm.manager.newMap(ev)) cmm.manager.refreshAllViewers();
			return cmm.processed(ev);
		});
	},
	menu_loadMap: function(place,label,tip,valign,halign){
		addBTN(place,label,tip,function(ev){
			var ev=ev||window.event; var cmm=config.macros.moveablePanel;
			var popup=addPOP(this,'panelManagerPopup'); if (!popup) return false;
			var tids=store.getTaggedTiddlers(cmm.manager.mapTags[0]||cmm.manager.mapTag);
			addTXT(popup,tids.length?cmm.manager.selectMapMsg:cmm.manager.noMaps);
			for (var t=0;t<tids.length;t++) { var title=tids[t].title;
				var b=addCMD(popup,title,cmm.manager.loadThisMapTip.format([title]),function(ev){
					var ev=ev||window.event; var cmm=config.macros.moveablePanel;
					if (!cmm.manager.loadMap(this.map,ev)) { 
						cmm.manager.refreshAllViewers();
						displayMessage(cmm.manager.switchMapMsg.format([this.map]));
					}
					Popup.remove(Popup.find(this)); return cmm.processed(ev);
				}); b.map=title;
			}
			if (valign||halign) Popup.show(valign,halign); else Popup.showHere(this,ev);
			return cmm.processed(ev);
		});
	},
	command_editMap: function(place){
		var map=config.options.txtMoveablePanelMapName;
		addBTN(place,this.editMapCmd.format([map]),this.editMapTip,function(ev){
			var ev=ev||window.event; var cmm=config.macros.moveablePanel;
			if (!store.tiddlerExists(this.map)&&cmm.manager.saveMap(this.map,ev)) return cmm.processed(ev);
			cmm.manager.refreshAllViewers();
			story.displayTiddler(null,this.map,DEFAULT_EDIT_TEMPLATE);
			return cmm.processed(ev);
		}).map=map;
	},
	command_saveMap: function(place){
		addBTN(place,this.saveMapCmd,this.saveMapTip,function(ev){
			var ev=ev||window.event; var cmm=config.macros.moveablePanel;
			if (!cmm.manager.saveMap(this.map,ev)) cmm.manager.refreshAllViewers();
			return cmm.processed(ev);
		}).map=config.options.txtMoveablePanelMapName;
	},
	command_viewerTable: function(place){
		addBTN(place,this.viewerTableCmd,this.viewerTableTip,function(ev){
			var ev=ev||window.event; var cmm=config.macros.moveablePanel;
			var popup=addPOP(this.parentNode,'panelManagerPopup'); if (!popup) return false;
			cmm.manager.viewer_table(popup);
			Popup.showHere(place,ev); return cmm.processed(ev);
		});
	},
//}}}
// // CSS definitions
//{{{
	css: '/*{{{*/\n'
		+'.panelManagerPopup\n'
			+'\t{ white-space:nowrap; }\n'
		+'.panelManagerPopup input\n'
			+'\t{ text-align:center; font-size:90%; }\n'
		+'.panelManagerPopupCompass {\n'
			+'\tbackground:#999; margin:1em;\n'
			+'\t-moz-border-radius:.5em; -webkit-border-radius:.5em;\n'
			+'}\n'
		+'.panelManagerPopupCompass td {\n'
			+'\tfont-size:2em; width:1.5em; height:1.5em; text-align:center; vertical-align;center;\n'
			+'\tbackground:#eee !important; color:#000 !important;\n'
			+'\tborder:1px solid #666; padding:0; margin:0;\n'
			+'\t-moz-border-radius:2px; -webkit-border-radius:2px;\n'
			+'}\n'
		+'.panelManagerPopupCompass td:hover\n'
			+'\t{ background:#fff !important; color:#000 !important; }\n'
		+'.panelManagerPopupCompassButton:hover\n'
			+'\t{ background:transparent !important; color:#000; }\n'
		+'.panelManagerMapPopup\n'
			+'\t{ text-align:center; white-space:nowrap; }\n'
		+'.panelManagerMapPopupEdit input\n'
			+'\t{ width:5em; margin-top:.2em; }\n'
		+'.panelManagerMapViewer .map {\n'
			+'\tposition:relative; overflow:hidden;\n'
			+'\tcolor:#000; background-color:#fff;\n'
			+'\tmargin:0; border:1px solid;\n'
			+'\t-moz-border-radius:3px; -webkit-border-radius:3px;\n'
			+'}\n'
		+'.panelManagerViewerMapBox\n'
			+'\t{ border:1px solid; -moz-border-radius:2px; -webkit-border-radius:2px; }\n'
		+'.panelManagerMapStats\n'
			+'\t{ font-size:80%; }\n'
		+'.panelManagerMapStats .twtable, .panelManagerMapStats .twtable tr, .panelManagerMapStats .twtable td\n'
			+'\t{ padding:0; margin:0; border:0; }\n'
		+'.panelManagerMapStats .twtable\n'
			+'\t{ width:100%; }\n'
		+'.panelManagerMapStats .twtable td\n'
			+'\t{ width:50%; }\n'
		+'/*}}}*/'
});
//}}}
// // CSS initialization (during startup)
//{{{
// set up shadow stylesheet, then load styles so customized CSS (if any) will be applied
config.shadowTiddlers.PanelManagerStyles=config.macros.moveablePanel.manager.css;
var css=store.getRecursiveTiddlerText('PanelManagerStyles',config.macros.moveablePanel.manager.css,10);
setStylesheet(css,'panelManagerStyles');
//}}}
// // hijack: sticky popups (allows interaction inside popup)
// // COPIED FROM [[StickyPopupPlugin]] TO ELIMINATE PLUGIN DEPENDENCY
//{{{
if (config.options.chkStickyPopups==undefined) config.options.chkStickyPopups=false;
try{removeEvent(document,"click",Popup.onDocumentClick);}catch(e){};
try{removeEvent(document,"click",Popup.stickyPopup_onDocumentClick);}catch(e){};
Popup.stickyPopup_onDocumentClick = function(ev)
{
	// if click is in a sticky popup, ignore it so popup will remain visible
	var e = ev ? ev : window.event; var target = resolveTarget(e);
	var p=target; while (p) {
		if (hasClass(p,"popup") && (hasClass(p,"sticky")||config.options.chkStickyPopups)) break;
		else p=p.parentNode;
	}
	// if not a sticky popup... use normal handling
	if (!p) {
		// HACK: if flag is set, ignore this click (and clear the flag)
		if (Popup.ignoreClick) Popup.ignoreClick=false; 
		else Popup.onDocumentClick(ev);
	}
	return true;
};
try{addEvent(document,"click",Popup.stickyPopup_onDocumentClick);}catch(e){};
//}}}
// // hijack: page background popup menu (ALT-CLICK)
//{{{
if (!document.getElementById('panelManagerPopupRoot')) { // only once
	var root=createTiddlyElement(document.body,'span','panelManagerPopupRoot');
	var s=root.style; s.width=0; s.height=0; s.top=0; s.left=0;
	s.display='inline'; s.overflow='visible'; s.position='absolute'; 
	document.onmousedown_panelmanager=document.onmousedown;
	document.onmousedown=function(ev) {
		var ev=ev||window.event; var target=resolveTarget(ev); var cmm=config.macros.moveablePanel;
		if (!ev||!ev.altKey) { // if not ALT-CLICK... handle event normally
			if (document.onmousedown_panelmanager==undefined) return;
			return document.onmousedown_panelmanager.apply(target,arguments);
		}
		var root=document.getElementById('panelManagerPopupRoot');
		var mX=findMouseX(ev); var mY=findMouseY(ev);
		root.style.left=mX+'px'; root.style.top =mY+'px';
		var p=cmm.getPanel(target); var t=story.findContainingTiddler(target);
		var id=p?p.pid:(t?t.getAttribute('tiddler'):'')
		// HACK: ignore next click on doc background (prevents IE from closing popup)
		Popup.ignoreClick=config.browser.isIE;
		cmm.manager.popup(root,ev,id);
		return cmm.processed(ev);
	}
}
//}}}
// // hijack: refresh map viewers when window is scrolled
//{{{
if (window.onscroll_panelManager_init===undefined) { // only once
	window.onscroll_panelManager_init=true;
	window.onscroll_panelManager=window.onscroll;
	window.onscroll=function() {
		config.macros.moveablePanel.manager.notify('refresh');
		if (window.onscroll_panelManager)
			return window.onscroll_panelManager.apply(this,arguments);
	}
}
//}}}
// // hijacks: refresh map viewers when tiddlers or nested sliders are opened/closed
//{{{
if (Story.prototype.displayTiddler_panelManager===undefined) { // only once
	Story.prototype.displayTiddler_panelManager=Story.prototype.displayTiddler;
	Story.prototype.displayTiddler=function() {
		var r=this.displayTiddler_panelManager.apply(this,arguments);
		config.macros.moveablePanel.manager.notify('refresh');
		return r;
	}
	Story.prototype.closeTiddler_panelManager=Story.prototype.closeTiddler;
	Story.prototype.closeTiddler=function() {
		var r=this.closeTiddler_panelManager.apply(this,arguments);
		// NOTE: ASYNC wait for core animation to finish, then update viewers
		var delay=config.options.chkAnimate?config.animDuration+100:0;
		setTimeout("config.macros.moveablePanel.manager.notify('refresh')",delay);
		return r;
	}
}
if (window.onClickNestedSlider && (window.onClickNestedSlider_panelManager===undefined)) { // only once
	window.onClickNestedSlider_panelManager=window.onClickNestedSlider;
	window.onClickNestedSlider=function() {
		var r=window.onClickNestedSlider_panelManager.apply(window,arguments);
		// NOTE: ASYNC wait for core animation to finish, then update viewers
		var delay=config.options.chkAnimate?config.animDuration+100:0;
		setTimeout("config.macros.moveablePanel.manager.notify('refresh')",delay);
		return r;
	}
}
//}}}
<<moveablePanel viewer>>
<!--{{{-->
<div class='moveablePanel'>
<div class='toolbar'>
	<span macro='moveablePanel load:{{tiddler?tiddler.title:""}} label:"load this map"'></span>
	<span macro='toolbar [[ToolbarCommands::ViewToolbar]]'></span>
</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='tagged' macro='tags'></div>
<div class='tagging' macro='tagging'></div>
<div class='viewer'>
	<div class='content' macro='view text wikified'></div>
</div>
<div class='tagClear'></div>
<div macro='moveablePanel name:{{story.findContainingTiddler(place).getAttribute("tiddler")}} undocked fold hover height:auto'></div>
</div>
<!--}}}-->
/***
|''Name:''|PasswordOptionPlugin|
|''Description:''|Extends TiddlyWiki options with non encrypted password option.|
|''Version:''|1.0.2|
|''Date:''|Apr 19, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#PasswordOptionPlugin|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0 (Beta 5)|
***/
//{{{
version.extensions.PasswordOptionPlugin = {
	major: 1, minor: 0, revision: 2, 
	date: new Date("Apr 19, 2007"),
	source: 'http://tiddlywiki.bidix.info/#PasswordOptionPlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	license: '[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D]]',
	coreVersion: '2.2.0 (Beta 5)'
};

config.macros.option.passwordCheckboxLabel = "Save this password on this computer";
config.macros.option.passwordInputType = "password"; // password | text
setStylesheet(".pasOptionInput {width: 11em;}\n","passwordInputTypeStyle");

merge(config.macros.option.types, {
	'pas': {
		elementType: "input",
		valueField: "value",
		eventName: "onkeyup",
		className: "pasOptionInput",
		typeValue: config.macros.option.passwordInputType,
		create: function(place,type,opt,className,desc) {
			// password field
			config.macros.option.genericCreate(place,'pas',opt,className,desc);
			// checkbox linked with this password "save this password on this computer"
			config.macros.option.genericCreate(place,'chk','chk'+opt,className,desc);			
			// text savePasswordCheckboxLabel
			place.appendChild(document.createTextNode(config.macros.option.passwordCheckboxLabel));
		},
		onChange: config.macros.option.genericOnChange
	}
});

merge(config.optionHandlers['chk'], {
	get: function(name) {
		// is there an option linked with this chk ?
		var opt = name.substr(3);
		if (config.options[opt]) 
			saveOptionCookie(opt);
		return config.options[name] ? "true" : "false";
	}
});

merge(config.optionHandlers, {
	'pas': {
 		get: function(name) {
			if (config.options["chk"+name]) {
				return encodeCookie(config.options[name].toString());
			} else {
				return "";
			}
		},
		set: function(name,value) {config.options[name] = decodeCookie(value);}
	}
});

// need to reload options to load passwordOptions
loadOptionsCookie();

/*
if (!config.options['pasPassword'])
	config.options['pasPassword'] = '';

merge(config.optionsDesc,{
		pasPassword: "Test password"
	});
*/
//}}}
[[Link:|http://maans.newp.dk/TiddlyHome/Projekt08/index.html]]
<html><div align="center"><iframe src="http://maans.newp.dk/TiddlyHome/Projekt08/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
/***
|Name:|QuickOpenTagPlugin|
|Description:|Changes tag links to make it easier to open tags as tiddlers|
|Version:|3.0.1 ($Rev: 3861 $)|
|Date:|$Date: 2008-03-08 10:53:09 +1000 (Sat, 08 Mar 2008) $|
|Source:|http://mptw.tiddlyspot.com/#QuickOpenTagPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
***/
//{{{
config.quickOpenTag = {

	dropdownChar: (document.all ? "\u25bc" : "\u25be"), // the little one doesn't work in IE?

	createTagButton: function(place,tag,excludeTiddler) {
		// little hack so we can do this: <<tag PrettyTagName|RealTagName>>
		var splitTag = tag.split("|");
		var pretty = tag;
		if (splitTag.length == 2) {
			tag = splitTag[1];
			pretty = splitTag[0];
		}
		
		var sp = createTiddlyElement(place,"span",null,"quickopentag");
		createTiddlyText(createTiddlyLink(sp,tag,false),pretty);
		
		var theTag = createTiddlyButton(sp,config.quickOpenTag.dropdownChar,
                        config.views.wikified.tag.tooltip.format([tag]),onClickTag);
		theTag.setAttribute("tag",tag);
		if (excludeTiddler)
			theTag.setAttribute("tiddler",excludeTiddler);
    		return(theTag);
	},

	miniTagHandler: function(place,macroName,params,wikifier,paramString,tiddler) {
		var tagged = store.getTaggedTiddlers(tiddler.title);
		if (tagged.length > 0) {
			var theTag = createTiddlyButton(place,config.quickOpenTag.dropdownChar,
                        	config.views.wikified.tag.tooltip.format([tiddler.title]),onClickTag);
			theTag.setAttribute("tag",tiddler.title);
			theTag.className = "miniTag";
		}
	},

	allTagsHandler: function(place,macroName,params) {
		var tags = store.getTags(params[0]);
		var filter = params[1]; // new feature
		var ul = createTiddlyElement(place,"ul");
		if(tags.length == 0)
			createTiddlyElement(ul,"li",null,"listTitle",this.noTags);
		for(var t=0; t<tags.length; t++) {
			var title = tags[t][0];
			if (!filter || (title.match(new RegExp('^'+filter)))) {
				var info = getTiddlyLinkInfo(title);
				var theListItem =createTiddlyElement(ul,"li");
				var theLink = createTiddlyLink(theListItem,tags[t][0],true);
				var theCount = " (" + tags[t][1] + ")";
				theLink.appendChild(document.createTextNode(theCount));
				var theDropDownBtn = createTiddlyButton(theListItem," " +
					config.quickOpenTag.dropdownChar,this.tooltip.format([tags[t][0]]),onClickTag);
				theDropDownBtn.setAttribute("tag",tags[t][0]);
			}
		}
	},

	// todo fix these up a bit
	styles: [
"/*{{{*/",
"/* created by QuickOpenTagPlugin */",
".tagglyTagged .quickopentag, .tagged .quickopentag ",
"	{ margin-right:1.2em; padding:2px; padding-right:0px; padding-left:1px; }",
".quickopentag .tiddlyLink { padding:2px; padding-left:3px; }",
".quickopentag a.button { padding:1px; padding-left:2px; padding-right:2px;}",
"/* extra specificity to make it work right */",
"#displayArea .viewer .quickopentag a.button, ",
"#displayArea .viewer .quickopentag a.tiddyLink, ",
"#mainMenu .quickopentag a.tiddyLink, ",
"#mainMenu .quickopentag a.tiddyLink ",
"	{ border:0px solid black; }",
"#displayArea .viewer .quickopentag a.button, ",
"#mainMenu .quickopentag a.button ",
"	{ margin-left:0px; padding-left:2px; }",
"#displayArea .viewer .quickopentag a.tiddlyLink, ",
"#mainMenu .quickopentag a.tiddlyLink ",
"	{ margin-right:0px; padding-right:0px; padding-left:0px; margin-left:0px; }",
"a.miniTag {font-size:150%;} ",
"#mainMenu .quickopentag a.button ",
"	/* looks better in right justified main menus */",
"	{ margin-left:0px; padding-left:2px; margin-right:0px; padding-right:0px; }", 
"#topMenu .quickopentag { padding:0px; margin:0px; border:0px; }",
"#topMenu .quickopentag .tiddlyLink { padding-right:1px; margin-right:0px; }",
"#topMenu .quickopentag .button { padding-left:1px; margin-left:0px; border:0px; }",
"/*}}}*/",
		""].join("\n"),

	init: function() {
		// we fully replace these builtins. can't hijack them easily
		window.createTagButton = this.createTagButton;
		config.macros.allTags.handler = this.allTagsHandler;
		config.macros.miniTag = { handler: this.miniTagHandler };
		config.shadowTiddlers["QuickOpenTagStyles"] = this.styles;
		store.addNotification("QuickOpenTagStyles",refreshStyles);
	}
}

config.quickOpenTag.init();

//}}}

config.renameTags = {

	prompts: {
		rename: "Rename the tag '%0' to '%1' in %2 tidder%3?",
		remove: "Remove the tag '%0' from %1 tidder%2?"
	},

	removeTag: function(tag,tiddlers) {
		for (var i=0;i<tiddlers.length;i++) {
			store.setTiddlerTag(tiddlers[i].title,false,tag);
		}
	},

	renameTag: function(oldTag,newTag,tiddlers) {
		for (var i=0;i<tiddlers.length;i++) {
			store.setTiddlerTag(tiddlers[i].title,false,oldTag); // remove old
			store.setTiddlerTag(tiddlers[i].title,true,newTag);  // add new
		}
	},

	storeMethods: {

		saveTiddler_orig_renameTags: TiddlyWiki.prototype.saveTiddler,

		saveTiddler: function(title,newTitle,newBody,modifier,modified,tags,fields) {
			if (title != newTitle) {
				var tagged = this.getTaggedTiddlers(title);
				if (tagged.length > 0) {
					// then we are renaming a tag
					if (confirm(config.renameTags.prompts.rename.format([title,newTitle,tagged.length,tagged.length>1?"s":""])))
						config.renameTags.renameTag(title,newTitle,tagged);

					if (!this.tiddlerExists(title) && newBody == "")
						// dont create unwanted tiddler
						return null;
				}
			}
			return this.saveTiddler_orig_renameTags(title,newTitle,newBody,modifier,modified,tags,fields);
		},

		removeTiddler_orig_renameTags: TiddlyWiki.prototype.removeTiddler,

		removeTiddler: function(title) {
			var tagged = this.getTaggedTiddlers(title);
			if (tagged.length > 0)
				if (confirm(config.renameTags.prompts.remove.format([title,tagged.length,tagged.length>1?"s":""])))
					config.renameTags.removeTag(title,tagged);
			return this.removeTiddler_orig_renameTags(title);
		}

	},

	init: function() {
		merge(TiddlyWiki.prototype,this.storeMethods);
	}
}

config.renameTags.init();
[[Link:|http://sang.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://sang.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
/%
|Name|SetTiddlerBackground|
|Source|http://www.TiddlyTools.com/#SetTiddlerBackground|
|Version|1.1.2|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|script|
|Requires|InlineJavascriptPlugin|
|Overrides||
|Description|set tiddler background and font color CSS attributes|

usage:
	<<tiddler SetTiddlerBackground with: bgstyle fgstyle matchtag class>>
where:
	'bgstyle' and 'fgstyle' (optional, but specify at least one)
		are CSS background style attributes (most often color values, e.g., #rgb or #rrggbb)
	'matchtag' (optional)
		is a tag value that allows selective control of tiddler background/foreground colors
	'class' (optional)
		is the class of the tiddler element to which the fgstyle/bgstyle will be applied (default is "viewer").
		Use "title" to set the background of the tiddler's 'title' area instead of its 'viewer' area.

The bgstyle and fgstyle assignments are only performed if the tiddler has the matching tag (or if no matchtag value is specfied).  In addition, to set just the background or the foreground color (but not both), you can use a dash ("-") as a placeholder value for whichever value you do NOT want to set.  For example:
	<<tiddler SetTiddlerBackground with: #F00 - urgent>>
sets the background color (but NOT the foreground color) to RED for only those tiddlers tagged with "urgent".  Also, note that in that instead of using #RGB color definitions, you can also use CSS color keywords (i.e., "red", "yellow", "green") or *any* other valid CSS value that can be applied to the 'background' style attribute.  For example, to use a background image for any tiddler tagged with "wallpaper", you can write:
        <<tiddler SetTiddlerBackground with: url(images/bg.jpg) - wallpaper>>

You can use this script several times in a row to define a set of tag-to-color mappings, stored in a *single* convenient tiddler... first, create a tiddler (e.g. [[BackgroundColors]]) containing something like this:
	<<tiddler SetTiddlerBackground with: red - urgent>>
	<<tiddler SetTiddlerBackground with: yellow - active>>
	<<tiddler SetTiddlerBackground with: green - done>>

To apply tag-based color mapping to any specific tiddler, just embed:
	<<tiddler BackgroundColors>>
directly in that tiddler's content and set the appropriate tag to select the desired background color.

To apply tag-based color mapping to ALL tiddlers in your document without having to embed the <<tiddler BackgroundColors>> macro into each 'colorized' tiddler, add:
        <span macro="tiddler BackgroundColors" style="display:none"></span>
in your [[ViewTemplate]].  Then, anytime you want to add another tag-to-color mapping, all you have to do is just edit the [[BackgroundColors]] tiddler and then start tagging the desired tiddlers accordingly.

%/<script>
	if ("$1"!="$"+"1" && "$1"!="-") var bg="$1";
	if ("$2"!="$"+"2" && "$2"!="-") var fg="$2";
	if ("$3"!="$"+"3" && "$3"!="-") var tag="$3";
	if ("$4"!="$"+"4" && "$4"!="-") var c="$4"; else var c="viewer";
	var here=story.findContainingTiddler(place); if (!here) return;
	var tiddler=store.getTiddler(here.getAttribute("tiddler"));
	if (tag && (!tiddler||!tiddler.isTagged(tag))) return;
	if (c=="tiddler") target=here;
	else {
		var children=here.getElementsByTagName("*");
		for (var i=0; i<children.length; i++)
			if (hasClass(children[i],c)) { var target=children[i]; break; }
	}
	if (!target) return;
	if (bg) target.style.background=bg;
	if (fg) target.style.color=fg;
</script>
By default, SetTiddlerBackground sets the background of the tiddler's
"viewer" class (the area containing the rendered tiddler content).
However, I've just updated the script to support an additional,
optional 'class' parameter:

<<tiddler SetTiddlerBackground with: bgstyle fgstyle matchtag class>>

For example, to set the tiddler title's background to RED if the
tiddler is tagged with "urgent":

<<tiddler SetTiddlerBackground with: red - urgent title>>

Get the update (v1.1.0) here:
   http://www.TiddlyTools.com/#SetTiddlerBackground 
@@display:block;margin:0 .2em;padding:.2em .3em;goto@@<<gotoTiddler>><<search>><<closeAll>><<permaview>><<newTiddler>> <<saveChanges>><<tiddler TspotSidebar>><<moveablePanel menu label:"panels">>
<<tabs txtMainTab "Timeline" "Timeline" TabTimeline "All" "All tiddlers" TabAll "Tags" "All tags" TabTags "More" "More lists" TabMore>>
[[Link:|http://simplenoter.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://simplenoter.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
/***
|Name|SinglePageModePlugin|
|Source|http://www.TiddlyTools.com/#SinglePageModePlugin|
|Documentation|http://www.TiddlyTools.com/#SinglePageModePluginInfo|
|Version|2.9.6|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides|Story.prototype.displayTiddler(), Story.prototype.displayTiddlers()|
|Options|##Configuration|
|Description|Show tiddlers one at a time with automatic permalink, or always open tiddlers at top/bottom of page.|
This plugin allows you to configure TiddlyWiki to navigate more like a traditional multipage web site with only one tiddler displayed at a time.
!!!!!Documentation
>see [[SinglePageModePluginInfo]]
!!!!!Configuration
<<<
<<option chkSinglePageMode>> Display one tiddler at a time
><<option chkSinglePagePermalink>> Automatically permalink current tiddler
><<option chkSinglePageKeepFoldedTiddlers>> Don't close tiddlers that are folded
><<option chkSinglePageKeepEditedTiddlers>> Don't close tiddlers that are being edited
<<option chkTopOfPageMode>> Open tiddlers at the top of the page
<<option chkBottomOfPageMode>> Open tiddlers at the bottom of the page
<<option chkSinglePageAutoScroll>> Automatically scroll tiddler into view (if needed)

Notes:
* The "display one tiddler at a time" option can also be //temporarily// set/reset by including a 'paramifier' in the document URL: {{{#SPM:true}}} or {{{#SPM:false}}}.
* If more than one display mode is selected, 'one at a time' display takes precedence over both 'top' and 'bottom' settings, and if 'one at a time' setting is not used, 'top of page' takes precedence over 'bottom of page'.
* When using Apple's Safari browser, automatically setting the permalink causes an error and is disabled.
<<<
!!!!!Revisions
<<<
2008.10.17 [2.9.6] changed chkSinglePageAutoScroll default to false
| Please see [[SinglePageModePluginInfo]] for previous revision details |
2005.08.15 [1.0.0] Initial Release.  Support for BACK/FORWARD buttons adapted from code developed by Clint Checketts.
<<<
!!!!!Code
***/
//{{{
version.extensions.SinglePageModePlugin= {major: 2, minor: 9, revision: 6, date: new Date(2008,10,17)};
//}}}
//{{{
config.paramifiers.SPM = { onstart: function(v) {
	config.options.chkSinglePageMode=eval(v);
	if (config.options.chkSinglePageMode && config.options.chkSinglePagePermalink && !config.browser.isSafari) {
		config.lastURL = window.location.hash;
		if (!config.SPMTimer) config.SPMTimer=window.setInterval(function() {checkLastURL();},1000);
	}
} };
//}}}
//{{{
if (config.options.chkSinglePageMode==undefined)
	config.options.chkSinglePageMode=false;
if (config.options.chkSinglePagePermalink==undefined)
	config.options.chkSinglePagePermalink=true;
if (config.options.chkSinglePageKeepFoldedTiddlers==undefined)
	config.options.chkSinglePageKeepFoldedTiddlers=false;
if (config.options.chkSinglePageKeepEditedTiddlers==undefined)
	config.options.chkSinglePageKeepEditedTiddlers=false;
if (config.options.chkTopOfPageMode==undefined)
	config.options.chkTopOfPageMode=false;
if (config.options.chkBottomOfPageMode==undefined)
	config.options.chkBottomOfPageMode=false;
if (config.options.chkSinglePageAutoScroll==undefined)
	config.options.chkSinglePageAutoScroll=false;
//}}}
//{{{
config.SPMTimer = 0;
config.lastURL = window.location.hash;
function checkLastURL()
{
	if (!config.options.chkSinglePageMode)
		{ window.clearInterval(config.SPMTimer); config.SPMTimer=0; return; }
	if (config.lastURL == window.location.hash) return; // no change in hash
	var tids=decodeURIComponent(window.location.hash.substr(1)).readBracketedList();
	if (tids.length==1) // permalink (single tiddler in URL)
		story.displayTiddler(null,tids[0]);
	else { // restore permaview or default view
		config.lastURL = window.location.hash;
		if (!tids.length) tids=store.getTiddlerText("DefaultTiddlers").readBracketedList();
		story.closeAllTiddlers();
		story.displayTiddlers(null,tids);
	}
}


if (Story.prototype.SPM_coreDisplayTiddler==undefined)
	Story.prototype.SPM_coreDisplayTiddler=Story.prototype.displayTiddler;
Story.prototype.displayTiddler = function(srcElement,tiddler,template,animate,slowly)
{
	var title=(tiddler instanceof Tiddler)?tiddler.title:tiddler;
	var tiddlerElem=document.getElementById(story.idPrefix+title); // ==null unless tiddler is already displayed
	var opt=config.options;
	var single=opt.chkSinglePageMode && !startingUp;
	var top=opt.chkTopOfPageMode && !startingUp;
	var bottom=opt.chkBottomOfPageMode && !startingUp;
	if (single) {
		story.forEachTiddler(function(tid,elem) {
			// skip current tiddler and, optionally, tiddlers that are folded.
			if (	tid==title
				|| (opt.chkSinglePageKeepFoldedTiddlers && elem.getAttribute("folded")=="true"))
				return;
			// if a tiddler is being edited, ask before closing
			if (elem.getAttribute("dirty")=="true") {
				if (opt.chkSinglePageKeepEditedTiddlers) return;
				// if tiddler to be displayed is already shown, then leave active tiddler editor as is
				// (occurs when switching between view and edit modes)
				if (tiddlerElem) return;
				// otherwise, ask for permission
				var msg="'"+tid+"' is currently being edited.\n\n";
				msg+="Press OK to save and close this tiddler\nor press Cancel to leave it opened";
				if (!confirm(msg)) return; else story.saveTiddler(tid);
			}
			story.closeTiddler(tid);
		});
	}
	else if (top)
		arguments[0]=null;
	else if (bottom)
		arguments[0]="bottom";
	if (single && opt.chkSinglePagePermalink && !config.browser.isSafari) {
		window.location.hash = encodeURIComponent(String.encodeTiddlyLink(title));
		config.lastURL = window.location.hash;
		document.title = wikifyPlain("SiteTitle") + " - " + title;
		if (!config.SPMTimer) config.SPMTimer=window.setInterval(function() {checkLastURL();},1000);
	}
	if (tiddlerElem && tiddlerElem.getAttribute("dirty")=="true") { // editing... move tiddler without re-rendering
		var isTopTiddler=(tiddlerElem.previousSibling==null);
		if (!isTopTiddler && (single || top))
			tiddlerElem.parentNode.insertBefore(tiddlerElem,tiddlerElem.parentNode.firstChild);
		else if (bottom)
			tiddlerElem.parentNode.insertBefore(tiddlerElem,null);
		else this.SPM_coreDisplayTiddler.apply(this,arguments); // let CORE render tiddler
	} else
		this.SPM_coreDisplayTiddler.apply(this,arguments); // let CORE render tiddler
	var tiddlerElem=document.getElementById(story.idPrefix+title);
	if (tiddlerElem&&opt.chkSinglePageAutoScroll) {
		// scroll to top of page or top of tiddler
		var isTopTiddler=(tiddlerElem.previousSibling==null);
		var yPos=isTopTiddler?0:ensureVisible(tiddlerElem);
		// if animating, defer scroll until after animation completes
		var delay=opt.chkAnimate?config.animDuration+10:0;
		setTimeout("window.scrollTo(0,"+yPos+")",delay); 
	}
}

if (Story.prototype.SPM_coreDisplayTiddlers==undefined)
	Story.prototype.SPM_coreDisplayTiddlers=Story.prototype.displayTiddlers;
Story.prototype.displayTiddlers = function() {
	// suspend single/top/bottom modes when showing multiple tiddlers
	var opt=config.options;
	var saveSPM=opt.chkSinglePageMode; opt.chkSinglePageMode=false;
	var saveTPM=opt.chkTopOfPageMode; opt.chkTopOfPageMode=false;
	var saveBPM=opt.chkBottomOfPageMode; opt.chkBottomOfPageMode=false;
	this.SPM_coreDisplayTiddlers.apply(this,arguments);
	opt.chkBottomOfPageMode=saveBPM;
	opt.chkTopOfPageMode=saveTPM;
	opt.chkSinglePageMode=saveSPM;
}
//}}}
{{textcenter{
@@padding-left:1.2em;En mindmap over mine wikis @@}}}
[[TiddlyWiki]]
http://måns.dk/cv
[[Link:|http://måns.dk/blog/index.html]]
<html><div align="center"><iframe src="http://måns.dk/blog/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
[[Link:|http://styrkersvagheder.tiddlyspot.com]]
<html><div align="center"><iframe src="http://styrkersvagheder.tiddlyspot.com" frameborder="0" width="100%" height="600"></iframe></div></html>
[[MoveableTiddlerStyles]]	/* styles for moveable tiddlers */
[[EricsStyleTweaks]]	/* GENERAL STYLE TWEAKS */

/*{{{*/
/* body and links */
a,
.button
{color:#ff0066;text-decoration:none;}
a:hover,
.button:hover 
{color:#ff0066;text-decoration:underline;background:transparent;}
a:visited
{color:#ff0066;}

 .inlineList ul li { display:inline; } 
.popup
	{ border:1px solid #778899; background:#D3D3D3; color:#778899; }
.popup li a
	{ margin:0 1px;padding:2px; background:#D3D3D3; color:#778899; }
.popup li a:hover {background:#D3D3D3; color:#778899;}

body {background-color:#E1E3FF
;font-size:12px;font-family:Tahoma;color:#778899;}

.header
{background:#F0F1FE;border:3px solid #ff0066;padding:0 1 0 1em;}
.undocked .header {
	border:3px solid #ff0066;padding:0 1 0 1em;}

.siteTitle, .siteTitle a, .siteTitle a:hover{color:#ff0066;font:1.8em  "Arial Black";line-height: 1.3em;text-transform:smallcaps;text-decoration:none;}
.siteSubtitle {color:#ff0066;letter-spacing:1px;text-transform:uppercase;font:1.0em "Trebuchet MS";}

#displayArea {}
#tiddlerDisplay {}
#mainMenu
	{ background:#F0F1FE;
	border:3px solid #ff0066; line-height: 17px }
#mainMenu a:hover {color:#ff0066;text-decoration:underline;}
.txtOptionInput {background-color:#D3D3D3;font-size:12px;font-family:Tahoma;color:#778899; border:1px solid #778899;  padding:.5em;margin-left:.5em;}
#messageArea, #messageArea .button {background-color:#D3D3D3;font-size:12px;font-family:Tahoma;color:#778899; border:1px solid #778899; }
#messageArea .button:hover{color:#ccc;border:1px solid #ccc; margin:0 1px;}
/* TIDDLER 'TITLEBAR' */
.title {font:2.2em "Trebuchet MS";
	background-color:#D3D3D3;
	color:#778899;
	border:none;
	
	padding-left:.5em;
}
.selected .title {
	background-color:#D3D3D3;
	color:#778899;
}
.subtitle { display:none; }

/* TIDDLER TOOLBAR */
.tiddler .folded
	{ height:2em !important; }
.tiddler .folded .title
	{  border:1px solid #778899; }
.tiddler .moveablePanelMenu, .toolbar .moveablePanelMenu
	{ visibility:hidden; display:none; top:.4em !important }	/* shift buttons to fit in titlebar */
.undocked .toolbar
	{ padding-right:8em !important; } /* make room for buttons next to toolbar */
.toolbar
	{ float:right; visibility:hidden; margin-top:.5em; margin-right:.5em; }
.toolbar .button
	{ padding:0px .5em; }
.selected .toolbar 
	{ visibility:visible; }
.selected .toolbar .button {
	background:#D3D3D3; color:#778899; border:1px solid #778899; margin:0 1px;
	 }
.selected .toolbar .button:hover
	{ background:#D3D3D3; color:#F8F8FF;border:1px solid #F8F8FF; margin:0 1px;
	}

.moveablePanelButton, .undocked .moveablePanelButton
 {	display:none; background:#D3D3D3 !important; color:#778899 !important;
	border:1px solid #778899; padding:0 .25em; margin:0px 1px;
}
.moveablePanelButton:hover
	{ display:none; background:#D3D3D3 !important; color:#F8F8FF !important; border:1px solid #F8F8FF;}
.undocked .selected .moveablePanelMenu, .moveablePanelMenu
			{ display:none; }
.floatingPanel .selected .moveablePanelMenu, .selected .moveablePanelMenu
			{ display:none; }

/* TIDDLER BODY */

.viewer 
{border:none; padding:1em; background:#F0F1FE;overflow: auto;}
.viewer .content
{ min-height:100px; max-height:50em; overflow:auto; } /* limit tiddler height */
.viewer pre {color:#778899;background-color:#D3D3D3;border:none}
.selected .moveablePanel
	{ border:1px solid #778899;
	 background:#F0F1FE;}
.moveablePanel{ border:1px solid #ff0066;
	 background:#F0F1FE;}
.viewer .button{
 border: 0;
}
/* TAGGING BOXES */
.tagging a, .tagged a{color:#778899;}
.tagging a:hover,.tagged a:hover{color:#778899;}
.tagging {margin:0.5em 0.5em 0.5em 0.5em; float:left; display:none;}
.tagged {margin:0.5em; float:right;}
.tagging, .tagged {font:0.9em "Trebuchet MS"; padding:0.25em;background-color:#D3D3D3;color:#778899;border:#778899;}
.selected .tagging,.selected .tagged{background-color:#D3D3D3;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;color:#778899;}
.undocked .tagged .listTitle {color:#778899;}
.selected .undocked .tagged .listTitle {color:#778899;}
	.undocked .tagging .listTitle {color:#778899;}
.selected .undocked .tagging .listTitle {color:#778899;}
.selected .tagglyTagged .quickopentag, .selected .tagged .quickopentag {border:none;}
.quickopentag .button, .quickopentag .button:hover,.undocked .quickopentag .button, .undocked .quickopentag .button:hover,.selected .quickopentag .button,.selected .quickopentag .button:hover,.button .highlight{border:none;background:transparent;}
.quickopentag .tiddlyLink{border:none;}
.selected .undocked .button .highlight {border:none;background:transparent;}
/* TAGMINDMAP */
#tagmindmap 
{background:transparent;}
.ttmm 
{background:transparent;}
.ttmm_toolbar 
{background-color:#2A2A2F;visibility: hidden;}
.easyControl 
{color: #D3D3D3;}
.path
{padding:10px;background:transparent;text-align:center;margin-bottom:13px;}
.node {color:#3300CC
;font-size:22px;background:transparent;padding:1px;cursor:pointer;}
.node:hover {font-size:2em;color:#ff0066;}
.nodeColor{color:#CCCCFF;}
/*}}}*/
[[Link:|http://twcv.tiddlyspot.com/index.html]] [[Download:|http://twcv.tiddlyspot.com/download]]
<html><div align="center"><iframe src="http://twcv.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
<<list all >>
{{inlineList{<<allTags excludeLists >>
/***
|Name|TaggedTemplateTweak|
|Source|http://www.TiddlyTools.com/#TaggedTemplateTweak|
|Documentation|http://www.TiddlyTools.com/#TaggedTemplateTweakInfo|
|Version|1.5.0|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides|Story.prototype.chooseTemplateForTiddler()|
|Description|use alternative ViewTemplate/EditTemplate for tiddler's tagged with specific tag values|
This tweak extends story.chooseTemplateForTiddler() so that ''whenever a tiddler is marked with a specific tag value, it can be viewed and/or edited using alternatives to the standard tiddler templates.'' 
!!!!!Documentation
>see [[TaggedTemplateTweakInfo]]
!!!!!Revisions
<<<
2008.12.18 [1.5.0] added handling for using tiddler //title// as prefix (e.g., {{{SomeTiddlerViewTemplate}}}) 
| please see [[TaggedTemplateTweakInfo]] for previous revision details |
2007.06.11 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.TaggedTemplateTweak= {major: 1, minor: 5, revision: 0, date: new Date(2008,12,18)};

Story.prototype.taggedTemplate_chooseTemplateForTiddler = Story.prototype.chooseTemplateForTiddler
Story.prototype.chooseTemplateForTiddler = function(title,template)
{
	// get default template from core
	var coreTemplate=this.taggedTemplate_chooseTemplateForTiddler.apply(this,arguments);

	// if the tiddler doesn't exist yet, return core result
	var tiddler=store.getTiddler(title); if (!tiddler) return coreTemplate;

	// split core template into theme prefix and template name
	var theme="";
	var template=coreTemplate;
	var parts=template.split(config.textPrimitives.sectionSeparator);
	if (parts[1]) { theme=parts[0]; template=parts[1]; }
	else theme=config.options.txtTheme||""; // fallback if theme is not specified
	theme+=config.textPrimitives.sectionSeparator;

	// look for template whose prefix matches a *tag* on this tiddler (if any)
	for (i=0; i<tiddler.tags.length; i++) {
		var t=tiddler.tags[i]+template; // add tag prefix to template
		var c=t.substr(0,1).toUpperCase()+t.substr(1); // capitalized for WikiWord title
		if (store.getTiddlerText(theme+t))	{ return theme+t; } // theme##tagTemplate
		if (store.getTiddlerText(theme+c))	{ return theme+c; } // theme##TagTemplate
		if (store.getTiddlerText(t)) 		{ return t; }	    // tagTemplate
		if (store.getTiddlerText(c))		{ return c; }	    // TagTemplate
	}

	// if no tagged template found, and tiddler title is not itself a tag,
	// then look for the template whose prefix matches the *title* of this tiddler
	var isTag=store.getTaggedTiddlers(title).length;
	if (!isTag && store.getTiddlerText(title+template)) { return title+template; }

	// no matching tag OR title prefix... return core result
	return coreTemplate;
}
//}}}
/***
|Name|TextAreaPlugin|
|Source|http://www.TiddlyTools.com/#TextAreaPlugin|
|Documentation|http://www.TiddlyTools.com/#TextAreaPluginInfo|
|Version|2.1.9|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides|Story.prototype.focusTiddler|
|Options|##Configuration|
|Description|Adds Find/Again keyboard search, autosize, and 'stretch bar' resize for textarea controls|
* ''Control-F'' and ''control-G'' will ''"Find text"'' and ''"find text aGain"'', respectively, allowing you to copy, find, paste, findagain, paste, etc to perform "search-and-replace" actions.  
* ''autosizeEditor'' - toggles the tiddler editor textarea height between fixed-height and "automatically fit the contents".
* ''resizeEditor'' - adds 'grab handle' below textarea to stretch field height
!!!!!Documentation
>see [[TextAreaPluginInfo]]
!!!!!Configuration
<<<
<<option chkTextAreaExtensions>> use control-f (find), control-g (find again) inside text area
<<option chkDisableAutoSelect>> place cursor at start of textarea instead of pre-selecting content
<<option chkResizeEditor>> modify shadow EditTemplate to add resizeable text area (and autosize command)
<<<
!!!!!Revisions
<<<
2008.01.08 [2.1.9] fixed default setting of uninitialized option values so that "false" is not treated as "undefined"
|please see [[TextAreaPluginInfo]] for additional revision details|
2006.01.22 [1.0.0] Moved from temporary "System Tweaks" tiddler into 'real' TextAreaPlugin tiddler.
<<<
!!!!!Code
***/
//{{{
version.extensions.TextAreaPlugin= {major: 2, minor: 1, revision: 9, date: new Date(2008,1,8)};

if (config.options.chkTextAreaExtensions===undefined) config.options.chkTextAreaExtensions=true;
if (config.options.chkDisableAutoSelect===undefined) config.options.chkDisableAutoSelect=true;
if (config.options.chkResizeEditor===undefined) config.options.chkResizeEditor=true;

// automatically tweak shadow EditTemplate to add "autosizeEditor" toolbar command
if (config.options.chkResizeEditor)
	config.shadowTiddlers.EditTemplate=config.shadowTiddlers.EditTemplate.replace(/deleteTiddler/,"deleteTiddler autosizeEditor");
// automatically tweak shadow EditTemplate to add "resizeEditor" macro
if (config.options.chkResizeEditor)
	config.shadowTiddlers.EditTemplate+="<span macro='resizeEditor'></span>";

// Put focus in a specified tiddler field
Story.prototype.TextAreaExtensions_focusTiddler=Story.prototype.focusTiddler;
Story.prototype.focusTiddler = function(title,field)
{
	this.TextAreaExtensions_focusTiddler.apply(this,arguments); // first call core
	var e = this.getTiddlerField(title,field);
	if (e && config.options.chkDisableAutoSelect) {
		if (e.setSelectionRange) // FF
			e.setSelectionRange(0,0);
		else if (e.createTextRange) // IE
			{ var r=e.createTextRange(); r.collapse(true); r.select(); }
	}
	if (e && config.options.chkTextAreaExtensions) addKeyDownHandlers(e);
}
//}}}

//{{{
function addKeyDownHandlers(e)
{
	// exit if not textarea or element doesn't allow selections
	if (e.tagName.toLowerCase()!="textarea"||!e.setSelectionRange||e.initialized) return;

	// utility function: exits keydown handler and prevents browser from processing the keystroke
	var processed=function(ev) {
		ev.cancelBubble=true; // IE4+
		try{event.keyCode=0;}catch(e){}; // IE5
		if (window.event) ev.returnValue=false; // IE6
		if (ev.preventDefault) ev.preventDefault(); // moz/opera/konqueror
		if (ev.stopPropagation) ev.stopPropagation(); // all
		return false;
	}
	// capture keydown in edit field
	e.saved_onkeydown=e.onkeydown; // save current keydown handler (if any)
	e.onkeydown=function(ev) { if (!ev) var ev=window.event;
		var key=ev.keyCode;
		if (!key) {
			var char=event.which?event.which:event.charCode;
			if (char==102) key=70;
			if (char==103) key=71;
		}
		// process CTRL-F (find matching text) or CTRL-G (find next match)
		if (ev.ctrlKey && (key==70||key==71)) {

			// prompt for text to find
			var defFind=e.findText?e.findText:e.value.substring(e.selectionStart,e.selectionEnd);
			if (key==70||!e.findText||!e.findText.length) // ctrl-f or no saved search text
				{ var f=prompt("find:", defFind); e.focus(); if (f) e.findText=f; }
			if (!e.findText||!e.findText.length) return processed(ev); //  if no search text, exit

			// do case-insensitive match with 'wraparound'...  if not found, alert and exit 
			var newstart=e.value.toLowerCase().indexOf(e.findText.toLowerCase(),e.selectionStart+1);
			if (newstart==-1) newstart=e.value.toLowerCase().indexOf(e.findText.toLowerCase());
			if (newstart==-1) { alert("'"+e.findText+"' not found"); e.focus(); return processed(ev); }

			// set new selection, scroll it into view, and report line position in status bar
			e.setSelectionRange(newstart,newstart+e.findText.length);
			var linecount=e.value.split('\n').length;
			var thisline=e.value.substr(0,e.selectionStart).split('\n').length;
			e.scrollTop=Math.floor((thisline-1-e.rows/2)*e.scrollHeight/linecount);
			window.status="line: "+thisline+"/"+linecount;
			return processed(ev);
		}
		if (e.saved_onkeydown) // call previous keydown handler (if any)
			e.saved_onkeydown(ev);
	}
	e.initialized=true;
}
//}}}

// // 'autosize' toolbar command
//{{{
config.commands.autosizeEditor = {
	text: 'autosize',
	tooltip: 'automatically adjust the editor height to fit the contents',
	text_alt: '\u221Aautosize',
	hideReadOnly: false,
	handler: function(event,src,title) {
		var here=story.findContainingTiddler(src); if (!here) return;
		var ta=here.getElementsByTagName('textarea'); if (!ta) return;
		for (i=0;i<ta.length;i++) {
			// only autosize textareas actually used to edit tiddler fields
			if (ta[i].getAttribute("edit")==undefined) continue;
			ta[i].button=src;
			if (!ta[i].maxed)
				config.commands.autosizeEditor.on(ta[i]);
			else
				config.commands.autosizeEditor.off(ta[i],true);
		}
		return false;
	},
	on: function(e) {
		if (e.maxed) return; // already autosizing!
		if (e.savedheight==undefined)
			e.savedheight=e.style.height;
		if (e.savedkeyup==undefined) {
			e.savedkeyup=e.onkeyup;
			e.onkeyup=function(ev) {
				if (!ev) var ev=window.event; var e=resolveTarget(ev);
				e.style.height=e.scrollHeight+'px';
				if (e.savedkeyup) e.savedkeyup();
			}
		}
		// IE reports error: "not implemented" for onkeypress
		if (!config.browser.isIE && e.savedkeypress==undefined) {
			e.savedkeypress=e.onkeypress;
			e.onkeypress=function(ev) {
				if (!ev) var ev=window.event; var e=resolveTarget(ev);
				if (ev.keyCode==33) { // PGUP
					if (window.scrollByPages) window.scrollByPages(-1);
					return false;
				}
				if (ev.keyCode==34) { // PGDN
					if (window.scrollByPages) window.scrollByPages(1);
					return false;
				}
				if (e.savedkeypress) e.savedkeypress();
			}
		}
		e.style.height=e.scrollHeight+'px';
		e.button.innerHTML=config.commands.autosizeEditor.text_alt;
		e.maxed=true;
	},
	off: function(e,resetHeight) {
		if (resetHeight) e.style.height=e.savedheight;
		e.onkeyup=e.savedkeyup;
		// IE reports error: "not implemented" for onkeypress
		if (!config.browser.isIE) e.onkeypress=e.savedkeypress;
		e.button.innerHTML=config.commands.autosizeEditor.text;
		e.maxed=false;
	}
};
//}}}

// // grab-and-stretch handle
//{{{
config.macros.resizeEditor = { // add stretch bar to editor textarea
	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		var here=story.findContainingTiddler(place); if (!here) return;
		var ta=here.getElementsByTagName('textarea');
		if (ta) for (i=0;i<ta.length;i++) {
			// only resize tiddler editor textareas
			if (ta[i].getAttribute("edit")==undefined) continue;
			new window.TextAreaResizer(ta[i]);
		}
	}
}

config.macros.resizeTiddler = { // add stretch bar to tiddler viewer element
	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		var here=story.findContainingTiddler(place); if (!here) return;
		var elems=here.getElementsByTagName('div');
		if (elems) for (i=0;i<elems.length;i++) if (hasClass(elems[i],'viewer')) break;
		if (i<elems.length) new window.TextAreaResizer(elems[i]);
	}
}

config.macros.resizeFrame = { // add stretch bar to iframes
	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		var here=story.findContainingTiddler(place); if (!here) return;
		var fr=here.getElementsByTagName('iframe');
		if (fr) for (i=0;i<fr.length;i++) new window.TextAreaResizer(fr[i]);
	}
}

// TextAreaResizer script by Jason Johnston (jj@lojjic.net)
// Created August 2003.  Use freely, but give me credit.
// adds a handle below textareas that the user can drag with the mouse to resize the textarea.
// MODIFIED by ELS for cross-browser (IE) compatibility, including:
//    fixups and adjustments to CSS styles,
//    use 'old style' assignment of mouse event handlers instead of using addEventListener(),
//    use window.event if event param is null,
//    use offsetHeight instead of getComputedStyle()
//    use explicit window.* global scope declaration for functions called from event handlers

window.TextAreaResizer = function(elt) {
	this.element = elt;
	this.create();
}
window.TextAreaResizer.prototype = {
	create : function() {
		var elt = this.element;
		var thisRef = this;
		var h = this.handle = document.createElement("div");
		h.style.height = "3px"; // was 4px... looked too fat!
		h.style.overflow = "hidden"; // ELS: force IE to trim height to < 1em
		h.style.width="auto";
		h.style.backgroundColor = "#999"; // ELS: standard mid-tone (dark) gray
		h.style.cursor = "s-resize";
		h.title = "Drag to resize text box";
		h.onmousedown=function(evt){thisRef.dragStart(evt)};
		elt.parentNode.insertBefore(h, elt.nextSibling);
	},
	dragStart : function(evt) {
		if (!evt) var evt=window.event;
		this.dragStop(evt); // ELS: stop any current drag processing first
		var thisRef = this;
		this.dragStartY = evt.clientY;
		this.dragStartH = this.element.offsetHeight;
		document.savedmousemove=document.onmousemove;
		document.onmousemove=this.dragMoveHdlr=function(evt){thisRef.dragMove(evt)};
		document.savedmouseup=document.onmouseup;
		document.onmouseup=this.dragStopHdlr=function(evt){thisRef.dragStop(evt)};
	},
	dragMove : function(evt) {
		if (!evt) var evt=window.event;
		// ELS: make sure height is at least 10px
		var h=this.dragStartH+evt.clientY-this.dragStartY;
		if (h<10) h=10; this.element.style.height=h+"px";
		// ELS: match handle to textarea width (which may have changed due to document scrollbars)
		this.handle.style.width=(this.element.offsetWidth-4)+"px"; // 4-pixel fudge factor for textarea border edge
		// ELS: when manually resizing, disable autoresizing (without restoring saved height)
		if (this.element.maxed!=undefined && this.element.maxed)
			config.commands.autosizeEditor.off(this.element,false);
	},
	dragStop : function(evt) {
		if (!evt) var evt=window.event;
		document.onmousemove=(document.savedmousemove!=undefined)?document.savedmousemove:null;
		document.onmousemove=(document.savedmouseup!=undefined)?document.savedmouseup:null;
	},
	destroy : function() {
		var elt = this.element;
		elt.parentNode.removeChild(this.handle);
		elt.style.height = "";
	}
};
//}}}
[[Link:|http://weekend.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://weekend.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
[[Link:|http://maans.newp.dk/TiddlyHome/index.html]]
<html><div align="center"><iframe src="http://maans.bplaced.net/TiddlyHome/HU.html" frameborder="2" width="100%" height="800"></iframe></div></html>
config.commands.newChild ={text: "add child",tooltip: "Add child tiddler"};
config.commands.newChild.handler = function(event,src,title){
	
	clearMessage();
	var newTitle = prompt("Please name a child of this tiddler");
		if(!newTitle) return;
	var tags = [title];
	story.displayTiddler(null,newTitle,DEFAULT_EDIT_TEMPLATE,false,null,null);
		config.commands.editTiddler.handler(event,src,newTitle);
	for(var t=0;t<tags.length;t++)
		story.setTiddlerTag(newTitle,tags[t],+1);
	//story.focusTiddler(newTitle,config.options.txtEditorFocus||"text");
	
		config.commands.saveTiddler.handler(event,src,newTitle);
	return false;
};

config.commands.newParent ={text: "add parent",tooltip: "Name and thus associate a parent with this tiddler"};
	
config.commands.newParent.handler = function(event,src,title){
	clearMessage();

	var newTitle = prompt("Please name a parent of this tiddler");
	
	if(!newTitle) return;
	var tags = [newTitle];
	
	/*update existing tiddler */
	config.commands.editTiddler.handler(event,src,title);
	var tiddlerElem = story.getTiddler(title);
	for(var t=0;t<tags.length;t++){
		story.setTiddlerTag(title,tags[t],+1);
	}
	config.commands.saveTiddler.handler(event,src,title);
	
	/*allowing editing of this new tiddler */
	if(story.getTiddler(newTitle) == null);
		story.displayTiddler(null,newTitle,null,false,null,null);
		
	//config.commands.editTiddler.handler(event,src,newTitle);
	
	return false;

}
/***
|''Name''|TiddlyTagMindMap|
|''Description''|Bring your tiddlers to life in a radial graph which displays all your tiddlywiki tiddlers and the relationships between them. (A bit like The Brain)|
|''Author''|Jon Robson|
|''Contributors''|Nicolas Garcia Belmonte|
|''Version''|1.5 in progress|
|''Date''|Nov 2008|
|''Status''|@@experimental@@;|
|''License''|BSD|
|''CoreVersion''|<...>|
|''Documentation''|<...>|
|''Keywords''|data visualisation, mindmap, The Brain, Mind Manager, FreeMind,tag relationships,graph|
!Description
Bring your TiddlyWiki to life!
!Notes
To install you will need to paste a line of text into your Theme {{{ <div id="tagmindmap"></div>}}} in the location where you would like to see the visualisation.

Currently we are unable to support this working in internet explorer.. we are working on it however.. sorry! :(
!Usage
{{{
The tagmindmap can be created from a macro call using <<tiddlytagmindmap //params//>>
alternatively paste <div id='tagmindmap'></div> into your page template.

The following macros may be useful however they can be included in the toolbar settings of the tiddlytagmindmap macro.
<<ToggleTagMindMap id>> (create button to toggle mind map with id 'id' on/off)
<<LoadMindMap id>> (create button to load all nodes into mind map  with id 'id')

There are a variety of configuration options in the backstage area under tweak. They all begin with TiddlyTagMindMapPlugin: 
}}}
!!Parameters
tiddlytagmindmap takes several (but all optional) parameters. Some examples can be seen below, note the order is irrelevant of these parameters.

!!!Nodes and Edges
!!!!Directional edges 
The directed parameter allows you to add arrowheads to your edges. usage: <<tiddlytagmindmap directed:true>>
!!!!Name Length
nodeNameLength:x where x is an integer will shorten the name of any node with a name longer than x. If x =0, the labels will disappear so you can rely on tooltips.

!!!!Variable node sizes (tagcloud)
notagcloud:true as a parameter will flatten the nodes to have the same size font

!!!Dimensions
{{{<<tiddlytagmindmap height:100 width:100>>}}} will set a tiddlytagmindmap with height and width 100.

!!!Zooming
A parameter zoom allows you to specify an integer representing the initial inflation of the mind map. The smaller it is - the closer the nodes will be together.
{{{<<tiddlytagmindmap zoom:1000>>}}} will give you a very inflated TagMindMap!

!!!Breadcrumb trail
You can turn visited nodes red when they are clicked on by using the {{{breadcrumb:true}}} parameter by default this is false.
!!!The toolbar
A parameter toolbar is a string of 1s. These signify the buttons. The first digit sets whether the bar should appear vertically or horizontally.
The following digits turn off or on the other available buttons.
!!!!The buttons
The digits preceding the first digit represent these buttons in this order..
toggle, loadall

{{{<<tiddlytagmindmap toolbar:101>>}}} would give you a vertical toolbar with a loadall button

!!!The Start State
A parameter can be used to specify how the map looks on start up. 
Currently the options are empty OR all OR a custom executable javascript function.
The first two options are simple strings eg.
{{{<<tiddlytagmindmap startState:empty>>}}} loads a blank tag mind map however {{{<<tiddlytagmindmap startState:all>>}}} loads all nodes excluding those in the exclude List.
the latter is more interesting. Have a look at [[Example 2]]!
!Revision History
1.5 xx/xx 
	*Ability to add arrow heads to show direction
	*better performance
	*better control panel: replacement of macros for toggle/zoom in and out with built in toolbar to plugin
	*update to new version of RGraph (JIT)
	*Ability to set meta-data specific to nodes within a tiddler in optional fields see
	 http://TiddlyWiki.abego-software.de/#PartTiddlerPlugin (eg. colouring of children/parents/images in node label)
	*definable meta data (prefix,suffix,label,color)
	*can turn off click function
1.4 11/08 tag cloud integration/multiple tag mind maps/ability to call from macro
1.3 22/10/08 working with ie/packaged up code

!To Do
*bug: zoomin breaks on more than 1 mind map.. no idea why but scale resets :(
*fix ie clicking nodes
*display tiddler loads below the place it;s called from
*Ability to define your own function for relative sizing (ie. you could weight them on some meta field)
*color property should become css property
*ability to resize using %
*distinguish between tags and tiddlers (see CreateNodeJSON - tiddlers that don't exist in store are tagged in data field)
*rss hooks - specify where tags and node names come from in feed
*ability to specify what click function is
*ability to stop displayTiddler from loading into graph (*ability to turn off dynamic as you go updates)
*allow resizing
*auto spread out messy nodes (ie. zoom out if nodes are too close)
*node repositioning and saving of state
*better css support
*performance issues
*deleting edges as tiddlers are deleted
*adapt to parabolic tree mode and other visualisation types
*Self-defined click functions
*Continued code cleanup
*breadcrumbs to become encoded in the node colour (rather than label colour) define a colour of the breadcrumb trail - make it change colour each click (increment colour, so you can get an idea of path you took to get somewhere)
*add pins to locations in the mind map to jump back to (definable in meta data long term, also short term in tiddler edit menu)
!Code
***/
/***
!Layer 1: TiddlyWiki Specific 
Jon Robson
***/

{{{

//set descriptions
merge(config.optionsDesc,{
txtTTMM_canvasWidth: "TiddlyTagMindMapPlugin : Width of  visualisation. You will need to refresh the page to see the change."
,txtTTMM_canvasHeight: "TiddlyTagMindMapPlugin : Height of visualisation. You will need to refresh the page to see the change."
,txtTTMM_inflation: "TiddlyTagMindMapPlugin : The visualisation can be inflated and deflated to allow you to see the structure from above or close up. This value allows you to set a start inflation."
,txtTTMM_maxNodeNameLength:"TiddlyTagMindMapPlugin : maximum length of a tiddler name. Any tiddlers with names longer than this will be abbrieviated using '...'. Set to zero to make labels disappear altogether and rely completely on tooltips."
,chkTTMM_ignoreLoneNodes: "TiddlyTagMindMapPlugin : any lone tiddlers/nodes (ie. tiddlers that are not tagged with any data) will be ignored in the visualisation. This cleans up the visualisation greatly."
,txtTTMM_excludeNodeList: "TiddlyTagMindMapPlugin :Anything tagged with this will be ignored in the visualisation. Formatted in form ['tiddlername1', 'tiddlername2']"
,chkTTMM_leaveRedBreadCrumbTrail: "TiddlyTagMindMapPlugin : When you visit a node it will be coloured red leaving a breadcrumb trail of where you have been."
}
);

/*MACROS*/


config.macros.TagMindMapEdge={ /* params: node1|commit node2 */
	handler: function (place,macroName,params,wikifier,paramString,tiddler) {
		
		var id1 = params[0]; var id2 = params[1];
		var ttmm = config.macros.tiddlytagmindmap.getAssociatedTiddlyTagMindMapObject(null,false);
		if(!ttmm) return;
			
			
		if(id1 == "commit") {
			this.commit(); 
			return;
		}
		var name1,name2,data1,data2;
		if(id1.id){
			//its a json
			if(id1.name)name1 = id1.name;
			if(id1.data)data1 = id1.data;
			id1 = id1.id;
	
		}

		if(id2.id){
			//its a json
			if(id1.name)name2 = id2.name;
			if(id2.data)data2 = id2.data;
			id2 = id2.id;
		}
		
		if(!data1){ data1 = {};}
		if(!data2){ data2 = {};}
		
		if(!store.tiddlerExists(id1)){
			if(!data1.color && ttmm.settings.emptyTiddlerColor)data1.color = ttmm.settings.emptyTiddlerColor;
			data1.emptyTiddler = true;
		}
		if(!store.tiddlerExists(id2)){
			if(!data2.color && ttmm.settings.emptyTiddlerColor)data2.color = ttmm.settings.emptyTiddlerColor;
			data2.emptyTiddler = true;
		}
		


		if(ttmm){
		ttmm.drawEdge(id1,id2,name1,name2,data1,data2);
		
		}
	}
	,commit: function(){
		var ttmm = config.macros.tiddlytagmindmap.getAssociatedTiddlyTagMindMapObject(null,false);
		ttmm.computeThenPlot();
	}
};
	
config.macros.LoadMindMap={
	label: "loadall",
	prompt: "load all tiddlers into the tag mind map",
	handler: function (place,macroName,params,wikifier,paramString,tiddler) {
		var btn =createTiddlyButton(place,this.label,this.prompt,this.onClick);	
		
		if(params[0]){
			btn.wrapperID = params[0];
		}
	}
	,onClick: function(e,id)
	{
		var ttmm;
		if(id) 
			ttmm = config.macros.tiddlytagmindmap.store[id];
		else
			ttmm = config.macros.tiddlytagmindmap.getAssociatedTiddlyTagMindMapObject(this.wrapperID,true); 
		var list = store.getTiddlers();
		for (var t=0; t<list.length; t++) { 

			ttmm.createNodeFromJSON(config.macros.tiddlytagmindmap.createJSON(list[t].title,ttmm));
		}

		ttmm.computeThenPlot();


	}
};

config.macros.ToggleTagMindMap={
	label: "toggle",
	prompt: "Toggle on or off the tag mind map",
	handler: function(place,macroName,params,wikifier,paramString,tiddler){
		var btn = createTiddlyButton(place,this.label,this.prompt,this.onClick);
		if(params[0])btn.wrapperID = params[0];
	}
	,onClick: function(e){ 
	
		var id = config.macros.tiddlytagmindmap.getAssociatedTiddlyTagMindMapObject(this.wrapperID,true).wrapper.id;

		if(document.getElementById(id).style.display== "none"){
			document.getElementById(id).style.display=  "";
		}
		else{
			document.getElementById(id).style.display=  "none";
		}
	}

};

config.macros.tiddlytagmindmap={
	store: {}, //a library of created ttmm objects
	handler: function (place,macroName,params,wikifier,paramString,tiddler) {
		if(!place) { //give a default place
			place = document.getElementById('tagmindmap');
			if(!place) throw "no place to put ttmm!"; //unable to create
			paramString =place.getAttribute("parameters");

		}
		
		var settings = this.get_ttmm_settings(paramString);

		var elem = this.setup_ttmm_html(place,settings);

			try{
			
			this.store[elem.id]= new Tagmindmap(elem,settings);
			
			this.store['last'] = elem.id;
			this.store['cur'] = elem.id;
		
	
			if(settings.startupFunction){
				settings.startupFunction(elem.id);
			}
		
		}
		catch(e){console.log("exception thrown during tiddlytagmindmap creation:"+e);}
	},
	get_ttmm_setting_defaults: function(){
		if(!config.options.txtTTMM_canvasWidth)config.options.txtTTMM_canvasWidth = 800;
		if(!config.options.txtTTMM_canvasHeight)config.options.txtTTMM_canvasHeight = 200;
		if(!config.options.txtTTMM_inflation)config.options.txtTTMM_inflation = 80;
		if(!config.options.txtTTMM_maxNodeNameLength)config.options.txtTTMM_maxNodeNameLength = 25;
		if(config.options.chkTTMM_ignoreLoneNodes == null) config.options.chkTTMM_ignoreLoneNodes = false;
		if(config.options.chkTTMM_leaveRedBreadCrumbTrail == null) config.options.chkTTMM_leaveRedBreadCrumbTrail = true;
		if(!config.options.txtTTMM_excludeNodeList) config.options.txtTTMM_excludeNodeList= ['excludeLists'];
	
		var settings = {};
		/*set some defaults based on tweaked preferences if none passed as parameters*/
		settings.maxNodeNameLength = config.options.txtTTMM_maxNodeNameLength;
		settings.ignoreLoneNodes = config.options.chkTTMM_ignoreLoneNodes;
		settings.tagcloud = {off:false};
		settings.width = config.options.txtTTMM_canvasWidth;
		settings.height =config.options.txtTTMM_canvasHeight;
		settings.toolbar = "010";
		settings.zoomLevel = parseInt(config.options.txtTTMM_inflation);
		settings.breadcrumbs = config.options.chkTTMM_leaveRedBreadCrumbTrail;
		
		var clickfunction = function(node,id,e){
			//var tiddlerElem = story.findContainingTiddler(resolveTarget(e));
			story.displayTiddler(null, node.id,null,null,null,null,null,id);
		};
		settings.clickFunction = clickfunction;
		return settings;
	},
	get_ttmm_settings: function(paramString){
		var settings = this.get_ttmm_setting_defaults();

		var exList = null;
		var that = this;
		var startupFunction = function(id){};


	
		settings.dynamicUpdateFunction = this.createJSON;
	
		if(paramString){
			var prms = paramString.parseParams(null, null, true);
			settings.breadcrumbs = eval(getParam(prms, "breadcrumbs"));
			settings.ignoreLoneNodes = eval(getParam(prms, "ignoreLoneNodes"));
			settings.arrowheads = eval(getParam(prms, "directed"));
			settings.tagcloud.off = eval(getParam(prms, "notagcloud"));
		
			if(getParam(prms, "id")) settings.id = getParam(prms, "id");	
			if(getParam(prms, "width")) settings.width = getParam(prms, "width");
			if(getParam(prms, "height"))settings.height = getParam(prms, "height");
			if(getParam(prms, "toolbar")) settings.toolbar= getParam(prms, "toolbar");
			if(getParam(prms,"zoom")) settings.zoomLevel = parseInt(getParam(prms,"zoom"));
			if(getParam(prms,"maxNodeNameLength"))settings.maxNodeNameLength = getParam(prms,"maxNodeNameLength");
			if(getParam(prms, "exclude")) exList = getParam(prms, "exclude");
			if(getParam(prms,"displayemptytiddlers"))settings.displayemptytiddlers = getParam(prms,"displayemptytiddlers");
			if(getParam(prms,"emptyTiddlerColor")){
				settings.emptyTiddlerColor =getParam(prms,"emptyTiddlerColor");
			}
			
			if(getParam(prms, "click") == "none") {
				settings.clickFunction = function(node,id){return;} ;	
			}
			else if(getParam(prms, "click") == "existing") {
				settings.clickFunction = function(node,id,e){
					if(!node.data.emptyTiddler){
						//var tiddlerElem = story.findContainingTiddler(resolveTarget(e));
						story.displayTiddler(null, node.id,null,null,null,null,null,id);
					}
					return;
					} ;	
			}

			var startState = getParam(prms, "startState");
			if(startState){
				if(startState == 'all')
					startupFunction = function(id){
						config.macros.LoadMindMap.onClick(null,id);
					}
				else if(startState == 'empty'){
					startupFunction = function(id){
					
					};
				}
				/*else if(startState == 'default'){

					startupFunction = function(id){
						con
						var startState = store.filterTiddlers(store.getTiddlerText("DefaultTiddlers"));
						that.loadTiddlersIntoTTMM(startState,id);
					}
				}*/
				else{//parse as a list of tiddler names
					if(startState){
						startupFunction = function(id){
							if(startState.length == 0) return;
							that.loadTiddlersIntoTTMM(startState,id);
						} 
					}
			
				}
				settings.startupFunction = startupFunction;
			}
		}
		
		
		if(!exList){
			exList = [];
			if(config.options.txtTTMM_excludeNodeList)exList = config.options.txtTTMM_excludeNodeList;
		}
		/*set the excluded nodes */
		var l = eval(exList);
		settings.excludeNodeList = [];
		for(var i=0; i < l.length; i++){
			settings.excludeNodeList.push(l[i]);	
			settings.excludeNodeList = settings.excludeNodeList.concat(getChildren(l[i]));		
		}

	

	
	
		return settings;
	}
	,setup_ttmm_html: function(place,settings){
		if(place.id == 'tagmindmap')settings.id = 'default';
		if(place.style.width) settings.width = place.style.width;
		if(place.style.height) settings.height = place.style.height;

		/*setup tag mind map */
		var newTTMM = document.createElement("div");
		if(!settings.id) settings.id="ttmm_" +Math.random();
		newTTMM.id = settings.id;
		newTTMM.style.width = settings.width +"px";
		newTTMM.style.height= settings.height +"px";
		newTTMM.setAttribute("class","ttmm");
		/*setup toolbar */
		var toolbar = document.createElement("div");
		toolbar.setAttribute("class","ttmm_toolbar");
		var html="", divider = " ";

		if(settings.toolbar[0] == 1){//MAKE VERTICAL
			toolbar.style.height = "1px";
			toolbar.style.position = "relative";
			var temp =  parseInt(settings.width) +10;
			toolbar.style.top = "0px";
			toolbar.style.left = temp+"px";
			divider = "\n";
		}
		if(settings.toolbar[1] == 1) html += "<<ToggleTagMindMap " + newTTMM.id+">>" + divider;
		if(settings.toolbar[2] == 1) html += "<<LoadMindMap " + newTTMM.id+">>"+ divider;


		place.appendChild(toolbar);
		wikify(html,toolbar);
		place.appendChild(newTTMM);

		if(!document.getElementById(this.store['first']))this.store['first'] = newTTMM.id; //no primary ttmm exists

		if(!newTTMM.style.width) newTTMM.style.width = settings.width;
		if(!newTTMM.style.height) newTTMM.style.height = settings.height;

		return newTTMM;
	}

	,loadTiddlersIntoTTMM: function(tiddlerList,visualisationID){
		var viz;
		if(!visualisationID) 
			viz = this.store[this.store['first']];
		else
			viz = this.store[visualisationID];
		var nodesLoaded = false;


		var title ="";var firstTitle="";
		for(var i =0; i < tiddlerList.length; i++){

			if(tiddlerList[i].title) title = tiddlerList[i].title;
			else title = tiddlerList[i];
			if(i==0) firstTitle = title;
	
			nodesLoaded = viz.createNodeFromJSON(this.createJSON(title,viz)) | nodesLoaded;
	
		}


		if(viz.rgraph){
			if(nodesLoaded !=0){
				viz.rgraph.compute();
				viz.rgraph.plot();

			}
		
			viz.centerOnNode(title);
		}
	}

	,getAssociatedTiddlyTagMindMapObject: function(id,getFirstCreatedTTMM){
			if(id) return this.store[id];
			else {
				if(getFirstCreatedTTMM) 
					return this.store[this.store['first']];
				else 
					return this.store[this.store['last']];
			}
	}


	,_createJSONTagMindMapNodes: function(mylist,storeElement) { 
		var res=[]; 
		for (var t=0; t<mylist.length; t++){
			var node =mylist[t];
			res.push(config.macros.tiddlytagmindmap._createJSONTagMindMapNode(node,storeElement));
		} 
		
		return res;
	}
	
	,_createJSONTagMindMapNode: function(id,storeElement){


		var json = {};
		json.id = id;
		json.name = id;

		nodeData = {};

		var parents = getParents(id);
		for(var i=0; i < parents.length; i++){
			var nodeid = parents[i];
					
			if(store.tiddlerExists(nodeid)){
				var tiddler = store.getTiddler(nodeid);
				if(tiddler.fields.childrencolor) nodeData.color = tiddler.fields.childrencolor;
			}	
		}
				
		var children = getChildren(id);
		for(var i=0; i < children.length; i++){
			var nodeid = children[i];
			
			if(store.tiddlerExists(nodeid)){
				var tiddler = store.getTiddler(nodeid);
				if(tiddler.fields.parentcolor) nodeData.color = tiddler.fields.parentcolor;
			}	
		}

		
		
		if(store.tiddlerExists(id)){
			var tiddler = store.getTiddler(id);
			if(tiddler.fields.nodecolor) nodeData.color = tiddler.fields.nodecolor;
			if(tiddler.fields.nodeprefix) {
				var place =createTiddlyElement(null,"div");
				wikify(tiddler.fields.nodeprefix,place);
				nodeData.nodeLabelPrefix = place;
			}
			if(tiddler.fields.nodesuffix) {
				var place =createTiddlyElement(null,"div");
				wikify(tiddler.fields.nodesuffix,place);
				nodeData.nodeLabelSuffix = place;

			}
			if(tiddler.fields.nodelabel) {
				var place =createTiddlyElement(null,"div");
				wikify(tiddler.fields.nodelabel,place);
				nodeData.label = place;
			}
			if(tiddler.fields.nodetooltip) {nodeData.title = tiddler.fields.nodetooltip;}

		}

		if(!nodeData.color){
			var empty = false;
			 if(!store.tiddlerExists(id) && !store.isShadowTiddler(id)){		
				empty = true;
			}
			if(store.tiddlerExists(id)){
				var tiddler = store.getTiddler(id);
				if(tiddler.text == null || tiddler.text == "") empty =true; 
			}

			if(empty){
				nodeData.emptyTiddler = true;
				//nodeData.color = "#cccccc";
				//console.log(storeElement.settings);
				if(storeElement && storeElement.settings.emptyTiddlerColor) nodeData.color= storeElement.settings.emptyTiddlerColor;
				//storeElement
				
			}
		}

		if(nodeData){
			json.data =nodeData;
		}
		
		return json;
	}
	
	,createJSON: function(nodeid,storeElement){
		if(!nodeid) return "{}";
	    	var myjson = {};

		var children = getChildren(nodeid);
	    	var parents = getParents(nodeid);
	

		myjson.children = config.macros.tiddlytagmindmap._createJSONTagMindMapNodes(children,storeElement);
	 	myjson.parents = config.macros.tiddlytagmindmap._createJSONTagMindMapNodes(parents,storeElement);
		myjson.node  = config.macros.tiddlytagmindmap._createJSONTagMindMapNode(nodeid,storeElement);

		return myjson;
	}
	
	

};


function getParents(a){ 

  if(store.getTiddler(a)){
    return store.getTiddler(a).tags;
  }
  else
    return [];
} 

function getChildren(a){
	  if(store.getTaggedTiddlers(a)){
	    var tags = store.getTaggedTiddlers(a);
	    if(tags.length == 0) return [];

	    var a = new Array();
	    for (var t=0; t<tags.length; t++) { 
		a.push(tags[t].title);
	    }
	    return a;
	}
	  else
	    return [];
}



	



story.beforettmm_displayTiddler = story.displayTiddler;
story.displayTiddler = function(srcElement,tiddler,template,animate,unused,customFields,toggle,visualisationID)
{
	try{
		if(!document.getElementById(config.macros.tiddlytagmindmap.store['first'])) {					
			config.macros.tiddlytagmindmap.handler(); //try and setup a default one
		}
	}
	catch(e){
		
	};

	
	var title = (tiddler instanceof Tiddler)? tiddler.title : tiddler;

	if(config.macros.tiddlytagmindmap.store){
		try{
			if(!visualisationID && config.macros.tiddlytagmindmap.store['first']) { //call came from outside tagmindmap
				visualisationID =config.macros.tiddlytagmindmap.store['first'];
			}
			if(visualisationID){
				res = config.macros.tiddlytagmindmap.loadTiddlersIntoTTMM([title],visualisationID);
			}
		}
		catch(e){
			console.log("exception in display tiddler for "+title+" in visualisation" + visualisationID +": " + e);
		}
	}
	story.beforettmm_displayTiddler(srcElement,tiddler,template,animate,unused,customFields,toggle);
};

}}}
/***
296.56
58 * 151.46
246.56
!Layer 2: DynamicInteract: Extension of RGraph
***/
{{{
Array.prototype.contains = function(item)
{
	return this.indexOf(item) != -1;
};
if(!Array.indexOf) {
	Array.prototype.indexOf = function(item,from)
	{
		if(!from)
			from = 0;
		for(var i=from; i<this.length; i++) {
			if(this[i] === item)
				return i;
		}
		return -1;
	};
}

var Tagmindmap = function(wrapper,settings){
	if(settings.clickFunction)
		this.callWhenClickOnNode = settings.clickFunction;
	else
		this.callWhenClickOnNode = function(node,id){return};
	
	if(settings.dynamicUpdateFunction)
		this.dynamicUpdateFunction = settings.dynamicUpdateFunction;
	else
		this.dynamicUpdateFunction = function(node,id){return {};};		
	this.wrapper = wrapper;
	
	this._setup(settings);
	//this._init_html_elements(wrapper.id);
	
	
	this.controlpanel =new EasyMapController(this,wrapper);
	this._init_html_elements();
	
	var x = this.controlpanel;
	initialT = {translate: {x:0,y:0}, scale: {x:this.settings.zoomLevel,y:this.settings.zoomLevel}};
	x.setTransformation(initialT);
	x.addControl("zoom");
	x.addControl("pan");
	x.addControl("mousepanning");
	x.addControl("mousewheelzooming");


	this.children = {};
	this.parents = {};
};

Tagmindmap.prototype = {
	transform: function(t){

		var compute = false;
		if(this.settings.zoomLevel != t.scale.x) {
			if(t.scale.x > 0){			
			this.settings.zoomLevel = parseFloat(t.scale.x);
		
			}
			compute = true;
		}
	
		if(this.rgraph){
			var c= {x:t.translate.x*t.scale.x, y:t.translate.y*t.scale.y};
		
			this.rgraph.offsetCenter(c.x,c.y);
			if(compute) this.rgraph.compute();
			this.rgraph.plot();
		}
	},
	_setup: function(settings){
		
		
		this.settings = {'arrowheads':false,'maxNodeNameLength':99999,'breadcrumbs': true,'lineColor':'#778899','nodeColor':'#778899','zoomLevel':120, 'ignoreLoneNodes':false,'excludeNodeList': ['excludeLists']}; //put all default settings here
		this.settings.tagcloud = {'smallest': 1, 'largest': 1.6, 'upper':0, 'off': false}; //upper is the maximum sized node
	
		this.graph_showCirclesFlag = false; //shows circles in the mind map
		this.maxNodeNameLength = 0;
		this.displacement = {'x':0, 'y':0};
		this.maxChildrenOnSingleNode = 0;	
		this.thehiddenbridge = "RGRAPHTREEBRIDGE"; //a hidden node which bridges all dislocated nodes.

		this.settings.breadcrumb_startcolor = "#ff0066"; //rgb(0,0,0)
		/*above defaults below read in */
		for(var i in settings){
			this.settings[i] = settings[i];
		}
		this.settings.arrowheads = settings.arrowheads;
		this.settings.breadcrumbs = settings.breadcrumbs;
		this.settings.tagcloud.off = settings.tagcloud.off;
		this.settings.excludeNodeList = settings.excludeNodeList;
		this.settings.ignoreLoneNodes = settings.ignoreLoneNodes;
		this.maxNodeNameLength = settings.maxNodeNameLength;
		this.settings.zoomLevel = settings.zoomLevel;
		var ttmm = this;
	},

	
	_init_html_elements: function(){
			var wrapperID = this.wrapper.id;
			if(!document.getElementById(wrapperID)){ throw (wrapperID + " html element doesn't exist");}

			var canvasID = wrapperID + "_canvas"; //the canvas object ID
			this.labelContainer = wrapperID + "_label_container";
			this.nodeLabelPrefix = canvasID +"_";
			/*setup the divs */
			var wrapper = this.wrapper;
			wrapper.style.position = "relative";
			
			if(!wrapper.style.height){wrapper.style.height = "200px";}
			if(!wrapper.style.width){wrapper.style.width = "200px";}

			var labelContainer = document.createElement("div");
			labelContainer.id=this.labelContainer;
			labelContainer.style.position= 'relative';		
			var canvas = document.createElement("canvas");
			canvas.id = canvasID;
			canvas.width = parseInt(wrapper.style.width);
			canvas.height =parseInt(wrapper.style.height);
			
			wrapper.appendChild(labelContainer);
			wrapper.appendChild(canvas);
			this.canvas = canvas;
			if(config.browser.isIE && G_vmlCanvasManager) {G_vmlCanvasManager.init_(document);} //ie hack - needs changing to work outside tw		
	},
	

	createNodeFromJSON: function(json){

		if(json == {}) return;
		var temp = false;
		var res = false;
		var node1= json['node'];
		if(json['parents']){ 
			for(var i=0; i < json['parents'].length; i++){
				var parent = json['parents'][i];
				temp = this.drawEdge(parent['id'],node1['id'],parent['name'],node1['name'],parent['data'],node1['data']);
				res = temp | res;
			}
		 }

		if(json['children']){
			for(var i=0; i < json['children'].length; i++){
			   	var child = json['children'][i];
				temp = this.drawEdge(node1['id'],child['id'],node1['name'],child['name'],node1['data'],child['data']);
				res = temp | res;
			 }
		}

		if(json['children'] && json['parents']){
		 if(!this.settings.ignoreLoneNodes && json['children'].length ==0 && json['parents'].length == 0)
		   temp = this.drawEdge(this.thehiddenbridge, node1['id'],null,node1['name'],null,node1['data']);
		res = temp | res;
		 }
		
		return res;
	},

	centerOnNode:function(id){
		//var cur =this.getCurrentNodeID();
		//if(cur == id) return;

		this.rgraph.onClick(id);
		
	},

	getCurrentNodeID: function(){
		if(!this.rgraph.graph.root) return false;
		if(this.rgraph.graph.root.id == this.thehiddenbridge) return false;
		else return this.rgraph.graph.root.id;
	},

	setNodeName: function(nodeid,newName){
		var node = this.controller.getNode(nodeid);
		if(node.name != newName){
			node.name = newName;
			if(this.thehiddenbridge != nodeid && this.graph_index) this.graph_index[newName] = nodeid;
		}
	},
	mergeNodeData: function(id,data){

			var node = this.controller.getNode(id);
			if(!node) return;

			for (var key in data){
				if(typeof node.data[key] == 'array')
					node.data[key] = node.data[key].concat(data[key]);
				else
					node.data[key] = data[key];
			}
			
			if(node.data.weight > this.settings.tagcloud.upper) {
				this.settings.tagcloud.upper = node.data.weight;
			}
	},
	setNodeData: function(id,data,newvalue){
		var node = this.controller.getNode(id);
		if(!node) return;
		
		if(!newvalue){
			node.data = data;
		}
		else{
			node.data[data] = newvalue;	
		}

		if(node.data.weight > this.settings.tagcloud.upper) {
			this.settings.tagcloud.upper = node.data.weight;
		}
	
	},

	_nodeInExcludeList: function(id){
		return this.settings.excludeNodeList.contains(id);
	},

	drawEdge: function(id_a,id_b,name_a,name_b,data_a,data_b){
		if(this._nodeInExcludeList(id_a) || this._nodeInExcludeList(id_b)) return false;
		plotNeeded=false;

		if(id_a != "" && id_b != ""){

		  plotNeeded = this._make_connection(id_a,id_b);

		  if(name_a){this.setNodeName(id_a,name_a);}
		  if(name_b){this.setNodeName(id_b,name_b);}
		
		  if(data_a) {this.mergeNodeData(id_a,data_a); }
		  if(data_b) {this.mergeNodeData(id_b,data_b);}

		}  
		return plotNeeded;
	},

	_make_connection: function(a,b){
	  	  var drawn = this._setupMapIfNeeded(a);
		  var node1, node2; 
		  node1 = this.controller.getNode(a);
		  node2 = this.controller.getNode(b);

		  if(node1 && node2){
			if(node1.adjacentTo(node2)) {return false;}

		  }
		  else if(!node1 && !node2) {//neither in graph yet
			drawn = this._make_connection(this.thehiddenbridge,a);  //if neither node is currently in tree, then we need to create a "bridge" to connect the trees
		  
		  }
		
		
		  if(!node1) {node1= new Graph.Node(a,a,{});drawn= true; }//create this node
		  if(!node2) {node2= new Graph.Node(b,b,{});drawn= true; }//create that node

		  if(node1){			
	
			  if(!node1.adjacentTo(node2)){

		
				this.controller.addAdjacence(node1,node2);
				
				node1 = this.controller.getNode(a);
			  	node2 = this.controller.getNode(b);
			
				if(!this.children[a]) this.children[a] = [];
				if(!this.parents[b]) this.parents[b] = [];
				this.children[a].push(b);
				this.parents[b].push(a);

				return true;
			 }
		 }
	},
	deleteNode: function(id){
		var node = this.rgraph.controller.getNode(id);
		//console.log("start",node,"end");
		var parents = node.data.parents;
		var children = node.data.children;
		//console.log(id,parents,children);
		if(children){
			//sort out children
			for(var i=0; i < children.length; i++){
				var childNode = this.rgraph.controller.getNode(children[i]);
			
				var oldparents = childNode.data.parents;
				var newparents = [];
				for(var j=0; j < oldparents.length; j++){
					if(oldparents[j] != id)newparents.push(oldparents[j]);
				}

				this.setNodeData(children[i],"parents",newparents);
				
				if(newparents.length == 0) { //connect it up to the bridge
					this.drawEdge(this.thehiddenbridge,children[i]);
			
				}
			}
		}
			
		//sort out parents
		if(parents){
			for(var i=0; i < parents.length; i++){
				if(parents[i] != this.thehiddenbridge){
					
			
					var parentNode = this.rgraph.controller.getNode(parents[i]);
			
					var oldchildren = parentNode.data.children;
					var newchildren = [];
					for(var j=0; j < oldchildren.length; j++){
						if(oldchildren[j] != id)newchildren.push(oldchildren[j]);
					}
					this.setNodeData(parents[i],"children",newchildren);
				}
			}
		}
				
		this.rgraph.controller.removeNode(id);


	},
	computeThenPlot: function(){
		try{
		  this.rgraph.compute();
		  this.rgraph.plot(); 
		}
		catch(e){
			console.log(e+"in computeThenPlot");
		}
	},

	_trimNodeName: function(node_name){
		if(this.maxNodeNameLength ==0) return "<span>&nbsp;&nbsp;&nbsp;</span>";
		if(this.maxNodeNameLength){
			var nlength = this.maxNodeNameLength;

			  if(node_name.length > nlength)
			    return node_name.substr(0,nlength/2) + "..." + node_name.substr(node_name.length-nlength/2,node_name.length);
			  else
			    return node_name;
		}
		return node_name;
	},

	
	_getController: function(){

  		var ttmm = this;
		    var effectHash = {};
		  var controller =  {
			
			removeNode: function(id){
				
				var el = document.getElementById(this.getNodeLabelPrefix()+id);
				el.parentNode.removeChild(el);
				
				var graph = ttmm.rgraph.graph;
				if(graph) graph.removeNode(id);	
			
			},
			getNode: function(id){
			
			  var n = GraphUtil.getNode(ttmm.rgraph.graph,id); 

			return n;
			},
			addAdjacence: function(node1,node2){
				ttmm.rgraph.graph.addAdjacence(node1,node2);	
			},
			/*some custom defined controller operations (search in RGraph source)*/
			getZoomLevel: function(){	
				return parseFloat(ttmm.settings.zoomLevel);
			},
			setOffset: function(d){ttmm.displacement = d;},
			getOffset: function(){return ttmm.displacement;},
			getNodeLabelContainer: function(){
				return ttmm.labelContainer;
			},
			getNodeLabelPrefix: function(){return ttmm.nodeLabelPrefix;},
		  	onBeforeCompute: function(node) {									
				ttmm.createNodeFromJSON(ttmm.dynamicUpdateFunction(node.id));
				if(ttmm.settings.breadcrumbs) {
					ttmm.setNodeData(node.id,"color",ttmm.settings.breadcrumb_startcolor);	
				}
			},
		  	getName: function(node1, node2) {
		  		for(var i=0; i<node1.data.length; i++) {
		  			var dataset = node1.data[i];
		  			if(dataset.key == node2.name) return dataset.value;
		  		}
  		
				for(var i=0; i<node2.data.length; i++) {
		  			var dataset = node2.data[i];
		  			if(dataset.key == node1.name) return dataset.value;
		  		}
		  	},	
		
		  	onCreateLabel: function(domElement, node) {			
			}

			,attachClickFunction: function(domElement,node){	
				if(node.id == this.thehiddenbridge) return;
				
				var clickfunction = function(event){
					
					if(ttmm.rgraph.root == node.id){ //special case for when node is already centered
						ttmm.callWhenClickOnNode(node,ttmm.wrapper.id,event);
						return;	
					} 
					else{ //need to center first
						var t = ttmm.controlpanel.transformation;
						t.translate = {x:0,y:0};
						ttmm.controlpanel.setTransformation(t);
						ttmm.rgraph.onClick(node.id);
						var todo = function(){

							ttmm.callWhenClickOnNode(node,ttmm.wrapper.id,event);
						};
						ttmm._afterComputeFunction = todo;
									

					}
					return false;
				};
				
				if(domElement.addEvent){ //for ie
					domElement.addEvent('click',clickfunction);
				}
				else {
					domElement.onclick = clickfunction;
				}
				
			}
			,getMaxChildren: function(){
				var max =0,num;
				for(var i in ttmm.children){
					if(ttmm.children[i])
						num = ttmm.children[i].length;
					else
						num =0;
					if(num > max) max = num;
				}
				return max;
			},
		  	
			calculateNodeWeight: function(node){
				var weight=0, u=0;
				
				if(node.data.weight) { //user has defined some sort of weight
					weight = parseFloat(node.data.weight);
					u =parseFloat(ttmm.settings.tagcloud.upper);
				}
				else{ //just take number of children
					
					if(ttmm.children[node.id]){
						weight = ttmm.children[node.id].length;
					}
					u = this.getMaxChildren();
				}
				
				var s,l;
				
				if(ttmm.settings.tagcloud.smallest){
					s = parseFloat(ttmm.settings.tagcloud.smallest);
				}
				else{	
					s = 0.5;
				} 
				if(ttmm.settings.tagcloud.largest) {
					l =parseFloat(ttmm.settings.tagcloud.largest);
				}
				else{
					l = 2;
				}
				
				var fontsize = s + ((l - s) * parseFloat(weight / u));	
				//console.log(s,l,weight,u,fontsize);
				
				return fontsize;
			},
			onPlaceLabel: function(domElement, node) {
				domElement.innerHTML = ""; //quick and dirty flush
 				if(node.id != ttmm.thehiddenbridge){
						if(node.data.color) domElement.style.color = node.data.color;
							
						if(node.data.title){
							domElement.title = node.data.title;
						}
						else{
							domElement.title = node.name;
						}
						
					
						var prefix, nodeLabel,suffix;
						if(node.data.nodeLabelPrefix) prefix =node.data.nodeLabelPrefix;
						
						if(prefix){
							prefix.setAttribute("class","nodeLabelPrefix");
							domElement.appendChild(prefix);
						}
					
						if(!node.data.label){
							nodeLabel = document.createElement("span");
						 	var labelText = ttmm._trimNodeName(node.name);
							nodeLabel.appendChild(document.createTextNode(labelText));
						}
						else{
							nodeLabel = node.data.label;
						}
						
						if(!ttmm.settings.tagcloud.off){
							var fontsize = this.calculateNodeWeight(node);

						
							
							nodeLabel.style.fontSize = fontsize + "em";
							
						}
					
						nodeLabel.setAttribute("class","nodeLabel");
				
						this.attachClickFunction(nodeLabel,node);

						domElement.appendChild(nodeLabel);
						
					
						if(node.data.nodeLabelSuffix) suffix =node.data.nodeLabelSuffix;
						if(suffix){	
							suffix.setAttribute("class","nodeLabelSuffix");																						 
							domElement.appendChild(suffix);		
						}
						
						
				}
				else domElement.style.display = "none";

			
				var left = parseInt(domElement.style.left);
				domElement.style.width = '';
				domElement.style.height = '';
				var w = domElement.offsetWidth;
				domElement.style.left = (left - w /2) + 'px';


			},
	
			onAfterCompute: function() {
				if(ttmm._afterComputeFunction){
					ttmm._afterComputeFunction();	
					ttmm._afterComputeFunction = false;			
				}
						
			},
	
			onBeforePlotLine: function(adj){
			  lineW = ttmm.canvas.getContext().lineWidth;  
		      nodeid = adj.nodeFrom.id;
			  nodeid2 = adj.nodeTo.id;
			  if(nodeid == ttmm.thehiddenbridge || nodeid2 == ttmm.thehiddenbridge){
				ttmm.canvas.getContext().lineWidth = "0";
				}
			  else ttmm.canvas.getContext().lineWidth = "1";

			},
	
			onAfterPlotLine: function(adj){
				var l =this.getNodeLabelContainer();				
				//document.getElementById(l).innerHTML = "";	
				var context = ttmm.canvas.getContext();
				var canvas = ttmm.canvas;
				var node = adj.nodeFrom, child = adj.nodeTo;

				var pos = node.pos.toComplex();
				var posChild = child.pos.toComplex();
				var d = this.getOffset();//jon
				
				//draw arrowhead.. (angle needs to be calculated)
				if(ttmm.settings.arrowheads){
					if(node.id == ttmm.thehiddenbridge)return;
					//console.log("arrowhead from",node.id, "to",child.id)
					canvas.path('stroke', function(context) {
					var r = 20;
					var ctx = context;
					ctx.save();
					//ctx.beginPath();
					ctx.translate(posChild.x +d.x,posChild.y+d.y);
					var o = parseFloat(posChild.y-pos.y);
					var a = parseFloat(posChild.x -pos.x);
   
					if(a !=0){
					var rad = Math.atan2(o,a);
					ctx.rotate(rad);
					ctx.moveTo(2,0);
					ctx.lineTo(-r,-4);
					ctx.lineTo(-r,4);

				    ctx.lineTo(2,0);
					ctx.fill();		
					}

					ctx.restore();

				});
			
			}
			
			ttmm.canvas.getContext().lineWidth = "1";

			}
  	
		  };
  
		return controller;

},

	_setupMapIfNeeded: function(lastOpenNode){
		
		if(!this.canvas){
			this._init_html_elements();
		}
		var ctx = this.canvas.getContext;
		if(!ctx) {console.log("no context available! Please install ExplorerCanvas");}

		if(this.graphloaded) return false;
		
		this.graphloaded = true;
		//this._firstnode =lastOpenNode;


	  	var json = {"id":this.thehiddenbridge,"children":[{"id":lastOpenNode,"name":lastOpenNode, "data":{"parents":[this.thehiddenbridge], "children":[]}, "children":[]}], 'data':{"parents":[], "children":[lastOpenNode]}};
		
		json.data = {};
		json.data.nodraw=true;
	    	this.canvas= new Canvas(this.canvas.id, this.settings.nodeColor, this.settings.lineColor); 
		controller = this._getController();	
		this.rgraph= new RGraph(this.canvas, controller,this.labelContainer);
  


	  	Config['drawConcentricCircles'] = this.graph_showCirclesFlag;
 
	 	this.rgraph.loadTreeFromJSON(json);

		this.controller = controller;
		this.rgraph.compute();
		this.centerOnNode(lastOpenNode);
		//this.rgraph.graph.root = this.controller.getNode(lastOpenNode);	

	  		
				  //if(!this.rgraph.graph.root) this.rgraph.graph.root =this.controller.getNode(this._firstNode);
		return true;
	
	}


};

}}}
/*
 * File: RGraph.js
 * 
 * Author: Nicolas Garcia Belmonte
 * 
 * Copyright: Copyright 2008 by Nicolas Garcia Belmonte.
 *
 * Homepage: <http://thejit.org>
 * 
 * Version: 1.0.7a
 * 
 * License: BSD License
 * 
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the organization nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Nicolas Garcia Belmonte ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Nicolas Garcia Belmonte BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 */

/*
   Object: $_

   Provides some common utility functions.
*/
var $_ = {
	fn: function() { return function() {}; },

	merge: function(){
		var mix = {};
		for (var i = 0, l = arguments.length; i < l; i++){
			var object = arguments[i];
			if (typeof object != 'object') continue;
			for (var key in object){
				var op = object[key], mp = mix[key];
				mix[key] = (mp && typeof op == 'object' && typeof mp == 'object') ? this.merge(mp, op) : this.unlink(op);
			}
		}
		return mix;
	},

	unlink: function (object){
		var unlinked = null;
		if(this.isArray(object)) {
				unlinked = [];
				for (var i = 0, l = object.length; i < l; i++) unlinked[i] = this.unlink(object[i]);
		} else if(this.isObject(object)) {
				unlinked = {};
				for (var p in object) unlinked[p] = this.unlink(object[p]);
		} else return object;

		return unlinked;
	},
	
	isArray: function(obj) {
		return obj.constructor.toString().match(/array/i);
	},
	
	isString: function(obj) {
		return obj.constructor.toString().match(/string/i);
	},
	
	isObject: function(obj) {
		return obj.constructor.toString().match(/object/i);
	}
} ;


/*
   Class: Canvas

   A multi-purpose Canvas object decorator.
*/

/*
   Constructor: Canvas

   Canvas initializer.

   Parameters:

      canvasId - The canvas tag id.
      fillStyle - (optional) fill color style. Default's to black
      strokeStyle - (optional) stroke color style. Default's to black

   Returns:

      A new Canvas instance.
*/
var Canvas= function (canvasId, fillStyle, strokeStyle) {
	//browser supports canvas element
		this.canvasId= canvasId;
		this.fillStyle = fillStyle;
		this.strokeStyle = strokeStyle;
		//canvas element exists
		if((this.canvas= document.getElementById(this.canvasId)) 
			&& this.canvas.getContext) {
		      this.ctx = this.canvas.getContext('2d');
		      this.ctx.fillStyle = fillStyle || 'black';
		      this.ctx.strokeStyle = strokeStyle || 'white';
			  this.setPosition();
			  this.translateToCenter();
		
		} else {
			throw "Canvas object could not initialize.";
		}
	
};


Canvas.prototype= {
	/*
	   Method: getContext

	   Returns:
	
	      Canvas context handler.
	*/
	getContext: function () {
		return this.ctx;
	},

	/*
	   Method: setPosition
	
	   Calculates canvas absolute position on HTML document.
	*/	
	setPosition: function() {
		var obj= this.canvas;
		var curleft = curtop = 0;
		if (obj.offsetParent) {
			curleft = obj.offsetLeft
			curtop = obj.offsetTop
			while (obj = obj.offsetParent) {
				curleft += obj.offsetLeft
				curtop += obj.offsetTop
			}
		}
		this.position= { x: curleft, y: curtop };
	},

	/*
	   Method: getPosition

	   Returns:
	
	      Canvas absolute position to the HTML document.
	*/
	getPosition: function() {
		return this.position;
	},

	/*
	   Method: clear
	
	   Clears the canvas object.
	*/		
	clear: function () {
		this.ctx.clearRect(-this.getSize().x / 2, -this.getSize().x / 2, this.getSize().x, this.getSize().x);
	},

	/*
	   Method: drawConcentricCircles
	
	   Draws concentric circles. Receives an integer specifying the number of concentric circles.
	*/		
	drawConcentricCircles: function (elem) {
		var config = Config;
		var times = elem || 6;
		var c = this.ctx;
		c.strokeStyle = config.concentricCirclesColor;
		var pi2 = Math.PI*2;
		for(var i=1; i<=times; i++) {
		    c.beginPath();
		  	c.arc(0, 0, (i*config.levelDistance), 0, pi2, true);
		  	c.stroke();
			c.closePath();
		}
		c.strokeStyle = this.strokeStyle;
	},
	
	/*
	   Method: translateToCenter
	
	   Translates canvas coordinates system to the center of the canvas object.
	*/
	translateToCenter: function() {
		this.ctx.translate(this.canvas.width / 2, this.canvas.height / 2);
	},
	

	/*
	   Method: getSize

	   Returns:
	
	      An object that contains the canvas width and height.
	      i.e. { x: canvasWidth, y: canvasHeight }
	*/
	getSize: function () {
		var width = this.canvas.width;
		var height = this.canvas.height;
		return { x: width, y: height };
	},
	
	/*
	   Method: path
	   
	  Performs a _beginPath_ executes _action_ doing then a _type_ ('fill' or 'stroke') and closing the path with closePath.
	*/
	path: function(type, action) {
		this.ctx.beginPath();
		action(this.ctx);
		this.ctx[type]();
		this.ctx.closePath();
	}
	
};


/*
   Class: Complex
	
	 A multi-purpose Complex Class with common methods.

*/


/*
   Constructor: Complex

   Complex constructor.

   Parameters:

      re - A real number.
      im - An real number representing the imaginary part.


   Returns:

      A new Complex instance.
*/
var Complex= function() {
	if (arguments.length > 1) {
		this.x= arguments[0];
		this.y= arguments[1];
		
	} else {
		this.x= null;
		this.y= null;
	}
	
};

Complex.prototype= {
	/*
	   Method: clone
	
	   Returns a copy of the current object.
	
	   Returns:
	
	      A copy of the real object.
	*/
	clone: function() {
		return new Complex(this.x, this.y);
	},

	/*
	   Method: toPolar
	
	   Transforms cartesian to polar coordinates.
	
	   Returns:
	
	      A new <Polar> instance.
	*/
	
	toPolar: function() {
		var rho = this.norm();
		var atan = Math.atan2(this.y, this.x);
		if(atan < 0) atan += Math.PI * 2;
		return new Polar(atan, rho);
	},
	/*
	   Method: norm
	
	   Calculates the complex norm.
	
	   Returns:
	
	      A real number representing the complex norm.
	*/
	norm: function () {
		return Math.sqrt(this.squaredNorm());
	},
	
	/*
	   Method: squaredNorm
	
	   Calculates the complex squared norm.
	
	   Returns:
	
	      A real number representing the complex squared norm.
	*/
	squaredNorm: function () {
		return this.x*this.x + this.y*this.y;
	},

	/*
	   Method: add
	
	   Returns the result of adding two complex numbers.
	   Does not alter the original object.

	   Parameters:
	
	      pos - A Complex initialized instance.
	
	   Returns:
	
	     The result of adding two complex numbers.
	*/
	add: function(pos) {
		return new Complex(this.x + pos.x, this.y + pos.y);
	},

	/*
	   Method: prod
	
	   Returns the result of multiplying two complex numbers.
	   Does not alter the original object.

	   Parameters:
	
	      pos - A Complex initialized instance.
	
	   Returns:
	
	     The result of multiplying two complex numbers.
	*/
	prod: function(pos) {
		return new Complex(this.x*pos.x - this.y*pos.y, this.y*pos.x + this.x*pos.y);
	},

	/*
	   Method: conjugate
	
	   Returns the conjugate por this complex.

	   Returns:
	
	     The conjugate por this complex.
	*/
	conjugate: function() {
		return new Complex(this.x, -this.y);
	},


	/*
	   Method: scale
	
	   Returns the result of scaling a Complex instance.
	   Does not alter the original object.

	   Parameters:
	
	      factor - A scale factor.
	
	   Returns:
	
	     The result of scaling this complex to a factor.
	*/
	scale: function(factor) {
		return new Complex(this.x * factor, this.y * factor);
	},

	/*
	   Method: equals
	
	   Comparison method.
	*/
	equals: function(c) {
		return this.x == c.x && this.y == c.y;
	},

	/*
	   Method: $add
	
	   Returns the result of adding two complex numbers.
	   Alters the original object.

	   Parameters:
	
	      pos - A Complex initialized instance.
	
	   Returns:
	
	     The result of adding two complex numbers.
	*/
	$add: function(pos) {
		this.x += pos.x; this.y += pos.y;
		return this;	
	},
	
	/*
	   Method: $prod
	
	   Returns the result of multiplying two complex numbers.
	   Alters the original object.

	   Parameters:
	
	      pos - A Complex initialized instance.
	
	   Returns:
	
	     The result of multiplying two complex numbers.
	*/
	$prod:function(pos) {
		var x = this.x, y = this.y
		this.x = x*pos.x - y*pos.y;
		this.y = y*pos.x + x*pos.y;
		return this;
	},
	
	/*
	   Method: $conjugate
	
	   Returns the conjugate for this complex.
	   Alters the original object.

	   Returns:
	
	     The conjugate for this complex.
	*/
	$conjugate: function() {
		this.y = -this.y;
		return this;
	},
	
	/*
	   Method: $scale
	
	   Returns the result of scaling a Complex instance.
	   Alters the original object.

	   Parameters:
	
	      factor - A scale factor.
	
	   Returns:
	
	     The result of scaling this complex to a factor.
	*/
	$scale: function(factor) {
		this.x *= factor; this.y *= factor;
		return this;
	},
	
	/*
	   Method: $div
	
	   Returns the division of two complex numbers.
	   Alters the original object.

	   Parameters:
	
	      pos - A Complex number.
	
	   Returns:
	
	     The result of scaling this complex to a factor.
	*/
	$div: function(pos) {
		var x = this.x, y = this.y;
		var sq = pos.squaredNorm();
		this.x = x * pos.x + y * pos.y; this.y = y * pos.x - x * pos.y;
		return this.$scale(1 / sq);
	}
};

Complex.KER = new Complex(0, 0);

/*
   Class: Polar

   A multi purpose polar representation.

*/

/*
   Constructor: Polar

   Polar constructor.

   Parameters:

      theta - An angle.
      rho - The norm.


   Returns:

      A new Polar instance.
*/

var Polar = function(theta, rho) {
	this.theta = theta;
	this.rho = rho;
};

Polar.prototype = {
	/*
	   Method: clone
	
	   Returns a copy of the current object.
	
	   Returns:
	
	      A copy of the real object.
	*/
	clone: function() {
		return new Polar(this.theta, this.rho);
	},

	/*
	   Method: toComplex
	
	    Translates from polar to cartesian coordinates and returns a new <Complex> instance.
	
	   Returns:
	
	      A new Complex instance.
	*/
	toComplex: function() {
		return new Complex(Math.cos(this.theta), Math.sin(this.theta)).$scale(this.rho);
	},

	/*
	   Method: add
	
	    Adds two <Polar> instances.
	
	   Returns:
	
	      A new Polar instance.
	*/
	add: function(polar) {
		return new Polar(this.theta + polar.theta, this.rho + polar.rho);
	},
	
	/*
	   Method: scale
	
	    Scales a polar norm.
	
	   Returns:
	
	      A new Polar instance.
	*/
	scale: function(number) {
		return new Polar(this.theta, this.rho * number);
	},
	
	/*
	   Method: equals
	
	   Comparison method.
	*/
	equals: function(c) {
		return this.theta == c.theta && this.rho == c.rho;
	},
	
	/*
	   Method: $add
	
	    Adds two <Polar> instances affecting the current object.
	
	   Returns:
	
	      The changed object.
	*/
	$add: function(polar) {
		this.theta = this.theta + polar.theta; this.rho += polar.rho;
		return this;
	},

	/*
	   Method: $madd
	
	    Adds two <Polar> instances affecting the current object. The resulting theta angle is modulo 2pi.
	
	   Returns:
	
	      The changed object.
	*/
	$madd: function(polar) {
		this.theta = (this.theta + polar.theta) % (Math.PI * 2); this.rho += polar.rho;
		return this;
	},

	
	/*
	   Method: $scale
	
	    Scales a polar instance affecting the object.
	
	   Returns:
	
	      The changed object.
	*/
	$scale: function(number) {
		this.rho *= number;
		return this;
	},
	
	/*
	   Method: interpolate
	
	    Calculates a polar interpolation between two points at a given delta moment.
	
	   Returns:
	
	      A new Polar instance representing an interpolation between _this_ and _elem_
	*/
	interpolate: function(elem, delta) {
		var pi2 = Math.PI * 2;
		var ch = function(t) {
			return (t < 0)? (t % pi2) + pi2 : t % pi2;
		};
		var tt = ch(this.theta) , et = ch(elem.theta);
		var sum;
		if(Math.abs(tt - et) > Math.PI) {
			if(tt - et > 0) {
				sum =ch((et + ((tt - pi2) - et)* delta)) ;
			} else {
				sum =ch((et - pi2 + (tt - (et - pi2))* delta));
			}
			
		} else {
				sum =ch((et + (tt - et)* delta)) ;
		}
		var  t = (sum);
		var r = (this.rho - elem.rho) * delta + elem.rho;
		return new Polar(t, r);
	}
};

Polar.KER = new Polar(0, 0);


/*
   Object: Config

   <RGraph> global configuration object. Contains important properties to enable customization and proper behavior for the <RGraph>.
*/

var Config= {
		//Property: labelContainer
		//Id for label container. The label container is a div dom element that must be explicitly added to your page in order to enable the RGraph.
		labelContainer: 'label_container',
		
		//Property: drawConcentricCircles
		//show/hide concentricCircles
		drawConcentricCircles: 4,
		
		//Property: concentricCirclesColor
		//The color of the concentric circles
		concentricCirclesColor: '#444',

		//Property: levelDistance
		//The actual distance between levels
		levelDistance: 100,
		
		//Property: nodeRadius
		//The radius of the nodes displayed
		nodeRadius: 4,
		
		//Property: allowVariableNodeDiameters
		//Set this to true if you want your node diameters to be proportional to you first dataset object value property (i.e _data[0].value_).
		//This will allow you to represent weighted tree/graph nodes.
		allowVariableNodeDiameters: false,
		
		//Property: nodeRangeDiameters
		//Diameters range. For variable node weights.
		nodeRangeDiameters: {
			min: 10,
			max: 35
		},
			
		//Property: nodeRangeValues
		// The interval of the values of the first object of your dataSet.
		// A superset of the values can also be specified.
		nodeRangeValues: {
			min: 1,
			max: 35
		},

		//Property: fps
		//animation frames per second
		fps:40,
		
		//Property: animationTime
		animationTime: 2500,
		
		//Property: interpolation
		interpolation: 'linear'
};

/*
   Object: GraphUtil

   A multi purpose object to do graph traversal and processing.
*/
var GraphUtil = {
	/*
	   Method: filter
	
	   For internal use only. Provides a filtering function based on flags.
	*/
	filter: function(param) {
		if(!param || !$_.isString(param)) return function() { return true; };
		var props = param.split(" ");
		return function(elem) {
			for(var i=0; i<props.length; i++) if(elem[props[i]]) return false;
			return true;
		};
	},
	/*
	   Method: getNode
	
	   Returns a graph's node with a specified _id_.
	*/
	getNode: function(graph, id) {
		return graph.getNode(id);
	},
	
	/*
	   Method: eachNode
	
	   Iterates over graph nodes performing an action.
	*/
	eachNode: function(graph, action, flags) {
		var filter = this.filter(flags);
		for(var i in graph.nodes) if(filter(graph.nodes[i])) action(graph.nodes[i]);
	},
	
	/*
	   Method: eachAdjacency
	
	   Iterates over a _node_ adjacencies applying the _action_ function.
	*/
	eachAdjacency: function(node, action, flags) {
		var adj = node.adjacencies, filter = this.filter(flags);
		for(var id in adj) if(filter(adj[id])) action(adj[id], id);
	},

	/*
	   Method: computeLevels
	
	   Performs a BFS traversal setting correct level for nodes.
	*/
	computeLevels: function(graph, id, flags) {
		var filter = this.filter(flags);
		this.eachNode(graph, function(elem) {
			elem._flag = false;
			elem._depth = -1;
		}, flags);
		var root = graph.getNode(id);
		root._depth = 0;
		var queue = [root];
		while(queue.length != 0) {
			var node = queue.pop();
			node._flag = true;
			this.eachAdjacency(node, function(adj) {
				var n = adj.nodeTo;
				if(n._flag == false && filter(n)) {
					if(n._depth < 0) n._depth = node._depth + 1;
					queue.unshift(n);
				}
			}, flags);
		}
	},

	/*
	   Method: eachBFS
	
	   Performs a BFS traversal of a graph beginning by the node of id _id_ and performing _action_ on each node.
	   This traversal ignores nodes or edges having the property _ignore_ setted to _true_.
	*/
	eachBFS: function(graph, id, action, flags) {
		var filter = this.filter(flags);
		this.clean(graph);
		var queue = [graph.getNode(id)];
		while(queue.length != 0) {
			var node = queue.pop();
			node._flag = true;
			action(node, node._depth);
			this.eachAdjacency(node, function(adj) {
				var n = adj.nodeTo;
				if(n._flag == false && filter(n)) {
					n._flag = true;
					queue.unshift(n);
				}
			}, flags);
		}
	},
	
	/*
	   Method: eachSubnode
	
	   After a BFS traversal the _depth_ property of each node has been modified. Now the graph can be traversed as a tree. This method iterates for each subnode that has depth larger than the specified node.
	*/
	eachSubnode: function(graph, node, action, flags) {
		var d = node._depth, filter = this.filter(flags);
		this.eachAdjacency(node, function(adj) {
			var n = adj.nodeTo;
			if(n._depth > d && filter(n)) action(n);
		}, flags);
	},
	
	/*
	   Method: getSubnodes
	
	   Collects all subnodes for a specified node. The _level_ parameter filters nodes having relative depth of _level_ from the root node.
	*/
	getSubnodes: function(graph, id, level, flags) {
		var ans = new Array(), that = this, node = graph.getNode(id);
		(function(graph, node) {
			var fn = arguments.callee;
			if(!level || level <= node._depth)	ans.push(node);
			that.eachSubnode(graph, node, function(elem) {
				fn(graph, elem);
			}, flags);
		})(graph, node);
		return ans;
	},

	/*
	   Method: getParents
	
	   Returns all nodes having a depth that is less than the node's depth property.
	*/
	getParents: function(graph, node) {
		var adj = node.adjacencies;
		var ans = new Array();
		this.eachAdjacency(node, function(adj) {
			var n = adj.nodeTo;
			if(n._depth < node._depth) ans.push(n);
		});
		return ans;
	},
	
	/*
	   Method: clean
	
	   Cleans flags from nodes (by setting the _flag_ property to false).
	*/
	clean: function(graph) { this.eachNode(graph, function(elem) { elem._flag = false; }); }
};

/*
   Object: GraphOp

   An object holding unary and binary graph operations such as removingNodes, removingEdges, adding two graphs and morphing.
*/
var GraphOp = {

	options: {
		type: 'nothing',
		duration: 2000,
		fps:30
	},
	
	/*
	   Method: removeNode
	
	   Removes one or more nodes from the visualization. It can also perform several animations like fading sequentially, fading concurrently, iterating or replotting.

	   Parameters:
	
	      viz - The visualization object (an RGraph instance in this case).
	      node - The node's id. Can also be an array having many ids.
	      opt - Animation options. It's an object with two properties: _type_, which can be _nothing_, _replot_, _fade:seq_,  _fade:con_ or _iter_. The other property is the _duration_ in milliseconds. 
	
	*/
	removeNode: function(viz, node, opt) {
		var options = $_.merge(viz.controller, this.options, opt);
		var n = $_.isString(node)? [node] : node;
		switch(options.type) {
			case 'nothing':
				for(var i=0; i<n.length; i++) 	viz.graph.removeNode(n[i]);
				break;
			
			case 'replot':
				this.removeNode(viz, n, { type: 'nothing' });
				GraphPlot.clearLabels(viz);
				viz.refresh();
				break;
			
			case 'fade:seq': case 'fade':
				var GPlot = GraphPlot, that = this;
				//set alpha to 0 for nodes to remove.
				for(var i=0; i<n.length; i++) {
					var nodeObj = viz.graph.getNode(n[i]);
					nodeObj.endAlpha = 0;
				}
				GPlot.animate(viz, $_.merge(options, {
					modes: ['fade:nodes'],
					onComplete: function() {
						that.removeNode(viz, n, { type: 'nothing' });
						GPlot.clearLabels(viz);
						viz.compute('endPos');
						GPlot.animate(viz, $_.merge(options, {
							modes: ['linear']
						}));
					}
				}));
				break;
			
			case 'fade:con':
				var GPlot = GraphPlot, that = this;
				//set alpha to 0 for nodes to remove. Tag them for being ignored on computing positions.
				for(var i=0; i<n.length; i++) {
					var nodeObj = viz.graph.getNode(n[i]);
					nodeObj.endAlpha = 0;
					nodeObj.ignore = true;
				}
				viz.compute('endPos');
				GPlot.animate(viz, $_.merge(options, {
					modes: ['fade:nodes', 'linear'],
					onComplete: function() {
						that.removeNode(viz, n, { type: 'nothing' });
					}
				}));
				break;
			
			case 'iter':
				var that = this, GPlot = GraphPlot;
				GPlot.sequence(viz, {
					condition: function() { return n.length != 0; },
					step: function() { that.removeNode(viz, n.shift(), { type: 'nothing' });  GPlot.clearLabels(viz); },
					onComplete: function() { options.onComplete(); },
					duration: Math.ceil(options.duration / n.length)
				});
				break;
				
			default: this.doError();
		}
	},
	
	/*
	   Method: removeEdge
	
	   Removes one or more edges from the visualization. It can also perform several animations like fading sequentially, fading concurrently, iterating or replotting.

	   Parameters:
	
	      viz - The visualization object (an RGraph instance in this case).
	      vertex - An array having two strings which are the ids of the nodes connected by this edge: ['id1', 'id2']. Can also be a two dimensional array holding many edges: [['id1', 'id2'], ['id3', 'id4'], ...].
	      opt - Animation options. It's an object with two properties: _type_, which can be _nothing_, _replot_, _fade:seq_,  _fade:con_ or _iter_. The other property is the _duration_ in milliseconds. 
	
	*/
	removeEdge: function(viz, vertex, opt) {
		var options = $_.merge(viz.controller, this.options, opt);
		var v = $_.isString(vertex[0])? [vertex] : vertex;
		switch(options.type) {
			case 'nothing':
				for(var i=0; i<v.length; i++) 	viz.graph.removeAdjacence(v[i][0], v[i][1]);
				break;
			
			case 'replot':
				this.removeEdge(viz, v, { type: 'nothing' });
				viz.refresh();
				break;
			
			case 'fade:seq': case 'fade':
				var GPlot = GraphPlot, that = this;
				//set alpha to 0 for nodes to remove.
				for(var i=0; i<v.length; i++) {
					var adjs = viz.graph.getAdjacence(v[i][0], v[i][1]);
					if(adjs) {
						adjs[0].endAlpha = 0;
						adjs[1].endAlpha = 0;
					}
				}
				GPlot.animate(viz, $_.merge(options, {
					modes: ['fade:vertex'],
					onComplete: function() {
						that.removeEdge(viz, v, { type: 'nothing' });
						viz.compute('endPos');
						GPlot.animate(viz, $_.merge(options, {
							modes: ['linear']
						}));
					}
				}));
				break;
			
			case 'fade:con':
				var GPlot = GraphPlot, that = this;
				//set alpha to 0 for nodes to remove. Tag them for being ignored when computing positions.
				for(var i=0; i<v.length; i++) {
					var adjs = viz.graph.getAdjacence(v[i][0], v[i][1]);
					if(adjs) {
						adjs[0].endAlpha = 0;
						adjs[0].ignore = true;
						adjs[1].endAlpha = 0;
						adjs[1].ignore = true;
					}
				}
				viz.compute('endPos');
				GPlot.animate(viz, $_.merge(options, {
					modes: ['fade:vertex', 'linear'],
					onComplete: function() {
						that.removeEdge(viz, v, { type: 'nothing' });
					}
				}));
				break;
			
			case 'iter':
				var that = this, GPlot = GraphPlot;
				GPlot.sequence(viz, {
					condition: function() { return v.length != 0; },
					step: function() { that.removeEdge(viz, v.shift(), { type: 'nothing' }); GPlot.clearLabels(viz); },
					onComplete: function() { options.onComplete(); },
					duration: Math.ceil(options.duration / v.length)
				});
				break;
				
			default: this.doError();
		}
	},
	
	/*
	   Method: sum
	
	   Adds a new graph to the visualization. The json graph (or tree) must at least have a common node with the current graph plotted by the visualization. The resulting graph can be defined as follows: <http://mathworld.wolfram.com/GraphSum.html>

	   Parameters:
	
	      viz - The visualization object (an RGraph instance in this case).
	      json - A json tree <http://blog.thejit.org/2008/04/27/feeding-json-tree-structures-to-the-jit/>, a json graph <http://blog.thejit.org/2008/07/02/feeding-json-graph-structures-to-the-jit/> or an extended json graph <http://blog.thejit.org/2008/08/05/weighted-nodes-weighted-edges/>.
	      opt - Animation options. It's an object with two properties: _type_, which can be _nothing_, _replot_, _fade:seq_,  or _fade:con_. The other property is the _duration_ in milliseconds. 
	
	*/
	sum: function(viz, json, opt) {
		var options = $_.merge(viz.controller, this.options, opt), root = viz.root;
		viz.root = opt.id || viz.root;
		switch(options.type) {
			case 'nothing':
				var graph = viz.construct(json), GUtil = GraphUtil;
				GUtil.eachNode(graph, function(elem) {
					GUtil.eachAdjacency(elem, function(adj) {
						viz.graph.addAdjacence(adj.nodeFrom, adj.nodeTo, adj.data);
					});
				});
				break;
			
			case 'replot':
				this.sum(viz, json, { type: 'nothing' });
				viz.refresh();
				break;
			
			case 'fade:seq': case 'fade': case 'fade:con':
				var GUtil = GraphUtil, GPlot = GraphPlot, that = this, graph = viz.construct(json);
				//set alpha to 0 for nodes to add.
				var fadeEdges = this.preprocessSum(viz, graph);
				var modes = !fadeEdges? ['fade:nodes'] : ['fade:nodes', 'fade:vertex'];
				viz.compute('endPos');
				if(options.type != 'fade:con') {
					GPlot.animate(viz, $_.merge(options, {
						modes: ['linear'],
						onComplete: function() {
							GPlot.animate(viz, $_.merge(options, {
								modes: modes,
								onComplete: function() {
									options.onComplete();
								}
							}));
						}
					}));
				} else {
					GUtil.eachNode(viz.graph, function(elem) {
						if(elem.id != root && elem.pos.equals(Polar.KER)) elem.pos = elem.startPos = elem.endPos;
					});
					GPlot.animate(viz, $_.merge(options, {
						modes: ['linear'].concat(modes),
						onComplete: function() {
							options.onComplete();
						}
					}));
				}
				break;

			default: this.doError();
		}
	},
	
	/*
	   Method: morph
	
	   This method will _morph_ the current visualized graph into the new _json_ representation passed in the method. Can also perform multiple animations. The _json_ object must at least have the root node in common with the current visualized graph.

	   Parameters:
	
	      viz - The visualization object (an RGraph instance in this case).
	      json - A json tree <http://blog.thejit.org/2008/04/27/feeding-json-tree-structures-to-the-jit/>, a json graph <http://blog.thejit.org/2008/07/02/feeding-json-graph-structures-to-the-jit/> or an extended json graph <http://blog.thejit.org/2008/08/05/weighted-nodes-weighted-edges/>.
	      opt - Animation options. It's an object with two properties: _type_, which can be _nothing_, _replot_, or _fade_. The other property is the _duration_ in milliseconds. 
	
	*/
	morph: function(viz, json, opt) {
		var options = $_.merge(viz.controller, this.options, opt), root = viz.root;
		viz.root = opt.id || viz.root;
		switch(options.type) {
			case 'nothing':
				var graph = viz.construct(json), GUtil = GraphUtil;
				GUtil.eachNode(graph, function(elem) {
					GUtil.eachAdjacency(elem, function(adj) {
						viz.graph.addAdjacence(adj.nodeFrom, adj.nodeTo, adj.data);
					});
				});
				GUtil.eachNode(viz.graph, function(elem) {
					GUtil.eachAdjacency(elem, function(adj) {
						if(!graph.getAdjacence(adj.nodeFrom.id, adj.nodeTo.id)) {
							viz.graph.removeAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
						}
						if(!viz.graph.hasNode(elem.id)) viz.graph.removeNode(elem.id);
					});
				});
				
				break;
			
			case 'replot':
				this.morph(viz, json, { type: 'nothing' });
				viz.refresh();
				break;
			
			case 'fade:seq': case 'fade': case 'fade:con':
				var GUtil = GraphUtil, GPlot = GraphPlot, that = this, graph = viz.construct(json);
				//preprocessing for adding nodes.
				var fadeEdges = this.preprocessSum(viz, graph);
				//preprocessing for nodes to delete.
				GUtil.eachNode(viz.graph, function(elem) {
					if(!graph.hasNode(elem.id)) {
						elem.alpha = 1; elem.startAlpha = 1; elem.endAlpha = 0; elem.ignore = true;
					}
				});	
				GUtil.eachNode(viz.graph, function(elem) {
					if(elem.ignore) return;
					GUtil.eachAdjacency(elem, function(adj) {
						if(adj.nodeFrom.ignore || adj.nodeTo.ignore) return;
						var nodeFrom = graph.getNode(adj.nodeFrom.id);
						var nodeTo = graph.getNode(adj.nodeTo.id);
						if(!nodeFrom.adjacentTo(nodeTo)) {
							var adjs = viz.graph.getAdjacence(nodeFrom.id, nodeTo.id);
							fadeEdges = true;
							adjs[0].alpha = 1; adjs[0].startAlpha = 1; adjs[0].endAlpha = 0; adjs[0].ignore = true;
							adjs[1].alpha = 1; adjs[1].startAlpha = 1; adjs[1].endAlpha = 0; adjs[1].ignore = true;
						}
					});
				});	
				var modes = !fadeEdges? ['fade:nodes'] : ['fade:nodes', 'fade:vertex'];
				viz.compute('endPos');
				GUtil.eachNode(viz.graph, function(elem) {
					if(elem.id != root && elem.pos.equals(Polar.KER)) elem.pos = elem.startPos = elem.endPos;
				});
				GPlot.animate(viz, $_.merge(options, {
					modes: ['polar'].concat(modes),
					onComplete: function() {
						GUtil.eachNode(viz.graph, function(elem) {
							if(elem.ignore) viz.graph.removeNode(elem.id);
						});
						GUtil.eachNode(viz.graph, function(elem) {
							GUtil.eachAdjacency(elem, function(adj) {
								if(adj.ignore) viz.graph.removeAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
							});
						});
						options.onComplete();
					}
				}));
				break;

			default: this.doError();
		}
	},
	
	preprocessSum: function(viz, graph) {
		var GUtil = GraphUtil;
		GUtil.eachNode(graph, function(elem) {
			if(!viz.graph.hasNode(elem.id)) {
				viz.graph.addNode(elem);
				var n = viz.graph.getNode(elem.id);
				n.alpha = 0; n.startAlpha = 0; n.endAlpha = 1;
			}
		});	
		var fadeEdges = false;
		GUtil.eachNode(graph, function(elem) {
			GUtil.eachAdjacency(elem, function(adj) {
				var nodeFrom = viz.graph.getNode(adj.nodeFrom.id);
				var nodeTo = viz.graph.getNode(adj.nodeTo.id);
				if(!nodeFrom.adjacentTo(nodeTo)) {
					var adjs = viz.graph.addAdjacence(nodeFrom, nodeTo, adj.data);
					if(nodeFrom.startAlpha == nodeFrom.endAlpha 
					&& nodeTo.startAlpha == nodeTo.endAlpha) {
						fadeEdges = true;
						adjs[0].alpha = 0; adjs[0].startAlpha = 0; adjs[0].endAlpha = 1;
						adjs[1].alpha = 0; adjs[1].startAlpha = 0; adjs[1].endAlpha = 1;
					} 
				}
			});
		});	
		return fadeEdges;
	}
};


/*
   Object: GraphPlot

   An object that performs specific radial layouts for a generic graph structure.
*/
var GraphPlot = {

	Interpolator: {
		'polar': function(elem, delta) {
			var from = elem.startPos;
			var to = elem.endPos;
			elem.pos = to.interpolate(from, delta);
		},
		
		'linear': function(elem, delta) {
			var from = elem.startPos.toComplex();
			var to = elem.endPos.toComplex();
			elem.pos = ((to.$add(from.scale(-1))).$scale(delta).$add(from)).toPolar();
		},
		
		'fade:nodes': function(elem, delta) {
			if(elem.endAlpha != elem.alpha) {
				var from = elem.startAlpha;
				var to   = elem.endAlpha;
				elem.alpha = from + (to - from) * delta;
			}
		},
		
		'fade:vertex': function(elem, delta) {
			var adjs = elem.adjacencies;
			for(var id in adjs) this['fade:nodes'](adjs[id], delta);
		}
	},

	//Property: labelsHidden
	//A flag value indicating if node labels are being displayed or not.
	labelsHidden: false,
	//Property: labelContainer
	//Label DOM element
	labelContainer: false,
	//Property: labels
	//Label DOM elements hash.
	labels: {},

	/*
	   Method: getLabelContainer
	
	   Lazy fetcher for the label container.
	*/
	getLabelContainer: function() {
		return this.labelContainer? this.labelContainer : this.labelContainer = document.getElementById(Config.labelContainer);
	},
	
	/*
	   Method: getLabel
	
	   Lazy fetcher for the label DOM element.
	*/
	getLabel: function(id) {
		return (id in this.labels && this.labels[id] != null)? this.labels[id] : this.labels[id] = document.getElementById(id);
	},
	
	/*
	   Method: hideLabels
	
	   Hides all labels.
	*/
	hideLabels: function (hide) {
		var container = this.getLabelContainer();
		if(hide) container.style.display = 'none';
		else container.style.display = '';
		this.labelsHidden = hide;
	},

	/*
	   Method: clearLabels
	
	   Clears the label container.
	*/
	clearLabels: function(viz) {
		for(var id in this.labels) 
			if(!viz.graph.hasNode(id)) {
				this.disposeLabel(id);
				delete this.labels[id];
			}
	},
	
	/*
	   Method: disposeLabel
	
	   Removes a label.
	*/
	disposeLabel: function(id) {
		var elem = this.getLabel(id);
		if(elem && elem.parentNode) {
			elem.parentNode.removeChild(elem);
		}  
	},
	
	/*
	   Method: animate
	
	   Animates the graph mantaining a radial layout.
	*/
	animate: function(viz, opt) {
		var that = this, GUtil = GraphUtil, Anim = Animation, duration = opt.duration || Anim.duration, fps = opt.fps || Anim.fps;
		//Should be changed eventually, when Animation becomes a class.
		var prevDuration = Anim.duration, prevFps = Anim.fps;
		
		Anim.duration = duration;
		Anim.fps = fps;
		
		if(opt.hideLabels) this.hideLabels(true);
		var animationController = {
			compute: function(delta) {
				GUtil.eachNode(viz.graph, function(node) { 
					for(var i=0; i<opt.modes.length; i++) {
							that.Interpolator[opt.modes[i]](node, delta);
					}
				});
				that.plot(viz, opt);
			},

			complete: function() {
				GUtil.eachNode(viz.graph, function(node) { 
					node.startPos = node.pos;
					node.startAlpha = node.alpha;
				});
				if(opt.hideLabels) that.hideLabels(false);
				that.plot(viz, opt);
				Anim.duration = prevDuration;
				Anim.fps = prevFps;
				opt.onComplete();
				opt.onAfterCompute();
			}		
		};
		Anim.controller = animationController;
		Anim.start();
	},


	/*
	   Method: sequence
	
	   Iteratively performs an action while refreshing the state of the visualization.
	*/
	sequence: function(viz, options) {
		options = $_.merge({
			condition: function() { return false; },
			step: $_.fn(),
			onComplete: $_.fn(),
			duration: 200
		}, options);

		var interval = setInterval(function() {
			if(options.condition()) {
				options.step();
			} else {
				clearInterval(interval);
				options.onComplete();
			}
			viz.refresh();
		}, options.duration);
	},

	/*
	   Method: plot
	
	   Plots a Graph.
	*/
	plot: function(viz, opt) {
		var aGraph = viz.graph, canvas = viz.canvas, id = viz.root;
		var that = this, ctx = canvas.getContext(), GUtil = GraphUtil;
		canvas.clear();
		if(Config.drawConcentricCircles) canvas.drawConcentricCircles(Config.drawConcentricCircles);
		var T = !!aGraph.getNode(id).visited;
		GUtil.eachNode(aGraph, function(node) {
			GUtil.eachAdjacency(node, function(adj) {
				if(!!adj.nodeTo.visited === T) {
					opt.onBeforePlotLine(adj);
					ctx.save();
					ctx.globalAlpha = Math.min(Math.min(node.alpha, adj.nodeTo.alpha), adj.alpha);
					that.plotLine(adj, canvas);
					ctx.restore();
					opt.onAfterPlotLine(adj);
				}
			});
			ctx.save();
			ctx.globalAlpha = node.alpha;
			that.plotNode(node, canvas);
	 		if(!that.labelsHidden && ctx.globalAlpha >= .95) that.plotLabel(canvas, node, opt);
	 		else if(!that.labelsHidden && ctx.globalAlpha < .95) that.hideLabel(node);
			ctx.restore();
			node.visited = !T;
		});
	},
	
	
	/*
	   Method: plotNode
	
	   Plots a graph node.
	*/
	plotNode: function(node, canvas) {
		var pos = node.pos.toComplex();
		canvas.path('fill', function(context) {
			context.arc(pos.x, pos.y, node._radius, 0, Math.PI*2, true);			
		});
	},
	
	/*
	   Method: plotLine
	
	   Plots a line connecting _node_ and _child_ nodes.
	*/
	plotLine: function(adj, canvas) {
		var node = adj.nodeFrom, child = adj.nodeTo;
		var pos = node.pos.toComplex();
		var posChild = child.pos.toComplex();
		canvas.path('stroke', function(context) {
			context.moveTo(pos.x, pos.y);
		  	context.lineTo(posChild.x, posChild.y);
		});
	},
	
	/*
	   Method: hideLabel
	
	   Hides a label having _node.id_ as id.
	*/
	hideLabel: function(node) {
		var n; if(n = document.getElementById(node.id)) n.style.display = "none";
	},
	
	/*
	   Method: plotLabel
	
	   Plots a label for a given node.
	*/
	plotLabel: function(canvas, node, controller) {
		var size = node._radius, id = node.id, tag = this.getLabel(id);
		if(!tag && !(tag = document.getElementById(id))) {
			tag = document.createElement('div');
			var container = this.getLabelContainer();
			container.appendChild(tag);
			tag.id = id;
			tag.className = 'node';
			tag.style.position = 'absolute';
			controller.onCreateLabel(tag, node);
		}
		var pos = node.pos.toComplex();
		var radius= canvas.getSize();
		var cpos = canvas.getPosition();
		var labelPos= {
			x: Math.round(pos.x + cpos.x + radius.x/2 - size /2),
			y: Math.round(pos.y + cpos.y + radius.y/2 - size /2)
		};
		tag.style.width = size + 'px';
		tag.style.height = size + 'px';
		tag.style.left = labelPos.x + 'px';
		tag.style.top = labelPos.y  + 'px';
		tag.style.display = this.fitsInCanvas(labelPos, canvas)? '' : 'none';
		controller.onPlaceLabel(tag, node);
	},
	
	/*
	   Method: fitsInCanvas
	
	   Returns _true_ or _false_ if the label for the node is contained on the canvas dom element or not.
	*/
	fitsInCanvas: function(pos, canvas) {
		var size = canvas.getSize();
		if(pos.x >= size.x + canvas.position.x || pos.x < canvas.position.x 
			|| pos.y >= size.y + canvas.position.y || pos.y < canvas.position.y) return false;
		return true;					
	}
};

/*
   Class: RGraph

	An animated Graph with radial layout.

	Go to <http://blog.thejit.org> to know what kind of JSON structure feeds this object.
	
	Go to <http://blog.thejit.org/?p=8> to know what kind of controller this class accepts.
	
	Refer to the <Config> object to know what properties can be modified in order to customize this object. 

	The simplest way to create and layout a RGraph is:
	
	(start code)

	  var canvas= new Canvas('infovis', '#ccddee', '#772277');
	  var rgraph= new RGraph(canvas, controller);
	  rgraph.loadTreeFromJSON(json);
	  rgraph.compute();
	  rgraph.plot();

	(end code)

	A user should only interact with the Canvas, RGraph and Config objects/classes.
	By implementing RGraph controllers you can also customize the RGraph behavior.
*/

/*
 Constructor: RGraph

 Creates a new RGraph instance.
 
 Parameters:

    canvas - A <Canvas> instance.
    controller - _optional_ a RGraph controller <http://blog.thejit.org/?p=8>
*/
var RGraph = function(canvas, controller) {
	var innerController = {
		onBeforeCompute: $_.fn(),
		onAfterCompute:  $_.fn(),
		onCreateLabel:   $_.fn(),
		onPlaceLabel:    $_.fn(),
		onCreateElement: $_.fn(),
		onComplete:      $_.fn(),
		onBeforePlotLine: $_.fn(),
		onAfterPlotLine: $_.fn(),
		request:         false
	};
	this.controller = $_.merge(innerController, controller);
	this.graph = new Graph();
	this.json = null;
	this.canvas = canvas;
	this.root = null;
	this.busy = false;
	
	Animation.duration = Config.animationTime;
	Animation.fps = Config.fps;
	
};

RGraph.prototype = {
	
	
	construct: function(json) {
		var isGraph = $_.isArray(json);
		var ans = new Graph();
		if(!isGraph) 
			//make tree
			(function (ans, json) {
				ans.addNode(json);
				for(var i=0, ch = json.children; i<ch.length; i++) {
					ans.addAdjacence(json, ch[i]);
					arguments.callee(ans, ch[i]);
				}
			})(ans, json);
		else
			//make graph
			(function (ans, json) {
				var getNode = function(id) {
					for(var w=0; w<json.length; w++) if(json[w].id == id) return json[w];
				};
				for(var i=0; i<json.length; i++) {
					ans.addNode(json[i]);
					for(var j=0, adj = json[i].adjacencies; j<adj.length; j++) {
						var node = adj[j], data;
						if(typeof adj[j] != 'string') {
							data = node.data;
							node = node.nodeTo;
						}
						ans.addAdjacence(json[i], getNode(node), data);
					}
				}
			})(ans, json);

		return ans;
	},
	
	/*
	 Method: loadTree
	
	 Loads a Graph from a json tree object <http://blog.thejit.org>
	 
	*/
	loadTree: function(json) {
		this.graph = this.construct(json);
	},

	/*
	 Method: loadGraph
	
	 Loads a Graph from a json graph object <http://blog.thejit.org>
	 
	*/
	loadGraph: function(json) {
		this.graph = this.construct(json);
	},
	
	/*
	 Method: refresh
	
	 Computes positions and then plots.
	 
	*/
	refresh: function() {
		this.compute();
		this.plot();
	},
	
  /*
	 Method: flagRoot
	
	 Flags a node specified by _id_ as root.
	*/
	flagRoot: function(id) {
		this.unflagRoot();
		this.graph.nodes[id]._root = true;
	},

	/*
	 Method: unflagRoot
	
	 Unflags all nodes.
	*/
	unflagRoot: function() {
		GraphUtil.eachNode(this.graph, function(elem) {elem._root = false;});
	},

	/*
	 Method: getRoot
	
	 Returns the node flagged as root.
	*/
	getRoot: function() {
		var root = false;
		GraphUtil.eachNode(this.graph, function(elem){ if(elem._root) root = elem; });
		return root;
	},
	
	/*
	 Method: loadTreeFromJSON
	
	 Loads a RGraph from a _json_ object <http://blog.thejit.org>
	*/
	loadTreeFromJSON: function(json) {
		this.json = json;
		this.loadTree(json);
		this.root = json.id;
	},
	
	/*
	 Method: loadGraphFromJSON
	
	 Loads a RGraph from a _json_ object <http://blog.thejit.org>
	*/
	loadGraphFromJSON: function(json, i) {
		this.json = json;
		this.loadGraph(json);
		this.root = json[i? i : 0].id;
	},
	
	
	/*
	 Method: plot
	
	 Plots the RGraph
	*/
	plot: function() {
		GraphPlot.plot(this, this.controller);
	},
	
	/*
	 Method: compute
	
	 Computes the graph nodes positions and stores this positions on _property_.
	*/
	compute: function(property) {
		var prop = property || ['pos', 'startPos', 'endPos'];
		var node = this.graph.getNode(this.root);
		node._depth = 0;
		this.flagRoot(this.root);
		GraphUtil.computeLevels(this.graph, this.root, "ignore");
		this.computeAngularWidths();
		this.computePositions(prop);
	},
	
	/*
	 Method: computePositions
	
	 Performs the main algorithm for computing node positions.
	*/
	computePositions: function(property) {
		var propArray = (typeof property == 'array' || typeof property == 'object')? property : [property];
		var aGraph = this.graph;
		var GUtil = GraphUtil;
		var root = this.graph.getNode(this.root);

		for(var i=0; i<propArray.length; i++)
			root[propArray[i]] = new Polar(0, 0);
		
		root.angleSpan = {
			begin: 0,
			end: 2 * Math.PI
		};
		root._rel = 1;
		
		GUtil.eachBFS(this.graph, this.root, function (elem) {
			var angleSpan = elem.angleSpan.end - elem.angleSpan.begin;
			var rho = (elem._depth + 1) * Config.levelDistance;
			var angleInit = elem.angleSpan.begin;
			var totalAngularWidths = (function (element){
				var total = 0;
				GUtil.eachSubnode(aGraph, element, function(sib) {
					total += sib._treeAngularWidth;
				}, "ignore");
				return total;
			})(elem);
			
			GUtil.eachSubnode(aGraph, elem, function(child) {
				if(!child._flag) {
					child._rel = child._treeAngularWidth / totalAngularWidths;
					var angleProportion = child._rel * angleSpan;
					var theta = angleInit + angleProportion / 2;

					for(var i=0; i<propArray.length; i++)
						child[propArray[i]] = new Polar(theta, rho);

					child.angleSpan = {
						begin: angleInit,
						end: angleInit + angleProportion
					};
					angleInit += angleProportion;
				}
			}, "ignore");
		}, "ignore");
	},


	
	/*
	 Method: setAngularWidthForNodes
	
	 Sets nodes angular widths.
	*/
	setAngularWidthForNodes: function() {
		var rVal = Config.nodeRangeValues, rDiam = Config.nodeRangeDiameters, nr = Config.nodeRadius, allow = Config.allowVariableNodeDiameters; 
		var diam = function(value) { return (((rDiam.max - rDiam.min)/(rVal.max - rVal.min)) * (value - rVal.min) + rDiam.min) };
		GraphUtil.eachBFS(this.graph, this.root, function(elem, i) {
			var dataValue = (allow && elem.data && elem.data.length > 0)? elem.data[0].value : nr;
			var diamValue = diam(dataValue);
			var rho = Config.levelDistance * i;
			elem._angularWidth = diamValue / rho;
			elem._radius = allow? diamValue / 2 : nr;
		}, "ignore");
	},
	
	/*
	 Method: setSubtreesAngularWidths
	
	 Sets subtrees angular widths.
	*/
	setSubtreesAngularWidth: function() {
		var that = this;
		GraphUtil.eachNode(this.graph, function(elem) {
			that.setSubtreeAngularWidth(elem);
		}, "ignore");
	},
	
	/*
	 Method: setSubtreeAngularWidth
	
	 Sets the angular width for a subtree.
	*/
	setSubtreeAngularWidth: function(elem) {
		var that = this, nodeAW = elem._angularWidth, sumAW = 0;
		GraphUtil.eachSubnode(this.graph, elem, function(child) {
			that.setSubtreeAngularWidth(child);
			sumAW += child._treeAngularWidth;
		}, "ignore");
		elem._treeAngularWidth = Math.max(nodeAW, sumAW);
	},
	
	/*
	 Method: computeAngularWidths
	
	 Computes nodes and subtrees angular widths.
	*/
	computeAngularWidths: function () {
		this.setAngularWidthForNodes();
		this.setSubtreesAngularWidth();
	},
	
	/*
	 Method: getNodeAndParentAngle
	
	 Returns the _parent_ of the given node, also calculating its angle span.
	*/
	getNodeAndParentAngle: function(id) {
		var theta = false;
		var n  = this.graph.getNode(id);
		var ps = GraphUtil.getParents(this.graph, n);
		var p  = (ps.length > 0)? ps[0] : false;
		if(p) {
			var posParent = p.pos.toComplex(), posChild = n.pos.toComplex();
			var newPos    = posParent.add(posChild.scale(-1));
			theta = (function(pos) {
				var t = Math.atan2(pos.y, pos.x);
				if(t < 0) t = 2 * Math.PI + t;
				return t;
			})(newPos);
		}
		return {_parent: p, theta: theta};
		
	},
	
	/*
	 Method: onClick
	
	 Performs all calculations and animation when clicking on a label specified by _id_. The label id is the same id as its homologue node.
	*/
	onClick: function(id) {		
		if(this.root != id && !this.busy) {
			this.busy = true;
			//we apply first constraint to the algorithm
			var obj = this.getNodeAndParentAngle(id);
			this.root = id, that = this;
			this.controller.onBeforeCompute(this.graph.getNode(id));

			console.log("!");
			var thetaDiff = obj.theta - obj._parent.endPos.theta;

												GraphUtil.eachNode(this.graph, function(elem) {
				elem.endPos = elem.endPos.add(new Polar(thetaDiff, 0));
			});
			
			var mode = (Config.interpolation == 'linear')? 'linear' : 'polar';


			GraphPlot.animate(this, $_.merge(this.controller, {
				hideLabels:true,
				modes: [mode],
				onComplete: function() {
					that.busy = false;
				}
			}));
				}		

	}
};

/*
 Class: Graph

 A generic Graph class.
 
*/	

/*
 Constructor: Graph

 Creates a new Graph instance.
 
*/	
var Graph= function()  {
	//Property: nodes
	//graph nodes
	this.nodes= {};
};
	
	
Graph.prototype= {

/*
	 Method: getNode
	
	 Returns a <Graph.Node> from a specified _id_.
*/	
 getNode: function(id) {
 	if(this.hasNode(id)) 	return this.nodes[id];
 	return false;
 },


/*
	 Method: getAdjacence
	
	 Returns an array of <Graph.Adjacence> that connects nodes with id _id_ and _id2_.
*/	
  getAdjacence: function (id, id2) {
	var adjs = [];
	if(this.hasNode(id) 	&& this.hasNode(id2) 
	&& this.nodes[id].adjacentTo({ 'id':id2 }) && this.nodes[id2].adjacentTo({ 'id':id })) {
		adjs.push(this.nodes[id].getAdjacency(id2));
		adjs.push(this.nodes[id2].getAdjacency(id));
		return adjs;
	}
	return false;	
 },

	/*
	 Method: addNode
	
	 Adds a node.
	 
	 Parameters:
	
	    obj - A <Graph.Node> object.
	*/	
  addNode: function(obj) {
  	if(!this.nodes[obj.id]) {
	  	this.nodes[obj.id] = new Graph.Node(obj.id, obj.name, obj.data);
  	}
  	return this.nodes[obj.id];
  },
  
	/*
	 Method: addAdjacence
	
	 Connects nodes specified by *obj* and *obj2*. If not found, nodes are created.
	 
	 Parameters:
	
	    obj - a <Graph.Node> object.
	    obj2 - Another <Graph.Node> object.
	    data - A DataSet object.
	*/	
  addAdjacence: function (obj, obj2, weight) {
  	var adjs = []
  	if(!this.hasNode(obj.id)) this.addNode(obj);
  	if(!this.hasNode(obj2.id)) this.addNode(obj2);
	obj = this.nodes[obj.id]; obj2 = this.nodes[obj2.id];
	
  	for(var i in this.nodes) {
  		if(this.nodes[i].id == obj.id) {
  			if(!this.nodes[i].adjacentTo(obj2)) {
  				adjs.push(this.nodes[i].addAdjacency(obj2, weight));
  			}
  		}
  		
  		if(this.nodes[i].id == obj2.id) {	
  			if(!this.nodes[i].adjacentTo(obj)) {
  				adjs.push(this.nodes[i].addAdjacency(obj, weight));
  			}
  		}
  	}
  	return adjs;
 },

	/*
	 Method: removeNode
	
	 Removes a <Graph.Node> from <Graph> that matches the specified _id_.
	*/	
  removeNode: function(id) {
  	if(this.hasNode(id)) {
  		var node = this.nodes[id];
  		for(var i=0 in node.adjacencies) {
  			var adj = node.adjacencies[i];
  			this.removeAdjacence(id, adj.nodeTo.id);
  		}
  		delete this.nodes[id];
  	}
  },
  
/*
	 Method: removeAdjacence
	
	 Removes a <Graph.Adjacence> from <Graph> that matches the specified _id1_ and _id2_.
*/	
  removeAdjacence: function(id1, id2) {
  	if(this.hasNode(id1)) this.nodes[id1].removeAdjacency(id2);
  	if(this.hasNode(id2)) this.nodes[id2].removeAdjacency(id1);
  },

	/*
	 Method: hasNode
	
	 Returns a Boolean instance indicating if node belongs to graph or not.
	 
	 Parameters:
	
	    id - Node id.

	 Returns:
	  
	 		A Boolean instance indicating if node belongs to graph or not.
	*/	
  hasNode: function(id) {
	return id in this.nodes;
  }
};
/*
   Class: Graph.Node
	
	 Behaviour of the <Graph> node.

*/
/*
   Constructor: Graph.Node

   Node constructor.

   Parameters:

      id - The node *unique identifier* id.
      name - A node's name.
      data - Place to store some extra information (can be left to null).


   Returns:

      A new <Graph.Node> instance.
*/
Graph.Node = function(id, name, data) {
	//Property: id
	//A node's id
	this.id= id;
	//Property: name
	//A node's name
	this.name = name;
	//Property: data
	//The dataSet object <http://blog.thejit.org/?p=7>
	this.data = data;
	//Property: drawn
	//Node flag
	this.drawn= false;
	//Property: angle span
	//allowed angle span for adjacencies placement
	this.angleSpan= {
		begin:0,
		end:0
	};
	//Property: pos
	//node position
	this.pos= new Polar(0, 0);
	//Property: startPos
	//node from position
	this.startPos= new Polar(0, 0);
	//Property: endPos
	//node to position
	this.endPos= new Polar(0, 0);
	//Property: alpha
	//node alpha
	this.alpha = 1;
	//Property: startAlpha
	//node start alpha
	this.startAlpha = 1;
	//Property: endAlpha
	//node end alpha
	this.endAlpha = 1;
	//Property: adjacencies
	//node adjacencies
	this.adjacencies= {};
};

Graph.Node.prototype= {
	
	/*
	   Method: adjacentTo
	
	   Indicates if the node is adjacent to the node indicated by the specified id

	   Parameters:
	
	      id - A node id.
	
	   Returns:
	
	     A Boolean instance indicating whether this node is adjacent to the specified by id or not.
	*/
	adjacentTo: function(node) {
		return node.id in this.adjacencies;
	},

	/*
	   Method: getAdjacency
	
	   Returns a <Graph.Adjacence> that connects the current <Graph.Node> with the node having _id_ as id.

	   Parameters:
	
	      id - A node id.
	*/	
	getAdjacency: function(id) {
		return this.adjacencies[id];
	},
	/*
	   Method: addAdjacency
	
	   Connects the node to the specified by id.

	   Parameters:
	
	      id - A node id.
	*/	
	addAdjacency: function(node, data) {
		var adj = new Graph.Adjacence(this, node, data);
		return this.adjacencies[node.id] = adj;
	},
	
	/*
	   Method: removeAdjacency
	
	   Deletes the <Graph.Adjacence> by _id_.

	   Parameters:
	
	      id - A node id.
	*/	
	removeAdjacency: function(id) {
		delete this.adjacencies[id];
	}
};
/*
   Class: Graph.Adjacence
	
	 Creates a new <Graph> adjacence.

*/
Graph.Adjacence = function(nodeFrom, nodeTo, data) {
	//Property: nodeFrom
	//One of the two <Graph.Node>s connected by this edge.
	this.nodeFrom = nodeFrom;
	//Property: nodeTo
	//One of the two <Graph.Node>s connected by this edge.
	this.nodeTo = nodeTo;
	//Property: data
	//A dataset object
	this.data = data;
	//Property: alpha
	//node alpha
	this.alpha = 1;
	//Property: startAlpha
	//node start alpha
	this.startAlpha = 1;
	//Property: endAlpha
	//node end alpha
	this.endAlpha = 1;
};

/*
   Object: Trans
	
	 An object containing multiple type of transformations. Based on the mootools library <http://mootools.net>.

*/
var Trans = {
	linear: function(p) { return p;	},
	Quart: function(p) {
		return Math.pow(p, 4);
	},
	easeIn: function(transition, pos){
		return transition(pos);
	},
	easeOut: function(transition, pos){
		return 1 - transition(1 - pos);
	},
	easeInOut: function(transition, pos){
		return (pos <= 0.5) ? transition(2 * pos) / 2 : (2 - transition(2 * (1 - pos))) / 2;
	}
};

/*
   Object: Animation
	
	 An object that performs animations. Based on Fx.Base from Mootools.

*/

var Animation = {

	duration: Config.animationTime,
	fps: Config.fps,
	transition: function(p) {return Trans.easeInOut(Trans.Quart, p);},
	//transition: Trans.linear,
	controller: false,
	
	getTime: function() {
		var ans = (Date.now)? Date.now() : new Date().getTime();
		return ans;
	},
	
	step: function(){
		var time = this.getTime();
		if (time < this.time + this.duration){
			var delta = this.transition((time - this.time) / this.duration);
			this.controller.compute(delta);
		} else {
			this.timer = clearInterval(this.timer);
			this.controller.compute(1);
			this.controller.complete();
		}
	},

	start: function(){
		this.time = 0;
		this.startTimer();
		return this;
	},

	startTimer: function(){
		if (this.timer) return false;
		this.time = this.getTime() - this.time;
		this.timer = setInterval((function () { Animation.step(); }), Math.round(1000 / this.fps));
		return true;
	}
};
/*Jon's JIT Hacks */

Config.animationTime = 1000;

/*RGRAPH*/
RGraph.prototype.offsetCenter= function(x,y){
	var d =this.controller.getOffset();
	d.x = x;
	d.y = y;
	this.controller.setOffset(d);
};

RGraph.prototype.setAngularWidthForNodes= function() {
	var rVal = Config.nodeRangeValues, rDiam = Config.nodeRangeDiameters, nr = Config.nodeRadius, allow = Config.allowVariableNodeDiameters; 
	
	var zoom = this.controller.getZoomLevel();
	var diam = function(value) { return (((rDiam.max - rDiam.min)/(rVal.max - rVal.min)) * (value - rVal.min) + rDiam.min) };
	GraphUtil.eachBFS(this.graph, this.root, function(elem, i) {
		var dataValue = (allow && elem.data && elem.data.length > 0)? elem.data[0].value : nr;
		var diamValue = diam(dataValue);

		var rho = zoom * i;//jon
		elem._angularWidth = diamValue / rho;
		elem._radius = allow? diamValue / 2 : nr;
	}, "ignore");
};



RGraph.prototype.computePositions= function(property) {
	var propArray = (typeof property == 'array' || typeof property == 'object')? property : [property];
	var aGraph = this.graph;
	var GUtil = GraphUtil;
	var root = this.graph.getNode(this.root);

	for(var i=0; i<propArray.length; i++)
		root[propArray[i]] = new Polar(0, 0);
	
	root.angleSpan = {
		begin: 0,
		end: 2 * Math.PI
	};
	root._rel = 1;
	var zoom =this.controller.getZoomLevel();
	GUtil.eachBFS(this.graph, this.root, function (elem) {
		var angleSpan = elem.angleSpan.end - elem.angleSpan.begin;
		var rho = (elem._depth + 1) * zoom;//jon
		var angleInit = elem.angleSpan.begin;
		var totalAngularWidths = (function (element){
			var total = 0;
			GUtil.eachSubnode(aGraph, element, function(sib) {
				total += sib._treeAngularWidth;
			}, "ignore");
			return total;
		})(elem);
		
		GUtil.eachSubnode(aGraph, elem, function(child) {
			if(!child._flag) {
				child._rel = child._treeAngularWidth / totalAngularWidths;
				var angleProportion = child._rel * angleSpan;
				var theta = angleInit + angleProportion / 2;

				for(var i=0; i<propArray.length; i++)
					child[propArray[i]] = new Polar(theta, rho);

				child.angleSpan = {
					begin: angleInit,
					end: angleInit + angleProportion
				};
				angleInit += angleProportion;
			}
		}, "ignore");
	}, "ignore");
};



RGraph.prototype.onClick= function(id) { //weird bug in here
		
		if(this.root != id) {//jon
			this.busy = true;
			//we apply first constraint to the algorithm
			var obj = this.getNodeAndParentAngle(id);
			this.root = id, that = this;
			this.controller.onBeforeCompute(this.graph.getNode(id));

			this.compute('endPos');

			var thetaDiff = obj.theta - obj._parent.endPos.theta;

			GraphUtil.eachNode(this.graph, function(elem) {
				elem.endPos = elem.endPos.add(new Polar(thetaDiff, 0));
			});
			
			var mode = (Config.interpolation == 'linear')? 'linear' : 'polar';


			this.controller.modes = [mode];//jon
			GraphPlot.animate(this, this.controller);//jon
		}

};
/*GRAPH PLOT */

GraphPlot.plotLine = function(adj, canvas,controller) {//jon
	var d = controller.getOffset();//jon
	var node = adj.nodeFrom, child = adj.nodeTo;
	var pos = node.pos.toComplex();
	var posChild = child.pos.toComplex();
	canvas.path('stroke', function(context) {
		
		pos.x += d.x;//jon..
		pos.y += d.y;
		posChild.x += d.x;
		posChild.y += d.y;//..jon
		
		context.moveTo(pos.x, pos.y);
	  	context.lineTo(posChild.x, posChild.y);
		
		 

			
		
	});
};


GraphPlot.getLabelContainer = function(controller){
	return document.getElementById(controller.getNodeLabelContainer());
};



GraphPlot.fitsInCanvas= function(pos, canvas) {
	//canvas.setPosition();
    var size = canvas.getSize();
	var offset1 = parseInt(size.x);
	var offset2 = parseInt(size.y);
	if(pos.x <  0 || pos.x > offset1 || pos.y < 0 || pos.y > offset2) return false;
	else
	  return true;						
};

GraphPlot.plotLabel= function(canvas, node, controller) {
	
	var size = node._radius;
	var id = controller.getNodeLabelPrefix() + node.id;//jon change
	var d = controller.getOffset(); //jon

	var pos = node.pos.toComplex();
	var radius= canvas.getSize();
	canvas.setPosition(); //jon
	var cpos = canvas.getPosition();
	
	var labelPos= {
		x: Math.round((pos.x  + radius.x/2 - size /2) +d.x),//jon
		y: Math.round((pos.y + radius.y/2 - size /2) +d.y)//jon
	};

	var tag = this.getLabel(id);
	
	if(!this.fitsInCanvas(labelPos,canvas)) {
		
		//if(tag && tag.parentNode)tag.parentNode.removeChild(tag);
		return;
	}
	
	if(!tag && !(tag = document.getElementById(id))) {
		tag = document.createElement('div');
		var container = this.getLabelContainer(controller); //jon change
		container.style.position= 'relative';//jon change
		container.appendChild(tag);
		tag.id = id;
		tag.className = 'node';
		tag.style.position = 'absolute';
		controller.onCreateLabel(tag, node);
	}

	tag.style.width = size + 'px';
	tag.style.height = size + 'px';
	tag.style.left = labelPos.x + 'px';
	tag.style.top = labelPos.y  + 'px';
	tag.style.display = this.fitsInCanvas(labelPos, canvas)? '' : 'none';
	controller.onPlaceLabel(tag, node);
};


/*overriding of several functions */
GraphPlot.plotNode =  function(node, canvas,controller) {
	var pos = node.pos.toComplex();
	var d = controller.getOffset();

	if(node.data.nodraw == undefined)  		//jon
	canvas.path('fill', function(context) {
  		context.arc(pos.x +d.x, pos.y +d.y, node._radius, 0, Math.PI*2, true);	//jon

	});		
};


GraphPlot.plot= function(viz, opt) {

	var aGraph = viz.graph, canvas = viz.canvas, id = viz.root;

	var controller = viz.controller;//jon
	var container = this.getLabelContainer(controller); //jon change
	
	container.innerHTML = "";
	var that = this, ctx = canvas.getContext(), GUtil = GraphUtil;
	canvas.clear();
	if(Config.drawConcentricCircles) canvas.drawConcentricCircles(Config.drawConcentricCircles);
	var T = !!aGraph.getNode(id).visited;
	GUtil.eachNode(aGraph, function(node) {
		GUtil.eachAdjacency(node, function(adj) {
			if(!!adj.nodeTo.visited === T) {
				opt.onBeforePlotLine(adj);
				ctx.save();
				ctx.globalAlpha = Math.min(Math.min(node.alpha, adj.nodeTo.alpha), adj.alpha);
				that.plotLine(adj, canvas,controller);//jon
				ctx.restore();
				opt.onAfterPlotLine(adj);
			}
		});
		ctx.save();
		ctx.globalAlpha = node.alpha;
		that.plotNode(node, canvas,controller); //jon
 		if(!that.labelsHidden && ctx.globalAlpha >= .95) that.plotLabel(canvas, node, opt);
 		else if(!that.labelsHidden && ctx.globalAlpha < .95) that.hideLabel(node);
		ctx.restore();
		node.visited = !T;
	});
};



//georss support please

var EasyShape = function(properties,coordinates,geojson){
	this.grid = {};
	this.coords = [];
	if(geojson){
		this._constructFromGeoJSONObject(properties,coordinates);
	}
	else{
		this._constructBasicShape(properties,coordinates);
	}

	this._iemultiplier = 1000; //since vml doesn't accept floats you have to define the precision of your points 100 means you can get float coordinates 0.01 and 0.04 but not 0.015 and 0.042 etc..
};
EasyShape.prototype={
	_calculateBounds: function(coords){
		if(this.properties.shape == 'path' | this.properties.shape =='text'){
			this.grid = {x1:0,x2:1,y1:0,y2:1};
			return;
		}
		if(!coords) coords = this.coords;
		this.grid.x1 = coords[0];
		this.grid.y1 = coords[1];
		this.grid.x2 = coords[0];
		this.grid.y2 = coords[1];
		
		this._deltas = []
		var d = this._deltas;

		var lastX, lastY;
		var index = 0;
	
		lastX = coords[0];
		lastY = coords[1];
		for(var i=0; i < coords.length-1; i+=2){
			var xPos = parseFloat(coords[i]); //long
			var yPos = parseFloat(coords[i+1]); //lat
			var deltax =xPos - lastX;
			var deltay= yPos - lastY;
			if(deltax < 0) deltax = - deltax;
			if(deltay < 0) deltay = -deltay;
			d.push(deltax);
			d.push(deltay);
			if(xPos < this.grid.x1) this.grid.x1 = xPos;
			if(yPos < this.grid.y1) this.grid.y1 = yPos;	
			if(xPos > this.grid.x2) this.grid.x2 = xPos;
			if(yPos > this.grid.y2) this.grid.y2 = yPos;
			
			lastX = xPos;
			lastY = yPos;
		}
	}
	
	,setCoordinates: function(coordinates){
		this.coords = coordinates;
		this.grid = {}; //an enclosing grid
		this._calculateBounds();
		if(this.vml) this.vml.path = false; //reset path so recalculation will occur
	}


	,_constructPointShape: function(properties,coordinates){
		var x = coordinates[0]; var y = coordinates[1];
		this.pointcoords = [x,y];
		var ps = 0.5;
		var newcoords =[x-ps,y-ps,x+ps,y-ps,x+ps,y+ps,x-ps, y+ps];
		this._constructPolygonShape(properties,newcoords);
	}
	,_constructTextShape: function(properties,coordinates){
		this.properties = properties;
		var t = document.createElement("div");
		t.className = "easyShape";
		t.style.position = "absolute";
		this._textLabel = t;
		this.setCoordinates(coordinates);
	}
	
	,_constructPolygonShape: function(properties,coordinates){
		this.properties = properties;
		this.setCoordinates(coordinates);
		if(!properties.stroke)properties.stroke = '#000000';		
		if(properties.colour){
			properties.fill =  properties.colour;
		}
	}
	,_constructBasicShape: function(properties, coordinates){
		if(properties.shape == 'text'){
			this._constructTextShape(properties,coordinates);
		}
		else if(properties.shape == 'point'){
			this._constructPointShape(properties,coordinates);
		}
		else if(properties.shape == 'polygon' || properties.shape == 'path')
		{
			this._constructPolygonShape(properties,coordinates);
		}
		else{
			console.log("don't know how to construct basic shape " + properties.shape);
		}		
		
		
	}	
	
	 /*following 3 functions may be better in EasyMaps*/
	,_constructFromGeoJSONObject: function(properties,coordinates){
		if(properties.shape == 'polygon'){
			this._constructFromGeoJSONPolygon(properties,coordinates);	
		}
		else if(properties.shape == 'point'){
			this._constructPointShape(properties,coordinates);
		}
		else{
			console.log("don't know what to do with shape " + element.shape);
		}
	}
	,_constructFromGeoJSONPolygon: function(properties,coordinates){		
		var newcoords = this._convertGeoJSONCoords(coordinates[0]);
		this._constructBasicShape(properties,newcoords);
				//we ignore any holes in the polygon (for time being.. coords[1][0..n], coords[2][0..n])
	}	
	,_convertGeoJSONCoords: function(coords){
	//converts [[x1,y1], [x2,y2],...[xn,yn]] to [x1,y1,x2,y2..xn,yn]
		var res = [];
		if(!coords) return res;
		for(var i=0; i < coords.length; i++){
			//geojson says coords order should be longitude,latitude eg. 0,51 for London

			// longitude goes from -180 (W) to 180 (E), latitude from -90 (S) to 90 (N)
			// in our data, lat goes from 90 (S) to -90 (N), so we negate
			
			var x = coords[i][0];
			var y = - coords[i][1];
			
			//var y = -coords[i][0];
			//var x = coords[i][1];
			res.push(x);
			res.push(y);
		}

		return res;
	}	


	


	 /*RENDERING */
	,_renderTextShape: function(canvas,transformation){
		var t =this._textLabel;
		var coordinates = this.coords;
		var x= coordinates[0];
		var y =coordinates[1];
		t.innerHTML = this.properties.name;


		
		if(t.parentNode == null){
			t.style.left =   parseInt(x) +"px";
			t.style.top = parseInt(y)+"px";
			t.style.width =  "200px";
			t.style.height = "200px";
			t.style.textAlign = "center";
			
			canvas.appendChild(this._textLabel);
		}
		
		this._cssTransform(t,transformation,false);
		t.style.lineHeight = t.style.height;
	}
	,_canvasrender: function(canvas,transformation,projection,optimisations){
		var c;	
		var shapetype = this.properties.shape;	
		if(projection)
			c = this._applyProjection(projection,transformation);
		else
			c = this.coords;
		
		if(c.length == 0) return;
		
		var initialX,initialY;
		if(c[0] == 'M'){//starts with an "M"
			initialX = parseFloat(c[1]);
			initialY = parseFloat(c[2]);
		}
		else{
			initialX = parseFloat(c[0]);
			initialY = parseFloat(c[1]);			
		}

		
		var threshold = 2;

		var ctx = canvas.getContext('2d');

		if(this.properties.lineWidth) ctx.lineWidth = this.properties.lineWidth;
		
		var o = transformation.origin;
		var tr = transformation.translate;
		var s = transformation.scale;
		var r = transformation.rotate;
		ctx.save();

		ctx.translate(o.x,o.y);
		ctx.scale(s.x,s.y);
		ctx.translate(tr.x,tr.y);
		//if(r && r.x)ctx.rotate(r.x,o.x,o.y);

		ctx.beginPath();
		
		ctx.moveTo(initialX,initialY);

		var move;
		for(var i=2; i < c.length-1; i+=2){
			if(c[i]== "M") {
				i+= 1; 
				move=true;
			}
			var x = parseFloat(c[i]);
			var y = parseFloat(c[i+1]);	
			if(x == NaN || y == NaN){
				throw "error in EasyShape render: the coordinates for this EasyShape contain invalid numbers";
			}
			else{
				if(move){
					ctx.moveTo(x,y);
					move = false;
				}
				else{
					ctx.lineTo(x,y);
				}
			}
			
		}
		//connect last to first
		//if(shapetype != 'path') ctx.lineTo(initialX,initialY);
		ctx.closePath();

		if(!this.properties.hidden) {
			ctx.strokeStyle = this.properties.stroke;
			if(typeof this.properties.fill == 'string') 
				fill = this.properties.fill;
			else
				fill = "#ffffff";

			
			ctx.stroke();
			if(shapetype != 'path') {
				ctx.fillStyle = fill;
				ctx.fill();
			}
		}
		ctx.restore();
	}
	,_createvmlpathstring: function(vml,transformation,projection){ //mr bottleneck
		if(!vml) return;
		var o = transformation.origin;
		var t = transformation.translate;
		var s = transformation.scale;
		var path;
		
		var buffer = [];
		
		if(projection){
			c = this._applyProjection(projection,transformation);
		}
		else{
			c = this.coords;
		}	
		if(c.length < 2) return;
		

		
		var x =o.x  + c[0];
		var y =o.y+c[1];		
		x *=this._iemultiplier;
		y *= this._iemultiplier;
		x = parseInt(x);
		y = parseInt(y);

		//path = "M";
		buffer.push("M");
		//path+= x + "," +y + " L";
		buffer.push([x,",",y," L"].join(""))
		var lineTo = true;
		for(var i =2; i < c.length; i+=2){
			if(c[i] == 'M') {
				//path += " M";
				buffer.push(" M");
				lineTo = false;
				i+=1;
			}
			else if(!lineTo) {
				//path += " L";
				buffer.push(" L");
				lineTo = true;
			}
			else if(lineTo){
				//path += " ";
				buffer.push(" ");
			}
			var x =o.x+c[i];
			var y =o.y+c[i+1];
			x *= this._iemultiplier;
			y *= this._iemultiplier;
			x = parseInt(x);
			y = parseInt(y);
			buffer.push([x, ",",y].join(""));
			//path += x +"," + y;
			
			//if(i < c.length - 2) path += "";
		}
		//path += " XE";	
		buffer.push(" XE");
		//console.log(buffer.join(""));

	path = buffer.join("");
	//if(path != vml.getAttribute("path")){
		
		vml.setAttribute("path", path);	
//	}

	}

	,_cssTransform: function(vml,transformation,projection){
		var d1,d2,t;
		if(!vml) return;
	
		if(vml.tagName == 'shape' && (!vml.path || this.properties.shape =='point' ||projection)) {
			//causes slow down..
			this._createvmlpathstring(vml,transformation,projection);
		//	this.vml.parentNode.replaceChild(clonedNode,this.vml);
		}

		var o = transformation.origin;
		var t = transformation.translate;
		var s = transformation.scale;
		if(!this.initialStyle) {
			var initTop = parseInt(vml.style.top);
			if(!initTop) initTop = 0;
			initTop += o.y;
			var initLeft = parseInt(vml.style.left);
			if(!initLeft) initLeft = 0;
			initLeft += o.x;
			var w =parseInt(vml.style.width);
			var h = parseInt(vml.style.height)
			this.initialStyle = {top: initTop, left: initLeft, width: w, height: h};
		}
		var scalingRequired = true;
		var translatingRequired = true;
		if(this._lastTransformation){
			if(s.x == this._lastTransformation.scale.x && s.y == this._lastTransformation.scale.y){			
				scalingRequired = false;
			}

		}

	
		var initialStyle= this.initialStyle;

		var style = vml.style;			
		var newtop,newleft;
		newtop = initialStyle.top;
		newleft = initialStyle.left;

		//scale
		if(scalingRequired){
			var newwidth = initialStyle.width * s.x;
			var newheight = initialStyle.height * s.y; 	
		}
		//translate into right place

		var temp;
		temp = (t.x - o.x);
		temp *= s.x;
		newleft += temp;

		temp = (t.y - o.y);
		temp *= s.x;
		newtop += temp;						

		style.left = newleft +"px";
		style.top = newtop +"px";
		
		if(scalingRequired){
			style.width = newwidth +"px";
			style.height = newheight + "px";
		}
		this._lastTransformation = {scale:{}};
		this._lastTransformation.scale.x = s.x;
		this._lastTransformation.scale.y = s.y;
	}	
	
	
	,_ierender: function(canvas,transformation,projection,optimisations,appendTo){
		var shape;
		if(this.vml){
			shape = this.vml;
		
			if(this.properties.fill && shapetype != 'path'){
				shape.filled = "t";
				shape.fillcolor = this.properties.fill;			
			}
			this._cssTransform(shape,transformation,projection);
			return;
		}
		else{
			shape = document.createElement("g_vml_:shape");
			var o = transformation.origin;
			var t = transformation.translate;
			var s = transformation.scale;


			//path ="M 0,0 L50,0, 50,50, 0,50 X";
			var nclass= "easyShape";
			var shapetype =this.properties.shape;
			if(shapetype == 'path') nclass= "easyShapePath";
			shape.setAttribute("class", nclass);
			shape.style.height = canvas.height;
			shape.style.width = canvas.width;
			shape.style.position = "absolute";
			shape.style['z-index'] = 1;
			shape.stroked = "t";
			shape.strokecolor = "#000000";

			if(this.properties.fill && shapetype != 'path'){
				shape.filled = "t";
				shape.fillcolor = this.properties.fill;			
			}
			shape.strokeweight = ".75pt";

			var xspace = parseInt(canvas.width);
			xspace *=this._iemultiplier;
			var yspace =parseInt(canvas.height);
			yspace *= this._iemultiplier;
			coordsize = xspace +"," + yspace;

			shape.coordsize = coordsize;
			shape.easyShape = this;
			if(!appendTo){
				appendTo = canvas;
			}
			
			
			this._cssTransform(shape,transformation,projection);	
			this.vml = shape;
			appendTo.appendChild(shape);
		}
		
	
	}

	
	,_applyProjection: function(projection,transformation){
		var c = this.coords;
		if(!projection) return c;
		
		var newc = [];
		for(var i=0; i < c.length-1; i+=2){
			var x = parseFloat(c[i]);
			var y = parseFloat(c[i+1]);
			
			if(projection.xy){
				var t = projection.xy(c[i],c[i+1],transformation);
				newx= t.x;
				newy= t.y;
			}

			cok = true;
			//check we haven't wrapped around world (For flat projections sss)
			if(!projection.nowrap){
				var diff;
				if(newx > x) diff = newx - x;
				if(x > newx) diff = x - newx;
				if(diff > 100) cok = false; //too extreme change
			}
			
			if(cok){
				if(typeof newx == 'number' && typeof newy =='number'){
				newc.push(newx);
				newc.push(newy);
				}
	
			}
			
			
		}	

		this._tcoords = newc;
		this._calculateBounds(this._tcoords);
		return newc;
	}
	,_calculateVisibleArea: function(canvas,transformation){
		var left = 0,top = 0;
		var right =  parseInt(canvas.width) + left; 
		var bottom = parseInt(canvas.height) + top;
		var topleft =  EasyClickingUtils.undotransformation(left,top,transformation);
		var bottomright =  EasyClickingUtils.undotransformation(right,bottom,transformation);				
		var frame = {};
		frame.top = topleft.y;
		frame.bottom = bottomright.y;
		frame.right = bottomright.x;
		frame.left = topleft.x;
		return frame;
	}
	
	,_calculatePointCoordinates: function(transformation){
		if(!this.pointcoords) {
			this.pointcoords = [this.coords[0],this.coords[1]];
		}
		var x =parseFloat(this.pointcoords[0]);
		var y =parseFloat(this.pointcoords[1]);
		this.setCoordinates([x,y]);
		var ps = 2.5 / parseFloat(transformation.scale.x);
		//should get bigger with scale increasing
		var smallest = 1 / this._iemultipler;
		if(ps < smallest) ps = smallest;
		var newcoords =[[x-ps,y-ps],[x+ps,y-ps],[x+ps,y+ps],[x-ps, y+ps]];
		var c = this._convertGeoJSONCoords(newcoords);
		this.setCoordinates(c);
	}
	,_shapeIsInVisibleArea: function(frame){
		var g = this.grid;
		if(g.x2 < frame.left) {
			return false;}
		if(g.y2 < frame.top) {
			return false;}
		if(g.x1 > frame.right){
			return false;
		}
		if(g.y1 > frame.bottom){
			return false;	
		}
		return true;
	}
	
	,_shapeIsTooSmall: function(transformation,projection){
		var g = this.grid;
		var s = transformation.scale;
		var t1 = g.x2 -g.x1;
		var t2 =g.y2- g.y1;
		var delta = {x:t1,y:t2};
		delta.x *= s.x;
		delta.y *= s.y;
		var area = delta.x * delta.y;
		if(area < 40) 
		{return false;}//too small
		else
			return true;
	}
	
	/*
	render the shape using canvas ctx 
	using ctx and a given transformation in form {translate: {x:<num>, y:<num>}, scale:{translate: {x:<num>, y:<num>}}
	projection: a function that takes xy coordinates and spits out a new x and y
	in a given viewableArea 
	optimisations: boolean - apply optimisations if required
	*/
	,render: function(canvas,transformation,projection,optimisations, browser){
		var optimisations = true;
		if(!transformation){
			transformation = {};
		}
		if(!transformation.origin)transformation.origin = {x:0,y:0};
		if(!transformation.scale)transformation.scale = {x:1,y:1};
		if(!transformation.translate)transformation.translate = {x:0,y:0};
		
		var frame = this._calculateVisibleArea(canvas,transformation);
		var shapetype = this.properties.shape;
		if(shapetype == 'text'){
			this._renderTextShape(canvas,transformation);
		}
		else if(shapetype == 'point'){
			this._calculatePointCoordinates(transformation);
		} 
		else if(shapetype == 'path' || shapetype =='polygon'){
			
		}
		else{
			console.log("no idea how to render" +this.properties.shape+" must be polygon|path|point");
			return;
		}		
		//optimisations = false;
		if(!projection && optimisations){
			if(shapetype != 'point' && shapetype != 'path' && frame){ //check if worth drawing				
				if(!this._shapeIsTooSmall(transformation)) {
					if(this.vml) this.vml.style.display = "none";
					return;	
				}
				if(!this._shapeIsInVisibleArea(frame)){
					if(this.vml) this.vml.style.display = "none";
					return;	
				}	
			}
		}	

		if(this.vml) this.vml.style.display = '';
		
		if(shapetype == 'text'){
			//special treatment!
		}
		else if(!canvas.getContext) {
			//this has been taken from Google ExplorerCanvas
			if (!document.namespaces['g_vml_']) {
			        document.namespaces.add('g_vml_', 'urn:schemas-microsoft-com:vml');
			}

			  // Setup default CSS.  Only add one style sheet per document
			 if (!document.styleSheets['ex_canvas_']) {
			        var ss = document.createStyleSheet();
			        ss.owningElement.id = 'ex_canvas_';
			        ss.cssText = 'canvas{display:inline-block;overflow:hidden;' +
			            // default size is 300x150 in Gecko and Opera
			            'text-align:left;width:300px;height:150px}' +
			            'g_vml_\\:*{behavior:url(#default#VML)}';
			}
			
			this._ierender(canvas,transformation,projection,optimisations); 

	
		}
		else{
			this._canvasrender(canvas,transformation,projection,optimisations);

		}
		
	}
};
/*requires EasyShapes and EasyController */

var EasyMapController = function(targetjs,elem){ //elem must have style.width and style.height
	this.setMaxScaling(99999999);
	if(!elem.style.position) elem.style.position = "relative";
	this.wrapper = elem; //a dom element to detect mouse actions
	this.targetjs = targetjs; //a js object to run actions on (with pan and zoom functions)	

	var controlDiv = this.wrapper.controlDiv;
	if(!controlDiv) {
		controlDiv = document.createElement('div');
		controlDiv.style.position = "absolute";
		controlDiv.style.top = "0";
		controlDiv.style.left = "0";
		this.wrapper.appendChild(controlDiv);
		this.wrapper.controlDiv = controlDiv;
	}
	this.transformation = {'translate':{x:0,y:0}, 'scale': {x:1, y:1},'rotate': {x:0,y:0,z:0}};	
	//looks for specifically named function in targetjs
	if(!this.targetjs.transform) alert("no transform function defined in " + targetjs+"!");
	this.wrapper.easyController = this;
	

};
EasyMapController.prototype = {
	addMouseWheelZooming: function(){ /*not supported for internet explorer*/
		var mw = this.wrapper.onmousewheel;
		var that = this;
		var onmousewheel = function(e){
	        	if (!e) /* For IE. */
	                e = window.event;
			e.preventDefault();		
					
			/* thanks to http://adomas.org/javascript-mouse-wheel */
			var delta = 0;

	
			var t = EasyClickingUtils.resolveTarget(e);
			
			if(t != that.wrapper && t.parentNode !=that.wrapper) return;
	       	 	if (e.wheelDelta) { /* IE/Opera. */
		                delta = e.wheelDelta/120;
		                /** In Opera 9, delta differs in sign as compared to IE.
		                 */
		                if (window.opera)
		                        delta = -delta;
		        } else if (e.detail) { /** Mozilla case. */
		                /** In Mozilla, sign of delta is different than in IE.
		                 * Also, delta is multiple of 3.
		                 */
		                delta = -e.detail/3;
		        }
	
			var sensitivity = 0.3;
			if(!this.lastdelta) this.lastdelta = delta;
			
			if(delta > this.lastdelta + sensitivity || delta < this.lastdelta - sensitivity){
				
			
				var s =that.transformation.scale;
				
				var pos = EasyClickingUtils.getMouseFromEventRelativeToCenter(e);
				var t=  that.transformation.translate;
				
				var newx,newy;
				if(delta > 0){
					newx = parseFloat(s.x) * 2;
					newy = parseFloat(s.y) * 2;					
				}
				else{
					newx = parseFloat(s.x) / 2;
					newy = parseFloat(s.y) / 2;
				}

				if(newx > 0 && newy > 0){
					s.x = newx;
					s.y = newy;
					that.transform();					
				}

			}
			
			this.lastdelta = delta;
			
			return false;

		}

		
		var element = this.wrapper;
		if (element.addEventListener){
		        /** DOMMouseScroll is for mozilla. */
		
		        element.addEventListener('DOMMouseScroll', onmousewheel, false);
		
		}
		else if(element.attachEvent){
			element.attachEvent("mousewheel", onmousewheel); //safari
		}
		else{ //it's ie.. or something non-standardised. do nowt
		//window.onmousewheel = document.onmousewheel = onmousewheel;	
		}

		
	},

	addMousePanning: function(){
		var that = this;
		var md = that.wrapper.onmousedown;
		var mu = that.wrapper.onmouseup;	
		var mm = that.wrapper.onmousemove;
		var onmousemove = function(e){
			if(!this.easyController)return;
			var p =this.easyController.panning_status;
			if(!p) return;
			if(!p) return;
			var t =  EasyClickingUtils.resolveTarget(e);
	
			if(t.getAttribute("class") == "easyControl") return;
			var pos =  EasyClickingUtils.getMouseFromEventRelativeToElement(e,p.clickpos.x,p.clickpos.y,p.elem);		
			if(!pos)return;
			
			var t = that.transformation;
			//if(this.transformation) t = this.transformation;
			var sc = t.scale;
			
			var xd =parseFloat(pos.x /sc.x);
			var yd = parseFloat(pos.y / sc.y);
			t.translate.x = p.translate.x + xd;
			t.translate.y =p.translate.y +yd;

			that.transform();
			
			if(pos.x > 5 || pos.y > 5) p.isClick = false;
			if(pos.x < 5 || pos.y < 5) p.isClick = false;
			return false;	
		};
		
		this.wrapper.onmousedown = function(e){
					
			if(md) md(e);
			var target =  EasyClickingUtils.resolveTarget(e);
			if(!target) return;

			if(target.getAttribute("class") == "easyControl") return;

			var t = that.transformation.translate;
			var sc =that.transformation.scale; 
			var realpos = EasyClickingUtils.getMouseFromEvent(e);
			if(!realpos) return;
			this.easyController = that;
			
			var element = EasyClickingUtils.resolveTargetWithEasyClicking(e);
			that.panning_status =  {clickpos: realpos, translate:{x: t.x,y:t.y},elem: element,isClick:true};
			
			that.wrapper.onmousemove = onmousemove;
			that.wrapper.style.cursor= "move";
			this.style.cursor = "move";
		
		};
		
		this.wrapper.onmouseup = function(e){
			that.wrapper.style.cursor= '';
			that.wrapper.onmousemove = mm;
			
			if(!this.easyController && mu){mu(e); return;};
			if(this.easyController.panning_status && this.easyController.panning_status.isClick && mu){ mu(e);}
			this.easyController.panning_status = null;
			
			return false;
		};
	
	
	},
	setTransformation: function(t){
		if(!t.scale && !t.translate && !t.rotate) alert("bad transformation applied - any call to setTransformation must contain translate,scale and rotate");
		this.transformation = t;
		//console.log("transformation set",t);
		//this.wrapper.transformation = t;
		this.targetjs.transform(t);
		//console.log("transformation set to ",t);
	},
	createButtonLabel: function(r,type){
		var properties=  {'shape':'path', stroke: '#000000',lineWidth: '1'};
		
		var coords=[];
		if(type == 'earrow'){
			coords =[r,0,-r,0,'M',r,0,0,-r,"M",r,0,0,r];
		}
		else if(type =='warrow'){
			coords =[-r,0,r,0,'M',-r,0,0,r,"M",-r,0,0,-r]; 
		}
		else if(type == 'sarrow'){
			coords =[0,-r,0,r,'M',0,r,-r,0,"M",0,r,r,0];	
		}
		else if(type == 'narrow'){
			coords =[0,-r,0,r,'M',0,-r,r,0,"M",0,-r,-r,0];	
		}
		else if(type == 'plus'){
			coords =[-r,0,r,0,"M",0,-r,0,r];
		}
		else if(type == 'minus'){
			coords = [-r,0,r,0];
		}
		
		return new EasyShape(properties,coords);
	},	
	createButton: function(canvas,width,direction,offset,properties) {
		if(!width) width = 100;
		var r = width/2;

		offset = {
			x: offset.x || 0,
			y: offset.y || 0
		};
		var coords = [
			offset.x, offset.y,
			offset.x + width, offset.y,
			offset.x + width, offset.y + width,
			offset.x, offset.y + width
		];
		properties.shape = 'polygon';
		properties.fill ='rgba(150,150,150,0.7)';
		var button = new EasyShape(properties,coords);
		button.render(canvas,{translate:{x:0,y:0}, scale:{x:1,y:1},origin:{x:0,y:0}});
		var label = this.createButtonLabel(r,properties.buttonType);
		label.render(canvas,{translate:{x:0,y:0}, scale:{x:1,y:1},origin:{x:offset.x + r,y:offset.y + r}});
		
		canvas.easyClicking.addToMemory(button);
		return button;
	},	
	addControl: function(controlType) {
		switch(controlType) {
			//case "zoom":
			case "pan":
				this.addPanningActions();
				break;
			case "zoom":
				this.addZoomingActions();
				break;
			case "mousepanning":
				this.addMousePanning();
				break;
			case "mousewheelzooming":
				this.addMouseWheelZooming();
				break;
			case "rotation":
		
				this.addRotatingActions();
				break;
			default:
				break;
		}
	
	},
	
	_createcontrollercanvas: function(width,height){
		var newCanvas = document.createElement('canvas');
		newCanvas.style.width = width;
		newCanvas.style.height = height;
		newCanvas.width = width;
		newCanvas.height = height;
		newCanvas.style.position = "absolute";
		newCanvas.style.left = 0;
		newCanvas.style.top = 0;
		newCanvas.style["z-index"] = 3;
		newCanvas.setAttribute("class","easyControl");
		this.wrapper.appendChild(newCanvas);

		if(!newCanvas.getContext) {
			newCanvas.browser = 'ie';
		}
		newCanvas.easyController = this;
		newCanvas.easyClicking = new EasyClicking(newCanvas);
		
		//newCanvas.memory = [];
		return newCanvas;
	},
	addPanningActions: function(controlDiv){
		var panCanvas = this._createcontrollercanvas(44,44);		
		this.createButton(panCanvas,10,180,{x:16,y:2},{'actiontype':'N','name':'pan north','buttonType': 'narrow'});
		this.createButton(panCanvas,10,270,{x:30,y:16},{'actiontype':'E','name':'pan east','buttonType': 'earrow'});
		this.createButton(panCanvas,10,90,{x:16,y:16},{'actiontype':'O','name':'re-center','buttonType': ''});
		this.createButton(panCanvas,10,90,{x:2,y:16},{'actiontype':'W','name':'pan west','buttonType': 'warrow'});
		this.createButton(panCanvas,10,0,{x:16,y:30},{'actiontype':'S','name':'pan south','buttonType': 'sarrow'});			
		panCanvas.onmouseup = this._panzoomClickHandler;		

	},
	addRotatingActions: function(){
		
		var rotateCanvas = this._createcontrollercanvas(44,40);		
		this.createButton(rotateCanvas,10,270,{x:30,y:16},{'actiontype':'rotatezright','name':'rotate to right','buttonType': 'earrow'});
		this.createButton(rotateCanvas,10,90,{x:2,y:16},{'actiontype':'rotatezleft','name':'rotate to left','buttonType': 'warrow'});
		rotateCanvas.onmouseup = this._panzoomClickHandler;

	},	
	addZoomingActions: function(){
		var zoomCanvas = this._createcontrollercanvas(20,30);

		var left = 14;
		var top = 50;
		zoomCanvas.style.left = left +"px";
		zoomCanvas.style.top = top + "px";
		this.createButton(zoomCanvas,10,180,{x:2,y:2},{'actiontype':'in','name':'zoom in','buttonType': 'plus'});		
		this.createButton(zoomCanvas,10,180,{x:2,y:16},{'actiontype':'out','name':'zoom out','buttonType': 'minus'});
		zoomCanvas.onmouseup = this._panzoomClickHandler;	
	},	
	
	setMaxScaling: function(max){
		this._maxscale = max;
	}
	,transform: function(){
		var t = this.transformation;
		var s = t.scale;
		var tr = t.translate;
		var style = this.wrapper.style;
		
		
		var width = parseInt(style.width);
		var height = parseInt(style.height);
		if(s.x <= 0) s.x = 0.1125;
		if(s.y <= 0) s.y = 0.1125;
		
		if(s.x > this._maxscale) s.x = this._maxscale;
		if(s.y > this._maxscale) s.y = this._maxscale;
		
		
		if(width && height){
			var max = {};
			max.x = parseFloat((width) - 10) * s.x;//the maximum possible translation
			max.y = parseFloat((height) - 10) * s.y;//the maximum possible translation
	
			if(tr.x > max.x){
				tr.x = max.x;
			}
			else if(tr.x < -max.x){
				tr.x= -max.x;
			}
		
			if(tr.y > max.y){
				tr.y = max.y;
			}
			else if(tr.y < -max.y){
				tr.y= -max.y;
			}
		}

		//console.log(this.transformation.rotate,"rotate");
		this.targetjs.transform(this.transformation);


	},
	_panzoomClickHandler: function(e) {
		
		if(!e) {
			e = window.event;
		}
		
		var controller = this.easyController;
	
		var hit = this.easyClicking.getShapeAtClick(e);	
		if(!hit) {
			return false;
		}
		if(!hit.properties) return false;
		
		var pan = {};
		var t =controller.transformation;
		//console.log(t.rotate,"hit");
		var scale =t.scale;
		pan.x = parseFloat(30 / scale.x);
		pan.y = parseFloat(30 / scale.y);

		if(!t.scale) t.scale = {x:1,y:1};
		if(!t.translate) t.translate = {x:0,y:0};
		if(!t.rotate) t.rotate = {x:0,y:0,z:0};

		switch(hit.properties.actiontype) {
			case "W":
				t.translate.x += pan.x;
				break;
			case "O":
				t.translate.x = 0;
				t.translate.y = 0;
				break;

			case "E":
				t.translate.x -= pan.x;
				break;
			case "N":
				t.translate.y += pan.y;
				break;
			case "S":
				t.translate.y -= pan.y;
				break;
			case "in":
				scale.x *= 2;
				scale.y *= 2;
				break;
			case "out":
				scale.x /= 2;
				scale.y /= 2;			
				break;
			case "rotatezright":
				if(!t.rotate.z) t.rotate.z = 0;
				//console.log("right",t.rotate.z);
				t.rotate.z -= 0.1;
				var left =6.28318531;
				
				if(t.rotate.z <0 )t.rotate.z =left;
				break;
			case "rotatezleft":
				if(!t.rotate.z) t.rotate.z = 0;
				t.rotate.z += 0.1;
				break;
			default:
				break;
		}
		controller.transform();

		return false;
	}
};
/*
Some common utils used throughout package 
*/

if(!window.console) {
	console = {
		log:function(message) {
			var d = document.getElementById('consolelogger');
			if(d) {
				d.innerHTML += message+"<<] ";
			}
		}
	};
}

Array.prototype.contains = function(item)
{
	return this.indexOf(item) != -1;
};

if(!Array.indexOf) {
	Array.prototype.indexOf = function(item,from)
	{
		if(!from)
			from = 0;
		for(var i=from; i<this.length; i++) {
			if(this[i] === item)
				return i;
		}
		return -1;
	};
}

var EasyMapUtils = {
	googlelocalsearchurl: "http://ajax.googleapis.com/ajax/services/search/local?v=1.0&q="
	
	,getLocationsFromQuery: function(query,callback){
		var that = this;
		var fileloadedcallback = function(status,params,responseText,url,xhr){
				var response = eval("("+responseText+")");

				if(response.responseStatus == 200){
					var results = response.responseData.results;
					callback(results);
					
					return;
				}

		};
			
	
		EasyFileUtils.loadRemoteFile(that.googlelocalsearchurl+query,fileloadedcallback);
	}
	,getLongLatFromMouse: function(x,y,easyMap){
		
		var pos = EasyClickingUtils.undotransformation(x,y,easyMap.controller.transformation);
		return {latitude:-pos.y,longitude:pos.x};
	}
	,_radToDeg: function(rad){
		return rad / (Math.PI /180);
	},
	_degToRad: function(deg) {
		return deg * Math.PI / 180;
	},
	fitgeojsontocanvas: function(json,canvas){ /*canvas must have style width and height properties*/
		var view ={};
		var f =json.features;
		for(var i=0; i < f.length; i++){
			var c = f[i].geometry.coordinates;
											
			for(var j=0; j < c.length; j++ ){
				for(var k=0; k < c[j].length; k++){
					

					for(var l=0; l < c[j][k].length;l++){
						
		
						var x =c[j][k][l][0];
						var y = c[j][k][l][1];
						if(!view.x1 || x <view.x1) {
							view.x1 = x;
						}
						else if(!view.x2 || x >view.x2) {
							view.x2 = x;
						}
						
						if(!view.y1 || y <view.y1) {
							view.y1 = y;
						}
						else if(!view.y2 || y >view.y2) {
							view.y2 = y;
						}
						

					}
						
				}
				
			}
		}
		if(!json.transform) json.transform ={};
		if(!json.transform.scale) json.transform.scale = {x:1, y:1};
		if(!json.transform.translate) json.transform.translate = {x:0,y:0};
		
		var canvasx =		parseFloat(canvas.style.width);
		var canvasy =parseFloat(canvas.style.height);
		view.center = {};
		view.width = (view.x2 - view.x1);
		view.height = (view.y2 - view.y1)
		view.center.x = view.x2 - (view.width/2);
		view.center.y = view.y2 - (view.height/2);
		//console.log(view.center.y, view.height);
		var scale = 1,temp;
		var tempx = parseFloat(canvasx/view.width);
		var tempy = parseFloat(canvasy/view.height);

		if(tempx < tempy) temp = tempx; else temp = tempy;
		json.transform.scale.x = temp;
		json.transform.scale.y = temp;

		json.boundingBox = view;

		json.transform.translate.x = -view.center.x;
		json.transform.translate.y = view.center.y;//view.center.y;	
		return json;
	},
	/*does not yet support undoing rotating */
	_testCanvas: function(ctx){
	ctx.beginPath();
	ctx.arc(75,75,50,0,Math.PI*2,true); // Outer circle
	ctx.moveTo(110,75);
	ctx.arc(75,75,35,0,Math.PI,false);   // Mouth (clockwise)
	ctx.moveTo(65,65);
	ctx.arc(60,65,5,0,Math.PI*2,true);  // Left eye
	ctx.moveTo(95,65);
	ctx.arc(90,65,5,0,Math.PI*2,true);  // Right eye
	ctx.stroke();

	},	
	_undospherify: function (x,y,transformation){
		var radius = transformation.spherical.radius;
		var pos= this._spherifycoordinate(x,y,transformation);
		var latitude = Math.asin(y / radius);
		var longitude = Math.asin(parseFloat(x / radius) / Math.cos(latitude));

				
	
		//if(transformation.rotate.z && longitude != 'NaN')longitude -= transformation.rotate.z;
		//longitude = longitude % (6.28318531);
		//if(longitude < 0) longitude = longitude 

		if(transformation.rotate) {
			var r =transformation.rotate.z;
			console.log("from",longitude);
			longitude +=r;
		
			//longitude =longitude% (6.28318531);
			
		}
		var lon = EasyMapUtils._radToDeg(longitude);
		var lat = EasyMapUtils._radToDeg(latitude);
		console.log("to",longitude,r,lon);
		return {x:lon,y:lat};
	},
	_spherifycoordinate: function(lon,lat,transformation){
		//http://board.flashkit.com/board/archive/index.php/t-666832.html
		var radius = transformation.spherical.radius;
		var utils = EasyMapUtils;
		var res = {};
		
		var longitude = EasyMapUtils._degToRad(lon);
		var latitude = EasyMapUtils._degToRad(lat);
 		
 		// assume rotate values given in radians
		if(transformation && transformation.rotate && transformation.rotate.z){
			//latitude += transformation.rotate.x;
			var r =parseFloat(transformation.rotate.z);
			
			var newl =parseFloat(longitude+r);
			//console.log(longitude,"->",newl,longitude,r,transformation.rotate.z);
			
			longitude +=r;
			 
		}
		// latitude is 90-theta, where theta is the polar angle in spherical coordinates
		// cos(90-theta) = sin(theta)
		// sin(90-theta) = cos(theta)
		// to transform from spherical to cartesian, we would normally use radius*Math.cos(theta)
		//   hence in this case we use radius*Math.sin(latitude)
		// similarly longitude is phi-180, where phi is the azimuthal angle
		// cos(phi-180) = -cos(phi)
		// sin(phi-180) = -sin(phi)
		// to transform from spherical to cartesian, we would normally use radius*Math.sin(theta)*Math.cos(phi)
		//   we must exchange for theta as above, but because of the circular symmetry
		//   it does not matter whether we multiply by sin or cos of longitude	
	
		longitude = longitude % 6.28318531; //360 degrees		

		res.y = (radius) * Math.sin(latitude);	
		if(longitude < 1.57079633 || longitude > 4.71238898){//0-90 (right) or 270-360 (left) then on other side 
			res.x = (radius) * Math.cos(latitude) * Math.sin(longitude);		
		}
		else{
			//console.log(longitude,"bad",transformation.rotate.z);
			res.x = false;
		}
	
		return res;
	}

};
/*
EasyClicking adds the ability to associate a dom element with lots of EasyShapes using addToMemory function
The getShapeAtClick function allows click detection on this dom element when used in a dom mouse event handler
*/

// Depends on JQuery for offset function
// Get the current horizontal page scroll position
function findScrollX()
{
	return window.scrollX || document.documentElement.scrollLeft;
}
// Get the current vertical page scroll position
function findScrollY()
{
	return window.scrollY || document.documentElement.scrollTop;
}

var EasyClicking = function(element,transformation,easyShapesList){
	if(element.easyClicking) {
		console.log("already has easyClicking");
		var update = element.easyClicking;
		return update;
	}

	this.memory = [];
	element.easyClicking = this;
	if(easyShapesList) this.memory = easyShapesList;
	if(transformation) this.transformation = transformation;
	
};

EasyClicking.prototype = {

	addToMemory: function(easyShape){
		if(!this.memory) this.memory = [];
		easyShape._easyClickingID = this.memory.length;
		this.memory.push(easyShape);
	}
	
	,clearMemory: function(){
		this.memory = [];
	},
	getMemory: function(){
		return this.memory;
	}
	,getMemoryID: function(easyShape){
		if(easyShape && easyShape._easyClickingID)
			return easyShape._easyClickingID;
		else{
			return false;
		}
	}
	,getShapeAtClick: function(e){
		if(!e) {
			e = window.event;
		}
		
		var node = EasyClickingUtils.resolveTarget(e);
		if(node.getAttribute("class") == 'easyShape') { //vml easyShape
			return node.easyShape;
		}
		var target = EasyClickingUtils.resolveTargetWithEasyClicking(e);
	
		if(!target) return;
		var offset = $(target).offset();
	

		x = e.clientX + window.findScrollX() - offset.left;
		y = e.clientY + window.findScrollY() - offset.top;

		//counter any positioning
		//if(target.style.left) x -= parseInt(target.style.left);
		//if(target.style.top) y -= parseInt(target.style.top);

	
		//var memory = target.memory;
		//var transformation = target.transformation;
		//console.log('memory length: '+memory.length);
		if(this.memory.length > 0){
			var shape = target.easyClicking.getShapeAtPosition(x,y);
			return shape;
		} else{
			//console.log("no shapes in memory");
			//return false;
		}
	},
	getShapeAtPosition: function(x,y) {
		var shapes = this.memory;
		if(this.transformation){
			var pos =  EasyClickingUtils.undotransformation(x,y,this.transformation);
			x = pos.x;
			y = pos.y;
		
		}
		var hitShapes = [];
	
		for(var i=0; i < shapes.length; i++){
			var g = shapes[i].grid;
			if(x >= g.x1 && x <= g.x2 && y >=  g.y1 && y <=g.y2){
				hitShapes.push(shapes[i]);
			}
		}
		var res = this._findNeedleInHaystack(x,y,hitShapes);
		
		
		return res;
	},
	_findNeedleInHaystack: function(x,y,shapes){
		var hits = [];
		for(var i=0; i < shapes.length; i++){
			if(this._inPoly(x,y,shapes[i])) {
				hits.push(shapes[i]);
			}
		}

		if(hits.length == 0){
			return null;
		}
		else if(hits.length == 1) 
			return hits[0];
		else {//the click is in a polygon which is inside another polygon
		
			var g = hits[0].grid;
			var min = Math.min(g.x2 - x,x - g.x1,g.y2 - y,y - g.y1);
		
			var closerEdge = {id:0, closeness:min};
			for(var i=1; i < hits.length; i++){
				g = hits[i].grid;
			var min = Math.min(g.x2 - x,x - g.x1,g.y2 - y,y - g.y1);
			
				if(closerEdge.closeness > min) {
					closerEdge.id = i; closerEdge.closeness = min;
				}
				return hits[closerEdge.id];
			}
		
		}

	},                     
	_inPoly: function(x,y,poly) {
		/* _inPoly adapted from inpoly.c
		Copyright (c) 1995-1996 Galacticomm, Inc.  Freeware source code.
		http://www.visibone.com/inpoly/inpoly.c.txt */
		var coords;
		if(poly._tcoords){
			coords = poly._tcoords;
			//console.log("using tcoords",x,y,poly.coords.length,poly._tcoords.length);
		}
		else
			coords= poly.coords;
		var npoints = coords.length;
		if (npoints/2 < 3) {
			//points don't describe a polygon
			return false;
		}
		var inside = false;
		var xold = coords[npoints-2];
		var yold = coords[npoints-1];
		var x1,x2,y1,y2,xnew,ynew;
		for (var i=0; i<npoints; i+=2) {
			xnew=coords[i];
			ynew=coords[i+1];
			if (xnew > xold) {
				x1=xold;
				x2=xnew;
				y1=yold;
				y2=ynew;
			} else {
				x1=xnew;
				x2=xold;
				y1=ynew;
				y2=yold;
			}
			if ((xnew < x) == (x <= xold)
				&& (y-y1)*(x2-x1) < (y2-y1)*(x-x1)) {
				   inside=!inside;
				}
			xold=xnew;
			yold=ynew;
		 }
		 return inside;
	}

};


var EasyClickingUtils = {
	undotransformation: function(x,y,transformation){
	
		var pos = {};
		var t =transformation;
		var tr =t.translate;
		var s = t.scale;
		var o = t.origin;
		if(!x || !y) 
			return false;
		pos.x = x;
		pos.y = y;
	

		pos.x -= o.x;
		pos.y -= o.y;

		pos.x /= s.x;
		pos.y /= s.y;
			
		pos.x -= tr.x;
		pos.y -= tr.y;			
		return pos;
	}	
	,resolveTarget:function(e)
	{
		if(!e) e = window.event;
		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;
	}
	
	,getMouseFromEvent : function(e){
			if(!e) e = window.event;
			var target = this.resolveTargetWithEasyClicking(e);
			if(!target)return false;
		

			var offset = $(target).offset();

			
			if(!offset.left) return false;
			x = e.clientX + window.findScrollX() - offset.left;
			y = e.clientY + window.findScrollY() - offset.top;
			return {'x':x, 'y':y};		
			
	}


	,resolveTargetWithEasyClicking: function(e)
	{
		var node = EasyClickingUtils.resolveTarget(e);
		var first = node;
		while(node && !node.easyClicking){
			node = node.parentNode;
		}
		if(!node) node = first;
		return node;
	}
	,getMouseFromEventRelativeToElement: function (e,x,y,target){
		if(!e) e = window.event;

		var offset = $(target).offset();
		if(!offset.left) return false;
		
		oldx = e.clientX + window.findScrollX() - offset.left;
		oldy = e.clientY + window.findScrollY() - offset.top;
		var pos = {'x':oldx, 'y':oldy};

		if(!pos) return false;
		pos.x -= x;
		pos.y -= y;
		

		return pos;
		
	}

	,getMouseFromEventRelativeTo: function (e,x,y){
	
		var pos = this.getMouseFromEvent(e);
		if(!pos) return false;
		pos.x -= x;
		pos.y -= y;

		return pos;
	
	}
	,getMouseFromEventRelativeToCenter: function(e){
		var w,h;
		var target = this.resolveTargetWithEasyClicking(e);
		if(!target)return;
		if(target.style.width)
			w = parseInt(target.style.width);
		else if(target.width)
			w =parseInt(target.width);

		if(target.style.height)
			h = parseInt(target.style.height);
		else if(target.height)
			h = parseInt(target.height);
	
		if(!w || !h) throw "target has no width or height (easymaputils)";
	
		return this.getMouseFromEventRelativeTo(e,w/2,h/2);
	}	
	

};

/***
!Layer 4: Support Internet Explorer (Safely remove if not required)
***/

// Copyright 2006 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


// Known Issues:
//
// * Patterns are not implemented.
// * Radial gradient are not implemented. The VML version of these look very
//   different from the canvas one.
// * Clipping paths are not implemented.
// * Coordsize. The width and height attribute have higher priority than the
//   width and height style values which isn't correct.
// * Painting mode isn't implemented.
// * Canvas width/height should is using content-box by default. IE in
//   Quirks mode will draw the canvas using border-box. Either change your
//   doctype to HTML5
//   (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype)
//   or use Box Sizing Behavior from WebFX
//   (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)
// * Non uniform scaling does not correctly scale strokes.
// * Optimize. There is always room for speed improvements.

// Only add this code if we do not already have a canvas implementation
if (!document.createElement('canvas').getContext) {

(function() {

  // alias some functions to make (compiled) code shorter
  var m = Math;
  var mr = m.round;
  var ms = m.sin;
  var mc = m.cos;
  var max = m.max;
  var abs = m.abs;
  var sqrt = m.sqrt;

  // this is used for sub pixel precision
  var Z = 10;
  var Z2 = Z / 2;

  /**
   * This funtion is assigned to the <canvas> elements as element.getContext().
   * @this {HTMLElement}
   * @return {CanvasRenderingContext2D_}
   */
  function getContext() {
    return this.context_ ||
        (this.context_ = new CanvasRenderingContext2D_(this));
  }

  var slice = Array.prototype.slice;

  /**
   * Binds a function to an object. The returned function will always use the
   * passed in {@code obj} as {@code this}.
   *
   * Example:
   *
   *   g = bind(f, obj, a, b)
   *   g(c, d) // will do f.call(obj, a, b, c, d)
   *
   * @param {Function} f The function to bind the object to
   * @param {Object} obj The object that should act as this when the function
   *     is called
   * @param {*} var_args Rest arguments that will be used as the initial
   *     arguments when the function is called
   * @return {Function} A new function that has bound this
   */
  function bind(f, obj, var_args) {
    var a = slice.call(arguments, 2);
    return function() {
      return f.apply(obj, a.concat(slice.call(arguments)));
    };
  }

  var G_vmlCanvasManager_ = {
    init: function(opt_doc) {
      if (/MSIE/.test(navigator.userAgent) && !window.opera) {
        var doc = opt_doc || document;
        // Create a dummy element so that IE will allow canvas elements to be
        // recognized.
        doc.createElement('canvas');
        doc.attachEvent('onreadystatechange', bind(this.init_, this, doc));
      }
    },

    init_: function(doc) {
      // create xmlns
      if (!doc.namespaces['g_vml_']) {
        doc.namespaces.add('g_vml_', 'urn:schemas-microsoft-com:vml');
      }

      // Setup default CSS.  Only add one style sheet per document
      if (!doc.styleSheets['ex_canvas_']) {
        var ss = doc.createStyleSheet();
        ss.owningElement.id = 'ex_canvas_';
        ss.cssText = 'canvas{display:inline-block;overflow:hidden;' +
            // default size is 300x150 in Gecko and Opera
            'text-align:left;width:300px;height:150px}' +
            'g_vml_\\:*{behavior:url(#default#VML)}';
      }

      // find all canvas elements
      var els = doc.getElementsByTagName('canvas');
      for (var i = 0; i < els.length; i++) {
        this.initElement(els[i]);
      }
    },

    /**
     * Public initializes a canvas element so that it can be used as canvas
     * element from now on. This is called automatically before the page is
     * loaded but if you are creating elements using createElement you need to
     * make sure this is called on the element.
     * @param {HTMLElement} el The canvas element to initialize.
     * @return {HTMLElement} the element that was created.
     */
    initElement: function(el) {
      if (!el.getContext) {

        el.getContext = getContext;

        // do not use inline function because that will leak memory
        el.attachEvent('onpropertychange', onPropertyChange);
        el.attachEvent('onresize', onResize);

        var attrs = el.attributes;
        if (attrs.width && attrs.width.specified) {
          // TODO: use runtimeStyle and coordsize
          // el.getContext().setWidth_(attrs.width.nodeValue);
          el.style.width = attrs.width.nodeValue + 'px';
        } else {
          el.width = el.clientWidth;
        }
        if (attrs.height && attrs.height.specified) {
          // TODO: use runtimeStyle and coordsize
          // el.getContext().setHeight_(attrs.height.nodeValue);
          el.style.height = attrs.height.nodeValue + 'px';
        } else {
          el.height = el.clientHeight;
        }
        //el.getContext().setCoordsize_()
      }
      return el;
    }
  };

  function onPropertyChange(e) {
    var el = e.srcElement;

    switch (e.propertyName) {
      case 'width':
        el.style.width = el.attributes.width.nodeValue + 'px';
        el.getContext().clearRect();
        break;
      case 'height':
        el.style.height = el.attributes.height.nodeValue + 'px';
        el.getContext().clearRect();
        break;
    }
  }

  function onResize(e) {
    var el = e.srcElement;
    if (el.firstChild) {
      el.firstChild.style.width =  el.clientWidth + 'px';
      el.firstChild.style.height = el.clientHeight + 'px';
    }
  }

  G_vmlCanvasManager_.init();

  // precompute "00" to "FF"
  var dec2hex = [];
  for (var i = 0; i < 16; i++) {
    for (var j = 0; j < 16; j++) {
      dec2hex[i * 16 + j] = i.toString(16) + j.toString(16);
    }
  }

  function createMatrixIdentity() {
    return [
      [1, 0, 0],
      [0, 1, 0],
      [0, 0, 1]
    ];
  }

  function matrixMultiply(m1, m2) {
    var result = createMatrixIdentity();

    for (var x = 0; x < 3; x++) {
      for (var y = 0; y < 3; y++) {
        var sum = 0;

        for (var z = 0; z < 3; z++) {
          sum += m1[x][z] * m2[z][y];
        }

        result[x][y] = sum;
      }
    }
    return result;
  }

  function copyState(o1, o2) {
    o2.fillStyle     = o1.fillStyle;
    o2.lineCap       = o1.lineCap;
    o2.lineJoin      = o1.lineJoin;
    o2.lineWidth     = o1.lineWidth;
    o2.miterLimit    = o1.miterLimit;
    o2.shadowBlur    = o1.shadowBlur;
    o2.shadowColor   = o1.shadowColor;
    o2.shadowOffsetX = o1.shadowOffsetX;
    o2.shadowOffsetY = o1.shadowOffsetY;
    o2.strokeStyle   = o1.strokeStyle;
    o2.globalAlpha   = o1.globalAlpha;
    o2.arcScaleX_    = o1.arcScaleX_;
    o2.arcScaleY_    = o1.arcScaleY_;
    o2.lineScale_    = o1.lineScale_;
  }

  function processStyle(styleString) {
    var str, alpha = 1;

    styleString = String(styleString);
    if (styleString.substring(0, 3) == 'rgb') {
      var start = styleString.indexOf('(', 3);
      var end = styleString.indexOf(')', start + 1);
      var guts = styleString.substring(start + 1, end).split(',');

      str = '#';
      for (var i = 0; i < 3; i++) {
        str += dec2hex[Number(guts[i])];
      }

      if (guts.length == 4 && styleString.substr(3, 1) == 'a') {
        alpha = guts[3];
      }
    } else {
      str = styleString;
    }

    return [str, alpha];
  }

  function processLineCap(lineCap) {
    switch (lineCap) {
      case 'butt':
        return 'flat';
      case 'round':
        return 'round';
      case 'square':
      default:
        return 'square';
    }
  }

  /**
   * This class implements CanvasRenderingContext2D interface as described by
   * the WHATWG.
   * @param {HTMLElement} surfaceElement The element that the 2D context should
   * be associated with
   */
  function CanvasRenderingContext2D_(surfaceElement) {
    this.m_ = createMatrixIdentity();

    this.mStack_ = [];
    this.aStack_ = [];
    this.currentPath_ = [];

    // Canvas context properties
    this.strokeStyle = '#000';
    this.fillStyle = '#000';

    this.lineWidth = 1;
    this.lineJoin = 'miter';
    this.lineCap = 'butt';
    this.miterLimit = Z * 1;
    this.globalAlpha = 1;
    this.canvas = surfaceElement;

    var el = surfaceElement.ownerDocument.createElement('div');
    el.style.width =  surfaceElement.clientWidth + 'px';
    el.style.height = surfaceElement.clientHeight + 'px';
    el.style.overflow = 'hidden';
    el.style.position = 'absolute';
    surfaceElement.appendChild(el);

    this.element_ = el;
    this.arcScaleX_ = 1;
    this.arcScaleY_ = 1;
    this.lineScale_ = 1;
  }

  var contextPrototype = CanvasRenderingContext2D_.prototype;
  contextPrototype.clearRect = function() {
    this.element_.innerHTML = '';
    this.currentPath_ = [];
  };

  contextPrototype.beginPath = function() {
    // TODO: Branch current matrix so that save/restore has no effect
    //       as per safari docs.
    this.currentPath_ = [];
  };

  contextPrototype.moveTo = function(aX, aY) {
    var p = this.getCoords_(aX, aY);
    this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y});
    this.currentX_ = p.x;
    this.currentY_ = p.y;
  };

  contextPrototype.lineTo = function(aX, aY) {
    var p = this.getCoords_(aX, aY);
    this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y});

    this.currentX_ = p.x;
    this.currentY_ = p.y;
  };

  contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
                                            aCP2x, aCP2y,
                                            aX, aY) {
    var p = this.getCoords_(aX, aY);
    var cp1 = this.getCoords_(aCP1x, aCP1y);
    var cp2 = this.getCoords_(aCP2x, aCP2y);
    bezierCurveTo(this, cp1, cp2, p);
  };

  // Helper function that takes the already fixed cordinates.
  function bezierCurveTo(self, cp1, cp2, p) {
    self.currentPath_.push({
      type: 'bezierCurveTo',
      cp1x: cp1.x,
      cp1y: cp1.y,
      cp2x: cp2.x,
      cp2y: cp2.y,
      x: p.x,
      y: p.y
    });
    self.currentX_ = p.x;
    self.currentY_ = p.y;
  }

  contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
    // the following is lifted almost directly from
    // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes

    var cp = this.getCoords_(aCPx, aCPy);
    var p = this.getCoords_(aX, aY);

    var cp1 = {
      x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_),
      y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_)
    };
    var cp2 = {
      x: cp1.x + (p.x - this.currentX_) / 3.0,
      y: cp1.y + (p.y - this.currentY_) / 3.0
    };

    bezierCurveTo(this, cp1, cp2, p);
  };

  contextPrototype.arc = function(aX, aY, aRadius,
                                  aStartAngle, aEndAngle, aClockwise) {
    aRadius *= Z;
    var arcType = aClockwise ? 'at' : 'wa';

    var xStart = aX + mc(aStartAngle) * aRadius - Z2;
    var yStart = aY + ms(aStartAngle) * aRadius - Z2;

    var xEnd = aX + mc(aEndAngle) * aRadius - Z2;
    var yEnd = aY + ms(aEndAngle) * aRadius - Z2;

    // IE won't render arches drawn counter clockwise if xStart == xEnd.
    if (xStart == xEnd && !aClockwise) {
      xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
                       // that can be represented in binary
    }

    var p = this.getCoords_(aX, aY);
    var pStart = this.getCoords_(xStart, yStart);
    var pEnd = this.getCoords_(xEnd, yEnd);

    this.currentPath_.push({type: arcType,
                           x: p.x,
                           y: p.y,
                           radius: aRadius,
                           xStart: pStart.x,
                           yStart: pStart.y,
                           xEnd: pEnd.x,
                           yEnd: pEnd.y});

  };

  contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
    this.moveTo(aX, aY);
    this.lineTo(aX + aWidth, aY);
    this.lineTo(aX + aWidth, aY + aHeight);
    this.lineTo(aX, aY + aHeight);
    this.closePath();
  };

  contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
    // Will destroy any existing path (same as FF behaviour)
    this.beginPath();
    this.moveTo(aX, aY);
    this.lineTo(aX + aWidth, aY);
    this.lineTo(aX + aWidth, aY + aHeight);
    this.lineTo(aX, aY + aHeight);
    this.closePath();
    this.stroke();
    this.currentPath_ = [];
  };

  contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
    // Will destroy any existing path (same as FF behaviour)
    this.beginPath();
    this.moveTo(aX, aY);
    this.lineTo(aX + aWidth, aY);
    this.lineTo(aX + aWidth, aY + aHeight);
    this.lineTo(aX, aY + aHeight);
    this.closePath();
    this.fill();
    this.currentPath_ = [];
  };

  contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
    return new CanvasGradient_('gradient');
  };

  contextPrototype.createRadialGradient = function(aX0, aY0,
                                                   aR0, aX1,
                                                   aY1, aR1) {
    var gradient = new CanvasGradient_('gradientradial');
    gradient.radius1_ = aR0;
    gradient.radius2_ = aR1;
    gradient.focus_.x = aX0;
    gradient.focus_.y = aY0;
    return gradient;
  };

  contextPrototype.drawImage = function(image, var_args) {
    var dx, dy, dw, dh, sx, sy, sw, sh;

    // to find the original width we overide the width and height
    var oldRuntimeWidth = image.runtimeStyle.width;
    var oldRuntimeHeight = image.runtimeStyle.height;
    image.runtimeStyle.width = 'auto';
    image.runtimeStyle.height = 'auto';

    // get the original size
    var w = image.width;
    var h = image.height;

    // and remove overides
    image.runtimeStyle.width = oldRuntimeWidth;
    image.runtimeStyle.height = oldRuntimeHeight;

    if (arguments.length == 3) {
      dx = arguments[1];
      dy = arguments[2];
      sx = sy = 0;
      sw = dw = w;
      sh = dh = h;
    } else if (arguments.length == 5) {
      dx = arguments[1];
      dy = arguments[2];
      dw = arguments[3];
      dh = arguments[4];
      sx = sy = 0;
      sw = w;
      sh = h;
    } else if (arguments.length == 9) {
      sx = arguments[1];
      sy = arguments[2];
      sw = arguments[3];
      sh = arguments[4];
      dx = arguments[5];
      dy = arguments[6];
      dw = arguments[7];
      dh = arguments[8];
    } else {
      throw Error('Invalid number of arguments');
    }

    var d = this.getCoords_(dx, dy);

    var w2 = sw / 2;
    var h2 = sh / 2;

    var vmlStr = [];

    var W = 10;
    var H = 10;

    // For some reason that I've now forgotten, using divs didn't work
    vmlStr.push(' <g_vml_:group',
                ' coordsize="', Z * W, ',', Z * H, '"',
                ' coordorigin="0,0"' ,
                ' style="width:', W, ';height:', H, ';position:absolute;');

    // If filters are necessary (rotation exists), create them
    // filters are bog-slow, so only create them if abbsolutely necessary
    // The following check doesn't account for skews (which don't exist
    // in the canvas spec (yet) anyway.

    if (this.m_[0][0] != 1 || this.m_[0][1]) {
      var filter = [];

      // Note the 12/21 reversal
      filter.push('M11=', this.m_[0][0], ',',
                  'M12=', this.m_[1][0], ',',
                  'M21=', this.m_[0][1], ',',
                  'M22=', this.m_[1][1], ',',
                  'Dx=', mr(d.x / Z), ',',
                  'Dy=', mr(d.y / Z), '');

      // Bounding box calculation (need to minimize displayed area so that
      // filters don't waste time on unused pixels.
      var max = d;
      var c2 = this.getCoords_(dx + dw, dy);
      var c3 = this.getCoords_(dx, dy + dh);
      var c4 = this.getCoords_(dx + dw, dy + dh);

      max.x = max(max.x, c2.x, c3.x, c4.x);
      max.y = max(max.y, c2.y, c3.y, c4.y);

      vmlStr.push('padding:0 ', mr(max.x / Z), 'px ', mr(max.y / Z),
                  'px 0;filter:progid:DXImageTransform.Microsoft.Matrix(',
                  filter.join(''), ", sizingmethod='clip');")
    } else {
      vmlStr.push('top:', mr(d.y / Z), 'px;left:', mr(d.x / Z), 'px;');
    }

    vmlStr.push(' ">' ,
                '<g_vml_:image src="', image.src, '"',
                ' style="width:', Z * dw, ';',
                ' height:', Z * dh, ';"',
                ' cropleft="', sx / w, '"',
                ' croptop="', sy / h, '"',
                ' cropright="', (w - sx - sw) / w, '"',
                ' cropbottom="', (h - sy - sh) / h, '"',
                ' />',
                '</g_vml_:group>');

    this.element_.insertAdjacentHTML('BeforeEnd',
                                    vmlStr.join(''));
  };

  contextPrototype.stroke = function(aFill) {
    var lineStr = [];
    var lineOpen = false;
    var a = processStyle(aFill ? this.fillStyle : this.strokeStyle);
    var color = a[0];
    var opacity = a[1] * this.globalAlpha;

    var W = 10;
    var H = 10;

    lineStr.push('<g_vml_:shape',
                 ' filled="', !!aFill, '"',
                 ' style="position:absolute;width:', W, ';height:', H, ';"',
                 ' coordorigin="0 0" coordsize="', Z * W, ' ', Z * H, '"',
                 ' stroked="', !aFill, '"',
                 ' path="');

    var newSeq = false;
    var min = {x: null, y: null};
    var max = {x: null, y: null};

    for (var i = 0; i < this.currentPath_.length; i++) {
      var p = this.currentPath_[i];
      var c;

      switch (p.type) {
        case 'moveTo':
          c = p;
          lineStr.push(' m ', mr(p.x), ',', mr(p.y));
          break;
        case 'lineTo':
          lineStr.push(' l ', mr(p.x), ',', mr(p.y));
          break;
        case 'close':
          lineStr.push(' x ');
          p = null;
          break;
        case 'bezierCurveTo':
          lineStr.push(' c ',
                       mr(p.cp1x), ',', mr(p.cp1y), ',',
                       mr(p.cp2x), ',', mr(p.cp2y), ',',
                       mr(p.x), ',', mr(p.y));
          break;
        case 'at':
        case 'wa':
          lineStr.push(' ', p.type, ' ',
                       mr(p.x - this.arcScaleX_ * p.radius), ',',
                       mr(p.y - this.arcScaleY_ * p.radius), ' ',
                       mr(p.x + this.arcScaleX_ * p.radius), ',',
                       mr(p.y + this.arcScaleY_ * p.radius), ' ',
                       mr(p.xStart), ',', mr(p.yStart), ' ',
                       mr(p.xEnd), ',', mr(p.yEnd));
          break;
      }


      // TODO: Following is broken for curves due to
      //       move to proper paths.

      // Figure out dimensions so we can do gradient fills
      // properly
      if (p) {
        if (min.x == null || p.x < min.x) {
          min.x = p.x;
        }
        if (max.x == null || p.x > max.x) {
          max.x = p.x;
        }
        if (min.y == null || p.y < min.y) {
          min.y = p.y;
        }
        if (max.y == null || p.y > max.y) {
          max.y = p.y;
        }
      }
    }
    lineStr.push(' ">');

    if (!aFill) {
      var lineWidth = this.lineScale_ * this.lineWidth;

      // VML cannot correctly render a line if the width is less than 1px.
      // In that case, we dilute the color to make the line look thinner.
      if (lineWidth < 1) {
        opacity *= lineWidth;
      }

      lineStr.push(
        '<g_vml_:stroke',
        ' opacity="', opacity, '"',
        ' joinstyle="', this.lineJoin, '"',
        ' miterlimit="', this.miterLimit, '"',
        ' endcap="', processLineCap(this.lineCap), '"',
        ' weight="', lineWidth, 'px"',
        ' color="', color, '" />'
      );
    } else if (typeof this.fillStyle == 'object') {
      var focus = {x: '50%', y: '50%'};
      var width = max.x - min.x;
      var height = max.y - min.y;
      var dimension = width > height ? width : height;

      focus.x = mr(this.fillStyle.focus_.x / width * 100 + 50) + '%';
      focus.y = mr(this.fillStyle.focus_.y / height * 100 + 50) + '%';

      var colors = [];

      // inside radius (%)
      if (this.fillStyle.type_ == 'gradientradial') {
        var inside = this.fillStyle.radius1_ / dimension * 100;

        // percentage that outside radius exceeds inside radius
        var expansion = this.fillStyle.radius2_ / dimension * 100 - inside;
      } else {
        var inside = 0;
        var expansion = 100;
      }

      var insidecolor = {offset: null, color: null};
      var outsidecolor = {offset: null, color: null};

      // We need to sort 'colors' by percentage, from 0 > 100 otherwise ie
      // won't interpret it correctly
      this.fillStyle.colors_.sort(function(cs1, cs2) {
        return cs1.offset - cs2.offset;
      });

      for (var i = 0; i < this.fillStyle.colors_.length; i++) {
        var fs = this.fillStyle.colors_[i];

        colors.push(fs.offset * expansion + inside, '% ', fs.color, ',');

        if (fs.offset > insidecolor.offset || insidecolor.offset == null) {
          insidecolor.offset = fs.offset;
          insidecolor.color = fs.color;
        }

        if (fs.offset < outsidecolor.offset || outsidecolor.offset == null) {
          outsidecolor.offset = fs.offset;
          outsidecolor.color = fs.color;
        }
      }
      colors.pop();

      lineStr.push('<g_vml_:fill',
                   ' color="', outsidecolor.color, '"',
                   ' color2="', insidecolor.color, '"',
                   ' type="', this.fillStyle.type_, '"',
                   ' focusposition="', focus.x, ', ', focus.y, '"',
                   ' colors="', colors.join(''), '"',
                   ' opacity="', opacity, '" />');
    } else {
      lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity,
                   '" />');
    }

    lineStr.push('</g_vml_:shape>');

    this.element_.insertAdjacentHTML('beforeEnd', lineStr.join(''));
  };

  contextPrototype.fill = function() {
    this.stroke(true);
  }

  contextPrototype.closePath = function() {
    this.currentPath_.push({type: 'close'});
  };

  /**
   * @private
   */
  contextPrototype.getCoords_ = function(aX, aY) {
    var m = this.m_;
    return {
      x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2,
      y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2
    }
  };

  contextPrototype.save = function() {
    var o = {};
    copyState(this, o);
    this.aStack_.push(o);
    this.mStack_.push(this.m_);
    this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
  };

  contextPrototype.restore = function() {
    copyState(this.aStack_.pop(), this);
    this.m_ = this.mStack_.pop();
  };

  contextPrototype.translate = function(aX, aY) {
    var m1 = [
      [1,  0,  0],
      [0,  1,  0],
      [aX, aY, 1]
    ];

    this.m_ = matrixMultiply(m1, this.m_);
  };

  contextPrototype.rotate = function(aRot) {
    var c = mc(aRot);
    var s = ms(aRot);

    var m1 = [
      [c,  s, 0],
      [-s, c, 0],
      [0,  0, 1]
    ];

    this.m_ = matrixMultiply(m1, this.m_);
  };

  contextPrototype.scale = function(aX, aY) {
    this.arcScaleX_ *= aX;
    this.arcScaleY_ *= aY;
    var m1 = [
      [aX, 0,  0],
      [0,  aY, 0],
      [0,  0,  1]
    ];

    var m = this.m_ = matrixMultiply(m1, this.m_);

    // Get the line scale.
    // Determinant of this.m_ means how much the area is enlarged by the
    // transformation. So its square root can be used as a scale factor
    // for width.
    var det = m[0][0] * m[1][1] - m[0][1] * m[1][0];
    this.lineScale_ = sqrt(abs(det));
  };

  /******** STUBS ********/
  contextPrototype.clip = function() {
    // TODO: Implement
  };

  contextPrototype.arcTo = function() {
    // TODO: Implement
  };

  contextPrototype.createPattern = function() {
    return new CanvasPattern_;
  };

  // Gradient / Pattern Stubs
  function CanvasGradient_(aType) {
    this.type_ = aType;
    this.radius1_ = 0;
    this.radius2_ = 0;
    this.colors_ = [];
    this.focus_ = {x: 0, y: 0};
  }

  CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
    aColor = processStyle(aColor);
    this.colors_.push({offset: 1 - aOffset, color: aColor});
  };

  function CanvasPattern_() {}

  // set up externs
  G_vmlCanvasManager = G_vmlCanvasManager_;
  CanvasRenderingContext2D = CanvasRenderingContext2D_;
  CanvasGradient = CanvasGradient_;
  CanvasPattern = CanvasPattern_;

})();

} // if
/*
 * jQuery 1.2.6 - New Wave Javascript
 *
 * Copyright (c) 2008 John Resig (jquery.com)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $
 * $Rev: 5685 $
 */
(function(){var _jQuery=window.jQuery,_$=window.$;var jQuery=window.jQuery=window.$=function(selector,context){return new jQuery.fn.init(selector,context);};var quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/,isSimple=/^.[^:#\[\.]*$/,undefined;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;return this;}if(typeof selector=="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1])selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem){if(elem.id!=match[3])return jQuery().find(selector);return jQuery(elem);}selector=[];}}else
return jQuery(context).find(selector);}else if(jQuery.isFunction(selector))return jQuery(document)[jQuery.fn.ready?"ready":"load"](selector);return this.setArray(jQuery.makeArray(selector));},jquery:"1.2.6",size:function(){return this.length;},length:0,get:function(num){return num==undefined?jQuery.makeArray(this):this[num];},pushStack:function(elems){var ret=jQuery(elems);ret.prevObject=this;return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){var ret=-1;return jQuery.inArray(elem&&elem.jquery?elem[0]:elem,this);},attr:function(name,value,type){var options=name;if(name.constructor==String)if(value===undefined)return this[0]&&jQuery[type||"attr"](this[0],name);else{options={};options[name]=value;}return this.each(function(i){for(name in options)jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0)value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!="object"&&text!=null)return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8)ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0])jQuery(html,this[0].ownerDocument).clone().insertBefore(this[0]).map(function(){var elem=this;while(elem.firstChild)elem=elem.firstChild;return elem;}).append(this);return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,false,function(elem){if(this.nodeType==1)this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,true,function(elem){if(this.nodeType==1)this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,true,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},find:function(selector){var elems=jQuery.map(this,function(elem){return jQuery.find(selector,elem);});return this.pushStack(/[^+>] [^+>]/.test(selector)||selector.indexOf("..")>-1?jQuery.unique(elems):elems);},clone:function(events){var ret=this.map(function(){if(jQuery.browser.msie&&!jQuery.isXMLDoc(this)){var clone=this.cloneNode(true),container=document.createElement("div");container.appendChild(clone);return jQuery.clean([container.innerHTML])[0];}else
return this.cloneNode(true);});var clone=ret.find("*").andSelf().each(function(){if(this[expando]!=undefined)this[expando]=null;});if(events===true)this.find("*").andSelf().each(function(i){if(this.nodeType==3)return;var events=jQuery.data(this,"events");for(var type in events)for(var handler in events[type])jQuery.event.add(clone[i],type,events[type][handler],events[type][handler].data);});return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,this));},not:function(selector){if(selector.constructor==String)if(isSimple.test(selector))return this.pushStack(jQuery.multiFilter(selector,this,true));else
selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),typeof selector=='string'?jQuery(selector):jQuery.makeArray(selector))));},is:function(selector){return!!selector&&jQuery.multiFilter(selector,this).length>0;},hasClass:function(selector){return this.is("."+selector);},val:function(value){if(value==undefined){if(this.length){var elem=this[0];if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0)return null;for(var i=one?index:0,max=one?index+1:options.length;i<max;i++){var option=options[i];if(option.selected){value=jQuery.browser.msie&&!option.attributes.value.specified?option.text:option.value;if(one)return value;values.push(value);}}return values;}else
return(this[0].value||"").replace(/\r/g,"");}return undefined;}if(value.constructor==Number)value+='';return this.each(function(){if(this.nodeType!=1)return;if(value.constructor==Array&&/radio|checkbox/.test(this.type))this.checked=(jQuery.inArray(this.value,value)>=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(value);jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length)this.selectedIndex=-1;}else
this.value=value;});},html:function(value){return value==undefined?(this[0]?this[0].innerHTML:null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length)data=jQuery.data(this[0],key);return data===undefined&&parts[1]?this.data(parts[0]):data;}else
return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});},domManip:function(args,table,reverse,callback){var clone=this.length>1,elems;return this.each(function(){if(!elems){elems=jQuery.clean(args,this.ownerDocument);if(reverse)elems.reverse();}var obj=this;if(table&&jQuery.nodeName(this,"table")&&jQuery.nodeName(elems[0],"tr"))obj=this.getElementsByTagName("tbody")[0]||this.appendChild(this.ownerDocument.createElement("tbody"));var scripts=jQuery([]);jQuery.each(elems,function(){var elem=clone?jQuery(this).clone(true)[0]:this;if(jQuery.nodeName(elem,"script"))scripts=scripts.add(elem);else{if(elem.nodeType==1)scripts=scripts.add(jQuery("script",elem).remove());callback.call(obj,elem);}});scripts.each(evalScript);});}};jQuery.fn.init.prototype=jQuery.fn;function evalScript(i,elem){if(elem.src)jQuery.ajax({url:elem.src,async:false,dataType:"script"});else
jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode)elem.parentNode.removeChild(elem);}function now(){return+new Date;}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(target.constructor==Boolean){deep=target;target=arguments[1]||{};i=2;}if(typeof target!="object"&&typeof target!="function")target={};if(length==i){target=this;--i;}for(;i<length;i++)if((options=arguments[i])!=null)for(var name in options){var src=target[name],copy=options[name];if(target===copy)continue;if(deep&&copy&&typeof copy=="object"&&!copy.nodeType)target[name]=jQuery.extend(deep,src||(copy.length!=null?[]:{}),copy);else if(copy!==undefined)target[name]=copy;}return target;};var expando="jQuery"+now(),uuid=0,windowData={},exclude=/z-?index|font-?weight|opacity|zoom|line-?height/i,defaultView=document.defaultView||{};jQuery.extend({noConflict:function(deep){window.$=_$;if(deep)window.jQuery=_jQuery;return jQuery;},isFunction:function(fn){return!!fn&&typeof fn!="string"&&!fn.nodeName&&fn.constructor!=Array&&/^[\s[]?function/.test(fn+"");},isXMLDoc:function(elem){return elem.documentElement&&!elem.body||elem.tagName&&elem.ownerDocument&&!elem.ownerDocument.body;},globalEval:function(data){data=jQuery.trim(data);if(data){var head=document.getElementsByTagName("head")[0]||document.documentElement,script=document.createElement("script");script.type="text/javascript";if(jQuery.browser.msie)script.text=data;else
script.appendChild(document.createTextNode(data));head.insertBefore(script,head.firstChild);head.removeChild(script);}},nodeName:function(elem,name){return elem.nodeName&&elem.nodeName.toUpperCase()==name.toUpperCase();},cache:{},data:function(elem,name,data){elem=elem==window?windowData:elem;var id=elem[expando];if(!id)id=elem[expando]=++uuid;if(name&&!jQuery.cache[id])jQuery.cache[id]={};if(data!==undefined)jQuery.cache[id][name]=data;return name?jQuery.cache[id][name]:id;},removeData:function(elem,name){elem=elem==window?windowData:elem;var id=elem[expando];if(name){if(jQuery.cache[id]){delete jQuery.cache[id][name];name="";for(name in jQuery.cache[id])break;if(!name)jQuery.removeData(elem);}}else{try{delete elem[expando];}catch(e){if(elem.removeAttribute)elem.removeAttribute(expando);}delete jQuery.cache[id];}},each:function(object,callback,args){var name,i=0,length=object.length;if(args){if(length==undefined){for(name in object)if(callback.apply(object[name],args)===false)break;}else
for(;i<length;)if(callback.apply(object[i++],args)===false)break;}else{if(length==undefined){for(name in object)if(callback.call(object[name],name,object[name])===false)break;}else
for(var value=object[0];i<length&&callback.call(value,i,value)!==false;value=object[++i]){}}return object;},prop:function(elem,value,type,i,name){if(jQuery.isFunction(value))value=value.call(elem,i);return value&&value.constructor==Number&&type=="curCSS"&&!exclude.test(name)?value+"px":value;},className:{add:function(elem,classNames){jQuery.each((classNames||"").split(/\s+/),function(i,className){if(elem.nodeType==1&&!jQuery.className.has(elem.className,className))elem.className+=(elem.className?" ":"")+className;});},remove:function(elem,classNames){if(elem.nodeType==1)elem.className=classNames!=undefined?jQuery.grep(elem.className.split(/\s+/),function(className){return!jQuery.className.has(classNames,className);}).join(" "):"";},has:function(elem,className){return jQuery.inArray(className,(elem.className||elem).toString().split(/\s+/))>-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];}callback.call(elem);for(var name in options)elem.style[name]=old[name];},css:function(elem,name,force){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;var padding=0,border=0;jQuery.each(which,function(){padding+=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;border+=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});val-=Math.round(padding+border);}if(jQuery(elem).is(":visible"))getWH();else
jQuery.swap(elem,props,getWH);return Math.max(0,val);}return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret,style=elem.style;function color(elem){if(!jQuery.browser.safari)return false;var ret=defaultView.getComputedStyle(elem,null);return!ret||ret.getPropertyValue("color")=="";}if(name=="opacity"&&jQuery.browser.msie){ret=jQuery.attr(style,"opacity");return ret==""?"1":ret;}if(jQuery.browser.opera&&name=="display"){var save=style.outline;style.outline="0 solid black";style.outline=save;}if(name.match(/float/i))name=styleFloat;if(!force&&style&&style[name])ret=style[name];else if(defaultView.getComputedStyle){if(name.match(/float/i))name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle&&!color(elem))ret=computedStyle.getPropertyValue(name);else{var swap=[],stack=[],a=elem,i=0;for(;a&&color(a);a=a.parentNode)stack.unshift(a);for(;i<stack.length;i++)if(color(stack[i])){swap[i]=stack[i].style.display;stack[i].style.display="block";}ret=name=="display"&&swap[stack.length-1]!=null?"none":(computedStyle&&computedStyle.getPropertyValue(name))||"";for(i=0;i<swap.length;i++)if(swap[i]!=null)stack[i].style.display=swap[i];}if(name=="opacity"&&ret=="")ret="1";}else if(elem.currentStyle){var camelCase=name.replace(/\-(\w)/g,function(all,letter){return letter.toUpperCase();});ret=elem.currentStyle[name]||elem.currentStyle[camelCase];if(!/^\d+(px)?$/i.test(ret)&&/^\d/.test(ret)){var left=style.left,rsLeft=elem.runtimeStyle.left;elem.runtimeStyle.left=elem.currentStyle.left;style.left=ret||0;ret=style.pixelLeft+"px";style.left=left;elem.runtimeStyle.left=rsLeft;}}return ret;},clean:function(elems,context){var ret=[];context=context||document;if(typeof context.createElement=='undefined')context=context.ownerDocument||context[0]&&context[0].ownerDocument||document;jQuery.each(elems,function(i,elem){if(!elem)return;if(elem.constructor==Number)elem+='';if(typeof elem=="string"){elem=elem.replace(/(<(\w+)[^>]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+"></"+tag+">";});var tags=jQuery.trim(elem).toLowerCase(),div=context.createElement("div");var wrap=!tags.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!tags.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!tags.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!tags.indexOf("<td")||!tags.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!tags.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||jQuery.browser.msie&&[1,"div<div>","</div>"]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--)div=div.lastChild;if(jQuery.browser.msie){var tbody=!tags.indexOf("<table")&&tags.indexOf("<tbody")<0?div.firstChild&&div.firstChild.childNodes:wrap[1]=="<table>"&&tags.indexOf("<tbody")<0?div.childNodes:[];for(var j=tbody.length-1;j>=0;--j)if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length)tbody[j].parentNode.removeChild(tbody[j]);if(/^\s/.test(elem))div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);}elem=jQuery.makeArray(div.childNodes);}if(elem.length===0&&(!jQuery.nodeName(elem,"form")&&!jQuery.nodeName(elem,"select")))return;if(elem[0]==undefined||jQuery.nodeName(elem,"form")||elem.options)ret.push(elem);else
ret=jQuery.merge(ret,elem);});return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8)return undefined;var notxml=!jQuery.isXMLDoc(elem),set=value!==undefined,msie=jQuery.browser.msie;name=notxml&&jQuery.props[name]||name;if(elem.tagName){var special=/href|src|style/.test(name);if(name=="selected"&&jQuery.browser.safari)elem.parentNode.selectedIndex;if(name in elem&&notxml&&!special){if(set){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode)throw"type property can't be changed";elem[name]=value;}if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name))return elem.getAttributeNode(name).nodeValue;return elem[name];}if(msie&&notxml&&name=="style")return jQuery.attr(elem.style,"cssText",value);if(set)elem.setAttribute(name,""+value);var attr=msie&&notxml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr;}if(msie&&name=="opacity"){if(set){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(value)+''=="NaN"?"":"alpha(opacity="+value*100+")");}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100)+'':"";}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(set)elem[name]=value;return elem[name];},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(array!=null){var i=array.length;if(i==null||array.split||array.setInterval||array.call)ret[0]=array;else
while(i)ret[--i]=array[i];}return ret;},inArray:function(elem,array){for(var i=0,length=array.length;i<length;i++)if(array[i]===elem)return i;return-1;},merge:function(first,second){var i=0,elem,pos=first.length;if(jQuery.browser.msie){while(elem=second[i++])if(elem.nodeType!=8)first[pos++]=elem;}else
while(elem=second[i++])first[pos++]=elem;return first;},unique:function(array){var ret=[],done={};try{for(var i=0,length=array.length;i<length;i++){var id=jQuery.data(array[i]);if(!done[id]){done[id]=true;ret.push(array[i]);}}}catch(e){ret=array;}return ret;},grep:function(elems,callback,inv){var ret=[];for(var i=0,length=elems.length;i<length;i++)if(!inv!=!callback(elems[i],i))ret.push(elems[i]);return ret;},map:function(elems,callback){var ret=[];for(var i=0,length=elems.length;i<length;i++){var value=callback(elems[i],i);if(value!=null)ret[ret.length]=value;}return ret.concat.apply([],ret);}});var userAgent=navigator.userAgent.toLowerCase();jQuery.browser={version:(userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[])[1],safari:/webkit/.test(userAgent),opera:/opera/.test(userAgent),msie:/msie/.test(userAgent)&&!/opera/.test(userAgent),mozilla:/mozilla/.test(userAgent)&&!/(compatible|webkit)/.test(userAgent)};var styleFloat=jQuery.browser.msie?"styleFloat":"cssFloat";jQuery.extend({boxModel:!jQuery.browser.msie||document.compatMode=="CSS1Compat",props:{"for":"htmlFor","class":"className","float":styleFloat,cssFloat:styleFloat,styleFloat:styleFloat,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing"}});jQuery.each({parent:function(elem){return elem.parentNode;},parents:function(elem){return jQuery.dir(elem,"parentNode");},next:function(elem){return jQuery.nth(elem,2,"nextSibling");},prev:function(elem){return jQuery.nth(elem,2,"previousSibling");},nextAll:function(elem){return jQuery.dir(elem,"nextSibling");},prevAll:function(elem){return jQuery.dir(elem,"previousSibling");},siblings:function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},children:function(elem){return jQuery.sibling(elem.firstChild);},contents:function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}},function(name,fn){jQuery.fn[name]=function(selector){var ret=jQuery.map(this,fn);if(selector&&typeof selector=="string")ret=jQuery.multiFilter(selector,ret);return this.pushStack(jQuery.unique(ret));};});jQuery.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(name,original){jQuery.fn[name]=function(){var args=arguments;return this.each(function(){for(var i=0,length=args.length;i<length;i++)jQuery(args[i])[original](this);});};});jQuery.each({removeAttr:function(name){jQuery.attr(this,name,"");if(this.nodeType==1)this.removeAttribute(name);},addClass:function(classNames){jQuery.className.add(this,classNames);},removeClass:function(classNames){jQuery.className.remove(this,classNames);},toggleClass:function(classNames){jQuery.className[jQuery.className.has(this,classNames)?"remove":"add"](this,classNames);},remove:function(selector){if(!selector||jQuery.filter(selector,[this]).r.length){jQuery("*",this).add(this).each(function(){jQuery.event.remove(this);jQuery.removeData(this);});if(this.parentNode)this.parentNode.removeChild(this);}},empty:function(){jQuery(">*",this).remove();while(this.firstChild)this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});jQuery.each(["Height","Width"],function(i,name){var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?jQuery.browser.opera&&document.body["client"+name]||jQuery.browser.safari&&window["inner"+name]||document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(Math.max(document.body["scroll"+name],document.documentElement["scroll"+name]),Math.max(document.body["offset"+name],document.documentElement["offset"+name])):size==undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,size.constructor==String?size:size+"px");};});function num(elem,prop){return elem[0]&&parseInt(jQuery.curCSS(elem[0],prop,true),10)||0;}var chars=jQuery.browser.safari&&parseInt(jQuery.browser.version)<417?"(?:[\\w*_-]|\\\\.)":"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",quickChild=new RegExp("^>\\s*("+chars+"+)"),quickID=new RegExp("^("+chars+"+)(#)("+chars+"+)"),quickClass=new RegExp("^([#.]?)("+chars+"*)");jQuery.extend({expr:{"":function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},"#":function(a,i,m){return a.getAttribute("id")==m[2];},":":{lt:function(a,i,m){return i<m[3]-0;},gt:function(a,i,m){return i>m[3]-0;},nth:function(a,i,m){return m[3]-0==i;},eq:function(a,i,m){return m[3]-0==i;},first:function(a,i){return i==0;},last:function(a,i,m,r){return i==r.length-1;},even:function(a,i){return i%2==0;},odd:function(a,i){return i%2;},"first-child":function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},"last-child":function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},"only-child":function(a){return!jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},parent:function(a){return a.firstChild;},empty:function(a){return!a.firstChild;},contains:function(a,i,m){return(a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},visible:function(a){return"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},hidden:function(a){return"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},enabled:function(a){return!a.disabled;},disabled:function(a){return a.disabled;},checked:function(a){return a.checked;},selected:function(a){return a.selected||jQuery.attr(a,"selected");},text:function(a){return"text"==a.type;},radio:function(a){return"radio"==a.type;},checkbox:function(a){return"checkbox"==a.type;},file:function(a){return"file"==a.type;},password:function(a){return"password"==a.type;},submit:function(a){return"submit"==a.type;},image:function(a){return"image"==a.type;},reset:function(a){return"reset"==a.type;},button:function(a){return"button"==a.type||jQuery.nodeName(a,"button");},input:function(a){return/input|select|textarea|button/i.test(a.nodeName);},has:function(a,i,m){return jQuery.find(m[3],a).length;},header:function(a){return/h\d/i.test(a.nodeName);},animated:function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}}},parse:[/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,new RegExp("^([:.#]*)("+chars+"+)")],multiFilter:function(expr,elems,not){var old,cur=[];while(expr&&expr!=old){old=expr;var f=jQuery.filter(expr,elems,not);expr=f.t.replace(/^\s*,\s*/,"");cur=not?elems=f.r:jQuery.merge(cur,f.r);}return cur;},find:function(t,context){if(typeof t!="string")return[t];if(context&&context.nodeType!=1&&context.nodeType!=9)return[];context=context||document;var ret=[context],done=[],last,nodeName;while(t&&last!=t){var r=[];last=t;t=jQuery.trim(t);var foundToken=false,re=quickChild,m=re.exec(t);if(m){nodeName=m[1].toUpperCase();for(var i=0;ret[i];i++)for(var c=ret[i].firstChild;c;c=c.nextSibling)if(c.nodeType==1&&(nodeName=="*"||c.nodeName.toUpperCase()==nodeName))r.push(c);ret=r;t=t.replace(re,"");if(t.indexOf(" ")==0)continue;foundToken=true;}else{re=/^([>+~])\s*(\w*)/i;if((m=re.exec(t))!=null){r=[];var merge={};nodeName=m[2].toUpperCase();m=m[1];for(var j=0,rl=ret.length;j<rl;j++){var n=m=="~"||m=="+"?ret[j].nextSibling:ret[j].firstChild;for(;n;n=n.nextSibling)if(n.nodeType==1){var id=jQuery.data(n);if(m=="~"&&merge[id])break;if(!nodeName||n.nodeName.toUpperCase()==nodeName){if(m=="~")merge[id]=true;r.push(n);}if(m=="+")break;}}ret=r;t=jQuery.trim(t.replace(re,""));foundToken=true;}}if(t&&!foundToken){if(!t.indexOf(",")){if(context==ret[0])ret.shift();done=jQuery.merge(done,ret);r=ret=[context];t=" "+t.substr(1,t.length);}else{var re2=quickID;var m=re2.exec(t);if(m){m=[0,m[2],m[3],m[1]];}else{re2=quickClass;m=re2.exec(t);}m[2]=m[2].replace(/\\/g,"");var elem=ret[ret.length-1];if(m[1]=="#"&&elem&&elem.getElementById&&!jQuery.isXMLDoc(elem)){var oid=elem.getElementById(m[2]);if((jQuery.browser.msie||jQuery.browser.opera)&&oid&&typeof oid.id=="string"&&oid.id!=m[2])oid=jQuery('[@id="'+m[2]+'"]',elem)[0];ret=r=oid&&(!m[3]||jQuery.nodeName(oid,m[3]))?[oid]:[];}else{for(var i=0;ret[i];i++){var tag=m[1]=="#"&&m[3]?m[3]:m[1]!=""||m[0]==""?"*":m[2];if(tag=="*"&&ret[i].nodeName.toLowerCase()=="object")tag="param";r=jQuery.merge(r,ret[i].getElementsByTagName(tag));}if(m[1]==".")r=jQuery.classFilter(r,m[2]);if(m[1]=="#"){var tmp=[];for(var i=0;r[i];i++)if(r[i].getAttribute("id")==m[2]){tmp=[r[i]];break;}r=tmp;}ret=r;}t=t.replace(re2,"");}}if(t){var val=jQuery.filter(t,r);ret=r=val.r;t=jQuery.trim(val.t);}}if(t)ret=[];if(ret&&context==ret[0])ret.shift();done=jQuery.merge(done,ret);return done;},classFilter:function(r,m,not){m=" "+m+" ";var tmp=[];for(var i=0;r[i];i++){var pass=(" "+r[i].className+" ").indexOf(m)>=0;if(!not&&pass||not&&!pass)tmp.push(r[i]);}return tmp;},filter:function(t,r,not){var last;while(t&&t!=last){last=t;var p=jQuery.parse,m;for(var i=0;p[i];i++){m=p[i].exec(t);if(m){t=t.substring(m[0].length);m[2]=m[2].replace(/\\/g,"");break;}}if(!m)break;if(m[1]==":"&&m[2]=="not")r=isSimple.test(m[3])?jQuery.filter(m[3],r,true).r:jQuery(r).not(m[3]);else if(m[1]==".")r=jQuery.classFilter(r,m[2],not);else if(m[1]=="["){var tmp=[],type=m[3];for(var i=0,rl=r.length;i<rl;i++){var a=r[i],z=a[jQuery.props[m[2]]||m[2]];if(z==null||/href|src|selected/.test(m[2]))z=jQuery.attr(a,m[2])||'';if((type==""&&!!z||type=="="&&z==m[5]||type=="!="&&z!=m[5]||type=="^="&&z&&!z.indexOf(m[5])||type=="$="&&z.substr(z.length-m[5].length)==m[5]||(type=="*="||type=="~=")&&z.indexOf(m[5])>=0)^not)tmp.push(a);}r=tmp;}else if(m[1]==":"&&m[2]=="nth-child"){var merge={},tmp=[],test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3]=="even"&&"2n"||m[3]=="odd"&&"2n+1"||!/\D/.test(m[3])&&"0n+"+m[3]||m[3]),first=(test[1]+(test[2]||1))-0,last=test[3]-0;for(var i=0,rl=r.length;i<rl;i++){var node=r[i],parentNode=node.parentNode,id=jQuery.data(parentNode);if(!merge[id]){var c=1;for(var n=parentNode.firstChild;n;n=n.nextSibling)if(n.nodeType==1)n.nodeIndex=c++;merge[id]=true;}var add=false;if(first==0){if(node.nodeIndex==last)add=true;}else if((node.nodeIndex-last)%first==0&&(node.nodeIndex-last)/first>=0)add=true;if(add^not)tmp.push(node);}r=tmp;}else{var fn=jQuery.expr[m[1]];if(typeof fn=="object")fn=fn[m[2]];if(typeof fn=="string")fn=eval("false||function(a,i){return "+fn+";}");r=jQuery.grep(r,function(elem,i){return fn(elem,i,m,r);},not);}}return{r:r,t:t};},dir:function(elem,dir){var matched=[],cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1)matched.push(cur);cur=cur[dir];}return matched;},nth:function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir])if(cur.nodeType==1&&++num==result)break;return cur;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&n!=elem)r.push(n);}return r;}});jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8)return;if(jQuery.browser.msie&&elem.setInterval)elem=window;if(!handler.guid)handler.guid=this.guid++;if(data!=undefined){var fn=handler;handler=this.proxy(fn,function(){return fn.apply(this,arguments);});handler.data=data;}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){if(typeof jQuery!="undefined"&&!jQuery.event.triggered)return jQuery.event.handle.apply(arguments.callee.elem,arguments);});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];handler.type=parts[1];var handlers=events[type];if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem)===false){if(elem.addEventListener)elem.addEventListener(type,handle,false);else if(elem.attachEvent)elem.attachEvent("on"+type,handle);}}handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8)return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types==undefined||(typeof types=="string"&&types.charAt(0)=="."))for(var type in events)this.remove(elem,type+(types||""));else{if(types.type){handler=types.handler;types=types.type;}jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];if(events[type]){if(handler)delete events[type][handler.guid];else
for(handler in events[type])if(!parts[1]||events[type][handler].type==parts[1])delete events[type][handler];for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem)===false){if(elem.removeEventListener)elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent)elem.detachEvent("on"+type,jQuery.data(elem,"handle"));}ret=null;delete events[type];}}});}for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(type,data,elem,donative,extra){data=jQuery.makeArray(data);if(type.indexOf("!")>=0){type=type.slice(0,-1);var exclusive=true;}if(!elem){if(this.global[type])jQuery("*").add([window,document]).trigger(type,data);}else{if(elem.nodeType==3||elem.nodeType==8)return undefined;var val,ret,fn=jQuery.isFunction(elem[type]||null),event=!data[0]||!data[0].preventDefault;if(event){data.unshift({type:type,target:elem,preventDefault:function(){},stopPropagation:function(){},timeStamp:now()});data[0][expando]=true;}data[0].type=type;if(exclusive)data[0].exclusive=true;var handle=jQuery.data(elem,"handle");if(handle)val=handle.apply(elem,data);if((!fn||(jQuery.nodeName(elem,'a')&&type=="click"))&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false)val=false;if(event)data.shift();if(extra&&jQuery.isFunction(extra)){ret=extra.apply(elem,val==null?data:data.concat(val));if(ret!==undefined)val=ret;}if(fn&&donative!==false&&val!==false&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}}this.triggered=false;}return val;},handle:function(event){var val,ret,namespace,all,handlers;event=arguments[0]=jQuery.event.fix(event||window.event);namespace=event.type.split(".");event.type=namespace[0];namespace=namespace[1];all=!namespace&&!event.exclusive;handlers=(jQuery.data(this,"events")||{})[event.type];for(var j in handlers){var handler=handlers[j];if(all||handler.type==namespace){event.handler=handler;event.data=handler.data;ret=handler.apply(this,arguments);if(val!==false)val=ret;if(ret===false){event.preventDefault();event.stopPropagation();}}}return val;},fix:function(event){if(event[expando]==true)return event;var originalEvent=event;event={originalEvent:originalEvent};var props="altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target timeStamp toElement type view wheelDelta which".split(" ");for(var i=props.length;i;i--)event[props[i]]=originalEvent[props[i]];event[expando]=true;event.preventDefault=function(){if(originalEvent.preventDefault)originalEvent.preventDefault();originalEvent.returnValue=false;};event.stopPropagation=function(){if(originalEvent.stopPropagation)originalEvent.stopPropagation();originalEvent.cancelBubble=true;};event.timeStamp=event.timeStamp||now();if(!event.target)event.target=event.srcElement||document;if(event.target.nodeType==3)event.target=event.target.parentNode;if(!event.relatedTarget&&event.fromElement)event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode))event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey)event.metaKey=event.ctrlKey;if(!event.which&&event.button)event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},proxy:function(fn,proxy){proxy.guid=fn.guid=fn.guid||proxy.guid||this.guid++;return proxy;},special:{ready:{setup:function(){bindReady();return;},teardown:function(){return;}},mouseenter:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseover",jQuery.event.special.mouseenter.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseover",jQuery.event.special.mouseenter.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseenter";return jQuery.event.handle.apply(this,arguments);}},mouseleave:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseout",jQuery.event.special.mouseleave.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseout",jQuery.event.special.mouseleave.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseleave";return jQuery.event.handle.apply(this,arguments);}}}};jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){var one=jQuery.event.proxy(fn||data,function(event){jQuery(this).unbind(event,one);return(fn||data).apply(this,arguments);});return this.each(function(){jQuery.event.add(this,type,one,fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data,fn){return this.each(function(){jQuery.event.trigger(type,data,this,true,fn);});},triggerHandler:function(type,data,fn){return this[0]&&jQuery.event.trigger(type,data,this[0],false,fn);},toggle:function(fn){var args=arguments,i=1;while(i<args.length)jQuery.event.proxy(fn,args[i++]);return this.click(jQuery.event.proxy(fn,function(event){this.lastToggle=(this.lastToggle||0)%i;event.preventDefault();return args[this.lastToggle++].apply(this,arguments)||false;}));},hover:function(fnOver,fnOut){return this.bind('mouseenter',fnOver).bind('mouseleave',fnOut);},ready:function(fn){bindReady();if(jQuery.isReady)fn.call(document,jQuery);else
jQuery.readyList.push(function(){return fn.call(this,jQuery);});return this;}});jQuery.extend({isReady:false,readyList:[],ready:function(){if(!jQuery.isReady){jQuery.isReady=true;if(jQuery.readyList){jQuery.each(jQuery.readyList,function(){this.call(document);});jQuery.readyList=null;}jQuery(document).triggerHandler("ready");}}});var readyBound=false;function bindReady(){if(readyBound)return;readyBound=true;if(document.addEventListener&&!jQuery.browser.opera)document.addEventListener("DOMContentLoaded",jQuery.ready,false);if(jQuery.browser.msie&&window==top)(function(){if(jQuery.isReady)return;try{document.documentElement.doScroll("left");}catch(error){setTimeout(arguments.callee,0);return;}jQuery.ready();})();if(jQuery.browser.opera)document.addEventListener("DOMContentLoaded",function(){if(jQuery.isReady)return;for(var i=0;i<document.styleSheets.length;i++)if(document.styleSheets[i].disabled){setTimeout(arguments.callee,0);return;}jQuery.ready();},false);if(jQuery.browser.safari){var numStyles;(function(){if(jQuery.isReady)return;if(document.readyState!="loaded"&&document.readyState!="complete"){setTimeout(arguments.callee,0);return;}if(numStyles===undefined)numStyles=jQuery("style, link[rel=stylesheet]").length;if(document.styleSheets.length!=numStyles){setTimeout(arguments.callee,0);return;}jQuery.ready();})();}jQuery.event.add(window,"load",jQuery.ready);}jQuery.each(("blur,focus,load,resize,scroll,unload,click,dblclick,"+"mousedown,mouseup,mousemove,mouseover,mouseout,change,select,"+"submit,keydown,keypress,keyup,error").split(","),function(i,name){jQuery.fn[name]=function(fn){return fn?this.bind(name,fn):this.trigger(name);};});var withinElement=function(event,elem){var parent=event.relatedTarget;while(parent&&parent!=elem)try{parent=parent.parentNode;}catch(error){parent=elem;}return parent==elem;};jQuery(window).bind("unload",function(){jQuery("*").add(document).unbind();});jQuery.fn.extend({_load:jQuery.fn.load,load:function(url,params,callback){if(typeof url!='string')return this._load(url);var off=url.indexOf(" ");if(off>=0){var selector=url.slice(off,url.length);url=url.slice(0,off);}callback=callback||function(){};var type="GET";if(params)if(jQuery.isFunction(params)){callback=params;params=null;}else{params=jQuery.param(params);type="POST";}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified")self.html(selector?jQuery("<div/>").append(res.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(selector):res.responseText);self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return jQuery.nodeName(this,"form")?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:val.constructor==Array?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=now();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{url:location.href,global:true,type:"GET",timeout:0,contentType:"application/x-www-form-urlencoded",processData:true,async:true,data:null,username:null,password:null,accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));var jsonp,jsre=/=\?(&|$)/g,status,data,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!="string")s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(type=="GET"){if(!s.url.match(jsre))s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre))s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data)s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){}if(head)head.removeChild(script);};}if(s.dataType=="script"&&s.cache==null)s.cache=false;if(s.cache===false&&type=="GET"){var ts=now();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");}if(s.data&&type=="GET"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;}if(s.global&&!jQuery.active++)jQuery.event.trigger("ajaxStart");var remote=/^(?:\w+:)?\/\/([^\/?#]+)/;if(s.dataType=="script"&&type=="GET"&&remote.test(s.url)&&remote.exec(s.url)[1]!=location.host){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset)script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();head.removeChild(script);}};}head.appendChild(script);return undefined;}var requestDone=false;var xhr=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();if(s.username)xhr.open(type,s.url,s.async,s.username,s.password);else
xhr.open(type,s.url,s.async);try{if(s.data)xhr.setRequestHeader("Content-Type",s.contentType);if(s.ifModified)xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){}if(s.beforeSend&&s.beforeSend(xhr,s)===false){s.global&&jQuery.active--;xhr.abort();return false;}if(s.global)jQuery.event.trigger("ajaxSend",[xhr,s]);var onreadystatechange=function(isTimeout){if(!requestDone&&xhr&&(xhr.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;}status=isTimeout=="timeout"&&"timeout"||!jQuery.httpSuccess(xhr)&&"error"||s.ifModified&&jQuery.httpNotModified(xhr,s.url)&&"notmodified"||"success";if(status=="success"){try{data=jQuery.httpData(xhr,s.dataType,s.dataFilter);}catch(e){status="parsererror";}}if(status=="success"){var modRes;try{modRes=xhr.getResponseHeader("Last-Modified");}catch(e){}if(s.ifModified&&modRes)jQuery.lastModified[s.url]=modRes;if(!jsonp)success();}else
jQuery.handleError(s,xhr,status);complete();if(s.async)xhr=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0)setTimeout(function(){if(xhr){xhr.abort();if(!requestDone)onreadystatechange("timeout");}},s.timeout);}try{xhr.send(s.data);}catch(e){jQuery.handleError(s,xhr,null,e);}if(!s.async)onreadystatechange();function success(){if(s.success)s.success(data,status);if(s.global)jQuery.event.trigger("ajaxSuccess",[xhr,s]);}function complete(){if(s.complete)s.complete(xhr,status);if(s.global)jQuery.event.trigger("ajaxComplete",[xhr,s]);if(s.global&&!--jQuery.active)jQuery.event.trigger("ajaxStop");}return xhr;},handleError:function(s,xhr,status,e){if(s.error)s.error(xhr,status,e);if(s.global)jQuery.event.trigger("ajaxError",[xhr,s,e]);},active:0,httpSuccess:function(xhr){try{return!xhr.status&&location.protocol=="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status==304||xhr.status==1223||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpNotModified:function(xhr,url){try{var xhrRes=xhr.getResponseHeader("Last-Modified");return xhr.status==304||xhrRes==jQuery.lastModified[url]||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpData:function(xhr,type,filter){var ct=xhr.getResponseHeader("content-type"),xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.tagName=="parsererror")throw"parsererror";if(filter)data=filter(data,type);if(type=="script")jQuery.globalEval(data);if(type=="json")data=eval("("+data+")");return data;},param:function(a){var s=[];if(a.constructor==Array||a.jquery)jQuery.each(a,function(){s.push(encodeURIComponent(this.name)+"="+encodeURIComponent(this.value));});else
for(var j in a)if(a[j]&&a[j].constructor==Array)jQuery.each(a[j],function(){s.push(encodeURIComponent(j)+"="+encodeURIComponent(this));});else
s.push(encodeURIComponent(j)+"="+encodeURIComponent(jQuery.isFunction(a[j])?a[j]():a[j]));return s.join("&").replace(/%20/g,"+");}});jQuery.fn.extend({show:function(speed,callback){return speed?this.animate({height:"show",width:"show",opacity:"show"},speed,callback):this.filter(":hidden").each(function(){this.style.display=this.oldblock||"";if(jQuery.css(this,"display")=="none"){var elem=jQuery("<"+this.tagName+" />").appendTo("body");this.style.display=elem.css("display");if(this.style.display=="none")this.style.display="block";elem.remove();}}).end();},hide:function(speed,callback){return speed?this.animate({height:"hide",width:"hide",opacity:"hide"},speed,callback):this.filter(":visible").each(function(){this.oldblock=this.oldblock||jQuery.css(this,"display");this.style.display="none";}).end();},_toggle:jQuery.fn.toggle,toggle:function(fn,fn2){return jQuery.isFunction(fn)&&jQuery.isFunction(fn2)?this._toggle.apply(this,arguments):fn?this.animate({height:"toggle",width:"toggle",opacity:"toggle"},fn,fn2):this.each(function(){jQuery(this)[jQuery(this).is(":hidden")?"show":"hide"]();});},slideDown:function(speed,callback){return this.animate({height:"show"},speed,callback);},slideUp:function(speed,callback){return this.animate({height:"hide"},speed,callback);},slideToggle:function(speed,callback){return this.animate({height:"toggle"},speed,callback);},fadeIn:function(speed,callback){return this.animate({opacity:"show"},speed,callback);},fadeOut:function(speed,callback){return this.animate({opacity:"hide"},speed,callback);},fadeTo:function(speed,to,callback){return this.animate({opacity:to},speed,callback);},animate:function(prop,speed,easing,callback){var optall=jQuery.speed(speed,easing,callback);return this[optall.queue===false?"each":"queue"](function(){if(this.nodeType!=1)return false;var opt=jQuery.extend({},optall),p,hidden=jQuery(this).is(":hidden"),self=this;for(p in prop){if(prop[p]=="hide"&&hidden||prop[p]=="show"&&!hidden)return opt.complete.call(this);if(p=="height"||p=="width"){opt.display=jQuery.css(this,"display");opt.overflow=this.style.overflow;}}if(opt.overflow!=null)this.style.overflow="hidden";opt.curAnim=jQuery.extend({},prop);jQuery.each(prop,function(name,val){var e=new jQuery.fx(self,opt,name);if(/toggle|show|hide/.test(val))e[val=="toggle"?hidden?"show":"hide":val](prop);else{var parts=val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),start=e.cur(true)||0;if(parts){var end=parseFloat(parts[2]),unit=parts[3]||"px";if(unit!="px"){self.style[name]=(end||1)+unit;start=((end||1)/e.cur(true))*start;self.style[name]=start+unit;}if(parts[1])end=((parts[1]=="-="?-1:1)*end)+start;e.custom(start,end,unit);}else
e.custom(start,val,"");}});return true;});},queue:function(type,fn){if(jQuery.isFunction(type)||(type&&type.constructor==Array)){fn=type;type="fx";}if(!type||(typeof type=="string"&&!fn))return queue(this[0],type);return this.each(function(){if(fn.constructor==Array)queue(this,type,fn);else{queue(this,type).push(fn);if(queue(this,type).length==1)fn.call(this);}});},stop:function(clearQueue,gotoEnd){var timers=jQuery.timers;if(clearQueue)this.queue([]);this.each(function(){for(var i=timers.length-1;i>=0;i--)if(timers[i].elem==this){if(gotoEnd)timers[i](true);timers.splice(i,1);}});if(!gotoEnd)this.dequeue();return this;}});var queue=function(elem,type,array){if(elem){type=type||"fx";var q=jQuery.data(elem,type+"queue");if(!q||array)q=jQuery.data(elem,type+"queue",jQuery.makeArray(array));}return q;};jQuery.fn.dequeue=function(type){type=type||"fx";return this.each(function(){var q=queue(this,type);q.shift();if(q.length)q[0].call(this);});};jQuery.extend({speed:function(speed,easing,fn){var opt=speed&&speed.constructor==Object?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&easing.constructor!=Function&&easing};opt.duration=(opt.duration&&opt.duration.constructor==Number?opt.duration:jQuery.fx.speeds[opt.duration])||jQuery.fx.speeds.def;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false)jQuery(this).dequeue();if(jQuery.isFunction(opt.old))opt.old.call(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],timerId:null,fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig)options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step)this.options.step.call(this.elem,this.now,this);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if(this.prop=="height"||this.prop=="width")this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&this.elem.style[this.prop]==null)return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;this.update();var self=this;function t(gotoEnd){return self.step(gotoEnd);}t.elem=this.elem;jQuery.timers.push(t);if(jQuery.timerId==null){jQuery.timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;i<timers.length;i++)if(!timers[i]())timers.splice(i--,1);if(!timers.length){clearInterval(jQuery.timerId);jQuery.timerId=null;}},13);}},show:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.show=true;this.custom(0,this.cur());if(this.prop=="width"||this.prop=="height")this.elem.style[this.prop]="1px";jQuery(this.elem).show();},hide:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0);},step:function(gotoEnd){var t=now();if(gotoEnd||t>this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim)if(this.options.curAnim[i]!==true)done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none")this.elem.style.display="block";}if(this.options.hide)this.elem.style.display="none";if(this.options.hide||this.options.show)for(var p in this.options.curAnim)jQuery.attr(this.elem.style,p,this.options.orig[p]);}if(done)this.options.complete.call(this.elem);return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();}return true;}};jQuery.extend(jQuery.fx,{speeds:{slow:600,fast:200,def:400},step:{scrollLeft:function(fx){fx.elem.scrollLeft=fx.now;},scrollTop:function(fx){fx.elem.scrollTop=fx.now;},opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){fx.elem.style[fx.prop]=fx.now+fx.unit;}}});jQuery.fn.offset=function(){var left=0,top=0,elem=this[0],results;if(elem)with(jQuery.browser){var parent=elem.parentNode,offsetChild=elem,offsetParent=elem.offsetParent,doc=elem.ownerDocument,safari2=safari&&parseInt(version)<522&&!/adobeair/i.test(userAgent),css=jQuery.curCSS,fixed=css(elem,"position")=="fixed";if(elem.getBoundingClientRect){var box=elem.getBoundingClientRect();add(box.left+Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),box.top+Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));add(-doc.documentElement.clientLeft,-doc.documentElement.clientTop);}else{add(elem.offsetLeft,elem.offsetTop);while(offsetParent){add(offsetParent.offsetLeft,offsetParent.offsetTop);if(mozilla&&!/^t(able|d|h)$/i.test(offsetParent.tagName)||safari&&!safari2)border(offsetParent);if(!fixed&&css(offsetParent,"position")=="fixed")fixed=true;offsetChild=/^body$/i.test(offsetParent.tagName)?offsetChild:offsetParent;offsetParent=offsetParent.offsetParent;}while(parent&&parent.tagName&&!/^body|html$/i.test(parent.tagName)){if(!/^inline|table.*$/i.test(css(parent,"display")))add(-parent.scrollLeft,-parent.scrollTop);if(mozilla&&css(parent,"overflow")!="visible")border(parent);parent=parent.parentNode;}if((safari2&&(fixed||css(offsetChild,"position")=="absolute"))||(mozilla&&css(offsetChild,"position")!="absolute"))add(-doc.body.offsetLeft,-doc.body.offsetTop);if(fixed)add(Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));}results={top:top,left:left};}function border(elem){add(jQuery.curCSS(elem,"borderLeftWidth",true),jQuery.curCSS(elem,"borderTopWidth",true));}function add(l,t){left+=parseInt(l,10)||0;top+=parseInt(t,10)||0;}return results;};jQuery.fn.extend({position:function(){var left=0,top=0,results;if(this[0]){var offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].tagName)?{top:0,left:0}:offsetParent.offset();offset.top-=num(this,'marginTop');offset.left-=num(this,'marginLeft');parentOffset.top+=num(offsetParent,'borderTopWidth');parentOffset.left+=num(offsetParent,'borderLeftWidth');results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};}return results;},offsetParent:function(){var offsetParent=this[0].offsetParent;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&jQuery.css(offsetParent,'position')=='static'))offsetParent=offsetParent.offsetParent;return jQuery(offsetParent);}});jQuery.each(['Left','Top'],function(i,name){var method='scroll'+name;jQuery.fn[method]=function(val){if(!this[0])return;return val!=undefined?this.each(function(){this==window||this==document?window.scrollTo(!i?val:jQuery(window).scrollLeft(),i?val:jQuery(window).scrollTop()):this[method]=val;}):this[0]==window||this[0]==document?self[i?'pageYOffset':'pageXOffset']||jQuery.boxModel&&document.documentElement[method]||document.body[method]:this[0][method];};});jQuery.each(["Height","Width"],function(i,name){var tl=i?"Left":"Top",br=i?"Right":"Bottom";jQuery.fn["inner"+name]=function(){return this[name.toLowerCase()]()+num(this,"padding"+tl)+num(this,"padding"+br);};jQuery.fn["outer"+name]=function(margin){return this["inner"+name]()+num(this,"border"+tl+"Width")+num(this,"border"+br+"Width")+(margin?num(this,"margin"+tl)+num(this,"margin"+br):0);};});})();
/***
|Name|TiddlerTweakerPlugin|
|Source|http://www.TiddlyTools.com/#TiddlerTweakerPlugin|
|Version|2.3.0|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Description|select multiple tiddlers and modify author, created, modified and/or tag values|
~TiddlerTweaker is a tool for TiddlyWiki authors.  It allows you to select multiple tiddlers from a listbox, either by direct interaction or automatically matching specific criteria.  You can then modify the creator, author, created, modified and/or tag values of those tiddlers using a compact set of form fields.  The values you enter into the fields simultantously overwrite the existing values in all tiddlers you have selected.
!!!!!Usage
<<<
{{{<<tiddlerTweaker>>}}}
{{smallform{<<tiddlerTweaker>>}}}
By default, any tags you enter into the TiddlerTweaker will //replace// the existing tags in all the tiddlers you have selected.  However, you can also use TiddlerTweaker to quickly filter specified tags from the selected tiddlers, while leaving any other tags assigned to those tiddlers unchanged:
>Any tag preceded by a "+" (plus) or "-" (minus), will be added or removed from the existing tags //instead of replacing the entire tag definition// of each tiddler (e.g., enter "-excludeLists" to remove that tag from all selected tiddlers.  When using this syntax, care should be taken to ensure that //every// tag is preceded by "+" or "-", to avoid inadvertently overwriting any other existing tags on the selected tiddlers.  (note: the "+" or "-" prefix on each tag value is NOT part of the tag value, and is only used by TiddlerTweaker to control how that tag value is processed)
Important Notes:
* Inasmuch as TiddlerTweaker is a 'power user' tool that can perform 'batch' functions (operating on many tiddlers at once), you should always have a recent backup of your document (or "save changes" just *before* tweaking the tiddlers), just in case you "shoot yourself in the foot".
* By design, TiddlerTweaker does NOT update the 'modified' date of tiddlers simply by making changes to the tiddler's values.  A tiddler's dates are ONLY updated when the corresponding 'created' and/or 'modified' checkboxes are selected and you enter new values for those dates.  As a general rule, after using TiddlerTweaker, always ''//remember to save your document//'' when you are done, even though the tiddler timeline tab may not show any recently modified tiddlers.
* Because you may be changing the values on many tiddlers simultaneously, selecting and updating all tiddlers in a document operation may take a while and your browser might warn about an "unresponsive script"... you should give it a whole bunch of time to 'continue'... it should complete the processing... eventually.
<<<
!!!!!Revisions
<<<
2009.01.22 [2.3.0] added support for text pattern find/replace
2008.10.27 [2.2.3] in setTiddlers(), fixed Safari bug by replacing static Array.concat(...) with new Array().concat(...)
2008.09.07 [2.2.2] added removeCookie() function for compatibility with [[CookieManagerPlugin]]
2008.05.12 [2.2.1] replace built-in backstage "tweak" task with tiddler tweaker control panel (moved from BackstageTweaks)
2008.01.13 [2.2.0] added "auto-selection" links: all, changed, tags, title, text
2007.12.26 [2.1.0] added support for managing 'creator' custom field (see [[CoreTweaks]])
2007.11.01 [2.0.3] added config.options.txtTweakerSortBy for cookie-based persistence of list display order preference setting.
2007.09.28 [2.0.2] in settiddlers() and deltiddlers(), added suspend/resume notification handling (improves performance when operating on multiple tiddlers)
2007.08.03 [2.0.1] added shadow definition for [[TiddlerTweaker]] tiddler for use as parameter references with {{{<<tiddler>>, <<slider>> or <<tabs>>}}} macros.
2007.08.03 [2.0.0] converted from inline script
2006.01.01 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.TiddlerTweakerPlugin= {major: 2, minor: 3, revision: 0, date: new Date(2009,1,22)};

// shadow tiddler
config.shadowTiddlers.TiddlerTweaker="<<tiddlerTweaker>>";

/// backstage task
if (config.tasks) { // for TW2.2b3 or above
	config.tasks.tweak.tooltip="review/modify tiddler internals: dates, authors, tags, etc.";
	config.tasks.tweak.content="{{smallform small groupbox{<<tiddlerTweaker>>}}}";
}

if (config.options.txtTweakerSortBy==undefined) config.options.txtTweakerSortBy="modified";

// if removeCookie() function is not defined by TW core, define it here.
if (window.removeCookie===undefined) {
	window.removeCookie=function(name) {
		document.cookie = name+'=; expires=Thu, 01-Jan-1970 00:00:01 UTC; path=/;'; 
	}
}

config.macros.tiddlerTweaker = {
	html: '<form style="display:inline"><!--\
		--><table style="padding:0;margin:0;border:0;width:100%"><tr valign="top" style="padding:0;margin:0;border:0"><!--\
		--><td style="text-align:center;white-space:nowrap;width:99%;padding:0;margin:0;border:0"><!--\
			--><font size=-2><div style="text-align:left;"><span style="float:right"><!--\
			-->&nbsp; <a href="javascript:;" \
				title="select all tiddlers"\
				onclick="\
				var f=this; while (f&&f.nodeName.toLowerCase()!=\'form\')f=f.parentNode;\
				for (var t=0; t<f.list.options.length; t++)\
					if (f.list.options[t].value.length) f.list.options[t].selected=true;\
				config.macros.tiddlerTweaker.selecttiddlers(f.list);\
				return false">all</a><!--\
			-->&nbsp; <a href="javascript:;" \
				title="select tiddlers that are new/changed since the last file save"\
				onclick="\
				var lastmod=new Date(document.lastModified);\
				var f=this; while (f&&f.nodeName.toLowerCase()!=\'form\')f=f.parentNode;\
				for (var t=0; t<f.list.options.length; t++) {\
					var tid=store.getTiddler(f.list.options[t].value);\
					f.list.options[t].selected=tid&&tid.modified>lastmod;\
				}\
				config.macros.tiddlerTweaker.selecttiddlers(f.list);\
				return false">changed</a><!--\
			-->&nbsp; <a href="javascript:;" \
				title="select tiddlers with at least one matching tag"\
				onclick="\
				var t=prompt(\'Enter space-separated tags (match ONE)\');\
				if (!t||!t.length) return false;\
				var tags=t.readBracketedList();\
				var f=this; while (f&&f.nodeName.toLowerCase()!=\'form\')f=f.parentNode;\
				for (var t=0; t<f.list.options.length; t++) {\
					f.list.options[t].selected=false;\
					var tid=store.getTiddler(f.list.options[t].value);\
					if (tid&&tid.tags.containsAny(tags)) f.list.options[t].selected=true;\
				}\
				config.macros.tiddlerTweaker.selecttiddlers(f.list);\
				return false">tags</a><!--\
			-->&nbsp; <a href="javascript:;" \
				title="select tiddlers whose titles include matching text"\
				onclick="\
				var txt=prompt(\'Enter a title (or portion of a title) to match\');\
				if (!txt||!txt.length) return false;\
				var f=this; while (f&&f.nodeName.toLowerCase()!=\'form\')f=f.parentNode;\
				for (var t=0; t<f.list.options.length; t++) {\
					f.list.options[t].selected=f.list.options[t].value.indexOf(txt)!=-1;\
				}\
				config.macros.tiddlerTweaker.selecttiddlers(f.list);\
				return false">titles</a><!--\
			-->&nbsp; <a href="javascript:;" \
				title="select tiddlers containing matching text"\
				onclick="\
				var txt=prompt(\'Enter tiddler text (content) to match\');\
				if (!txt||!txt.length) return false;\
				var f=this; while (f&&f.nodeName.toLowerCase()!=\'form\')f=f.parentNode;\
				for (var t=0; t<f.list.options.length; t++) {\
					var tt=store.getTiddlerText(f.list.options[t].value,\'\');\
					f.list.options[t].selected=(tt.indexOf(txt)!=-1);\
				}\
				config.macros.tiddlerTweaker.selecttiddlers(f.list);\
				return false">text</a> &nbsp;<!--\
			--></span><span>select tiddlers</span><!--\
			--></div><!--\
			--></font><select multiple name=list size="11" style="width:99.99%" \
				title="use click, shift-click and/or ctrl-click to select multiple tiddler titles" \
				onclick="config.macros.tiddlerTweaker.selecttiddlers(this)" \
				onchange="config.macros.tiddlerTweaker.setfields(this)"><!--\
			--></select><br><!--\
			-->show<input type=text size=1 value="11" \
				onchange="this.form.list.size=this.value; this.form.list.multiple=(this.value>1);"><!--\
			-->by<!--\
			--><select name=sortby size=1 \
				onchange="config.macros.tiddlerTweaker.init(this.form,this.value)"><!--\
			--><option value="title">title</option><!--\
			--><option value="size">size</option><!--\
			--><option value="modified">modified</option><!--\
			--><option value="created">created</option><!--\
			--></select><!--\
			--><input type="button" value="refresh" \
				onclick="config.macros.tiddlerTweaker.init(this.form,this.form.sortby.value)"<!--\
			--> <input type="button" name="stats" disabled value="totals..." \
				onclick="config.macros.tiddlerTweaker.stats(this)"><!--\
		--></td><td style="white-space:nowrap;padding:0;margin:0;border:0;width:1%"><!--\
			--><div style="text-align:left"><font size=-2>&nbsp;modify values</font></div><!--\
			--><table border=0 style="width:100%;padding:0;margin:0;border:0;"><tr style="padding:0;border:0;"><!--\
			--><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=checkbox name=settitle unchecked \
					title="allow changes to tiddler title (rename tiddler)" \
					onclick="this.form.title.disabled=!this.checked">title<!--\
			--></td><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=text name=title size=35 style="width:98%" disabled><!--\
			--></td></tr><tr style="padding:0;border:0;"><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=checkbox name=setcreator unchecked \
					title="allow changes to tiddler creator" \
					onclick="this.form.creator.disabled=!this.checked">created by<!--\
			--></td><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=text name=creator size=35 style="width:98%" disabled><!--\
			--></td></tr><tr style="padding:0;border:0;"><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=checkbox name=setwho unchecked \
					title="allow changes to tiddler author" \
					onclick="this.form.who.disabled=!this.checked">modified by<!--\
			--></td><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=text name=who size=35 style="width:98%" disabled><!--\
			--></td></tr><tr style="padding:0;border:0;"><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=checkbox name=setcdate unchecked \
					title="allow changes to created date" \
					onclick="var f=this.form; f.cm.disabled=f.cd.disabled=f.cy.disabled=f.ch.disabled=f.cn.disabled=!this.checked"><!--\
				-->created on<!--\
			--></td><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=text name=cm size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
				--> / <input type=text name=cd size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
				--> / <input type=text name=cy size=4 style="width:3em;padding:0;text-align:center" disabled><!--\
				--> at <input type=text name=ch size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
				--> : <input type=text name=cn size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
			--></td></tr><tr style="padding:0;border:0;"><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=checkbox name=setmdate unchecked \
					title="allow changes to modified date" \
					onclick="var f=this.form; f.mm.disabled=f.md.disabled=f.my.disabled=f.mh.disabled=f.mn.disabled=!this.checked"><!--\
				-->modified on<!--\
			--></td><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=text name=mm size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
				--> / <input type=text name=md size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
				--> / <input type=text name=my size=4 style="width:3em;padding:0;text-align:center" disabled><!--\
				--> at <input type=text name=mh size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
				--> : <input type=text name=mn size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
			--></td></tr><tr style="padding:0;border:0;"><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=checkbox name=replacetext unchecked\
					title="find/replace matching text" \
					onclick="this.form.pattern.disabled=this.form.replacement.disabled=!this.checked">replace text<!--\
			--></td><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=text name=pattern size=15 value="" style="width:40%" disabled \
					title="enter TEXT PATTERN (regular expression)"> with <!--\
				--><input type=text name=replacement size=15 value="" style="width:40%" disabled \
					title="enter REPLACEMENT TEXT"><!--\
			--></td></tr><tr style="padding:0;border:0;"><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=checkbox name=settags checked \
					title="allow changes to tiddler tags" \
					onclick="this.form.tags.disabled=!this.checked">tags<!--\
			--></td><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=text name=tags size=35 value="" style="width:98%" \
					title="enter new tags or use \'+tag\' and \'-tag\' to add/remove tags from existing tags"><!--\
			--></td></tr></table><!--\
			--><div style="text-align:center"><!--\
			--><nobr><input type=button name=display disabled style="width:32%" value="display tiddlers" \
				onclick="config.macros.tiddlerTweaker.displaytiddlers(this)"><!--\
			--> <input type=button name=del disabled style="width:32%" value="delete tiddlers" \
				onclick="config.macros.tiddlerTweaker.deltiddlers(this)"><!--\
			--> <input type=button name=set disabled style="width:32%" value="update tiddlers" \
				onclick="config.macros.tiddlerTweaker.settiddlers(this)"></nobr><!--\
			--></div><!--\
		--></td></tr></table><!--\
		--></form><span style="display:none"><!--content replaced by tiddler "stats"--></span>\
	',
	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		var span=createTiddlyElement(place,"span");
		span.innerHTML=this.html;
		this.init(span.firstChild,config.options.txtTweakerSortBy);
	},
	init: function(f,sortby) { // initialize form controls
		if (!f) return; // form might not be rendered yet...
		while (f.list.options[0]) f.list.options[0]=null; // empty current list content
		var tids=store.getTiddlers(sortby);
		if (sortby=="size") // descending order (largest tiddlers listed first)
			tids.sort(function(a,b) {return a.text.length > b.text.length ? -1 : (a.text.length == b.text.length ? 0 : +1);});
		for (i=0; i<tids.length; i++) {
			var label=tids[i].title; var value=tids[i].title;
			if (sortby=="modified" || sortby=="created") {
				label=tids[tids.length-i-1][sortby].formatString("YY.0MM.0DD 0hh:0mm ")+tids[tids.length-i-1].title;
				value=tids[tids.length-i-1].title;
			}
			if (sortby=="size") label="["+tids[i].text.length+"] "+label;
			f.list.options[f.list.length]=new Option(label,value,false,false);
		}
		f.title.value=f.who.value=f.creator.value=f.tags.value="";
		f.cm.value=f.cd.value=f.cy.value=f.ch.value=f.cn.value="";
		f.mm.value=f.md.value=f.my.value=f.mh.value=f.mn.value="";
		f.stats.disabled=f.set.disabled=f.del.disabled=f.display.disabled=true;
		f.settitle.disabled=false;
		config.options.txtTweakerSortBy=sortby; // remember current setting
		f.sortby.value=sortby; // sync droplist selection with current setting
		if (sortby!="modified") // non-default preference... save cookie
			saveOptionCookie("txtTweakerSortBy");
		else removeCookie("txtTweakerSortBy"); // default preference... clear cookie
	},
	selecttiddlers: function(here) { // enable/disable tweaker fields based on number of items selected
		// count how many tiddlers are selected
		var f=here.form; var list=f.list;
		var c=0; for (i=0;i<list.length;i++) if (list.options[i].selected) c++;
		if (c>1) f.title.disabled=true;
		if (c>1) f.settitle.checked=false;
		f.set.disabled=(c==0);
		f.del.disabled=(c==0);
		f.display.disabled=(c==0);
		f.settitle.disabled=(c>1);
		f.stats.disabled=(c==0);
		var msg=(c==0)?'select tiddlers':(c+' tiddler'+(c!=1?'s':'')+' selected');
		here.previousSibling.firstChild.firstChild.nextSibling.innerHTML=msg;
		if (c) clearMessage(); else displayMessage("no tiddlers selected");
	},
	setfields: function(here) { // set tweaker edit fields from first selected tiddler
		var f=here.form;
		if (!here.value.length) {
			f.title.value=f.who.value=f.creator.value=f.tags.value="";
			f.cm.value=f.cd.value=f.cy.value=f.ch.value=f.cn.value="";
			f.mm.value=f.md.value=f.my.value=f.mh.value=f.mn.value="";
			return;
		}
		var tid=store.getTiddler(here.value); if (!tid) return;
		f.title.value=tid.title;
		f.who.value=tid.modifier;
		f.creator.value=tid.fields['creator']||''; // custom field - might not exist
		f.tags.value=tid.tags.join(' ');
		var c=tid.created; var m=tid.modified;
		f.cm.value=c.getMonth()+1;
		f.cd.value=c.getDate();
		f.cy.value=c.getFullYear();
		f.ch.value=c.getHours();
		f.cn.value=c.getMinutes();
		f.mm.value=m.getMonth()+1;
		f.md.value=m.getDate();
		f.my.value=m.getFullYear();
		f.mh.value=m.getHours();
		f.mn.value=m.getMinutes();
	},
	settiddlers: function(here) {
		var f=here.form; var list=f.list;
		var tids=[];
		for (i=0;i<list.length;i++) if (list.options[i].selected) tids.push(list.options[i].value);
		if (!tids.length) { alert("please select at least one tiddler"); return; }
		var cdate=new Date(f.cy.value,f.cm.value-1,f.cd.value,f.ch.value,f.cn.value);
		var mdate=new Date(f.my.value,f.mm.value-1,f.md.value,f.mh.value,f.mn.value);
		if (tids.length>1 && !confirm("Are you sure you want to update these tiddlers:\n\n"+tids.join(', '))) return;
		store.suspendNotifications();
		for (t=0;t<tids.length;t++) {
			var tid=store.getTiddler(tids[t]); if (!tid) continue;
			var title=!f.settitle.checked?tid.title:f.title.value;
			var who=!f.setwho.checked?tid.modifier:f.who.value;
			var text=tid.text;
			if (f.replacetext.checked) text=text.replace(new RegExp(f.pattern.value,'mg'),f.replacement.value);
			var tags=tid.tags;
			if (f.settags.checked) { 
				var intags=f.tags.value.readBracketedList();
				var addtags=[]; var deltags=[]; var reptags=[];
				for (i=0;i<intags.length;i++) {
					if (intags[i].substr(0,1)=='+')
						addtags.push(intags[i].substr(1));
					else if (intags[i].substr(0,1)=='-')
						deltags.push(intags[i].substr(1));
					else
						reptags.push(intags[i]);
				}
				if (reptags.length)
					tags=reptags;
				if (addtags.length)
					tags=new Array().concat(tags,addtags);
				if (deltags.length)
					for (i=0;i<deltags.length;i++)
						{ var pos=tags.indexOf(deltags[i]); if (pos!=-1) tags.splice(pos,1); }
			}
			if (!f.setcdate.checked) cdate=tid.created;
			if (!f.setmdate.checked) mdate=tid.modified;
			store.saveTiddler(tid.title,title,text,who,mdate,tags,tid.fields);
			if (f.setcreator.checked) store.setValue(tid.title,'creator',f.creator.value); // set creator
			if (f.setcdate.checked) tid.assign(null,null,null,null,null,cdate); // set create date
		}
		store.resumeNotifications();
		this.init(f,f.sortby.value);
	},
	displaytiddlers: function(here) {
		var f=here.form; var list=f.list;
		var tids=[];
		for (i=0; i<list.length;i++) if (list.options[i].selected) tids.push(list.options[i].value);
		if (!tids.length) { alert("please select at least one tiddler"); return; }
		story.displayTiddlers(story.findContainingTiddler(f),tids)
	},
	deltiddlers: function(here) {
		var f=here.form; var list=f.list;
		var tids=[];
		for (i=0;i<list.length;i++) if (list.options[i].selected) tids.push(list.options[i].value);
		if (!tids.length) { alert("please select at least one tiddler"); return; }
		if (!confirm("Are you sure you want to delete these tiddlers:\n\n"+tids.join(', '))) return;
		store.suspendNotifications();
		for (t=0;t<tids.length;t++) {
			var tid=store.getTiddler(tids[t]); if (!tid) continue;
			if (tid.tags.contains("systemConfig"))
				if (!confirm("'"+tid.title+"' is tagged with 'systemConfig'.\n\nRemoving this tiddler may cause unexpected results.  Are you sure?"))
					continue;
			store.removeTiddler(tid.title);
			story.closeTiddler(tid.title);
		}
		store.resumeNotifications();
		this.init(f,f.sortby.value);
	},
	stats: function(here) {
		var f=here.form; var list=f.list; var tids=[]; var out=''; var tot=0;
		var target=f.nextSibling;
		for (i=0;i<list.length;i++) if (list.options[i].selected) tids.push(list.options[i].value);
		if (!tids.length) { alert("please select at least one tiddler"); return; }
		for (t=0;t<tids.length;t++) {
			var tid=store.getTiddler(tids[t]); if (!tid) continue;
			out+='[['+tid.title+']] '+tid.text.length+'\n'; tot+=tid.text.length;
		}
		var avg=tot/tids.length;
		out=tot+' bytes in '+tids.length+' selected tiddlers ('+avg+' bytes/tiddler)\n<<<\n'+out+'<<<\n';
		removeChildren(target);
		target.innerHTML="<hr><font size=-2><a href='javascript:;' style='float:right' "
			+"onclick='this.parentNode.parentNode.style.display=\"none\"'>close</a></font>";
		wikify(out,target);
		target.style.display="block";
	}
};
//}}}
<<tiddler Davs>>
<<tiddler [[MÃ¥ns MÃ¥rtensson]]>>
Samlingen er støt voksende og alle er "skræddersyede" ~TiddlyWikiVarianter.
De fleste af dem er mere eller mindre fyldt med data - for at vise deres funktionalitet.
Især [[Komplet EfterskoleWiki]] demonstrerer mange udvidede databaselignende muligheder.

Med tiden vil der også komme et "træ" med [[tomme skabeloner|Skabeloner]], der kan downloades og tages i brug uden der kræves en kraftig "oprydning" først..

Jeg har ingen prioritering på dette - så hvis du ønsker en af dem i "tom" udgave - er du velkommen til at [[kontakte|Kontakt]] mig.

Mvh [[MÃ¥ns MÃ¥rtensson]] 
|~ViewToolbar|closeTiddler editTiddler|
|~EditToolbar|saveTiddler cancelTiddler deleteTiddler|
/***
Description: Contains the stuff you need to use Tiddlyspot
Note, you also need UploadPlugin, PasswordOptionPlugin and LoadRemoteFileThroughProxy
from http://tiddlywiki.bidix.info for a complete working Tiddlyspot site.
***/
//{{{

// edit this if you are migrating sites or retrofitting an existing TW
config.tiddlyspotSiteId = 'twspot';

// make it so you can by default see edit controls via http
config.options.chkHttpReadOnly = false;
window.readOnly = false; // make sure of it (for tw 2.2)
window.showBackstage = true; // show backstage too

// disable autosave in d3
if (window.location.protocol != "file:")
	config.options.chkGTDLazyAutoSave = false;

// tweak shadow tiddlers to add upload button, password entry box etc
with (config.shadowTiddlers) {
	SiteUrl = 'http://'+config.tiddlyspotSiteId+'.tiddlyspot.com';
	SideBarOptions = SideBarOptions.replace(/(<<saveChanges>>)/,"$1<<tiddler TspotSidebar>>");
	OptionsPanel = OptionsPanel.replace(/^/,"<<tiddler TspotOptions>>");
	DefaultTiddlers = DefaultTiddlers.replace(/^/,"[[WelcomeToTiddlyspot]] ");
	MainMenu = MainMenu.replace(/^/,"[[WelcomeToTiddlyspot]] ");
}

// create some shadow tiddler content
merge(config.shadowTiddlers,{

'WelcomeToTiddlyspot':[
 "This document is a ~TiddlyWiki from tiddlyspot.com.  A ~TiddlyWiki is an electronic notebook that is great for managing todo lists, personal information, and all sorts of things.",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //What now?// &nbsp;&nbsp;@@ Before you can save any changes, you need to enter your password in the form below.  Then configure privacy and other site settings at your [[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]] (your control panel username is //" + config.tiddlyspotSiteId + "//).",
 "<<tiddler TspotControls>>",
 "See also GettingStarted.",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Working online// &nbsp;&nbsp;@@ You can edit this ~TiddlyWiki right now, and save your changes using the \"save to web\" button in the column on the right.",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Working offline// &nbsp;&nbsp;@@ A fully functioning copy of this ~TiddlyWiki can be saved onto your hard drive or USB stick.  You can make changes and save them locally without being connected to the Internet.  When you're ready to sync up again, just click \"upload\" and your ~TiddlyWiki will be saved back to tiddlyspot.com.",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Help!// &nbsp;&nbsp;@@ Find out more about ~TiddlyWiki at [[TiddlyWiki.com|http://tiddlywiki.com]].  Also visit [[TiddlyWiki.org|http://tiddlywiki.org]] for documentation on learning and using ~TiddlyWiki. New users are especially welcome on the [[TiddlyWiki mailing list|http://groups.google.com/group/TiddlyWiki]], which is an excellent place to ask questions and get help.  If you have a tiddlyspot related problem email [[tiddlyspot support|mailto:support@tiddlyspot.com]].",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Enjoy :)// &nbsp;&nbsp;@@ We hope you like using your tiddlyspot.com site.  Please email [[feedback@tiddlyspot.com|mailto:feedback@tiddlyspot.com]] with any comments or suggestions."
].join("\n"),

'TspotControls':[
 "| tiddlyspot password:|<<option pasUploadPassword>>|",
 "| site management:|<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . .  " + config.tiddlyspotSiteId + ">>//(requires tiddlyspot password)//<br>[[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]], [[download (go offline)|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download]]|",
 "| links:|[[tiddlyspot.com|http://tiddlyspot.com/]], [[FAQs|http://faq.tiddlyspot.com/]], [[blog|http://tiddlyspot.blogspot.com/]], email [[support|mailto:support@tiddlyspot.com]] & [[feedback|mailto:feedback@tiddlyspot.com]], [[donate|http://tiddlyspot.com/?page=donate]]|"
].join("\n"),

'TspotSidebar':[
 "<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . .  " + config.tiddlyspotSiteId + ">><html><a href='http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download' class='button'>download</a></html>"
].join("\n"),

'TspotOptions':[
 "tiddlyspot password:",
 "<<option pasUploadPassword>>",
 ""
].join("\n")

});
//}}}
[[Link:|http://tweetdk.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://tweetdk.tiddlyspot.com/index.html" frameborder="2" width="100%" height="400"></iframe></div></html>
| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |
| 07/11/2009 16:41:58 | DitNavn | [[/|http://twspot.tiddlyspot.com/]] | [[store.cgi|http://twspot.tiddlyspot.com/store.cgi]] | . | [[index.html | http://twspot.tiddlyspot.com/index.html]] | . | ok |
| 07/11/2009 16:58:20 | DitNavn | [[/|http://twspot.tiddlyspot.com/]] | [[store.cgi|http://twspot.tiddlyspot.com/store.cgi]] | . | [[index.html | http://twspot.tiddlyspot.com/index.html]] | . |
| 07/11/2009 17:14:30 | DitNavn | [[/|http://twspot.tiddlyspot.com/]] | [[store.cgi|http://twspot.tiddlyspot.com/store.cgi]] | . | [[index.html | http://twspot.tiddlyspot.com/index.html]] | . |
| 08/11/2009 00:01:31 | DitNavn | [[/|http://twspot.tiddlyspot.com/]] | [[store.cgi|http://twspot.tiddlyspot.com/store.cgi]] | . | [[index.html | http://twspot.tiddlyspot.com/index.html]] | backup |
| 24/01/2010 12:31:33 | DitNavn | [[/|http://twspot.tiddlyspot.com/]] | [[store.cgi|http://twspot.tiddlyspot.com/store.cgi]] | . | [[index.html | http://twspot.tiddlyspot.com/index.html]] | backup |
| 24/01/2010 12:36:57 | DitNavn | [[index.html|http://twspot.tiddlyspot.com/index.html]] | [[store.cgi|http://twspot.tiddlyspot.com/store.cgi]] | . | [[index.html | http://twspot.tiddlyspot.com/index.html]] | . |
| 24/01/2010 12:38:53 | DitNavn | [[index.html|http://twspot.tiddlyspot.com/index.html]] | [[store.cgi|http://twspot.tiddlyspot.com/store.cgi]] | . | [[index.html | http://twspot.tiddlyspot.com/index.html]] | . |
| 24/01/2010 12:43:03 | DitNavn | [[index.html|http://twspot.tiddlyspot.com/index.html]] | [[store.cgi|http://twspot.tiddlyspot.com/store.cgi]] | . | [[index.html | http://twspot.tiddlyspot.com/index.html]] | . |
| 16/12/2010 16:54:29 | DitNavn | [[/|http://twspot.tiddlyspot.com/]] | [[store.cgi|http://twspot.tiddlyspot.com/store.cgi]] | . | [[index.html | http://twspot.tiddlyspot.com/index.html]] | backup | failed |
| 16/12/2010 16:55:00 | DitNavn | [[/|http://twspot.tiddlyspot.com/]] | [[store.cgi|http://twspot.tiddlyspot.com/store.cgi]] | . | [[index.html | http://twspot.tiddlyspot.com/index.html]] | backup |
/***
|''Name:''|UploadPlugin|
|''Description:''|Save to web a TiddlyWiki|
|''Version:''|4.1.3|
|''Date:''|Feb 24, 2008|
|''Source:''|http://tiddlywiki.bidix.info/#UploadPlugin|
|''Documentation:''|http://tiddlywiki.bidix.info/#UploadPluginDoc|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0|
|''Requires:''|PasswordOptionPlugin|
***/
//{{{
version.extensions.UploadPlugin = {
	major: 4, minor: 1, revision: 3,
	date: new Date("Feb 24, 2008"),
	source: 'http://tiddlywiki.bidix.info/#UploadPlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	coreVersion: '2.2.0'
};

//
// Environment
//

if (!window.bidix) window.bidix = {}; // bidix namespace
bidix.debugMode = false;	// true to activate both in Plugin and UploadService
	
//
// Upload Macro
//

config.macros.upload = {
// default values
	defaultBackupDir: '',	//no backup
	defaultStoreScript: "store.php",
	defaultToFilename: "index.html",
	defaultUploadDir: ".",
	authenticateUser: true	// UploadService Authenticate User
};
	
config.macros.upload.label = {
	promptOption: "Gem og upload denne TiddlyWiki med UploadMuligheder",
	promptParamMacro: "Gem og upload denne TiddlyWiki i %0",
	saveLabel: "gem til nettet", 
	saveToDisk: "gem til disk",
	uploadLabel: "upload"	
};

config.macros.upload.messages = {
	noStoreUrl: "Der er ikke specificeret en URL i parametre eller muligheder",
	usernameOrPasswordMissing: "Brugernavn eller kodeord mangler"
};

config.macros.upload.handler = function(place,macroName,params) {
	if (readOnly)
		return;
	var label;
	if (document.location.toString().substr(0,4) == "http") 
		label = this.label.saveLabel;
	else
		label = this.label.uploadLabel;
	var prompt;
	if (params[0]) {
		prompt = this.label.promptParamMacro.toString().format([this.destFile(params[0], 
			(params[1] ? params[1]:bidix.basename(window.location.toString())), params[3])]);
	} else {
		prompt = this.label.promptOption;
	}
	createTiddlyButton(place, label, prompt, function() {config.macros.upload.action(params);}, null, null, this.accessKey);
};

config.macros.upload.action = function(params)
{
		// for missing macro parameter set value from options
		if (!params) params = {};
		var storeUrl = params[0] ? params[0] : config.options.txtUploadStoreUrl;
		var toFilename = params[1] ? params[1] : config.options.txtUploadFilename;
		var backupDir = params[2] ? params[2] : config.options.txtUploadBackupDir;
		var uploadDir = params[3] ? params[3] : config.options.txtUploadDir;
		var username = params[4] ? params[4] : config.options.txtUploadUserName;
		var password = config.options.pasUploadPassword; // for security reason no password as macro parameter	
		// for still missing parameter set default value
		if ((!storeUrl) && (document.location.toString().substr(0,4) == "http")) 
			storeUrl = bidix.dirname(document.location.toString())+'/'+config.macros.upload.defaultStoreScript;
		if (storeUrl.substr(0,4) != "http")
			storeUrl = bidix.dirname(document.location.toString()) +'/'+ storeUrl;
		if (!toFilename)
			toFilename = bidix.basename(window.location.toString());
		if (!toFilename)
			toFilename = config.macros.upload.defaultToFilename;
		if (!uploadDir)
			uploadDir = config.macros.upload.defaultUploadDir;
		if (!backupDir)
			backupDir = config.macros.upload.defaultBackupDir;
		// report error if still missing
		if (!storeUrl) {
			alert(config.macros.upload.messages.noStoreUrl);
			clearMessage();
			return false;
		}
		if (config.macros.upload.authenticateUser && (!username || !password)) {
			alert(config.macros.upload.messages.usernameOrPasswordMissing);
			clearMessage();
			return false;
		}
		bidix.upload.uploadChanges(false,null,storeUrl, toFilename, uploadDir, backupDir, username, password); 
		return false; 
};

config.macros.upload.destFile = function(storeUrl, toFilename, uploadDir) 
{
	if (!storeUrl)
		return null;
		var dest = bidix.dirname(storeUrl);
		if (uploadDir && uploadDir != '.')
			dest = dest + '/' + uploadDir;
		dest = dest + '/' + toFilename;
	return dest;
};

//
// uploadOptions Macro
//

config.macros.uploadOptions = {
	handler: function(place,macroName,params) {
		var wizard = new Wizard();
		wizard.createWizard(place,this.wizardTitle);
		wizard.addStep(this.step1Title,this.step1Html);
		var markList = wizard.getElement("markList");
		var listWrapper = document.createElement("div");
		markList.parentNode.insertBefore(listWrapper,markList);
		wizard.setValue("listWrapper",listWrapper);
		this.refreshOptions(listWrapper,false);
		var uploadCaption;
		if (document.location.toString().substr(0,4) == "http") 
			uploadCaption = config.macros.upload.label.saveLabel;
		else
			uploadCaption = config.macros.upload.label.uploadLabel;
		
		wizard.setButtons([
				{caption: uploadCaption, tooltip: config.macros.upload.label.promptOption, 
					onClick: config.macros.upload.action},
				{caption: this.cancelButton, tooltip: this.cancelButtonPrompt, onClick: this.onCancel}
				
			]);
	},
	options: [
		"txtUploadUserName",
		"pasUploadPassword",
		"txtUploadStoreUrl",
		"txtUploadDir",
		"txtUploadFilename",
		"txtUploadBackupDir",
		"chkUploadLog",
		"txtUploadLogMaxLine"		
	],
	refreshOptions: function(listWrapper) {
		var opts = [];
		for(i=0; i<this.options.length; i++) {
			var opt = {};
			opts.push();
			opt.option = "";
			n = this.options[i];
			opt.name = n;
			opt.lowlight = !config.optionsDesc[n];
			opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
			opts.push(opt);
		}
		var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
		for(n=0; n<opts.length; n++) {
			var type = opts[n].name.substr(0,3);
			var h = config.macros.option.types[type];
			if (h && h.create) {
				h.create(opts[n].colElements['option'],type,opts[n].name,opts[n].name,"no");
			}
		}
		
	},
	onCancel: function(e)
	{
		backstage.switchTab(null);
		return false;
	},
	
	wizardTitle: "Upload with options",
	step1Title: "These options are saved in cookies in your browser",
	step1Html: "<input type='hidden' name='markList'></input><br>",
	cancelButton: "Cancel",
	cancelButtonPrompt: "Cancel prompt",
	listViewTemplate: {
		columns: [
			{name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
			{name: 'Option', field: 'option', title: "Option", type: 'String'},
			{name: 'Name', field: 'name', title: "Name", type: 'String'}
			],
		rowClasses: [
			{className: 'lowlight', field: 'lowlight'} 
			]}
};

//
// upload functions
//

if (!bidix.upload) bidix.upload = {};

if (!bidix.upload.messages) bidix.upload.messages = {
	//from saving
	invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
	backupSaved: "Backup saved",
	backupFailed: "Failed to upload backup file",
	rssSaved: "RSS feed uploaded",
	rssFailed: "Failed to upload RSS feed file",
	emptySaved: "Empty template uploaded",
	emptyFailed: "Failed to upload empty template file",
	mainSaved: "Main TiddlyWiki file uploaded",
	mainFailed: "Failed to upload main TiddlyWiki file. Your changes have not been saved",
	//specific upload
	loadOriginalHttpPostError: "Can't get original file",
	aboutToSaveOnHttpPost: 'About to upload on %0 ...',
	storePhpNotFound: "The store script '%0' was not found."
};

bidix.upload.uploadChanges = function(onlyIfDirty,tiddlers,storeUrl,toFilename,uploadDir,backupDir,username,password)
{
	var callback = function(status,uploadParams,original,url,xhr) {
		if (!status) {
			displayMessage(bidix.upload.messages.loadOriginalHttpPostError);
			return;
		}
		if (bidix.debugMode) 
			alert(original.substr(0,500)+"\n...");
		// Locate the storeArea div's 
		var posDiv = locateStoreArea(original);
		if((posDiv[0] == -1) || (posDiv[1] == -1)) {
			alert(config.messages.invalidFileError.format([localPath]));
			return;
		}
		bidix.upload.uploadRss(uploadParams,original,posDiv);
	};
	
	if(onlyIfDirty && !store.isDirty())
		return;
	clearMessage();
	// save on localdisk ?
	if (document.location.toString().substr(0,4) == "file") {
		var path = document.location.toString();
		var localPath = getLocalPath(path);
		saveChanges();
	}
	// get original
	var uploadParams = new Array(storeUrl,toFilename,uploadDir,backupDir,username,password);
	var originalPath = document.location.toString();
	// If url is a directory : add index.html
	if (originalPath.charAt(originalPath.length-1) == "/")
		originalPath = originalPath + "index.html";
	var dest = config.macros.upload.destFile(storeUrl,toFilename,uploadDir);
	var log = new bidix.UploadLog();
	log.startUpload(storeUrl, dest, uploadDir,  backupDir);
	displayMessage(bidix.upload.messages.aboutToSaveOnHttpPost.format([dest]));
	if (bidix.debugMode) 
		alert("about to execute Http - GET on "+originalPath);
	var r = doHttp("GET",originalPath,null,null,username,password,callback,uploadParams,null);
	if (typeof r == "string")
		displayMessage(r);
	return r;
};

bidix.upload.uploadRss = function(uploadParams,original,posDiv) 
{
	var callback = function(status,params,responseText,url,xhr) {
		if(status) {
			var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
			displayMessage(bidix.upload.messages.rssSaved,bidix.dirname(url)+'/'+destfile);
			bidix.upload.uploadMain(params[0],params[1],params[2]);
		} else {
			displayMessage(bidix.upload.messages.rssFailed);			
		}
	};
	// do uploadRss
	if(config.options.chkGenerateAnRssFeed) {
		var rssPath = uploadParams[1].substr(0,uploadParams[1].lastIndexOf(".")) + ".xml";
		var rssUploadParams = new Array(uploadParams[0],rssPath,uploadParams[2],'',uploadParams[4],uploadParams[5]);
		var rssString = generateRss();
		// no UnicodeToUTF8 conversion needed when location is "file" !!!
		if (document.location.toString().substr(0,4) != "file")
			rssString = convertUnicodeToUTF8(rssString);	
		bidix.upload.httpUpload(rssUploadParams,rssString,callback,Array(uploadParams,original,posDiv));
	} else {
		bidix.upload.uploadMain(uploadParams,original,posDiv);
	}
};

bidix.upload.uploadMain = function(uploadParams,original,posDiv) 
{
	var callback = function(status,params,responseText,url,xhr) {
		var log = new bidix.UploadLog();
		if(status) {
			// if backupDir specified
			if ((params[3]) && (responseText.indexOf("backupfile:") > -1))  {
				var backupfile = responseText.substring(responseText.indexOf("backupfile:")+11,responseText.indexOf("\n", responseText.indexOf("backupfile:")));
				displayMessage(bidix.upload.messages.backupSaved,bidix.dirname(url)+'/'+backupfile);
			}
			var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
			displayMessage(bidix.upload.messages.mainSaved,bidix.dirname(url)+'/'+destfile);
			store.setDirty(false);
			log.endUpload("ok");
		} else {
			alert(bidix.upload.messages.mainFailed);
			displayMessage(bidix.upload.messages.mainFailed);
			log.endUpload("failed");			
		}
	};
	// do uploadMain
	var revised = bidix.upload.updateOriginal(original,posDiv);
	bidix.upload.httpUpload(uploadParams,revised,callback,uploadParams);
};

bidix.upload.httpUpload = function(uploadParams,data,callback,params)
{
	var localCallback = function(status,params,responseText,url,xhr) {
		url = (url.indexOf("nocache=") < 0 ? url : url.substring(0,url.indexOf("nocache=")-1));
		if (xhr.status == 404)
			alert(bidix.upload.messages.storePhpNotFound.format([url]));
		if ((bidix.debugMode) || (responseText.indexOf("Debug mode") >= 0 )) {
			alert(responseText);
			if (responseText.indexOf("Debug mode") >= 0 )
				responseText = responseText.substring(responseText.indexOf("\n\n")+2);
		} else if (responseText.charAt(0) != '0') 
			alert(responseText);
		if (responseText.charAt(0) != '0')
			status = null;
		callback(status,params,responseText,url,xhr);
	};
	// do httpUpload
	var boundary = "---------------------------"+"AaB03x";	
	var uploadFormName = "UploadPlugin";
	// compose headers data
	var sheader = "";
	sheader += "--" + boundary + "\r\nContent-disposition: form-data; name=\"";
	sheader += uploadFormName +"\"\r\n\r\n";
	sheader += "backupDir="+uploadParams[3] +
				";user=" + uploadParams[4] +
				";password=" + uploadParams[5] +
				";uploaddir=" + uploadParams[2];
	if (bidix.debugMode)
		sheader += ";debug=1";
	sheader += ";;\r\n"; 
	sheader += "\r\n" + "--" + boundary + "\r\n";
	sheader += "Content-disposition: form-data; name=\"userfile\"; filename=\""+uploadParams[1]+"\"\r\n";
	sheader += "Content-Type: text/html;charset=UTF-8" + "\r\n";
	sheader += "Content-Length: " + data.length + "\r\n\r\n";
	// compose trailer data
	var strailer = new String();
	strailer = "\r\n--" + boundary + "--\r\n";
	data = sheader + data + strailer;
	if (bidix.debugMode) alert("about to execute Http - POST on "+uploadParams[0]+"\n with \n"+data.substr(0,500)+ " ... ");
	var r = doHttp("POST",uploadParams[0],data,"multipart/form-data; ;charset=UTF-8; boundary="+boundary,uploadParams[4],uploadParams[5],localCallback,params,null);
	if (typeof r == "string")
		displayMessage(r);
	return r;
};

// same as Saving's updateOriginal but without convertUnicodeToUTF8 calls
bidix.upload.updateOriginal = function(original, posDiv)
{
	if (!posDiv)
		posDiv = locateStoreArea(original);
	if((posDiv[0] == -1) || (posDiv[1] == -1)) {
		alert(config.messages.invalidFileError.format([localPath]));
		return;
	}
	var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n" +
				store.allTiddlersAsHtml() + "\n" +
				original.substr(posDiv[1]);
	var newSiteTitle = getPageTitle().htmlEncode();
	revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
	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;
};

//
// UploadLog
// 
// config.options.chkUploadLog :
//		false : no logging
//		true : logging
// config.options.txtUploadLogMaxLine :
//		-1 : no limit
//      0 :  no Log lines but UploadLog is still in place
//		n :  the last n lines are only kept
//		NaN : no limit (-1)

bidix.UploadLog = function() {
	if (!config.options.chkUploadLog) 
		return; // this.tiddler = null
	this.tiddler = store.getTiddler("UploadLog");
	if (!this.tiddler) {
		this.tiddler = new Tiddler();
		this.tiddler.title = "UploadLog";
		this.tiddler.text = "| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |";
		this.tiddler.created = new Date();
		this.tiddler.modifier = config.options.txtUserName;
		this.tiddler.modified = new Date();
		store.addTiddler(this.tiddler);
	}
	return this;
};

bidix.UploadLog.prototype.addText = function(text) {
	if (!this.tiddler)
		return;
	// retrieve maxLine when we need it
	var maxLine = parseInt(config.options.txtUploadLogMaxLine,10);
	if (isNaN(maxLine))
		maxLine = -1;
	// add text
	if (maxLine != 0) 
		this.tiddler.text = this.tiddler.text + text;
	// Trunck to maxLine
	if (maxLine >= 0) {
		var textArray = this.tiddler.text.split('\n');
		if (textArray.length > maxLine + 1)
			textArray.splice(1,textArray.length-1-maxLine);
			this.tiddler.text = textArray.join('\n');		
	}
	// update tiddler fields
	this.tiddler.modifier = config.options.txtUserName;
	this.tiddler.modified = new Date();
	store.addTiddler(this.tiddler);
	// refresh and notifiy for immediate update
	story.refreshTiddler(this.tiddler.title);
	store.notify(this.tiddler.title, true);
};

bidix.UploadLog.prototype.startUpload = function(storeUrl, toFilename, uploadDir,  backupDir) {
	if (!this.tiddler)
		return;
	var now = new Date();
	var text = "\n| ";
	var filename = bidix.basename(document.location.toString());
	if (!filename) filename = '/';
	text += now.formatString("0DD/0MM/YYYY 0hh:0mm:0ss") +" | ";
	text += config.options.txtUserName + " | ";
	text += "[["+filename+"|"+location + "]] |";
	text += " [[" + bidix.basename(storeUrl) + "|" + storeUrl + "]] | ";
	text += uploadDir + " | ";
	text += "[[" + bidix.basename(toFilename) + " | " +toFilename + "]] | ";
	text += backupDir + " |";
	this.addText(text);
};

bidix.UploadLog.prototype.endUpload = function(status) {
	if (!this.tiddler)
		return;
	this.addText(" "+status+" |");
};

//
// Utilities
// 

bidix.checkPlugin = function(plugin, major, minor, revision) {
	var ext = version.extensions[plugin];
	if (!
		(ext  && 
			((ext.major > major) || 
			((ext.major == major) && (ext.minor > minor))  ||
			((ext.major == major) && (ext.minor == minor) && (ext.revision >= revision))))) {
			// write error in PluginManager
			if (pluginInfo)
				pluginInfo.log.push("Requires " + plugin + " " + major + "." + minor + "." + revision);
			eval(plugin); // generate an error : "Error: ReferenceError: xxxx is not defined"
	}
};

bidix.dirname = function(filePath) {
	if (!filePath) 
		return;
	var lastpos;
	if ((lastpos = filePath.lastIndexOf("/")) != -1) {
		return filePath.substring(0, lastpos);
	} else {
		return filePath.substring(0, filePath.lastIndexOf("\\"));
	}
};

bidix.basename = function(filePath) {
	if (!filePath) 
		return;
	var lastpos;
	if ((lastpos = filePath.lastIndexOf("#")) != -1) 
		filePath = filePath.substring(0, lastpos);
	if ((lastpos = filePath.lastIndexOf("/")) != -1) {
		return filePath.substring(lastpos + 1);
	} else
		return filePath.substring(filePath.lastIndexOf("\\")+1);
};

bidix.initOption = function(name,value) {
	if (!config.options[name])
		config.options[name] = value;
};

//
// Initializations
//

// require PasswordOptionPlugin 1.0.1 or better
bidix.checkPlugin("PasswordOptionPlugin", 1, 0, 1);

// styleSheet
setStylesheet('.txtUploadStoreUrl, .txtUploadBackupDir, .txtUploadDir {width: 22em;}',"uploadPluginStyles");

//optionsDesc
merge(config.optionsDesc,{
	txtUploadStoreUrl: "Url of the UploadService script (default: store.php)",
	txtUploadFilename: "Filename of the uploaded file (default: in index.html)",
	txtUploadDir: "Relative Directory where to store the file (default: . (downloadService directory))",
	txtUploadBackupDir: "Relative Directory where to backup the file. If empty no backup. (default: ''(empty))",
	txtUploadUserName: "Upload Username",
	pasUploadPassword: "Upload Password",
	chkUploadLog: "do Logging in UploadLog (default: true)",
	txtUploadLogMaxLine: "Maximum of lines in UploadLog (default: 10)"
});

// Options Initializations
bidix.initOption('txtUploadStoreUrl','');
bidix.initOption('txtUploadFilename','');
bidix.initOption('txtUploadDir','');
bidix.initOption('txtUploadBackupDir','');
bidix.initOption('txtUploadUserName','');
bidix.initOption('pasUploadPassword','');
bidix.initOption('chkUploadLog',true);
bidix.initOption('txtUploadLogMaxLine','10');


// Backstage
merge(config.tasks,{
	uploadOptions: {text: "upload", tooltip: "Change UploadOptions and Upload", content: '<<uploadOptions>>'}
});
config.backstageTasks.push("uploadOptions");


//}}}
<!--{{{-->
<div class='moveablePanel'>
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></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='tagged' macro='tags'></div>
<div class='tagging' macro='tagging'></div>
<div class='viewer'>
	<div class='content' macro='view text wikified'></div>
</div>
<div class='tagClear'></div>
<div macro='moveablePanel name:{{story.findContainingTiddler(place).getAttribute("tiddler")}} undocked fold hover height:auto'></div>
</div>
<!--}}}-->
For at kunne bruge links - tryk på //"Outline"//..
<html>
<iframe id='xmindshare_embedviewer' src='http://share.xmind.net/_embed/maans66/xmind-022438/' width='100%' height='900px' frameborder='0' scrolling='no'></iframe>
</html>
Der er forskel på hvad denne hjemmeside kan alt efter hvilken browser du bruger.
Det er en enkelt Html-side som kan downloades og bruges "lokalt" fra en harddisk eller en usb.

~TagMindMappen kan zoomes vha midtertasten eller et "scrollhjul" i ~FireFox, det kan det f.eks. ikke i Google Chrome.
Enkeltklik på en "node" i ~FireFox åbner et dokument i form af en såkaldt Tiddler. I Google Chrome kræver det dobbeltklik.
[[Link:|http://ssskema.tiddlyspot.com]]
<html><div align="center"><iframe src="http://ssskema.tiddlyspot.com" frameborder="0" width="100%" height="600"></iframe></div></html>
[[Link:|http://tegning.tiddlyspot.com/index.html]]
<html><div align="center"><iframe src="http://tegning.tiddlyspot.com/index.html" frameborder="2" width="100%" height="800"></iframe></div></html>
config.options.chkSaveBackups = true;
config.options.chkSinglePageMode=true;
config.options.chkSinglePagePermalink=true;
config.options.chkPanelManagerUseCookies=false;
config.options.chkMoveablePanelShowManager=false;
config.options.txtMoveablePanelMapName='DefaultMap';