// ---------------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------------

function main()
{
    // Do the header, menu and sidebar
    refreshAll();
    // Display the StartHere tiddler
    displayTiddler(null,'Start here',1);
}

// ---------------------------------------------------------------------------------
// Tiddler functions
// ---------------------------------------------------------------------------------

// Display a tiddler with animation and scrolling, as though a link to it has been clicked on
//  src = source element object (eg link) for animation effects and positioning
//  title = title of tiddler to display
//  state = 0 is default or current state, 1 is read only and 2 is edittable
function displayTiddler(src,title,state)
{
    // Figure out the tiddler this one must go after
    var after = findContainingTiddler(src);
    
    // Create the tiddler if needed
    var theTiddler = createTiddler(title,state,after);
    // Make it invisible if we got here on an event
    if(src != null)
        theTiddler.style.opacity = 0;
    // Ensure the new tiddler is visible
    ensureVisible(theTiddler);
    // Animate from the target of the event that followed the link
    if(src)
        {
        // Set the text of the floater to match the title
        var floater = document.getElementById("floater");
        var floaterTitle = document.createTextNode(title);
        floater.replaceChild(floaterTitle,floater.firstChild);
        // Animate the floater from the link location to the location of the new tiddler  
        startZoomer(floater,src,theTiddler);
        }
}

// Create a tiddler if it doesn't exist (with no fancy animating). The tiddler is invisible unless it was already visible
//  title = title of tiddler to display
//  state = 0 is default or current state, 1 is read only and 2 is edittable
//  after = optional existing tiddler element to put the new one after
function createTiddler(title,state,after)
{
    // See if the tiddler div is already there
    var theTiddler = document.getElementById("tiddler" + title);
    if(!theTiddler)
        {
        // If it's not there, create the tiddler header
        theTiddler = createTiddlerHeader(title,after);
        // Create the tiddler body appropriately
        if(state != 2)
            createTiddlerBody(theTiddler,title);
        else
            createTiddlerEditor(theTiddler,title);
        }
    else
        {
        // If the tiddler does exist, make sure that it's in the right state
        var theBody = document.getElementById("body" + title);
        var theEditor = document.getElementById("editor" + title);
        // Create and delete as appropriate
        switch (state)
            {
                case 0: // For default state, leave everything alone
                break;
                case 1: // For read-only state, delete any editor
                    if(!theBody)
                        {
                        if(theEditor)
                            theEditor.parentNode.removeChild(theEditor);
                        createTiddlerBody(theTiddler,title);
                        }
                break;
                case 2: // For editor state, delete any read-only body
                    if(!theEditor)
                        {
                        if(theBody)
                            theBody.parentNode.removeChild(theBody);
                        createTiddlerEditor(theTiddler,title);
                        }
                break;
            }
        }
    // Return the completed tiddler
    return(theTiddler);
}

// Create an invisible common header section of a tiddler
//  title = title of tiddler to display
//  after = optional existing tiddler element to put the new one after
function createTiddlerHeader(title,after)
{
    // Create the tiddler div
    theTiddler = createTiddlyElement(null,"div","tiddler",null);
    theTiddler.setAttribute("id","tiddler" + title);
    theTiddler.onmouseover = onMouseOverTiddler;
    theTiddler.onmouseout = onMouseOutTiddler;
    theTiddler.ondblclick = onDblClickTiddler;
    // Get the subtitle
    var subtitle = getTiddlerSubtitle(title);    	
    theTiddler.title = subtitle;
    // Link it in
    var place = document.getElementById("tiddlerDisplay");
    if(after)
        {
        if(after.nextSibling)
            place.insertBefore(theTiddler,after.nextSibling);
        else
            place.appendChild(theTiddler);
        }
    else
        {
        if(place.firstChild)
            place.insertBefore(theTiddler,place.firstChild);
        else
            place.appendChild(theTiddler);
        }
    // Create the anchor
    var theAnchor = createTiddlyElement(theTiddler,"a",null,null);
    theAnchor.setAttribute("name","link" + title);
    // Create the title
    var theTitle = createTiddlyElement(theTiddler,"div","title",title);
    theTitle.setAttribute("id","title" + title);
    // Return the created tiddler
    return(theTiddler);
}

// Create a tiddler toolbar according to whether it's an editor or not
function createTiddlerToolbar(title,editor)
{
    // Delete any existing toolbar
    var theToolbar = document.getElementById("toolbar" + title);
    if(theToolbar)
        theToolbar.parentNode.removeChild(theToolbar);
    // Create the toolbar
    var theTitle = document.getElementById("title" + title);
    var theToolbar = createTiddlyElement(theTitle,"div","toolbar", String.fromCharCode(160));
    theToolbar.setAttribute("id","toolbar" + title);
    // Create each button in turn
    if(!editor)
        {
        // Non-editor toolbar
        createTiddlyButton(theToolbar,
                           "close",
                           "Close this tiddler",
                           onClickToolbarClose);
        theToolbar.appendChild(document.createTextNode(String.fromCharCode(160)));
        createTiddlyButton(theToolbar,
                           "link",
                           "Permalink for this tiddler",
                           onClickToolbarLink);
        theToolbar.appendChild(document.createTextNode(String.fromCharCode(160)));
    
        if (isStandAlone() || isLoggedIn()) {        
	        createTiddlyButton(theToolbar,
	                           "edit",
	                           "Edit this tiddler",
	                           onClickToolbarEdit);
        }
    } else {
        // Editor toolbar
        createTiddlyButton(theToolbar,
                           "done",
                           "Finish changing this tiddler",
                           onClickToolbarSave);
        theToolbar.appendChild(document.createTextNode(String.fromCharCode(160)));
        createTiddlyButton(theToolbar,
                           "undo",
                           "Undo changes to this tiddler",
                           onClickToolbarUndo);        
        theToolbar.appendChild(document.createTextNode(String.fromCharCode(160)));
        
        createTiddlyButton(theToolbar,
                           "delete",
                           "Delete this tiddler",
                           onClickToolbarDelete);
		
        }
}

function isLoggedIn() {
	return typeof userlogin != "undefined";
}

function isStandAlone() {
	return typeof standalone != "undefined";
}

// Create the body section of a read-only tiddler
function createTiddlerBody(place,title)
{
    // Create the toolbar
    createTiddlerToolbar(title,false);
    // Get the body of the tiddler
    var tiddlerText = getTiddlerText(title);
    var tiddlerExists = (tiddlerText != null);
    if(!tiddlerExists)
        tiddlerText = "[This tiddler doesn't yet exist. Double-click to create it]";
    // Create the body
    var theBody = createTiddlyElement(place,"div","body",null);
    theBody.setAttribute("id","body" + title);
    if(!tiddlerExists)
        theBody.style.fontStyle = "italic";
    // Add the body text wikifing the links
    wikify(tiddlerText,theBody);
}

// Create the body section of a read-only tiddler
function createTiddlerEditor(place,title)
{
    // Create the toolbar
    createTiddlerToolbar(title,true);
    // Get the body of the tiddler
    var tiddlerText = getTiddlerText(title);
    var tiddlerExists = (tiddlerText != null);
    if(!tiddlerExists)
        tiddlerText = "Type the text for '" + title + "' here.";
    // Create the editor div
    var theEditor = createTiddlyElement(place,"div","editor",null);
    theEditor.setAttribute("id","editor" + title);
    // Create the title editor
    var theTitle = createTiddlyElement(theEditor,"div",null,null);
    var theTitleBox = createTiddlyElement(theTitle,"input",null,null);
    theTitleBox.setAttribute("id","editorTitle" + title);
    theTitleBox.setAttribute("type","text");
    theTitleBox.value = title;
    theTitleBox.setAttribute("size","40");
    // Do the body
    var theBody = createTiddlyElement(theEditor,"div",null,null);
    var theBodyBox = createTiddlyElement(theBody,"textarea",null,null);
    theBodyBox.value = tiddlerText;
    theBodyBox.setAttribute("id","editorBody" + title);
    theBodyBox.setAttribute("rows","10");
    theBodyBox.setAttribute("cols","50");
}

// Create child text nodes and link elements to represent a wiki-fied version of some text
function wikify(text,parent)
{
	var body = parent;
	// Link patterns		
	var wordBoundary = "(?:\\b|_+)";
	var upperLetter = "[A-Z]";
	var lowerLetter = "[_a-z]";
	var anyLetter = "[A-Za-z_0-9]";
	var urlPattern = "(?:http|https|mailto|ftp):\\S*";
	var breakPattern = "\\n";
	var explicitLinkPattern = "\\[\\[((?:[^\\[\\]\\|])+)\\|([^\\[\\]\\|]+)\\]\\]";
	var explicitTiddlyLinkPattern = "\\[\\[([^\\]]+)\\]\\]";
	var headerPattern = "^!{1,5}";
	var bulletListItemPattern = "^\\*+";
	var numberedListItemPattern = "^#+";	
	var imagePattern = "\\[[iI][mM][gG]\\[(" + urlPattern + ")\\]\\]";
	
	var theList = new Array();	// theList[0]: don't use
	var isInListMode = false;
	var isInHeaderMode = false;
	var isNewline = false;
	
	// Create the RegExp object
	var theRegExp = new RegExp("(" + urlPattern + ")|(" + breakPattern + ")|(?:" + explicitLinkPattern + ")|(?:" + explicitTiddlyLinkPattern +  ")|(" + headerPattern + ")|(" + bulletListItemPattern + ")|(" + numberedListItemPattern + ")|(" + imagePattern + ")","mg");
	// Set the position after the last match
	var lastMatch = 0;
	// Loop through the bits of the body text
	do {
		// Get the next match
		var theMatch = theRegExp.exec(text);
		if(theMatch)
			{
			// If so, dump out any text before the link
			if(theMatch.index > lastMatch)
				{
				isNewline = false;
				body.appendChild(document.createTextNode(text.substring(lastMatch,theMatch.index)));
				}
			lastMatch = theRegExp.lastIndex;
			if(theMatch[1]) // urlPattern
				{
				isNewline = false;
				createExternalLink(body,theMatch[0]);
				}
			else if(theMatch[2]) // breakPattern
				{
				if(isNewline && isInListMode)
					{
					var theList = new Array();
					body = parent;
					isInListMode = false;
					continue;
					}
				if(isInHeaderMode)
					{
					body = parent;
					isInHeaderMode = false;
					continue;
					}
				isNewline = true;
				body.appendChild(document.createElement("br"));
				}
			else if(theMatch[3]) // ExplicitLinkPattern
				{
				isNewline = false;
				createExplicitLink(body,theMatch[3],theMatch[4]);
				}
			else if(theMatch[5]) // ExplicitTiddlyLink
				{
				isNewline = false;
				createTiddlyLink(body,theMatch[5],true);
				}
			else if(theMatch[6])// HeaderPattern
				{
				var level = theMatch[6].length + 1;
				isNewline = false;
				isInHeaderMode = true;
				var theHeader = document.createElement("h" + level);
				body.appendChild(theHeader);
				body = theHeader;
				}
			else if(theMatch[7]) // Bullet-list
				{
				var level = theMatch[7].length;
				isNewline = false;
				isInListMode = true;
				if (theList[level] == null)
					{
					theList[level] = document.createElement("ul");
					body.appendChild(theList[level]);
					}
				theList = theList.slice(0,level + 1);
				body = document.createElement("li");
				theList[level].appendChild(body);
				}
			else if(theMatch[8]) // Numbered list
				{
				var level = theMatch[8].length;
				isNewline = false;
				isInListMode = true;
				if (theList[level] == null)
					{
					theList[level] = document.createElement("ol");
					body.appendChild(theList[level]);
					}
				theList = theList.slice(0,level + 1);
				body = document.createElement("li");
				theList[level].appendChild(body);
				}
			else if(theMatch[9]) // Image
				{
					var image = document.createElement("img");
					image.src = theMatch[10];
					body.appendChild(image);
				}
			}
		else
			{
			// If no match, just dump out the remaining text
			isNewline = false;
			body.appendChild(document.createTextNode(text.substring(lastMatch)));
			}
	} while(theMatch != null);
}

// Create an external link
function createExplicitLink(place,title,url)
{
	var theLink = document.createElement("a");
	theLink.setAttribute("href",url);
	theLink.setAttribute("title","External link to " + url);
	theLink.setAttribute("target","_blank");
	theLink.appendChild(document.createTextNode(title));
	place.appendChild(theLink);
}

function saveTiddler(title)
{
    // Get the title and body text
    var theNewTitle = document.getElementById("editorTitle" + title).value;
    var theNewBody = document.getElementById("editorBody" + title).value;
 	
 	if (!isStandAlone()) {
	 	var newurl = "store.php?title=" + encodeURI(theNewTitle) + "&text=" + encodeURI(theNewBody);
		frames['invisibleframe'].location.href = newurl;
	}

    // Remove any existing entry from the store
    var theExisting = document.getElementById("store" + title);
    if(theExisting)
        theExisting.parentNode.removeChild(theExisting);
    // Create the new entry in the store
    var place = document.getElementById("storeArea");
    var storeItem = createTiddlyElement(place,"div",null,theNewBody);
    storeItem.setAttribute("id","store" + theNewTitle);
    var now = new Date();
    storeItem.setAttribute("modified",ConvertToYYYYMMDDHHMM(now));
    storeItem.setAttribute("modifier","JeremyRuston");
    // Display the new tiddler read-only
    displayTiddler(null,theNewTitle,1,null);
    // Refresh the menu and sidebars to take it into account
    refreshAll();
}

function selectTiddler(title)
{
    // Change the background colour
    var e = document.getElementById("tiddler" + title);
    if(e != null)
        e.className = "tiddlerSelected";
    // Make the toolbar visible
    e = document.getElementById("toolbar" + title);
    if(e != null)
        e.style.visibility = "visible";
}

function deselectTiddler(title)
{
    // Change the background colour
    var e = document.getElementById("tiddler" + title);
    if(e != null)
        e.className = "tiddler";
    // Make the toolbar invisible
    e = document.getElementById("toolbar" + title);
    if(e != null)
        e.style.visibility = "hidden";
}

function deleteTiddler(title)
{

	if (!isStandAlone()) {
		// Send the edited text to the server.
		var newurl = "store.php?title=" + encodeURI(title) + "&action=delete";
		frames['invisibleframe'].location.href = newurl;
	}
	
    // Remove the tiddler from the display
    closeTiddler(title);
    // Delete it from the store
    var tiddler = document.getElementById("store" + title);
    if(tiddler)
        tiddler.parentNode.removeChild(tiddler);
    // Refresh the menu and sidebars to take it into account
    refreshAll();
}

function closeTiddler(title)
{
    var tiddler = document.getElementById("tiddler" + title);
    if(tiddler != null)
        tiddler.parentNode.removeChild(tiddler);
}

function closeAllTiddlers()
{
    // Delete all the elements in the displayArea
    var e = document.getElementById("tiddlerDisplay");
    while(e.firstChild != null)
        e.removeChild(e.firstChild);
}

// ---------------------------------------------------------------------------------
// Tiddler-related utility functions
// ---------------------------------------------------------------------------------

function getTiddlerText(title)
{
        // Attempt to retrieve it from the store
        var tiddlerStore = document.getElementById("store" + title);
        if(tiddlerStore != null)
            return(tiddlerStore.firstChild.nodeValue);
        else
            return(null);
}

function getTiddlerSubtitle(title)
{
    var tiddlerStore = document.getElementById("store" + title);
    if(tiddlerStore != null)
        {
        var theModifier = tiddlerStore.getAttribute("modifier");
        if(!theModifier)
            theModifier = "(unknown)";
        var theModified = tiddlerStore.getAttribute("modified");
        if(theModified)
            theModified = ConvertFromYYYYMMDDHHMM(theModified);
        else
            theModified = "(unknown)";
        return("Modified by " + theModifier + " on " + theModified);
        }
    else
        return(null);
}

function createTiddlyElement(theParent,theElement,theClass,theText)
{
    var e = document.createElement(theElement);
    if(theClass != null)
        e.className = theClass;
    if(theText != null)
        e.appendChild(document.createTextNode(theText));
    if(theParent != null)
        theParent.appendChild(e);
    return(e);
}

function createTiddlyButton(theParent,theText,theTooltip,theAction)
{
    var theButton = document.createElement("a");
    theButton.onclick = theAction;
    theButton.setAttribute("href","JavaScript:;");
    theButton.setAttribute("title",theTooltip);    
    theButton.appendChild(document.createTextNode(theText));    
    theParent.appendChild(theButton);
    return(theButton);
}

// format the text so that it is no longer wikified.
// Debug!
function formatTiddlyText(text) {
	split = text.split(/([A-Z][a-z0-9]+)/);	
	var res;
	for (i = 0 ; i < split.length ; i++) {
		if (split != undefined)
			res += split[i] + " ";		
	}
	
	return res;
}

// Create a link to a tiddler
function createTiddlyLink(place,title,styleIt)
{
    // Figure out if the wiki word exists
    var btn;

    if(getTiddlerText(title) == null)
        {
        // If it does not exist
        btn = createTiddlyButton(place,title,
                           title + " doesn't yet exist",
                           onClickTiddlerLink);
        if(styleIt)
            btn.style.fontStyle = "italic";
        }
    else
        {
        // If it does exist
        btn = createTiddlyButton(place,title,
                           getTiddlerSubtitle(title),
                           onClickTiddlerLink);
        if(styleIt)
            btn.style.fontWeight = "bold";
        }
}

// Create an external link
function createExternalLink(place,url)
{
    var theLink = document.createElement("a");
    theLink.setAttribute("href",url);
    theLink.setAttribute("title","External link to " + url);
    theLink.setAttribute("target","_blank");
    theLink.appendChild(document.createTextNode(url));
    place.appendChild(theLink);
}

// Find the tiddler instance (if any) containing a specified element
function findContainingTiddler(e)
{
    if(e == null)
        return(null);
    do {
        if(e.id)
            if(e.id.substr(0,7) == "tiddler")
                return(e);
        e = e.parentNode;
    } while(e != document);
    return(null);
}

// ---------------------------------------------------------------------------------
// Menu and sidebar functions
// ---------------------------------------------------------------------------------

// Refresh everything
function refreshAll()
{
    refreshHeader();
    refreshMenu();
    refreshSidebar();
}

// Refresh all parts of the header
function refreshHeader()
{
    // Get the site title and subtitle
    var theTitle = getTiddlerText("SiteTitle");
    var theSubtitle = getTiddlerText("SiteSubtitle");
    // Set the page title
    document.title = theTitle + " - " + theSubtitle;
    // Do the title
    var place = document.getElementById("headerTitle");
    while(place.firstChild != null)
        place.removeChild(place.firstChild);
    wikify(theTitle,place);
    // Do the subtitle
    var place = document.getElementById("headerSubtitle");
    while(place.firstChild != null)
        place.removeChild(place.firstChild);
    wikify(theSubtitle,place);
}

// Refresh all parts of the main menu
function refreshMenu()
{
    var place = document.getElementById("leftMenuMain");
    while(place.firstChild != null)
        place.removeChild(place.firstChild);
    var theMenu = document.createElement("div");
    place.appendChild(theMenu);
    wikify(getTiddlerText("MainMenu"),theMenu);
}

// Refresh all parts of the sidebar
function refreshSidebar()
{
    // Get names and dates of all tiddlers from the store
    var allTiddlers = new Array(); // Will be an array of 2-entry arrays, where entry 0 = name, 1 = date
    var storeNodes = document.getElementById("storeArea").childNodes;
    for (var t = 0; t < storeNodes.length; t++)
        {
        var n = storeNodes[t];
        if(n.id)
            if(n.id.substr(0,5) == "store")
                allTiddlers.push(new Array(n.id.substr(5),n.getAttribute("modified")));
        }
    // Sort the tiddlers by name
    allTiddlers.sort(function (a,b) { if(a[0] == b[0]) return(0); else return (a[0] > b[0]) ? +1 : -1; });
    // Delete any existing entries in the 'all' list
    var place = document.getElementById("sidebarAllTiddlers");
    while(place.firstChild != null)
        place.removeChild(place.firstChild);
    // Output the links
    var separator = false;
    for (t = 0; t < allTiddlers.length; t++)
        {
        if(separator)
            place.appendChild(document.createElement("br"));
        createTiddlyLink(place,allTiddlers[t][0],false)
        separator = true;
        }
    // Sort the tiddlers by date
    allTiddlers.sort(function (a,b) { if(a[1] == b[1]) return(1); else return (a[1] < b[1]) ? +1 : -1; });
    // Delete any existing entries in the 'recent' list
    var place = document.getElementById("sidebarRecentTiddlers");
    while(place.firstChild != null)
        place.removeChild(place.firstChild);
    // Output the most recent few as links
    var separator = false;
    var inList = 5;
    if (allTiddlers.length < inList)
        inList = allTiddlers.length;
    for (t = 0; t < inList; t++)
        {
        if(separator)
            place.appendChild(document.createElement("br"));
        createTiddlyLink(place,allTiddlers[t][0],false)
        separator = true;
        }
}

// ---------------------------------------------------------------------------------
// Quine (http://www.google.com/search?q=quine&ie=UTF-8&oe=UTF-8)
// ---------------------------------------------------------------------------------

// Get the text of the TiddlyWiki HTML file itself, incorporating new edits
function ShowSource()
{
    // Create the popup window
    var srcWindow = window.open("","sourceWindow","width=700,height=600");
    var srcDocument = srcWindow.document;
    // Jam in the text template
    srcDocument.write("<html><head></head><body>" +
                      window.document.getElementById("saveMessage").innerHTML +
                      "</body></html>");
    srcDocument.close();
    // Get a reference to the text area
    var theTextBox = srcDocument.getElementById("source");
    // Jam in the current source
    //theTextBox.value = "<html>\n" + window.document.getElementsByTagName("html")[0].innerHTML + "\n</html>";
    theTextBox.value = window.document.getElementById("storeArea").innerHTML; // Optionally, add .replace(/\n+/g, "\n");
    // Select the text in the textbox
    theTextBox.focus();
    theTextBox.select();
;
}

// ---------------------------------------------------------------------------------
// Event handlers
// ---------------------------------------------------------------------------------

// Event handler for clicking on the logo
function onClickLogo(e)
{
	closeAllTiddlers();
	displayTiddler(document.getElementById("headerTitle"),"Start here",1);
}

// Event handler for clicking on a tiddly link
function onClickTiddlerLink(e)
{
	// Get the text of the link
	var title;
	if(this.firstChild)
        title = this.firstChild.nodeValue;
    else if (this.nodeValue)
	   title = this.nodeValue; 
	// Display that tiddler
	if(title)
		displayTiddler(this,title,0);
}

// Event handler for mouse over a tiddler
function onMouseOverTiddler(e)
{
	// Get the name of this tiddler
	var tiddler;
	if(this.id.substr(0,7) == "tiddler")
		tiddler = this.id.substr(7);
	// Select that tiddler
	if(tiddler)
		selectTiddler(tiddler);
}

// Event handler for mouse out of a tiddler
function onMouseOutTiddler(e)
{
	// Get the name of this tiddler
	var tiddler;
	if(this.id.substr(0,7) == "tiddler")
		tiddler = this.id.substr(7);
	// Deselect that tiddler
	if(tiddler)
		deselectTiddler(tiddler);
}

function getMouseX(e) {
	// Explorer
	if (typeof event != "undefined") {
		return event.clientX;
	}
	
	// Decent browsers.
	return e.pageX;
}

function getMouseY(e) {
	// Explorer
	if (typeof event != "undefined") {
		return event.clientY;
	}
	
	// Decent browsers.
	return e.pageY;	
}

// Event handler for double click on a tiddler
function onDblClickTiddler(e)
{	
	if (isStandAlone() || isLoggedIn()) {
	    // Empty the current selection
	    if(document.selection)
	        document.selection.empty();
		// Get the name of this tiddler
		var tiddler;
		if(this.id.substr(0,7) == "tiddler")
			tiddler = this.id.substr(7);
		// Deselect that tiddler
		if(tiddler)
	        displayTiddler(null,tiddler,2);
    } else {    	
    	displayMessage("Please log in to edit this tiddler.", getMouseX(e), getMouseY(e));
   	}
}

// Event handler for clicking on toolbar close
function onClickToolbarClose(e)
{
	// Close that tiddler
	if(this.parentNode.id)
		closeTiddler(this.parentNode.id.substr(7));
}

// Event handler for clicking on toolbar close
function onClickToolbarDelete(e)
{
	// Close that tiddler
	if(this.parentNode.id)
		deleteTiddler(this.parentNode.id.substr(7));
}

// Event handler for clicking on toolbar link
function onClickToolbarLink(e)
{
	// Close all other tiddlers
	if(this.parentNode.id)
		{
		closeAllTiddlers();
		displayTiddler(null,this.parentNode.id.substr(7),1);
		}
}

// Event handler for clicking on toolbar close
function onClickToolbarEdit(e)
{
	// Edit that tiddler
	if(this.parentNode.id)
		displayTiddler(null,this.parentNode.id.substr(7),2);
}

// Event handler for clicking on toolbar save
function onClickToolbarSave(e)
{
	// Save that tiddler
	if(this.parentNode.id)
		saveTiddler(this.parentNode.id.substr(7));
}

// Event handler for clicking on toolbar save
function onClickToolbarUndo(e)
{
	// Redisplay that tiddler in read-only mode
	if(this.parentNode.id)
		displayTiddler(null,this.parentNode.id.substr(7),1);
}

// ---------------------------------------------------------------------------------
// Animation engine
// ---------------------------------------------------------------------------------

// Animation housekeeping
var animating = 0; // Incremented at start of each animation, decremented afterwards. If zero, the interval timer is disabled
var animaterID; // ID of the timer used for animating
// 'zoomer' module of the animation engine that smoothly moves an element from the position/size of the start element to th etarget element
var zoomerElement = null; // Element being shifted; null if none
var zoomerStart; // Where we're shifting to
var zoomerTarget; // Where we're shifting to
var zoomerProgress; // 0..1 of how far we are
var zoomerStep; // 0..1 of how much to shift each step

// Start animation engine
function startAnimating()
{
    if(animating++ == 0)
        animaterID = window.setInterval("doAnimate();",25);
}

// Stop animation engine
function stopAnimating()
{
    if(--animating == 0)
        window.clearInterval(animaterID)
}

// Perform an animation engine tick, calling each of the known animation modules
function doAnimate()
{
    if(zoomerElement != null)
        doZoomer();
}

// Start moving the element 'e' from the position of the element 'start' to the position of the element 'target'
function startZoomer(e,start,target)
{
    stopZoomer();
    zoomerElement = e;
    zoomerStart = start;
    zoomerTarget = target;
    zoomerProgress = 0;
    zoomerStep = 0.08;
    startAnimating();
}

// Stop any ongoing zoomer animation
function stopZoomer()
{
    if(zoomerElement != null)
        {
        stopAnimating();
        zoomerElement.style.visibility = "hidden"; 
        zoomerElement = null;
        }
}

// Perform a tick of the zoomer animation
function doZoomer()
{
    zoomerProgress += zoomerStep;
    if(zoomerProgress > 1.0)
        stopZoomer();
    else
        {        	
        zoomerTarget.style.opacity = zoomerProgress;
        var f = slowInSlowOut(zoomerProgress);        
        var zoomerStartLeft = findPosX(zoomerStart);
        var zoomerStartTop = findPosY(zoomerStart);
        var zoomerStartWidth = zoomerStart.offsetWidth;
        var zoomerStartHeight = zoomerStart.offsetHeight;
        var zoomerTargetLeft = findPosX(zoomerTarget);
        var zoomerTargetTop = findPosY(zoomerTarget);
        var zoomerTargetWidth = zoomerTarget.offsetWidth;
        var zoomerTargetHeight = zoomerTarget.offsetHeight;        
        zoomerElement.style.left = zoomerStartLeft + (zoomerTargetLeft-zoomerStartLeft) * f;
        zoomerElement.style.top = zoomerStartTop + (zoomerTargetTop-zoomerStartTop) * f;
        zoomerElement.style.width = zoomerStartWidth + (zoomerTargetWidth-zoomerStartWidth) * f;
        zoomerElement.style.height = zoomerStartHeight + (zoomerTargetHeight-zoomerStartHeight) * f;
        zoomerElement.style.visibility = "visible";        
        }
}

// ---------------------------------------------------------------------------------
// Standalone utility functions
// ---------------------------------------------------------------------------------

// Return a date in UTC YYYYMMDDHHMM format
function ConvertToYYYYMMDDHHMM(d)
{
    return(d.getFullYear() + '' + (d.getMonth() <= 9 ? '0' : '') + (d.getMonth() + 1) + '' + (d.getDate() <= 9 ? '0' : '') + d.getDate() + (d.getHours() <= 9 ? '0' : '') + d.getHours() + (d.getMinutes() <= 9 ? '0' : '') + d.getMinutes());
}

// Convert a date in UTC YYYYMMDDHHMM format to printable local time
function ConvertFromYYYYMMDDHHMM(d)
{
    var theDate = new Date(parseInt(d.substr(0,4),10),
                            parseInt(d.substr(4,2),10)-1,
                            parseInt(d.substr(6,2),10),
                            parseInt(d.substr(8,2),10),
                            parseInt(d.substr(10,2),10),0,0);
    return(theDate.toLocaleString());
}

// Map a 0..1 value to 0..1, but slow down at the start and end
function slowInSlowOut(progress)
{
    return(1-((Math.cos(progress * Math.PI)+1)/2));
}

// Scroll if necessary to ensure that a given element is visible
function ensureVisible(e)
{
    // The position we're trying to scroll into view; the top of it...
    var posY = findPosY(e);
    // ...or, if the element will fit on the screen, the bottom of it
    if(e.offsetHeight < window.innerHeight)
        posY += e.offsetHeight;
    // Make sure the chosen position is visible
    if ((posY < window.scrollY) || (posY > (window.scrollY + window.innerHeight)))
        window.scrollTo(0,posY);
}

// From QuirksMode.com
function findPosX(obj)
{
        var curleft = 0;
        if (obj.offsetParent)
        {
                while (obj.offsetParent)
                {
                        curleft += obj.offsetLeft
                        obj = obj.offsetParent;
                }
        }
        else if (obj.x)
                curleft += obj.x;

        return curleft;
}

// From QuirksMode.com
function findPosY(obj)
{
        var curtop = 0;
        if (obj.offsetParent)
        {
                while (obj.offsetParent)
                {
                        curtop += obj.offsetTop
                        obj = obj.offsetParent;
                }
        }
        else if (obj.y)
                curtop += obj.y;
        return curtop;
}

// Show a message to the user.
var msg;
var fadeSteps = 25;
var opacity = 1.0;
var fadeID;

function displayMessage(message, x, y) {
	msg = document.getElementById("message");  	
	var messageNode = document.createTextNode(message);
	msg.style.background = "#a2b3f9";
	if (msg.firstChild)
		msg.replaceChild(messageNode,msg.firstChild);    	
	else
		msg.appendChild(messageNode);
    
    msg.style.opacity = opacity = 1.0;
    msg.style.left = x;
    msg.style.top = y;
    msg.style.width = 300;
    msg.style.height = 20;
    msg.style.visibility = "visible";	
    fadeID = window.setInterval("removeMessage()", 4000);
}

function removeMessage() {
	msg.style.visibility = "hidden";
	window.clearInterval(fadeID);
}

// ---------------------------------------------------------------------------------
// End of scripts
// ---------------------------------------------------------------------------------

