Background: #fff
Foreground: #000
PrimaryPale: #8cf
PrimaryLight: #18f
PrimaryMid: #04b
PrimaryDark: #014
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
/*{{{*/
body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}

a {color:[[ColorPalette::PrimaryMid]];}
a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
a img {border:0;}

h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}

.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}

.header {background:[[ColorPalette::PrimaryMid]];}
.headerShadow {color:[[ColorPalette::Foreground]];}
.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
.headerForeground {color:[[ColorPalette::Background]];}
.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}

.tabSelected{color:[[ColorPalette::PrimaryDark]];
	background:[[ColorPalette::TertiaryPale]];
	border-left:1px solid [[ColorPalette::TertiaryLight]];
	border-top:1px solid [[ColorPalette::TertiaryLight]];
	border-right:1px solid [[ColorPalette::TertiaryLight]];
}
.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
.tabContents .button {border:0;}

#sidebar {}
#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}

.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
	border:1px solid [[ColorPalette::PrimaryMid]];}
.wizardStep.wizardStepDone {background:[[ColorPalette::TertiaryLight]];}
.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}

#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}

.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}

.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}

.tiddler .defaultCommand {font-weight:bold;}

.shadow .title {color:[[ColorPalette::TertiaryDark]];}

.title {color:[[ColorPalette::SecondaryDark]];}
.subtitle {color:[[ColorPalette::TertiaryDark]];}

.toolbar {color:[[ColorPalette::PrimaryMid]];}
.toolbar a {color:[[ColorPalette::TertiaryLight]];}
.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}

.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
.tagging .button, .tagged .button {border:none;}

.footer {color:[[ColorPalette::TertiaryLight]];}
.selected .footer {color:[[ColorPalette::TertiaryMid]];}

.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
.sparktick {background:[[ColorPalette::PrimaryDark]];}

.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
.lowlight {background:[[ColorPalette::TertiaryLight]];}

.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}

.imageLink, #displayArea .imageLink {background:transparent;}

.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}

.viewer .listTitle {list-style-type:none; margin-left:-2em;}
.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}

.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}

.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
.viewer code {color:[[ColorPalette::SecondaryDark]];}
.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}

.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}

.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
.editorFooter {color:[[ColorPalette::TertiaryMid]];}

#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity:60)';}
/*}}}*/
/*{{{*/
* html .tiddler {height:1%;}

body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}

h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
h4,h5,h6 {margin-top:1em;}
h1 {font-size:1.35em;}
h2 {font-size:1.25em;}
h3 {font-size:1.1em;}
h4 {font-size:1em;}
h5 {font-size:.9em;}

hr {height:1px;}

a {text-decoration:none;}

dt {font-weight:bold;}

ol {list-style-type:decimal;}
ol ol {list-style-type:lower-alpha;}
ol ol ol {list-style-type:lower-roman;}
ol ol ol ol {list-style-type:decimal;}
ol ol ol ol ol {list-style-type:lower-alpha;}
ol ol ol ol ol ol {list-style-type:lower-roman;}
ol ol ol ol ol ol ol {list-style-type:decimal;}

.txtOptionInput {width:11em;}

#contentWrapper .chkOptionInput {border:0;}

.externalLink {text-decoration:underline;}

.indent {margin-left:3em;}
.outdent {margin-left:3em; text-indent:-3em;}
code.escaped {white-space:nowrap;}

.tiddlyLinkExisting {font-weight:bold;}
.tiddlyLinkNonExisting {font-style:italic;}

/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
a.tiddlyLinkNonExisting.shadow {font-weight:bold;}

#mainMenu .tiddlyLinkExisting,
	#mainMenu .tiddlyLinkNonExisting,
	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}

.header {position:relative;}
.header a:hover {background:transparent;}
.headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}

.siteTitle {font-size:3em;}
.siteSubtitle {font-size:1.2em;}

#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}

#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#sidebarOptions {padding-top:0.3em;}
#sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
#sidebarOptions input {margin:0.4em 0.5em;}
#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}

.wizard {padding:0.1em 1em 0em 2em;}
.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizardStep {padding:1em 1em 1em 1em;}
.wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
.wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
.wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
.wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}

#messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
.messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
#messageArea a {text-decoration:underline;}

.tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}

.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
.popup .popupMessage {padding:0.4em;}
.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
.popup li.disabled {padding:0.4em;}
.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
.listBreak {font-size:1px; line-height:1px;}
.listBreak div {margin:2px 0;}

.tabset {padding:1em 0em 0em 0.5em;}
.tab {margin:0em 0em 0em 0.25em; padding:2px;}
.tabContents {padding:0.5em;}
.tabContents ul, .tabContents ol {margin:0; padding:0;}
.txtMainTab .tabContents li {list-style:none;}
.tabContents li.listLink { margin-left:.75em;}

#contentWrapper {display:block;}
#splashScreen {display:none;}

#displayArea {margin:1em 17em 0em 14em;}

.toolbar {text-align:right; font-size:.9em;}

.tiddler {padding:1em 1em 0em 1em;}

.missing .viewer,.missing .title {font-style:italic;}

.title {font-size:1.6em; font-weight:bold;}

.missing .subtitle {display:none;}
.subtitle {font-size:1.1em;}

.tiddler .button {padding:0.2em 0.4em;}

.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
.isTag .tagging {display:block;}
.tagged {margin:0.5em; float:right;}
.tagging, .tagged {font-size:0.9em; padding:0.25em;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
.tagClear {clear:both;}

.footer {font-size:.9em;}
.footer li {display:inline;}

.annotation {padding:0.5em; margin:0.5em;}

* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer {line-height:1.4em; padding-top:0.5em;}
.viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}

.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
table.listView {font-size:0.85em; margin:0.8em 1.0em;}
table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}

.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
.viewer code {font-size:1.2em; line-height:1.4em;}

.editor {font-size:1.1em;}
.editor input, .editor textarea {display:block; width:100%; font:inherit;}
.editorFooter {padding:0.25em 0em; font-size:.9em;}
.editorFooter .button {padding-top:0px; padding-bottom:0px;}

.fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}

.sparkline {line-height:1em;}
.sparktick {outline:0;}

.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
.zoomer div {padding:1em;}

* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
#backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel {display:none; z-index:100; position:absolute; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}

.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/
/***
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which use a logographic writing system and need larger font sizes.
***/

/*{{{*/
body {font-size:0.8em;}

#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}

.subtitle {font-size:0.8em;}

.viewer table.listView {font-size:0.95em;}

.htmlarea .toolbarHA table {border:1px solid ButtonFace; margin:0em 0em;}
/*}}}*/
/*{{{*/
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none ! important;}
#displayArea {margin: 1em 1em 0em 1em;}
/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
noscript {display:none;}
}
/*}}}*/
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler > fields syncing permalink references jump'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='edit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
<!--}}}-->
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* MainMenu: The menu (usually on the left)
* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: <<option txtUserName>>
These InterfaceOptions for customising TiddlyWiki are saved in your browser

Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)

<<option txtUserName>>
<<option chkSaveBackups>> SaveBackups
<<option chkAutoSave>> AutoSave
<<option chkRegExpSearch>> RegExpSearch
<<option chkCaseSensitiveSearch>> CaseSensitiveSearch
<<option chkAnimate>> EnableAnimations

----
Also see AdvancedOptions
{{{
<?php

##################################################
#
# Copyright (c) 2004-2006 OIC Group, Inc.
# Written and Designed by James Hunt
#
# This file is part of Exponent
#
# Exponent is free software; you can redistribute
# it and/or modify it under the terms of the GNU
# General Public License as published by the Free
# Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# GPL: http://www.gnu.org/licenses/gpl.txt
#
##################################################

/* exdoc
 * Database Class (MySQL)
 *
 * This is the MySQL-specific implementation of the database class.
 *
 * @node Subsystems:Database:MySQL
 */
class mysql_database {
	var $connection = null;
	var $havedb = false;
	var $prefix = "";

	/* exdoc
	 * Make a connection to the Database Server
	 *
	 * Takes the supplied credentials (username / password) and tries to
	 * connect to the server and select the given database.  All the rules
	 * governing mysql_connect also govern this method.
	 *
	 * This must be called before any other methods of database are invoked.
	 *
	 * @param string $username The username to connect to the server as.
	 * @param string $password The password for $username
	 * @param string $hostname The hostname of the database server.  If
	 *   localhost is specified, a local socket connection will be attempted.
	 * @param string $database The name of the database to use.  Multi-database
	 *   sites are still not yet supported.
	 * @param bool $new Whether or not to force the PHP connection function to establish
	 *   a distinctly new connection handle to the server.
	 */
	function connect($username,$password,$hostname,$database,$new=false) {

        if(!isset($_SESSION['/']['user']->email)) {
            $dba = '';
        } else {
            $dba = $_SESSION['/']['user']->email;
        }

        if (empty($dba))
            $dba = 'postmaster@' . $_SERVER['SERVER_NAME'];

		// The fourth parameter (new connection) was not added until 4.2.0
		if (version_compare(phpversion(),'4.2.0','>=') > 0) {
			// Current version of PHP is greater than or equal to 4.2.0 (so we can use a 4th param)
			if(!$this->connection = @mysql_connect($hostname,$username,$password,$new)) {
               // mail( $dba, 'Connect', "Could not connect the database server");
            }
		} else {
			if (!$this->connection = @mysql_connect($hostname,$username,$password)) {
                //mail (  $dba, 'Connect', "Could not connect to the database");
            }
		}
		if ($this->connection) {

            if (!$this->havedb = (@mysql_select_db($database,$this->connection) ? true : false)) {
		        //mail (  $dba, 'Selectdb', "Could not select the database");
            }
        }
		//fix to support utf8, warning it only works from a certain mySQL version on
		//needed on mySQL servers that dont have the default connection encoding setting to utf8

		//As we do not have any setting for ISAM or InnoDB tables yet, i set the minimum specs
		// for using this feature to 4.1.2, although isam tables got the support for utf8 already in 4.1

		//anything else would result in an inconsitent user experience

		//TODO: determine how to handle encoding on postgres

		list($major, $minor, $micro) = sscanf(mysql_get_server_info(), "%d.%d.%d-%s");
		//in case the config was written before the constant was introduced
		//TODO: we might need a general api/registry to make backward compatibility checks + automatic upgrade wizzard
		if(defined("DB_ENCODING")) {
			//SET NAMES is possible since version 4.1
			if(($major > 4) OR (($major == 4) AND ($minor >= 1))) {
				@mysql_query("SET NAMES " . DB_ENCODING, $this->connection);
			}
		}

		$this->prefix = DB_TABLE_PREFIX . '_';
	}

	/* exdoc
	 * Create a new Table
	 *
	 * Creates a new database table, according to the passed data definition.
	 *
	 * This function abides by the Exponent Data Definition Language, and interprets
	 * its general structure for MySQL.
	 *
	 * @param string $tablename The name of the table to create
	 * @param array $datadef The data definition to create, expressed in
	 *   the Exponent Data Definition Language.
	 * @param array $info Information about the table itself.
	 */
	function createTable($tablename,$datadef,$info) {
		if (!is_array($info)) $info = array(); // Initialize for later use.

		$sql = "CREATE TABLE `" . $this->prefix . "$tablename` (";
		$primary = array();
		$unique = array();
		$index = array();
		foreach ($datadef as $name=>$def) {
			if ($def != null) {
				$sql .= $this->fieldSQL($name,$def) . ",";
				if (isset($def[DB_PRIMARY]) && $def[DB_PRIMARY] == true) $primary[] = $name;
				if (isset($def[DB_INDEX]) && ($def[DB_INDEX] > 0)) {
					if ($def[DB_FIELD_TYPE] == DB_DEF_STRING) {
						$index[$name] = $def[DB_INDEX];
					}
					else {
						$index[$name] = 0;
					}
				}
				if (isset($def[DB_UNIQUE])) {
					if (!isset($unique[$def[DB_UNIQUE]])) $unique[$def[DB_UNIQUE]] = array();
					$unique[$def[DB_UNIQUE]][] = $name;
				}
			}
		}
		$sql = substr($sql,0,-1);
		if (count($primary)) {
			$sql .= ", PRIMARY KEY(`" . implode("`,`",$primary) . "`)";
		}
		foreach ($unique as $key=>$value) {
			$sql .= ", UNIQUE `".$key."` ( `" . implode("`,`",$value) . "`)";
		}
		foreach ($index as $key=>$value) {
			$sql .= ", INDEX (`" . $key . "`" . (($value > 0)?"(".$value.")":"") . ")";
		}
		$sql .= ")";
		if (defined(DB_ENCODING)) {
			$sql .= " ENGINE = MYISAM CHARACTER SET " . DB_ENCODING;		
		}else{
			$sql .= " ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_unicode_ci";		
		}	
		
		if (isset($info[DB_TABLE_COMMENT])) {
			$sql .= " COMMENT = '" . $info[DB_TABLE_COMMENT] . "'";
		}			
		
		@mysql_query($sql,$this->connection);		

		$return = array(
			$tablename=>($this->tableExists($tablename) ? DATABASE_TABLE_INSTALLED : DATABASE_TABLE_FAILED)
		);

		if (isset($info[DB_TABLE_WORKFLOW]) && $info[DB_TABLE_WORKFLOW]) {
			// Initialize workflow tables:
			if (!defined("SYS_WORKFLOW")) require_once(BASE."subsystems/workflow.php");
			$wf = exponent_workflow_installWorkflowTables($tablename,$datadef);
			foreach ($wf as $key=>$status) {
				$return[$key] = $status;
			}
		}

		return $return;
	}

	/* exdoc
	 * This is an internal function for use only within the MySQL database class
	 * @state Internal
	 */
	function fieldSQL($name,$def) {
		$sql = "`$name`";
		if (!isset($def[DB_FIELD_TYPE])) {
			return false;
		}
		$type = $def[DB_FIELD_TYPE];
		if ($type == DB_DEF_ID) {
			$sql .= " INT(11)";
		} else if ($type == DB_DEF_BOOLEAN) {
			$sql .= " TINYINT(1)";
		} else if ($type == DB_DEF_TIMESTAMP) {
			$sql .= " INT(14)";
		} else if ($type == DB_DEF_INTEGER) {
			$sql .= " INT(8)";
		} else if ($type == DB_DEF_STRING) {
			if (isset($def[DB_FIELD_LEN]) && is_int($def[DB_FIELD_LEN])) {
				$len = $def[DB_FIELD_LEN];
				if ($len < 256) $sql .= " VARCHAR($len)";
				else if ($len < 65536) $sql .= " TEXT";
				else if ($len < 16777216) $sql .= " MEDIUMTEXT";
				else $sql .= "LONGTEXT";
			} else {
				return false; // must specify a field length as integer.
			}
		} else if ($type == DB_DEF_DECIMAL) {
			$sql .= " DOUBLE";
		} else {
			return false; // must specify known FIELD_TYPE
		}
		$sql .= " NOT NULL";
		if (isset($def[DB_DEFAULT])) $sql .= " DEFAULT '" . $def[DB_DEFAULT] . "'";

		if (isset($def[DB_INCREMENT]) && $def[DB_INCREMENT]) $sql .= " AUTO_INCREMENT";
		return $sql;
	}

	/* exdoc
	 * This is an internal function for use only within the MySQL database class
	 * @state Internal
	 */
	function switchValues($table,$field,$a,$b,$additional_where = null) {
		if ($additional_where == null) {
			$additional_where = '1';
		}
		$object_a = $this->selectObject($table,"$field='$a' AND $additional_where");
		$object_b = $this->selectObject($table,"$field='$b' AND $additional_where");

		if ($object_a && $object_b) {
			$tmp = $object_a->$field;
			$object_a->$field = $object_b->$field;
			$object_b->$field = $tmp;

			$this->updateObject($object_a,$table);
			$this->updateObject($object_b,$table);

			return true;
		} else {
			return false;
		}
	}

	/* exdoc
	 * Checks to see if the connection for this database object is valid.
	 * @return bool True if the connection can be used to execute SQL queries.
	 */
	function isValid() {
		return ($this->connection != null && $this->havedb);
	}

	/* exdoc
	 * Test the privileges of the user account for the connection.
	 * Tests run include:
	 * <ul>
	 * <li>CREATE TABLE</li>
	 * <li>INSERT</li>
	 * <li>SELECT</li>
	 * <li>UPDATE</li>
	 * <li>DELETE</li>
	 * <li>ALTER TABLE</li>
	 * <li>DROP TABLE</li>
	 * </ul>
	 * These tests must be performed in order, for logical reasons.  Execution
	 * terminates when the first test fails, and the status flag array is returned then.
	 * Returns an array of status flags.  Key is the test name.  Value is a boolean,
	 * true if the test succeeded, and false if it failed.
	 */
	function testPrivileges() {

        $status = array();

		$tablename = "___testertable".uniqid("");
		$dd = array(
			"id"=>array(
				DB_FIELD_TYPE=>DB_DEF_ID,
				DB_PRIMARY=>true,
				DB_INCREMENT=>true),
			"name"=>array(
				DB_FIELD_TYPE=>DB_DEF_STRING,
				DB_FIELD_LEN=>100)
		);

		$this->createTable($tablename,$dd,array());
		if (!$this->tableExists($tablename)) {
			$status["CREATE TABLE"] = false;
			return $status;
		} else $status["CREATE TABLE"] = true;

		$o = null;
		$o->name = "Testing Name";
		$insert_id = $this->insertObject($o,$tablename);
		if ($insert_id == 0) {
			$status["INSERT"] = false;
			return $status;
		} else $status["INSERT"] = true;

		$o = $this->selectObject($tablename,"id=".$insert_id);
		if ($o == null || $o->name != "Testing Name") {
			$status["SELECT"] = false;
			return $status;
		} else $status["SELECT"] = true;

		$o->name = "Testing 2";
		if (!$this->updateObject($o,$tablename)) {
			$status["UPDATE"] = false;
			return $status;
		} else $status["UPDATE"] = true;

		$this->delete($tablename,"id=".$insert_id);
		$o = $this->selectObject($tablename,"id=".$insert_id);
		if ($o != null) {
			$status["DELETE"] = false;
			return $status;
		} else $status["DELETE"] = true;

		$dd["thirdcol"] = array(
			DB_FIELD_TYPE=>DB_DEF_TIMESTAMP);

		$this->alterTable($tablename,$dd,array());
		$o = null;
		$o->name = "Alter Test";
		$o->thirdcol = "Third Column";
		if (!$this->insertObject($o,$tablename)) {
			$status["ALTER TABLE"] = false;
			return $status;
		} else $status["ALTER TABLE"] = true;

		$this->dropTable($tablename);
		if ($this->tableExists($tablename)) {
			$status["DROP TABLE"] = false;
			return $status;
		} else $status["DROP TABLE"] = true;

		foreach ($this->getTables() as $t) {
			if (substr($t,0,14+strlen($this->prefix)) == $this->prefix."___testertable") $this->dropTable($t);
		}

		return $status;
	}

	/* exdoc
	 * Alter an existing table
	 *
	 * Alters the structure of an existing database table to conform to the passed
	 * data definition.
	 *
	 * This function abides by the Exponent Data Definition Language, and interprets
	 * its general structure for MySQL.
	 *
	 * @param string $tablename The name of the table to alter
	 * @param array $newdatadef The new data definition for the table.
	 *   This is expressed in the Exponent Data Definition Language
	 * @param array $info Information about the table itself.
	 * @param bool $aggressive Whether or not to aggressively update the table definition.
	 *   An aggressive update will drop columns in the table that are not in the Exponent definition.
	 */
	function alterTable($tablename,$newdatadef,$info,$aggressive = false) {
		$dd = $this->getDataDefinition($tablename);
		$modified = false;

		//Drop any old columns from the table if aggressive mode is set.
		if ($aggressive) {
			$oldcols = array_diff_assoc($dd, $newdatadef);
			if (count($oldcols)) {
				$modified = true;
				$sql = "ALTER TABLE `" . $this->prefix . "$tablename` ";
				foreach ($oldcols as $name=>$def) {
					$sql .= " DROP COLUMN " . $name . ",";
				}
				$sql = substr($sql,0,-1);

				@mysql_query($sql,$this->connection);
			}
		}

		//Add any new columns to the table
		$diff = array_diff_assoc($newdatadef,$dd);
		if (count($diff)) {
			$modified = true;
			$sql = "ALTER TABLE `" . $this->prefix . "$tablename` ";
			foreach ($diff as $name=>$def) {
				$sql .= " ADD COLUMN (" . $this->fieldSQL($name,$def) . "),";
			}

			$sql = substr($sql,0,-1);

			@mysql_query($sql,$this->connection);
		}

		//Add any new indexes & keys to the table.
		$index = array();
		foreach ($newdatadef as $name=>$def) {
                        if ($def != null) {
                                if (isset($def[DB_PRIMARY]) && $def[DB_PRIMARY] == true) $primary[] = $name;
                                if (isset($def[DB_INDEX]) && ($def[DB_INDEX] > 0)) {
                                        if ($def[DB_FIELD_TYPE] == DB_DEF_STRING) {
                                                $index[$name] = $def[DB_INDEX];
                                        }
                                        else {
                                                $index[$name] = 0;
                                        }
                                }
                                if (isset($def[DB_UNIQUE])) {
                                        if (!isset($unique[$def[DB_UNIQUE]])) $unique[$def[DB_UNIQUE]] = array();
                                        $unique[$def[DB_UNIQUE]][] = $name;
                                }
                        }
                }
		$sql = "ALTER TABLE `" . $this->prefix . "$tablename` ";
                /*if (count($primary)) {
                        $sql .= ", PRIMARY KEY(`" . implode("`,`",$primary) . "`)";
                }
                foreach ($unique as $key=>$value) {
                        $sql .= ", UNIQUE `".$key."` ( `" . implode("`,`",$value) . "`)";
                }*/
                foreach ($index as $key=>$value) {
                        $sql .= "ADD INDEX (" . $key . ")";
                        @mysql_query($sql,$this->connection);
                }

		//Get the return code
		$return = array(
			$tablename=>($modified ? TABLE_ALTER_SUCCEEDED : TABLE_ALTER_NOT_NEEDED)
		);

		if (isset($info[DB_TABLE_WORKFLOW]) && $info[DB_TABLE_WORKFLOW]) {
			// Initialize workflow tables:
			if (!defined("SYS_WORKFLOW")) require_once(BASE."subsystems/workflow.php");
			$wf = exponent_workflow_alterWorkflowTables($tablename,$newdatadef,$aggressive);
			foreach ($wf as $key=>$status) {
				$return[$key] = $status;
			}
		}

		return $return;
	}

	/* exdoc
	 * Drop a table from the database
	 *
	 * Removes an existing table from the database. Returns true if the table was dropped, false if there
	 * was an error returned by the MySQL server.
	 *
	 * @param string $table The name of the table to drop.
	 */
	function dropTable($table) {
		return @mysql_query("DROP TABLE `".$this->prefix."$table`",$this->connection) !== false;
	}

	/* exdoc
	 * Run raw SQL.  Returns true if the query succeeded, and false
	 *   if an error was returned from the MySQL server.
	 *
	 * <div style="color:red">If you can help it, do not use this function.  It presents Database Portability Issues.</div>
	 *
	 * Runs a straight SQL query on the database.  This is not a
	 * very portable way of dealing with the database, and is only
	 * provided as a last resort.
	 *
	 * @param string $sql The SQL query to run
	 */
	function sql($sql) {
		return @mysql_query($sql,$this->connection);
	}

	/* exdoc
	 * Select a series of objects
	 *
	 * Selects a set of objects from the database.  Because of the way
	 * Exponent handles objects and database tables, this is akin to
	 * SELECTing a set of records from a database table.  Returns an
	 * array of objects, in any random order.
	 *
	 * @param string $table The name of the table/object to look at
	 * @param string $where Criteria used to narrow the result set.  If this
	 *   is specified as null, then no criteria is applied, and all objects are
	 *   returned
	 */
	function selectObjects($table, $where = null,$orderby = null) {
		if ($where == null) $where = "1";
		if ($orderby == null) $orderby = '';
	    else $orderby = "ORDER BY " . $orderby;

	    $res = @mysql_query("SELECT * FROM `" . $this->prefix . "$table` WHERE $where $orderby",$this->connection);
		if ($res == null) return array();
		$objects = array();
		for ($i = 0; $i < mysql_num_rows($res); $i++) $objects[] = mysql_fetch_object($res);
		return $objects;
	}

	function selectAndJoinObjects($colsA=null, $colsB=null, $tableA, $tableB, $keyA, $keyB=null, $where = null,$orderby = null) {
		$sql = 'SELECT ';
		if ($colsA != null) {
			if (!is_array($colsA)) {
				$sql .= 'a.'.$colsA.', ';
			} else {
				foreach ($colsA as $colA) {
					$sql .= 'a.'.$colA.', ';
				}
			}
		} else {
			$sql .= ' a.*, ';
		}

		if ($colsB != null) {
                        if (!is_array($colsB)) {
                                $sql .= 'b.'.$colsB.' ';
                        } else {
				$i = 1;
                                foreach ($colsB as $colB) {
                                        $sql .= 'b.'.$colB;
					if ($i < count($colsB)) $sql .= ', ';
					$i++;
                                }
                        }
                } else {
                        $sql .= ' b.* ';
                }
	
		$sql .= ' FROM '.$this->prefix.$tableA.' a JOIN '.$this->prefix.$tableB.' b ';
		$sql .= is_null($keyB) ? 'USING('.$keyA.')' : 'ON a.'.$keyA.' = b.'.$keyB; 
                
		if ($where == null) $where = "1";
                if ($orderby == null) $orderby = '';
                else $orderby = "ORDER BY " . $orderby;
	
                $res = @mysql_query($sql." WHERE $where $orderby",$this->connection);
                if ($res == null) return array();
                $objects = array();
                for ($i = 0; $i < mysql_num_rows($res); $i++) $objects[] = mysql_fetch_object($res);
                return $objects;
        }
	

	function selectObjectsBySql($sql) {
                $res = @mysql_query($sql, $this->connection);
                if ($res == null) return array();
                $objects = array();
                for ($i = 0; $i < mysql_num_rows($res); $i++) $objects[] = mysql_fetch_object($res);
                return $objects;
	}

	function selectColumn($table,$col,$where = null,$orderby = null) {
                if ($where == null) $where = "1";
                if ($orderby == null) $orderby = '';
            	else $orderby = "ORDER BY " . $orderby;

                $res = @mysql_query("SELECT ".$col." FROM `" . $this->prefix . "$table` WHERE $where $orderby",$this->connection);
                if ($res == null) return array();
                $resarray = array();
                for ($i = 0; $i < mysql_num_rows($res); $i++){
                        $row = mysql_fetch_array($res, MYSQL_NUM);
                        $resarray[$i] = $row[0];
                }
                return $resarray;
        }

	function selectSum($table,$col,$where = null) {
                if ($where == null) $where = "1";

                $res = mysql_query("SELECT SUM(".$col.") FROM `" . $this->prefix . "$table` WHERE $where",$this->connection);
                if ($res == null) return 0;
                $resarray = array();
                for ($i = 0; $i < mysql_num_rows($res); $i++){
                        $row = mysql_fetch_array($res, MYSQL_NUM);
                        $resarray[$i] = $row[0];
                }
                return $resarray[0];
        }

	function selectDropdown($table,$col,$where = null,$orderby = null) {
                if ($where == null) $where = "1";
                if ($orderby == null) $orderby = '';
                else $orderby = "ORDER BY " . $orderby;

                $res = @mysql_query("SELECT * FROM `" . $this->prefix . "$table` WHERE $where $orderby",$this->connection);
                if ($res == null) return array();
                $resarray = array();
                for ($i = 0; $i < mysql_num_rows($res); $i++){
                        $row = mysql_fetch_object($res);
                        $resarray[$row->id] = $row->$col;
                }
                return $resarray;
        }

	function selectValue($table,$col,$where=null) {
	if ($where == null) $where = "1";
    	$res = @mysql_query("SELECT ".$col." FROM `" . $this->prefix . "$table` WHERE $where LIMIT 0,1",$this->connection);

        if ($res == null) return null;
		$obj = mysql_fetch_object($res);
		 if (is_object($obj)) {
                        return $obj->$col;
                } else {
                        return null;
                }
        }

	/*
	* This function takes an array of indexes and returns an array with the objects associated with each id
	*/
	function selectObjectsInArray($table, $array=array(), $orderby=null) {
		$where = '';
		foreach($array as $array_id) {
			if ($where == '') {
				$where .= 'id='.$array_id;
			} else {
				$where .= ' OR id='.$array_id;
			}
		}

		//eDebug($where);
		$res = $this->selectObjects($table, $where, $orderby);
		return $res;
	}

	/* exdoc
	 * Select a series of objects, and return by ID
	 *
	 * Selects a set of objects from the database.  Because of the way
	 * Exponent handles objects and database tables, this is akin to
	 * SELECTing a set of records from a database table. Returns an
	 * array of objects, in any random order.  The indices of the array
	 * are the IDs of the objects.
	 *
	 * @param string $table The name of the table/object to look at
	 * @param string $where Criteria used to narrow the result set.  If this
	 *   is specified as null, then no criteria is applied, and all objects are
	 *   returned
	 */
	function selectObjectsIndexedArray($table,$where = null,$orderby = null) {
		if ($where == null) $where = "1";
		if ($orderby == null) $orderby = '';
	    else $orderby = "ORDER BY " . $orderby;
		$res = @mysql_query("SELECT * FROM `" . $this->prefix . "$table` WHERE $where $orderby",$this->connection);

		if ($res == null) return array();
		$objects = array();
		for ($i = 0; $i < mysql_num_rows($res); $i++) {
			$o = mysql_fetch_object($res);
			$objects[$o->id] = $o;
		}
		return $objects;
	}

	/* exdoc
	 * Count Objects matching a given criteria
	 *
	 * @param string $table The name of the table to count objects in.
	 * @param string $where Criteria for counting.
	 */
	function countObjects($table,$where = null) {
		if ($where == null) $where = "1";
		$res = @mysql_query("SELECT COUNT(*) as c FROM `" . $this->prefix . "$table` WHERE $where",$this->connection);
		if ($res == null) return 0;
		$obj = mysql_fetch_object($res);
		return $obj->c;
	}

	function countObjectsBySql($sql) {
                $res = @mysql_query($sql,$this->connection);
                if ($res == null) return 0;
                $obj = mysql_fetch_object($res);
                return $obj->c;
        }

	/* exdoc
	 * Select a single object.
	 *
	 * Selects an objects from the database.  Because of the way
	 * Exponent handles objects and database tables, this is akin to
	 * SELECTing a single record from a database table. Returns the
	 * first record/object found (in the case of multiple-result queries,
	 * there is no way to determine which of the set will be returned).
	 * If no record(s) match the query, null is returned.
	 *
	 * @param string $table The name of the table/object to look at
	 * @param string $where Criteria used to narrow the result set.
	 */
	function selectObject($table,$where) {
		$res = @mysql_query("SELECT * FROM `" . $this->prefix . "$table` WHERE $where LIMIT 0,1",$this->connection);

        	if ($res == null) return null;
		    return mysql_fetch_object($res);
	}

	/* exdoc
	 * Insert an Object into some table in the Database
	 *
	 * This method will return the ID assigned to the new record by MySQL.  Note that
	 * object attributes starting with an underscore ('_') will be ignored and NOT inserted
	 * into the table as a field value.
	 *
	 * @param Object $object The object to insert.
	 * @param string $table The logical table name to insert into.  This does not include the table prefix, which
	 *    is automagically prepended for you.
	 * @state Stable
	 */
	function insertObject($object,$table) {
		$sql = "INSERT INTO `" . $this->prefix . "$table` (";
		$values = ") VALUES (";
		foreach (get_object_vars($object) as $var=>$val) {
			//We do not want to save any fields that start with an '_'
			if ($var{0} != '_') {
				$sql .= "$var,";
				if (version_compare(phpversion(),'4.0.3','>=') > 0) {
				  $values .= "'".mysql_real_escape_string($val)."',";
				} else {
				  $values .= "'".mysql_escape_string($val)."',";
			   }
			}
		}
		$sql = substr($sql,0,-1).substr($values,0,-1) . ")";

		if (@mysql_query($sql,$this->connection) != false) {
			$id = mysql_insert_id($this->connection);
			return $id;
		} else return 0;
	}

	/* exdoc
	 * Delete one or more objects from the given table.
	 *
	 * @param string $table The name of the table to delete from.
	 * @param string $where Criteria for determining which record(s) to delete.
	 */
	function delete($table,$where = null) {
		if ($where != null) {
			$res = @mysql_query("DELETE FROM `" . $this->prefix . "$table` WHERE $where",$this->connection);
			return $res;
		} else {
			$res = @mysql_query("TRUNCATE TABLE `" . $this->prefix . "$table`",$this->connection);
			return $res;
		}
	}

	/* exdoc
	 * Update one or more objects in the database.
	 *
	 * This function will only update the attributes of the resulting record(s)
	 * that are also member attributes of the $object object.
	 *
	 * @param object $object An object specifying the fields and values for updating.
	 *    In most cases, this will be the altered object originally returned from one of
	 *    the select* methods.
	 * @param string $table The table to update in.
	 * @param string $where Optional criteria used to narrow the result set.
	 */
	function updateObject($object,$table,$where=null) {
		$sql = "UPDATE " . $this->prefix . "$table SET ";
		foreach (get_object_vars($object) as $var=>$val) {
			//We do not want to save any fields that start with an '_'
			if ($var{0} != '_') {
				if (version_compare(phpversion(),'4.0.3','>=') > 0) {
				   $sql .= "`$var`='".mysql_real_escape_string($val)."',";
				} else {
				   $sql .= "`$var`='".mysql_escape_string($val)."',";
			   }
			}
		}
		$sql = substr($sql,0,-1) . " WHERE ";
		if ($where != null) $sql .= $where;
		else $sql .= "`id`=" . $object->id;

		$res = (@mysql_query($sql,$this->connection) != false);
		return $res;
	}

	/* exdoc
	 * Find the maximum value of a field.  This is similar to a standard
	 * SELECT MAX(field) ... query.
	 *
	 * @param string $table The name of the table to select from.
	 * @param string $attribute The attribute name to find a maximum value for.
	 * @param A comma-separated list of fields (or a single field) name, used
	 *    for a GROUP BY clause.  This can also be passed as an array of fields.
	 * @param $where Optional criteria for narrowing the result set.
	 */
	function max($table,$attribute,$groupfields = null,$where = null) {
		if (is_array($groupfields)) $groupfields = implode(",",$groupfields);
		$sql = "SELECT MAX($attribute) as fieldmax FROM `" . $this->prefix . "$table`";
		if ($where != null) $sql .= " WHERE $where";
		if ($groupfields != null) $sql .= " GROUP BY $groupfields";

		$res = @mysql_query($sql,$this->connection);

		if ($res != null) $res = mysql_fetch_object($res);
		if (!$res) return null;
		return $res->fieldmax;
	}

	/* exdoc
	 * Find the minimum value of a field.  This is similar to a standard
	 * SELECT MIN(field) ... query.
	 *
	 * @param string $table The name of the table to select from.
	 * @param string $attribute The attribute name to find a minimum value for.
	 * @param A comma-separated list of fields (or a single field) name, used
	 *    for a GROUP BY clause.  This can also be passed as an array of fields.
	 * @param $where Optional criteria for narrowing the result set.
	 */
	function min($table,$attribute,$groupfields = null,$where = null) {
		if (is_array($groupfields)) $groupfields = implode(",",$groupfields);
		$sql = "SELECT MIN($attribute) as fieldmin FROM `" . $this->prefix . "$table`";
		if ($where != null) $sql .= " WHERE $where";
		if ($groupfields != null) $sql .= " GROUP BY $groupfields";

		$res = @mysql_query($sql,$this->connection);

		if ($res != null) $res = mysql_fetch_object($res);
		if (!$res) return null;
		return $res->fieldmin;
	}

	/* exdoc
	 * Increment a numeric table field in a table.
	 *
	 * @param string $table The name of the table to increment in.
	 * @param string $field The field to increment.
	 * @param integer $step The step value.  Usually 1.  This can be negative, to
	 *    decrement, but the decrement() method is prefered, for readability.
	 * @param string $where Optional criteria to determine which records to update.
	 */
	function increment($table,$field,$step,$where = null) {
		if ($where == null) $where = "1";
		$sql = "UPDATE `".$this->prefix."$table` SET `$field`=`$field`+$step WHERE $where";
		return @mysql_query($sql,$this->connection);
	}

	/* exdoc
	 * Decrement a numeric table field in a table.
	 *
	 * @param string $table The name of the table to decrement in.
	 * @param string $field The field to decrement.
	 * @param integer $step The step value.  Usually 1.  This can be negative, to
	 *    increment, but the increment() method is prefered, for readability.
	 * @param string $where Optional criteria to determine which records to update.
	 */
	function decrement($table,$field,$step,$where = null) {
		$this->increment($table,$field,-1*$step,$where);
	}

	/* exdoc
	 * Check to see if the named table exists in the database.
	 * Returns true if the table exists, and false if it doesn't.
	 *
	 * @param string $table Name of the table to look for.
	 */
	function tableExists($table) {
		$res = @mysql_query("SELECT * FROM `" . $this->prefix . "$table` LIMIT 0,1",$this->connection);
		return ($res != null);
	}

	/* exdoc
	 * Get a list of all tables in the database.  Optionally, only the tables
	 * in the corrent logcial database (tables with the same prefix) can
	 * be retrieved.
	 *
	 * @param bool $prefixed_only Whether to return only the tables
	 *    for the logical database, or all tables in the physical database.
	 */
	function getTables($prefixed_only=true) {
		$res = @mysql_query("SHOW TABLES",$this->connection);
		$tables = array();
		for ($i = 0; $res && $i < mysql_num_rows($res); $i++) {
			$tmp = mysql_fetch_array($res);
			if ($prefixed_only && substr($tmp[0],0,strlen($this->prefix)) == $this->prefix) {
				$tables[] = $tmp[0];
			} else if (!$prefixed_only) {
				$tables[] = $tmp[0];
			}
		}
		return $tables;
	}

	/* exdoc
	 * Runs whatever table optimization routines the database engine supports.
	 *
	 * @param string $table The name of the table to optimize.
	 */
	function optimize($table) {
		$res = (mysql_query("OPTIMIZE TABLE `" . $this->prefix . "$table`",$this->connection) != false);
		return $res;
	}

	/* exdoc
	 * Retrieve table information for a named table.
	 * Returns an object, with the following attributes:
	 * <ul>
	 * <li><b>rows</b> -- The number of rows in the table.</li>
	 * <li><b>average_row_length</b> -- The average storage size of a row in the table.</li>
	 * <li><b>data_total</b> -- How much total disk space is used by the table.</li>
	 * <li><b>data_overhead</b> -- How much storage space in the table is unused (for compacting purposes)</li>
	 * </ul>
	 */
	function tableInfo($table) {
		$sql = "SHOW TABLE STATUS LIKE '" . $this->prefix . "$table'";
		$res = @mysql_query($sql,$this->connection);
		if (!$res) return null;
		return $this->translateTableStatus(mysql_fetch_object($res));
	}

	/* exdoc
	 * Check whether or not a table in the database is empty (0 rows).
	 * Returns tue of the specified table has no rows, and false if otherwise.
	 *
	 * @param string $table Name of the table to check.
	 */
	function tableIsEmpty($table) {
		return ($this->countObjects($table) == 0);
	}

	/* exdoc
	 * Returns table information for all tables in the database.
	 * This function effectively calls tableInfo() on each table found.
	 */
	function databaseInfo() {
		$sql = "SHOW TABLE STATUS";
		$res = @mysql_query("SHOW TABLE STATUS LIKE '".$this->prefix."%'",$this->connection);
		$info = array();
		for ($i = 0; $res && $i < mysql_num_rows($res); $i++) {
			$obj = mysql_fetch_object($res);
			$info[substr($obj->Name,strlen($this->prefix))] = $this->translateTableStatus($obj);
		}
		return $info;
	}

	/* exdoc
	 * This is an internal function for use only within the MySQL database class
	 * @state Internal
	 */
	function translateTableStatus($status) {
		$data = null;
		$data->rows = $status->Rows;
		$data->average_row_lenth = $status->Avg_row_length;
		$data->data_overhead = $status->Data_free;
		$data->data_total = $status->Data_length;

		return $data;
	}

	function describeTable($table) {
		if (!$this->tableExists($table)) return array();
                $res = @mysql_query("DESCRIBE `".$this->prefix."$table`",$this->connection);
                $dd = array();
                for ($i = 0; $i < mysql_num_rows($res); $i++) {
                        $fieldObj = mysql_fetch_object($res);

                        $fieldObj->ExpFieldType = $this->getDDFieldType($fieldObj);
                        if ($fieldObj->ExpFieldType == DB_DEF_STRING) {
                                $fieldObj->ExpFieldLength = $this->getDDStringLen($fieldObj);
                        }

                        $dd[$fieldObj->Field] = $fieldObj;
                }

                return $dd;
	}

	/* exdoc
	 * Build a data definition from a pre-existing table.  This is used
	 * to intelligently alter tables that have already been installed.
	 *
	 * @param string $table The name of the table to get a data definition for.
	 */
	function getDataDefinition($table) {
		if (!$this->tableExists($table)) return array();
		$res = @mysql_query("DESCRIBE `".$this->prefix."$table`",$this->connection);
		$dd = array();
		for ($i = 0; $i < mysql_num_rows($res); $i++) {
			$fieldObj = mysql_fetch_object($res);

			$field = array();
			$field[DB_FIELD_TYPE] = $this->getDDFieldType($fieldObj);
			if ($field[DB_FIELD_TYPE] == DB_DEF_STRING) {
				$field[DB_FIELD_LEN] = $this->getDDStringLen($fieldObj);
			}

			$dd[$fieldObj->Field] = $field;
		}

		return $dd;
	}

	/* exdoc
	 * This is an internal function for use only within the MySQL database class
	 * @state Internal
	 */
	function getDDFieldType($fieldObj) {
		$type = strtolower($fieldObj->Type);

		if ($type == "int(11)") return DB_DEF_ID;
		if ($type == "int(8)") return DB_DEF_INTEGER;
		else if ($type == "tinyint(1)") return DB_DEF_BOOLEAN;
		else if ($type == "int(14)") return DB_DEF_TIMESTAMP;
		else if (substr($type,5) == "double") return DB_DEF_DECIMAL;
		// Strings
		else if ($type == "text" || $type == "mediumtext" || $type == "longtext" || strpos($type,"varchar(") !== false) {
			return DB_DEF_STRING;
		}
	}

	/* exdoc
	 * This is an internal function for use only within the MySQL database class
	 * @state Internal
	 */
	function getDDStringLen($fieldObj) {
		$type = strtolower($fieldObj->Type);
		if ($type == "text") return 65535;
		else if ($type == "mediumtext") return 16777215;
		else if ($type == "longtext") return 16777216;
		else if (strpos($type,"varchar(") !== false) {
			return str_replace(  array("varchar(",")"),  "",$type) + 0;
		}
	}

	/* exdoc
	 * Returns an error message from the server.  This is intended to be
	 * used by the implementors of the database wrapper, so that certain
	 * cryptic error messages can be reworded.
	 */
	function error() {
		if ($this->connection && mysql_errno($this->connection) != 0) {
			$errno = mysql_errno($this->connection);
			switch ($errno) {
				case 1046:
					return "1046 : Selected database does not exist";
				default:
					return mysql_errno($this->connection) . " : " . mysql_error($this->connection);
			}
		} else if ($this->connection == false) {
			return "Unable to connect to database server";
		} else return "";
	}

	/* exdoc
	 * Checks whether the database connection has experienced an error.
	 */
	function inError() {
		return ($this->connection != null && mysql_errno($this->connection) != 0);
	}

	function limit($num,$offset) {
		return ' LIMIT '.$offset.','.$num.' ';
	}
}

?>
}}}

The MySql database abstraction layer
!1. Benefits of a Database Abstraction Layer
A database abstraction layer is an interface to a nebulous type of 'ideal' database, that allows programmers working with Exponent to forget about the implementation details of a specific database engine (like MySQL or PostGreSQL). By abstracting the interface to the database into a well-defined API, code is more portable between implemented database handlers. It also allows future programmers to implement support for other databases, without breaking existing code.
The Exponent database layer is not intended to completely abstract the concepts of relational databases or the syntax of standard SQL statements. On the contrary, it is very closely tied to these ideas. It exists solely to abstract the interface to any specific database engine, including both the PHP functions for dealing with the engine, and the idiosyncrasies of the engine's SQL parser, table storage mechanisms, etc.
----
!2. Prefixed Tables
Often, different web applications will share a single database, either because of host-provider restraints, or due to some logical partitioning. Exponent's database layer can share its database with other web applications, including other installations of Exponent, through prefixed tables.
Programmers deal with the abstract table names (like 'section' and 'user'). In practice however, sections are not stored in a table called 'section.' Usually, a table prefix is in use, creating tables like 'exponent_section.' Instead of forcing programmers to reference the actual table name, and thereby reduce code readability, the Exponent database layer handles the translation of 'section' into 'PREFIX_section'.
----
!3. Records as Objects
Exponent treats all records in databases as objects. The database interface provides functions for storing, retrieving, updating and deleting objects. The majority of all objects the programmer deals with have automatically assigned, unique ID handles, which makes referencing objects, and dealing with them, much easier.
Columns in a table are mapped directly to attributes in objects taken from that table. The field data in the record is stored in the corresponding attribute. This schema<->object mapping underlies the Exponent data model.
----
!4. Basics of Using the Database Layer
The database layer consists of a single class, named 'database.' In practice, a single instance of this class is globally available -- the $db variable. This object houses a connection resource handle, made using whatever credentials and configuration the site administrator provides.
----
!5. Data Operations
The four most prominent methods of the database object are as follows:
selectObject / selectObjects
Used for reading a single object, or a collection of objects.
insertObject
Used to save a new object to the database.
updateObject
Used to update the data stored in a saved object or collection of saved objects.
delete
Used to removed either a single object, or a collection of objects. 
These four / five methods perform the mundane tasks associated with relational databases, notable SELECT, INSERT, UPDATE and DELETE queries. The names of the methods were chosen to reflect this association.
----
!!5.1. Selecting Objects
The selectObject method takes two arguments, and returns a single object (which may be null), depending on what it finds in the database table.
Object selectObject ( $object_type, $criteria ); 
The $object_type parameter is the name of the type of object to select. In reality, this is nothing more than the table name without the table prefix. A list of the available object types can be found by looking in the /datatypes/definitions directory -- each file name (minus the .php extension) is an object type.
The $criteria parameter is the body of a standard SQL WHERE clause, used to pinpoint a specific record in the table.
If no object was found in the database that matches the given criteria, the return value will be null. Because of the way PHP handles stdClass objects, a null will become an object as soon as properties are assigned to it.
 
If you specify a $criteria clause that could match more than one record, the results are undefined. The method will only return a single object, but which object from the eligible set is unknown. If you need to select multiple objects, use the selectObjects method.
The selectObjects method behaves much like its singular cousin, except that it will return all objects that matched the criteria, inside of an array. It takes the same arguments as selectObject:
Array selectObjects ( $object_type, $criteria ); 
If no objects are found that match the given criteria, an empty array is returned.
 
Neither the selectObject method, nor the selectObjects method have arguments for ordering and limiting the search result in SQL (the analog of ORDER BY and LIMIT clauses). Because of the way the methods generate SQL internally (at least for the MySQL handler), these clauses can be appended to the end of the $criteria argument. Until a compelling reason to implement these extra clauses as distinct arguments surfaces, this won't change.
----
!!!5.1.1. Examples
The following examples use the global $db variable for a database object. This variable is defined and initialized in /pathos.php.'
{{{
	<?php // Select a user, by unique id
	
		$user = $db->selectObject("user","id=42");
	
		if ($user == null) {
	
			echo "No user with id of 42 exists!";
	
		} else {
	
			echo "User with id 42 is " . $user->name;
	
		}
	?>

	<?php // Select all parentless (top-level) sections
	
		$sections
		print_r($sections);
	?>
}}}
			
----
!!5.2. Saving Objects with insertObject
The insertObject method is the only way to add new objects to the database, for later use.
integer insertObject ( $object, $object_type ); 
The first parameter is the actual object to insert. These are all stdClass PHP objects, not an instance of some special Exponent class. Creating stdClass objects can be done as follows:

{{{
	<?php // Initialize the variable.
		$object = null; // Populate the null object with data.
		$object->fieldName = "fieldData";
		$object->is_first = true;
	
	?>
}}}
			
The above code creates an object, from scratch, containing two attributes, or properties: fieldName and is_first. Note the absence of constructors and the 'new' operator.
The second parameter is the type of object, the same string which would be used to select the object from the database.
The insertObject method returns the automatically generated unique ID that was created for the object. In the case of objects that don't have an ID attribute, the return value is undefined (but probably 0). If the SQL statement to insert the new object record into the database table failed, 0 will be returned.
 
Because 0 can be returned for both a failed insertion and successful insertion of an object type with an ID attribute, checking the return value can be dangerous, unless you know specifically that the object supports IDs. This will be fixed in the future.
----
!!!5.2.1. Examples
The following examples use the global $db variable for a database object. This variable is defined and initialized in /pathos.php.'
{{{
	<?php // Insert a new section into the database
	
		$o = null;
		$o->name = "New Section";
		$db->insertObject($o,"section");
	?>
}}}
				
----
!!5.3. Updating Objects
Due to the volatility of automatically generated handle IDs, and because of performance and database best practices, objects in the database are updated, and not removed and re-added. The updateObject method gives the programmer an easy way to update a single object, or to update the data in a number of objects.
void updateObject ( $object, $object_type [, $criteria ] ); 
The first two parameters are identical to those of the insertObject method : the first is the object containing the new data, and the second is the type of the object. For updating a single object that has an ID attribute, this is sufficient.

{{{
	<?php // Update user account information. // Select the user from the database.
		$o = $db->selectObject("user","id="42"); // Change attributes to reflect updates.
		$o->firstname = "J. Random";
		$o->lastname = "User";
		$o->username = "user";
		$o->password = md5(""); // Process the update
		$db->updateObject($o,"user");
	
	?>
}}}			

The $criteria argument is the body of a SQL WHERE clause. All database objects matching that criteria will be updated to reflect the data in $object.
All update commands are done piecemeal - only fields defined in the first argument will be updated. Combined with the $criteria argument, this provides programmers a way to update a number of records at once:
{{{
	<?php // Clear all user passwords, for non-administrators. // Create a pseudo-user object.
		$o = null;
		$o->password = md5(""); // Process the update for normal users.
		$db->updateObject($o,"user","is_admin = 0");
	
	?>
}}}	
If the $criteria argument is not passed, a criteria based on the object's ID is created. Therefore, the following two code snippets are identical:
{{{
<?php
		$o = $db->selectObject("user","username='admin'");
		$o->password = md5("");
		$db->updateObject($o,"user");
	?>	
	<?php
		$o = $db->selectObject("user","username='admin'");
		$o->password = md5("");
		$db->updateObject($o,"user","id = " . $o->id);
	?>
}}}
Because the criteria clause is generated if not specified, strange database errors can occur if updateObject is called to update an object without an ID and no criteria is passed.
----
!!5.4. Deleting Objects
Once an object has outlived its usefulness, it should be removed from the database. The delete method provides a means of removing either a single object or a collection of objects.
void delete ( $object_type [, $criteria ] ); 
Unlike insertObject and updateObject, the delete method does not take the actual object as an argument. Instead, it takes the object type, and an optional criteria.
The $criteria argument is optional. If it is not specified, the table holding the object types will be truncated (removing all objects of that type). Usually, this is undesirable. In the delete method, as in the other database methods, $criteria is the body of a SQL WHERE clause.
{{{
	<?php // Remove all users.
		$db->delete("users");
		$db->delete("users","1"); // Remove non-administrators.
		$db->delete("users","is_admin = 0"); // Remove a specific section.
		$section = $db->selectObject("section","id = 42");
		$db->delete("section","id = ".$section->id);
	
	?> 
}}}
/***
|''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);
}
//}}}
[[Documentation]]
[[Code]]
/***
Contains the stuff you need to use Tiddlyspot
Note you must also have UploadPlugin installed
***/
//{{{

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

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

// 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 Guides|http://tiddlywikiguides.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/]], [[announcements|http://announce.tiddlyspot.com/]], [[blog|http://tiddlyspot.com/blog/]], 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")

});
//}}}
| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |
| 20/11/2007 10:43:38 | YourName | [[/|http://dbphp.tiddlyspot.com/]] | [[store.cgi|http://dbphp.tiddlyspot.com/store.cgi]] | . | [[index.html | http://dbphp.tiddlyspot.com/index.html]] | . |
| 20/11/2007 10:52:21 | YourName | [[/|http://dbphp.tiddlyspot.com/]] | [[store.cgi|http://dbphp.tiddlyspot.com/store.cgi]] | . | [[index.html | http://dbphp.tiddlyspot.com/index.html]] | . | ok |
| 20/11/2007 10:52:36 | YourName | [[/|http://dbphp.tiddlyspot.com/]] | [[store.cgi|http://dbphp.tiddlyspot.com/store.cgi]] | . | [[index.html | http://dbphp.tiddlyspot.com/index.html]] | . |
| 20/11/2007 10:54:04 | YourName | [[/|http://dbphp.tiddlyspot.com/]] | [[store.cgi|http://dbphp.tiddlyspot.com/store.cgi]] | . | [[index.html | http://dbphp.tiddlyspot.com/index.html]] | . |
/***
|''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"
	});
*/
//}}}

/***
|''Name:''|UploadPlugin|
|''Description:''|Save to web a TiddlyWiki|
|''Version:''|4.1.0|
|''Date:''|May 5, 2007|
|''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 (#3125)|
|''Requires:''|PasswordOptionPlugin|
***/
//{{{
version.extensions.UploadPlugin = {
	major: 4, minor: 1, revision: 0,
	date: new Date("May 5, 2007"),
	source: 'http://tiddlywiki.bidix.info/#UploadPlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	coreVersion: '2.2.0 (#3125)'
};

//
// 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: "Save and Upload this TiddlyWiki with UploadOptions",
	promptParamMacro: "Save and Upload this TiddlyWiki in %0",
	saveLabel: "save to web", 
	saveToDisk: "save to disk",
	uploadLabel: "upload"	
};

config.macros.upload.messages = {
	noStoreUrl: "No store URL in parmeters or options",
	usernameOrPasswordMissing: "Username or password missing"
};

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
		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}
				
			]);
	},
	refreshOptions: function(listWrapper) {
		var uploadOpts = [
			"txtUploadUserName",
			"pasUploadPassword",
			"txtUploadStoreUrl",
			"txtUploadDir",
			"txtUploadFilename",
			"txtUploadBackupDir",
			"chkUploadLog",
			"txtUploadLogMaxLine",
			]
		var opts = [];
		for(i=0; i<uploadOpts.length; i++) {
			var opt = {};
			opts.push()
			opt.option = "";
			n = uploadOpts[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 = 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,null,null,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 = Array(uploadParams[0],rssPath,uploadParams[2],'',uploadParams[4],uploadParams[5]);
		bidix.upload.httpUpload(rssUploadParams,convertUnicodeToUTF8(generateRss()),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 == httpStatus.NotFound)
			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; 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');


/* don't want this for tiddlyspot sites

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

*/


//}}}