
// ---------------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------------

// PMC

// cookie stuff

function createCookie(name,value,days)
{
	if (days)
	{
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	}
	else var expires = "";
	document.cookie = name+"="+value+expires+"; path=/";
}

function readCookie(name)
{
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++)
	{
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return null;
}

function eraseCookie(name)
{
	createCookie(name,"",-1);
}

var modifierName = null;

function setModifierName()
{
	// Set user name
	if (modifierName){
		return true;
	}
	modifierName = readCookie("tiddly_wiki_modifier");
	if (!modifierName || modifierName == "" || modifierName == "undefined"){
		modifierName = prompt("お名前を入力してください。","");
		if (modifierName && modifierName != "")
		{
			createCookie("tiddly_wiki_modifier", modifierName, 365);
			return true;
		}else{
			return false;
		}
	}
	return true;
}

// end cookie stuff


function main()
{
	restart();
}

/*	Function Equivalent to java.net.URLDecoder.decode(String, "UTF-8")
	Copyright (C) 2002, Cresc Corp.
	Version: 1.0
*/
function myEncodeURL(str)
{
	var s0, i, s, u;
	var sp = "%"
	s0 = "";				// encoded str
	for (i = 0; i < str.length; i++){	// scan the source
		s = str.charAt(i);
		u = str.charCodeAt(i);			// get unicode of the char
		if (s == " "){
			s0 += "+";		 // SP should be converted to "+"
		}
		else{
			if (u == 0x2a || u == 0x2d || u == 0x2e || u == 0x5f || ((u >= 0x30) && (u <= 0x39)) || 
				((u >= 0x41) && (u <= 0x5a)) || ((u >= 0x61) && (u <= 0x7a))) { 	 // check for escape
				s0 = s0 + s;			// don't escape
			}
			else {					// escape
				if ((u >= 0x0) && (u <= 0x7f)){ 	// single byte format
					s = "0"+u.toString(16);
					s0 += sp+ s.substr(s.length-2);
				}
				else if (u > 0x1fffff){ 	// quaternary byte format (extended)
					s0 += sp + (oxf0 + ((u & 0x1c0000) >> 18)).toString(16);
					s0 += sp + (0x80 + ((u & 0x3f000) >> 12)).toString(16);
					s0 += sp + (0x80 + ((u & 0xfc0) >> 6)).toString(16);
					s0 += sp + (0x80 + (u & 0x3f)).toString(16);
				}
				else if (u > 0x7ff){		// triple byte format
					s0 += sp + (0xe0 + ((u & 0xf000) >> 12)).toString(16);
					s0 += sp + (0x80 + ((u & 0xfc0) >> 6)).toString(16);
					s0 += sp + (0x80 + (u & 0x3f)).toString(16);
				}
				else {						// double byte format
					s0 += sp + (0xc0 + ((u & 0x7c0) >> 6)).toString(16);
					s0 += sp + (0x80 + (u & 0x3f)).toString(16);
				}
			}
		}
	}
	return s0;
}
function myDecodeURL(str)
{
	var s0, i, j, s, ss, u, n, f;
	var sp = "%"
	s0 = "";				// decoded str
	for (i = 0; i < str.length; i++){	// scan the source str
		s = str.charAt(i);
		if (s == "+"){s0 += " ";}		// "+" should be changed to SP
		else {
			if (s != sp){
				s0 += s;	 // add an unescaped char
			}
			else{				// escape sequence decoding
				u = 0;			// unicode of the character
				f = 1;			// escape flag, zero means end of this sequence
				while (true) {
					ss = "";		// local str to parse as int
					for (j = 0; j < 2; j++ ) {	// get two maximum hex characters for parse
						sss = str.charAt(++i);
						if (((sss >= "0") && (sss <= "9")) || ((sss >= "a") && (sss <= "f"))  || 
							((sss >= "A") && (sss <= "F"))) {
							ss += sss;		// if hex, add the hex character
						}
						else {
							--i; break;  // not a hex char., exit the loop
						}
					}
					n = parseInt(ss, 16);			// parse the hex str as byte
					if (n <= 0x7f){u = n; f = 1;}	// single byte format
					if ((n >= 0xc0) && (n <= 0xdf)){u = n & 0x1f; f = 2;}	// double byte format
					if ((n >= 0xe0) && (n <= 0xef)){u = n & 0x0f; f = 3;}	// triple byte format
					if ((n >= 0xf0) && (n <= 0xf7)){u = n & 0x07; f = 4;}	// quaternary byte format (extended)
					if ((n >= 0x80) && (n <= 0xbf)){u = (u << 6) + (n & 0x3f); --f;}		 // not a first, shift and add 6 lower bits
					if (f <= 1){break;} 		// end of the utf byte sequence
					if (str.charAt(i + 1) == sp){ i++ ;}				   // test for the next shift byte
					else { break;}					 // abnormal, format error
				}
				s0 += String.fromCharCode(u);			// add the escaped character
			}
		}
	}
	return s0;

}
function restart()
{
	closeAllTiddlers();
	hideMessage();
	refreshTitlePattern();
	refreshAll();
	var start;
	if(window.location.hash && window.location.hash != "#"){
		displayTiddlers(null,myDecodeURL(location.href.replace(/.*?#/,"")),1,null,null);
//		displayTiddlers(null,window.location.hash,1,null,null);
//		displayTiddlers(null,decodeURI(decodeURI(window.location.hash.substr(1)),1,null,null));
	}else{
		var theStart = getTiddlerText("StartHere");
		displayTiddlers(null,theStart,1,null,null);
	}
}

// ---------------------------------------------------------------------------------
// 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
//	highlightText = text to highlight in the displayed tiddler
//	highlightCaseSensitive = flag for whether the highlight text is case sensitive
function displayTiddler(src,title,state,highlightText,highlightCaseSensitive,slowly)
{
	// Figure out the tiddler this one must go after
	var after = findContainingTiddler(src);
	// Create the tiddler if needed
	var theTiddler = createTiddler(title,state,after,highlightText,highlightCaseSensitive);
	// Animate from the target of the event that followed the link
	if(src)
		{
		// Start with the tiddler being transparent and fade it up
//		  theTiddler.style.opacity = 0;
		// 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,slowly);
		}
		
	 
}

// Display several tiddlers from a list of space separated titles
function displayTiddlers(src,titles,state,highlightText,highlightCaseSensitive,slowly)
{
	var tiddlers = titles.split(" ");
	for(var t=tiddlers.length-1;t>=0;t--)
		displayTiddler(src,tiddlers[t],state,highlightText,highlightCaseSensitive,slowly);
}

//Rakusai
// Get selected text and put it into curSelText
var curSelText = "";
function getSelectedText()
{
	//Yatsuさん
	//Safariにも対応 (05/06/14)
	var selText = "";
	if (window.getSelection) {
		// Safari, Mozilla
		// Safariは + "" が必要
		selText = window.getSelection() + "";
	} else if (document.getSelection) {
		// Mozilla
		selText = document.getSelection();
	} else if (document.selection) {
		// IE, Opera
		selText = document.selection.createRange().text;
	}
	curSelText = selText;
	return selText;

}

// PMC
function newTiddler(e)
{
	
	//Rakusai
	//選択中の文字をタイトルにする
	var selText = curSelText;
	if (selText != null){
		selText = selText.replace(/(¶|\r|\n|\t| |　)/ig,"");
		selText = selText.substr(0,40);
	}
	if (selText != null && selText != ""){
		createTiddler(selText,2);
	}else{
		var now = new Date();
		var curdate = ConvertToYYYYMMDD(now);
		newTitle = "新しい紙";
		var i = 2;
		while (document.getElementById("store" + newTitle)){
			newTitle = "新しい紙_" + i;
			i++;
		}
		createTiddler(newTitle,2);
	}
	playSound("KASHA",50);
	
}
// end PMC


// 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
//	highlightText = text to highlight in the displayed tiddler
//	highlightCaseSensitive = flag for whether the highlight text is case sensitive
function createTiddler(title,state,after,highlightText,highlightCaseSensitive)
{
	// 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,highlightText,highlightCaseSensitive);
		// Create the tiddler body appropriately
		if(state != 2)
			createTiddlerBody(theTiddler,title,highlightText,highlightCaseSensitive);
		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,highlightText,highlightCaseSensitive);
						}
				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,highlightText,highlightCaseSensitive)
{
	// Create the tiddler div
	theTiddler = createTiddlyElement(null,"div","tiddler",null);
	theTiddler.setAttribute("id","tiddler" + title);
	theTiddler.onmouseover = onMouseOverTiddler;
	theTiddler.onmouseout = onMouseOutTiddler;
	theTiddler.ondblclick = onDblClickTiddler;
	// 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);
	// Prepare the regexp for the highlighted selection in the title
	if(highlightText == "")
		highlightText = null;
	var highlightRegExp,highlightMatch;
	if(highlightText)
		{
		highlightRegExp = new RegExp(escapeRegExp(highlightText),highlightCaseSensitive ? "mg" : "img");
		highlightMatch = highlightRegExp.exec(title);
		}
	// Create the title with optional highlight
	
	//Rakusai 正規表現によって、作成済みのタイトルに自動でリンク
	//MainMenuに含まれるタイトルかどうか
	var mainMenuText = getTiddlerText("MainMenu");
	var titlePattern = "(" + getTitlePatternText() + ")";
	var formatRegExp = new RegExp(titlePattern,"mg");
	var theTitle;
	do {
		// Get the next formatting match
		var formatMatch = formatRegExp.exec(mainMenuText);
		// Dump out the formatted match
		if(formatMatch)
		{
			// Dump out the link itself in the appropriate format
			if(formatMatch[1])
			{
				if (formatMatch[0] == title)
					theTitle = createTiddlyElement(theTiddler,"div","title2",null);
			}
		}
	} while(formatMatch);
	if (!theTitle)
		theTitle = createTiddlyElement(theTiddler,"div","title",null);
	highlightMatch = subWikify(theTitle,title,0,title.length,highlightRegExp,highlightMatch);
	insertSpacer(theTitle);
	theTitle.setAttribute("id","title" + title);
	// Get the subtitle
	var subtitle = getTiddlerSubtitle(title);
	theTitle.title = subtitle;
	// 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(null,"div","toolbar", null);
	theTitle.appendChild(theToolbar);
	theToolbar.setAttribute("id","toolbar" + title);
	// Create each button in turn
	insertSpacer(theToolbar);
	if(!editor)
		{
		// Non-editor toolbar
		createTiddlyButton(theToolbar,
						   "閉じる",
						   "Close this tiddler",
						   onClickToolbarClose);
		insertSpacer(theToolbar);
		createTiddlyButton(theToolbar,
						   "編集",
						   "Edit this tiddler",
						   onClickToolbarEdit);
		insertSpacer(theToolbar);
		createTiddlyButton(theToolbar,
							"アドレス表示",
							"Permalink for this tiddler",
							onClickToolbarPermaLink);
		insertSpacer(theToolbar);
		createTiddlyButton(theToolbar,
						   "縁記事",
						   "Show tiddlers that link to this one",
						   onClickToolbarBackLink);
		}
	else
		{
		// Editor toolbar
		createTiddlyButton(theToolbar,
						   "保存",
						   "Finish changing this tiddler",
						   onClickToolbarSave);
		insertSpacer(theToolbar);
		createTiddlyButton(theToolbar,
						   "保存せずに終了",
						   "Undo changes to this tiddler",
						   onClickToolbarUndo);
		insertSpacer(theToolbar);
		createTiddlyButton(theToolbar,
						   "削除",
						   "Delete this tiddler",
						   onClickToolbarDelete);
		}
	insertSpacer(theToolbar);
}

//Rakusai リンク元のタイトルを「縁のある記事」として表示する
// Create the entitle section of a read-only tiddler
function createTiddlerEnTitle(title,highlightText,highlightCaseSensitive)
{
	// Delete any existing toolbar
	var theEnTitle = document.getElementById("entitle" + title);
	if(theEnTitle)
		theEnTitle.parentNode.removeChild(theEnTitle);
	// Create the toolbar
	var theTiddler = document.getElementById("tiddler" + title);
	var theEnTitle = createTiddlyElement(null,"div","entitle", null);
	theTiddler.appendChild(theEnTitle);
	theEnTitle.setAttribute("id","entitle" + title);

	// Get the body of the tiddler
	theEnTitle.appendChild(document.createTextNode("縁のある記事"));
	
	//全文で検索
	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(n.id.substr(5));
		}
	var tiddlerText = "";
	var num = 0;
	for (t = 0; t < allTiddlers.length; t++)
	{
		var text = getTiddlerText(allTiddlers[t]);
		if (text.indexOf(title) >= 0 && title != allTiddlers[t]){
			if (tiddlerText != ""){
				if ((num % 3) == 0)
					tiddlerText = tiddlerText + "¶";
				else
					tiddlerText = tiddlerText + " | ";
			}
			tiddlerText = tiddlerText + allTiddlers[t];
			num = num + 1;
		}
	}
	theEnTitle.appendChild(document.createTextNode("(" + num + ")"));
	if (num > 0)
		theEnTitle.appendChild(document.createTextNode("："));
	
	// Add the body text wikifing the links
	wikify(tiddlerText,theEnTitle,highlightText,highlightCaseSensitive,false);
}

// Create the body section of a read-only tiddler
function createTiddlerBody(place,title,highlightText,highlightCaseSensitive)
{
	// Create the toolbar
	createTiddlerToolbar(title,false);
	// Create Entitle
	createTiddlerEnTitle(title,highlightText,highlightCaseSensitive);
	
	// 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,highlightText,highlightCaseSensitive,false);
}

// 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 = "";
	tiddlerText = tiddlerText.replace(/¶/ig,"\n");
	// 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","60");
	// Do the body
	var theBody = createTiddlyElement(theEditor,"div",null,null);
	var theBodyBox = createTiddlyElement(theBody,"textarea",null,null);
	theBodyBox.value = tiddlerText;
	theBodyBox.wrap = "soft";
	
	//Rakusai 文章量によってテキストアリアのサイズを調整
	var linelen = 7 + (tiddlerText.length - tiddlerText.replace(/\n/ig,"").length);
	if (linelen < 10)
		linelen = 10;
	if (linelen > 30)
		linelen = 30;
	theBodyBox.setAttribute("id","editorBody" + title);
	theBodyBox.setAttribute("rows",linelen);
	theBodyBox.setAttribute("cols","60");
	theBodyBox.className = "textEditor";
	
	theBodyBox.focus();
}


// Create child text nodes and link elements to represent a wiki-fied version of some text
function wikify(text,parent,highlightText,highlightCaseSensitive,single)
{
	// Link patterns
	//Rakusai  正規表現によって、作成済みのタイトルに自動でリンク
	var breakPattern = "(¶|\r\n|\n)";
	var tabPattern = "|(\t)";
	var spacePattern = "|(  )";
	var upperLetter = "[A-Z]";
	var lowerLetter = "[a-z]";
	var anyLetter = "[A-Za-z_0-9]";
	var linkPattern = "";
//	var linkPattern = "|(" + upperLetter + "+" + lowerLetter + "+" + upperLetter + anyLetter + "*)";
	var titlePattern = "|(" + getTitlePatternText() + ")";
	var urlPattern = "|((?:http|https|mailto|ftp):[^(¶|\r|\n|\t| |　)]*)";
	// Create the RegExp object for matching formatting
	var formatRegExp = new RegExp(breakPattern + titlePattern + linkPattern + urlPattern + tabPattern + spacePattern ,"mg");
	// The start of the fragment of the text being considered
	var nextPos = 0;
	// Prepare the regexp for the highlighted selection
	if(highlightText == "")
		highlightText = null;
	var highlightRegExp,highlightMatch;
	if(highlightText)
		{
		highlightRegExp = new RegExp(escapeRegExp(highlightText),highlightCaseSensitive ? "mg" : "img");
		highlightMatch = highlightRegExp.exec(text);
		}
	// Loop through the bits of the body text
	var i=0;
	do {
		// Get the next formatting match
		var formatMatch = formatRegExp.exec(text);
		var matchPos = formatMatch ? formatMatch.index : text.length;
		// Subwikify the plain text before the match
		if(nextPos < matchPos)
			highlightMatch = subWikify(parent,text,nextPos,matchPos,highlightRegExp,highlightMatch);
		// Dump out the formatted match
		if(formatMatch)
		{
			// Dump out the link itself in the appropriate format
			if(formatMatch[1])
			{
				parent.appendChild(document.createElement("br"));
			}
			else if(formatMatch[4])
			{
				var pre = document.createElement("span");
				pre.className = "tab";
				pre.appendChild(document.createTextNode(formatMatch[4]));
				parent.appendChild(pre);
			}
			else if(formatMatch[5])
			{
				var pre = document.createElement("span");
				pre.className = "space";
				pre.appendChild(document.createTextNode(formatMatch[5]));
				parent.appendChild(pre);
			}
			else if(formatMatch[2])
			{
				var theLink = createTiddlyLink(parent,formatMatch[0],false,single);
				highlightMatch = subWikify(theLink,text,matchPos,formatRegExp.lastIndex,highlightRegExp,highlightMatch);
			}
			else if(formatMatch[3])
			{
				//Rakusai URLの最後が画像の拡張子なら画像を表示させる
				if (formatMatch[0].match(/(gif|jpg|jpeg|png)$/i)){
					//画像
					var cached = false;
					for (t = 0; t < document.images.length; t++)
					{
						if (document.images[t].src == formatMatch[0]){
							cached = true;
							break;
						}
					}
					if (cached == false){
						//画像キャッシュがない場合
						var img = new Image();
						img.src = formatMatch[0];
					}
					//数字と配列を結びつける
					allImageList.push(formatMatch[0]);
					var imgNumber = allImageList.length -1;
					
					var filename = formatMatch[0].substr(formatMatch[0].lastIndexOf("/")+1);
					var theImgDiv = createTiddlyElement(parent,"div","image")
					theImgDiv.setAttribute("id","image" + imgNumber);
					
					setTimeout("createImage(" + imgNumber + ",1);",10);
				}else{
					var theLink = createExternalLink(parent,formatMatch[0]);
					highlightMatch = subWikify(theLink,text,matchPos,formatRegExp.lastIndex,highlightRegExp,highlightMatch);
				}
			}
		}
		// Move the next position past the formatting match
		nextPos = formatRegExp.lastIndex;
		i = i + 1;
	} while(formatMatch);
}

//Rakusai 画像を表示
var allImageList = new Array();

function createImage(imgNumber,count)
{
	var url = allImageList[imgNumber];
	
	var img = new Image();
	img.src = url;
	//IEではimgタグを追加しただけでは素直に表示されないバグあり
	if (img.width <= 0 || img.width == null){
		//再読込(3秒待つ)
		if (count < 4){
			count += 1;
			setTimeout("createImage(" + imgNumber + "," + count + ");",1000);
			return;
		}
	}
	
	var filename = url.substr(url.lastIndexOf("/")+1);
	
	var place = document.getElementById("image" + imgNumber);
	if (!place){
		return;
	}
	if (img.width > 490){
		var theLink = createExternalLink(place,url);
		theLink.setAttribute("title","拡大表示：" + filename);
		var theImg = document.createElement("img");
		theLink.appendChild(theImg);
		theImg.setAttribute("alt","拡大表示：" + filename);
		theImg.setAttribute("src",url);
		theImg.setAttribute("width","490");
	}else{
		var theImg = document.createElement("img");
		theImg.setAttribute("alt",filename);
		theImg.setAttribute("src",url);
		place.appendChild(theImg);
	}


}

// Helper for wikify that handles highlights within runs of text
function subWikify(parent,text,startPos,endPos,highlightRegExp,highlightMatch)
{
	// Check for highlights
	while(highlightMatch && (!((highlightRegExp.lastIndex <= startPos) || (highlightMatch.index >= endPos))) && (startPos < endPos))
		{
		// Deal with the plain text before the highlight
		if(highlightMatch.index > startPos)
			{
			parent.appendChild(document.createTextNode(text.substring(startPos,highlightMatch.index)));
			startPos = highlightMatch.index;
			}
		// Deal with the highlight
		var highlightEnd = Math.min(highlightRegExp.lastIndex,endPos);
		var theHighlight = createTiddlyElement(parent,"span","highlight",text.substring(Math.max(startPos,highlightMatch.index),highlightEnd));
		startPos = highlightEnd;
		// Nudge along to the next highlight if we're done with this one
		if(startPos >= highlightRegExp.lastIndex)
			highlightMatch = highlightRegExp.exec(text);
		}
	// Do the unhighlighted text left over
	if(startPos < endPos)
		{
		parent.appendChild(document.createTextNode(text.substring(startPos,endPos)));
		startPos = endPos;
		}
	return(highlightMatch);
}

function undoTiddler(title)
{
	// Get the title and body text
	var theNewTitle = document.getElementById("editorTitle" + title).value;
	var theNewBody = document.getElementById("editorBody" + title).value;

	var theExisting = document.getElementById("store" + title);
	var text = "";
	var body = "";
	if(theExisting && theExisting.firstChild != null)
		body = theExisting.firstChild.nodeValue;
	theNewBody = encodeForSave(theNewBody);
	//Rakusai 変更があるか調べる
	if (body != theNewBody || title != theNewTitle){
		var areYouSure = confirm("保存せずに終了してもいいですか？");
		if (!areYouSure)
			return false;
	}

	if(theExisting)
	{
		displayTiddler(null,title,1,null,null,false);
	}else{
		//閉じる
		hideMessage();
		closeTiddler(title,false);
	}
}

function saveTiddler(title)
{
	// Get the title and body text
	var theNewTitle = document.getElementById("editorTitle" + title).value;
	var theNewBody = document.getElementById("editorBody" + title).value;
	theNewTitle = theNewTitle.replace(/(\\|\'|\")/g,"");
	
	if (theNewTitle == ""){
		alert("タイトルに何か入力してください。");
		return;
	}
	if (theNewBody == ""){
		alert("本文に何か入力してください。");
		return;
	}
	// Set user name
	if (!setModifierName()){
		alert("お名前を入力しないと保存できません。");
		return;
	}
	// attribute
	var now = new Date();
	var modifiedDate = ConvertToYYYYMMDDHHMM(now);
	var creatorName = modifierName;
	var createdDate = modifiedDate;
	// Remove any existing entry from the store
	var theExisting = document.getElementById("store" + title);
	var isNewTiddler = true;
	var body = "";
	//Rakusai check same title
	if(title != theNewTitle)
	{
		theTest = document.getElementById("store" + theNewTitle);
		if(theTest){
			alert("このタイトルの紙はすでに存在します。別の名前をつけてください。");
			return;
		}
	}
	//変更があるか
	if(theExisting)
	{
		body = theExisting.firstChild.nodeValue;
		var theTestBody = encodeForSave(theNewBody);
		if (body == theTestBody && title == theNewTitle){
			//変更なし
			displayTiddler(null,title,1,null,null,false);
			return;
		}
	}
	
	if(theExisting)
	{
		isNewTiddler = false;
		creatorName = theExisting.getAttribute("creator");
		createdDate = theExisting.getAttribute("created");
		//delete the old store
		theExisting.parentNode.removeChild(theExisting);
		if(title != theNewTitle)
		{
			//Rakusai Change the oldTiddler to "→newTtitle" format
			var place = document.getElementById("storeArea");
			var storeItem = createTiddlyElement(place,"div",null,"→" + theNewTitle);
			storeItem.setAttribute("id","store" + title);
			storeItem.setAttribute("modifier",modifierName);
			storeItem.setAttribute("modified",modifiedDate);
			storeItem.setAttribute("creator",creatorName);
			storeItem.setAttribute("created",createdDate);
		}
	}
	   
	// Create the new entry in the store
	var place = document.getElementById("storeArea");
	var storeItem = createTiddlyElement(place,"div",null,theNewBody);
	storeItem.setAttribute("id","store" + theNewTitle);
	storeItem.setAttribute("modifier",modifierName);
	storeItem.setAttribute("modified",modifiedDate);
	storeItem.setAttribute("creator",creatorName);
	storeItem.setAttribute("created",createdDate);

	// PMC -- now save it to the web!
	document.forms.invisiForm.msg.value = "save";
	document.forms.invisiForm.title.value = encodeForSave(theNewTitle);
	document.forms.invisiForm.body.value = encodeForSave(theNewBody);
	if(title != theNewTitle && isNewTiddler == false){
		// Change the oldTiddler to "→newTtitle" format
		document.forms.invisiForm.oldtitle.value = encodeForSave(title);
		var oldbody = "→" + theNewTitle;
		document.forms.invisiForm.oldbody.value = encodeForSave(oldbody);
	}else{
		document.forms.invisiForm.oldtitle.value = "";
		document.forms.invisiForm.oldbody.value = "";
	}
	document.forms.invisiForm.modifier.value = encodeForSave(modifierName);
	document.forms.invisiForm.modified.value = modifiedDate;
	document.forms.invisiForm.creator.value = encodeForSave(creatorName);
	document.forms.invisiForm.created.value = createdDate;
	
	document.forms.invisiForm.submit();

	refreshTitlePattern();
	// Display the new tiddler read-only
	displayTiddler(null,theNewTitle,1,null,null,null,false);
	// Close the old tiddler if this is a rename
	if(title != theNewTitle)
		{
		var oldTiddler = document.getElementById("tiddler" + title);
		oldTiddler.parentNode.removeChild(oldTiddler);
		}
	// Refresh the menu and sidebars to take it into account
	refreshAll();
	
	// end PMC
	playSound("PIKON",30);

}

function searchTiddlers(text,caseSensitive)
{
	// If we're searching case insensitively, put the search text in lower case
	if(!caseSensitive)
		text = text.toLowerCase();
	// Close any open tiddlers
	closeAllTiddlers();
	// If the search text is actually the full name of a tiddler, make sure that one is
	// displayed first, and get a reference to it to add the other matches afterwards
	if(document.getElementById("store" + text))
		displayTiddler(null,text,1,text,caseSensitive,false);
	var theTiddler = document.getElementById("tiddler" + text);
	// Traverse through the store area looking for the text in the title or body
	var store = document.getElementById("storeArea").childNodes;
	var c = 0;
	for(var t = 0; t < store.length; t++)
		{
		var e = store[t];
		if(e.id && e.firstChild != null)
			if(e.id.substr(0,5) == "store")
				{
				if(caseSensitive)
					{
					var matchTitle = e.id.substr(5).indexOf(text);
					var matchBody = e.firstChild.nodeValue.indexOf(text);
					}
				else
					{
					var matchTitle = e.id.substr(5).toLowerCase().indexOf(text);
					var matchBody = e.firstChild.nodeValue.toLowerCase().indexOf(text);
					}
				if((matchTitle != -1) || (matchBody != -1))
					{
					// Display matching tiddlers
					displayTiddler(null,e.id.substr(5),1,text,caseSensitive,false);
					c++;
					}
				}
		}
	// Jam in a message
	displayMessage(c + " tiddlers found matching '" + text + "'");
}

function selectTiddler(title)
{
	// Make the toolbar visible
	e = document.getElementById("toolbar" + title);
	if(e != null)
		e.style.visibility = "visible";
}

function deselectTiddler(title)
{

	// Make the toolbar invisible
	e = document.getElementById("toolbar" + title);
	if(e != null)
		e.style.visibility = "hidden";
}

function deleteTiddler(title)
{
	//Rakusai 規定タイトルの削除禁止
	if (title.match(/^(StartHere|MainMenu|SiteTitle|SiteSubtitle)$/)){
		alert("この紙はサイト構成に必須なので、削除できません。");
		return;
	}
	// Set user name
	if (!setModifierName()){
		alert("お名前を入力しないと削除できません。");
		return;
	}
	
	//作成者と編集者が異なる場合は削除できない
	var theExisting = document.getElementById("store" + title);
	creatorName = theExisting.getAttribute("creator");
	if (modifierName != creatorName){
		alert("この紙は削除が制限されています。削除したい場合は、本文に「削除願い」と記入してください。");
		return;
	}
	
	//Confirm
	var areYouSure = confirm("この紙を削除してもよろしいですか？");
	if (!areYouSure)
	{
		return false;
	}

	// PMC -- now delete it from the server!
	var now = new Date();
	var modifiedDate = ConvertToYYYYMMDDHHMM(now);
	
	document.forms.invisiForm.msg.value = "delete";
	document.forms.invisiForm.title.value = encodeForSave(title);
	document.forms.invisiForm.body.value = "";
	document.forms.invisiForm.oldbody.value = "";
	document.forms.invisiForm.oldtitle.value = "";
	document.forms.invisiForm.creator.value = "";
	document.forms.invisiForm.created.value = "";
	document.forms.invisiForm.modifier.value = encodeForSave(modifierName);
	document.forms.invisiForm.modified.value = modifiedDate;
	document.forms.invisiForm.submit();
	// end PMC
	
	// Remove the tiddler from the display
	closeTiddler(title,false);
	// 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
	refreshTitlePattern();
	refreshAll();
	
	playSound("MOVE",30);
}

function closeTiddler(title,slowly)
{
	var tiddler = document.getElementById("tiddler" + title);
	if(tiddler != null)
		{
		tiddler.id = "";
		startCloser(tiddler,slowly);
		}

	playSound("SHU",30);
		
}

function closeAllTiddlers(all)
{
	// Delete all the elements in the displayArea
	hideMessage();
	if (all){
		var e = document.getElementById("tiddlerDisplay");
		while(e.firstChild != null)
			e.removeChild(e.firstChild);

	}else{
		//remain editor
		var place = document.getElementById("tiddlerDisplay");
		var tiddler = place.firstChild;
		var nextTiddler;
		while(tiddler)
			{
			nextTiddler = tiddler.nextSibling;
			if(tiddler.id)
				if(tiddler.id.substr(0,7) == "tiddler")
					{
					var title = tiddler.id.substr(7);
					if(!document.getElementById("editor" + title))
						place.removeChild(tiddler);
					}
			tiddler = nextTiddler;
			}
	}
}

// ---------------------------------------------------------------------------------
// Tiddler-related utility functions
// ---------------------------------------------------------------------------------
function encodeForSave(text)
{
		// Encode Text For Saving
		text = text.replace(/(\r\n|\r|\n)/g,"¶");
		text = text.replace(/&/g,"&amp;");
		text = text.replace(/>/g,"&gt;");
		text = text.replace(/</g,"&lt;");
		text = text.replace(/"/g,"&quot;");
		text = text.replace(/'/g,"&#39;");

		return(text);
}

function getTiddlerText(title)
{
		// Attempt to retrieve it from the store
		var tiddlerStore = document.getElementById("store" + title);
		if(tiddlerStore != null && tiddlerStore.firstChild != null)
			return(tiddlerStore.firstChild.nodeValue);
		else
			return(null);
}

function getTitlePatternText()
{
		//Rakusai 本文をタイトルでマップさせるための正規表現を返す
		var tiddlerStore = document.getElementById("paramTitlePatternText");
		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 = LocalDateStringFromYYYYMMDDHHMM(theModified);
		else
			theModified = "(unknown)";
		return(theModifier + ", " + 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");
	if(theAction)
		{
		theButton.onclick = theAction;
		theButton.setAttribute("href","JavaScript:;");
		}
	theButton.setAttribute("title",theTooltip);
	if(theText)
		{
		theButton.appendChild(document.createTextNode(theText));
		}
	theParent.appendChild(theButton);
	return(theButton);
}

function createTiddlyLink(place,title,includeText,single)
{
	var btn;
	var text = includeText ? title : null;
	var subTitle = getTiddlerSubtitle(title);
	var theClass = subTitle ? "tiddlyLinkExisting" : "tiddlyLinkNonExisting";
	if(!subTitle)
		subTitle = title + " doesn't yet exist";
	var btn = createTiddlyButton(place,text,subTitle,onClickTiddlerLink);
	btn.className = theClass;
	btn.setAttribute("tiddlyLink",title);
	btn.setAttribute("tiddlySingle",single ? "true" : "false");
	return(btn);
}

// Create an external link
function createExternalLink(place,url)
{
	var theLink = document.createElement("a");
	theLink.href = url;
	theLink.title = "External link to " + url;
	theLink.target = "_blank";
	place.appendChild(theLink);
	return(theLink);
}

// Find the tiddler instance (if any) containing a specified element
function findContainingTiddler(e)
{
	if(e == null)
		return(null);
	do {
		if(e != document && e != null)
			{
			if(e.id)
				if(e.id.substr(0,7) == "tiddler")
					return(e);
			}
		e = e.parentNode;
	} while(e != document && e != null);
	return(null);
}

// Display some text in the message area
function displayMessage(text)
{
	var msgArea = document.getElementById("messageArea");
	var msg = document.createTextNode(text);
	msgArea.replaceChild(msg,msgArea.firstChild);
	msgArea.style.display = "block";
}

// Hide the message area and clear the search box
function hideMessage()
{
	var msgArea = document.getElementById("messageArea");
	msgArea.style.display = "none";
}

// ---------------------------------------------------------------------------------
// Menu and sidebar functions
// ---------------------------------------------------------------------------------

var currentTab; // The id of the currently selected tab

// Refresh everything
function refreshAll()
{
	refreshHeader();
	refreshMenu();
	refreshSidebar();
}

// Refresh title patterns
function refreshTitlePattern()
{
	// 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(n.id.substr(5));
		}
	// Sort the tiddlers by name
	allTiddlers.sort(function (a,b) { if(a == b) return(0); else return (a < b) ? +1 : -1; });
	
	// Output the links
	var titlePattern = ""
	for (t = 0; t < allTiddlers.length; t++)
	{
		if (titlePattern != "")
			titlePattern = titlePattern + "|";
			
		//Rakusai Title validation
		var s = allTiddlers[t];
		s = s.replace(/\\/g,"\\)");
		s = s.replace(/\//g,"\\/");
		s = s.replace(/\./g,"\\.");
		s = s.replace(/\(/g,"\\(");
		s = s.replace(/\)/g,"\\)");
		s = s.replace(/\?/g,"\\?");
		s = s.replace(/\+/g,"\\+");
		s = s.replace(/\[/g,"\\[");
		s = s.replace(/\]/g,"\\]");
		s = s.replace(/\$/g,"\\$");
		s = s.replace(/\^/g,"\\^");
		s = s.replace(/\"/g,"\\\"");
		s = s.replace(/\'/g,"\\'");
		titlePattern = titlePattern + s;
	}

	// Update titlePatternText
	
//	confirm(titlePattern);
	// Remove any existing entry from the store
	var theExisting = document.getElementById("paramTitlePatternText");
	if(theExisting)
		theExisting.parentNode.removeChild(theExisting);
	// Create the new entry in the store
	var place = document.getElementById("paramArea");  
	
	var storeItem = document.createElement("div");
	storeItem.setAttribute("id","paramTitlePatternText");
	storeItem.appendChild(document.createTextNode(titlePattern));
	place.appendChild(storeItem);
	

}


// Refresh all parts of the header
function refreshHeader()
{
/*	// Get the site title and subtitle
	var theTitle = getTiddlerText("SiteTitle");
	if(!theTitle) theTitle = "SiteTitle";
   var theSubtitle = getTiddlerText("SiteSubtitle");
	if(!theSubtitle) theSubtitle = "SiteSubtitle";
	// Set the page title
	document.title = theTitle;
	// Do the title
	var place = document.getElementById("siteTitle");
	while(place.firstChild != null)
		place.removeChild(place.firstChild);
	place.appendChild(document.createTextNode(theTitle));
	// Do the subtitle
	var place = document.getElementById("siteSubtitle");
	while(place.firstChild != null)
		place.removeChild(place.firstChild);
	wikify(theSubtitle,place,null,null,true);
*/	
}

// Refresh all parts of the main menu
function refreshMenu()
{
	var place = document.getElementById("mainMenu");
	while(place.firstChild != null)
		place.removeChild(place.firstChild);
	var menu = getTiddlerText("MainMenu");
	if(!menu) menu = "MainMenu";
	wikify(menu,place,null,null,true);
}

// Refresh all parts of the sidebar
function refreshSidebar()
{
	switch(currentTab)
		{
			case "tabTimeline":
				refreshTabTimeline(1);
				break;
			case "tabTimelineModified":
				refreshTabTimeline(2);
				break;
			case "tabAll":
				refreshTabAll();
				break;
			case "tabTags":
				refreshTabTags();
				break;
			default:
				refreshTabTimeline(2);
				break;
		}
}

// Timeline tab
function refreshTabTimeline(sorttype)
{
	//Rakusai　作成日順の並び替えを追加
	//引数 0 :	作成日順   1:更新日順

	// 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"){
				var sortdate = "";
				if (sorttype == 1){
					sortdate = n.getAttribute("created");
				}else{
					sortdate = n.getAttribute("modified");
				}
				if (!sortdate){
					sortdate = "";
				}
				allTiddlers.push(new Array(n.id.substr(5),sortdate));
			 }
		}
	// 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 tab
	var place = document.getElementById("sidebarContent");
	while(place.firstChild != null)
		place.removeChild(place.firstChild);
   // Output the links
	var lastDay = "";
	for (t = 0; t < allTiddlers.length; t++)
		{
		var theDay = allTiddlers[t][1].substr(0,8);
		if(theDay != lastDay)
			{
			var theDateElement = document.createElement("span");
			var theDateCaption = LocalDateStringFromYYYYMMDDHHMM(allTiddlers[t][1]);
			theDateElement.appendChild(document.createTextNode(theDateCaption));
			theDateElement.className = "sidebarSubHeading";
			place.appendChild(theDateElement);
			place.appendChild(document.createElement("br")); 
			lastDay = theDay;
			}
		place.appendChild(document.createTextNode(String.fromCharCode(160)));
		place.appendChild(document.createTextNode(String.fromCharCode(160)));
		createTiddlyLink(place,allTiddlers[t][0],true,true);
		place.appendChild(document.createElement("br"));
		}
}

// All tab
function refreshTabAll()
{
	// 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("sidebarContent");
	while(place.firstChild != null)
		place.removeChild(place.firstChild);
	// Output the links
	for (t = 0; t < allTiddlers.length; t++)
		{
		createTiddlyLink(place,allTiddlers[t][0],true,true);
		place.appendChild(document.createElement("br"));
		}
}

// Tags tab
function refreshTabTags()
{
}

// ---------------------------------------------------------------------------------
// 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 = window.document.getElementById("storeArea").innerHTML;
	// Select the text in the textbox
	theTextBox.focus();
	theTextBox.select();
;
}

// ---------------------------------------------------------------------------------
// Event handlers
// ---------------------------------------------------------------------------------

// Event handler for clicking on the logo
function onClickLogo(e)
{
	restart();
}

// Event handler for clicking on a tiddly link
function onClickTiddlerLink(e)
{

	if (!e) var e = window.event;
	hideMessage();
	// Get the text of the link
	var title = this.getAttribute("tiddlyLink");
	var single = this.getAttribute("tiddlySingle");
	//Rakusai single指定があるときは、単独でアイテムを開く
	// Close All
	if (single == "true")
		closeAllTiddlers();
	// Display that tiddler
	if(title)
		displayTiddler(this,title,0,null,null,false);
		
   playSound("KASHA",150);
}

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

// Event handler for double click on a tiddler
function onDblClickTiddler(e)
{
	hideMessage();
	// 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,null,null,false);
}

// Event handler for clicking on toolbar close
function onClickToolbarClose(e)
{
	if (!e) var e = window.event;
	// Close that tiddler
	hideMessage();
	if(this.parentNode.id)
		closeTiddler(this.parentNode.id.substr(7),false);
}

// Event handler for clicking on toolbar permalink
function onClickToolbarPermaLink(e)
{
	if(this.parentNode.id)
		window.location.hash = myEncodeURL(this.parentNode.id.substr(7));
//		window.location.hash = encodeURIComponent(encodeURIComponent(this.parentNode.id.substr(7)));
}

// Event handler for clicking on toolbar close
function onClickToolbarDelete(e)
{
	hideMessage();
	// Close that tiddler
	if(this.parentNode.id)
		deleteTiddler(this.parentNode.id.substr(7));
}

// Event handler for clicking on the toolbar backlink
function onClickToolbarBackLink(e)
{
	hideMessage();
	if(this.parentNode.id)
		searchTiddlers(this.parentNode.id.substr(7),true);
}

// Event handler for clicking on toolbar close
function onClickToolbarEdit(e)
{
	hideMessage();
	// Edit that tiddler
	if(this.parentNode.id)
		displayTiddler(null,this.parentNode.id.substr(7),2,null,null,false);
}

// 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)
		undoTiddler(this.parentNode.id.substr(7));
}

// Event handler for clicking on toolbar close
function onClickTab(e)
{

	if (!e) var e = window.event;
	// Get the tab
	var theTab;
	if (e.target)
		theTab = e.target;
	else if (e.srcElement)
		theTab = e.srcElement;
	if (theTab.nodeType == 3) // defeat Safari bug
		theTab = theTab.parentNode;
	// Set the current tab
	currentTab = theTab.id;
	// Reset the classes of the tabs
	var tabs = document.getElementById("sidebarTabs").childNodes;
	for(var i=0;i < tabs.length;i++)
		if(tabs[i].id)
			{
			if(tabs[i].id == currentTab)
				tabs[i].className = "tabSelected";
			else
				tabs[i].className = "tabUnselected";
			}
	// Refresh the content
	refreshSidebar();
	
	// Sound
	playSound("PAGE",0);
}

// Event handler for typing into the search box. We need to detect when the text of the control actually changes, because we get keypresses
// for things like cursor keys that we don't want to trigger a search

var lastSearchText = "";

function onSearch(e)
{
	// Do nothing if there's less than three characters or if it hasn't changed since last time
	var text = document.getElementById("searchText").value;
	if((text.length > 1) && (text != lastSearchText))
		{
		searchTiddlers(text,false);
		lastSearchText = text;
		}
}

function onClearSearch()
{
	var text = document.getElementById("searchText");
	text.value = "";
	hideMessage();
}

// Eek... it's bad that this is done via a function rather than a normal, copy-able href
function onClickPermaView()
{
	var tiddlerDisplay = document.getElementById("tiddlerDisplay");
	var openTiddlers = "";
	var separator = "";
	for(var t=0;t<tiddlerDisplay.childNodes.length;t++)
		{
		openTiddlers = openTiddlers + separator + tiddlerDisplay.childNodes[t].id.substr(7);
		separator = " ";
		}
		window.location.hash = encodeURIComponent(openTiddlers);
}

// ---------------------------------------------------------------------------------
// Sound engine
// ---------------------------------------------------------------------------------

//Rakusai Flashを埋め込むことで、効果音を鳴らす
function playSound(snd,delay)
{


	var theExisting = document.getElementById("soundFlash");
	if(theExisting)
		theExisting.parentNode.removeChild(theExisting);
   
	var place = document.getElementById("mainMenu");
		
	var theEmbed = document.createElement("embed");
	theEmbed.src = "sound.swf";
	theEmbed.FlashVars = "snd=" + snd + "&delay=" + delay;
	theEmbed.quality = "high";
	theEmbed.bgcolor = "#ffffff";
	theEmbed.width = "1";
	theEmbed.height = "1";
	theEmbed.id = "soundFlash";
	theEmbed.name = "soundFlash";
	theEmbed.align = "middle";
	theEmbed.allowScriptAccess = "sameDomain";
	theEmbed.type = "application/x-shockwave-flash";
//	theEmbed.pluginspage = "http://www.macromedia.com/go/getflashplayer";
	place.appendChild(theEmbed);
	

}

// ---------------------------------------------------------------------------------
// 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 smoothly moves an element from the position/size of the start element to the target element
var zoomerElement = null; // Element being shifted; null if none
var zoomerStart; // Where we're shifting from
var zoomerTarget; // Where we're shifting to
var zoomerStartScroll; // Where we're scrolling from
var zoomerTargetScroll; // Where we're scrolling to
var zoomerProgress; // 0..1 of how far we are
var zoomerStep; // 0..1 of how much to shift each step
// 'closer' module of the animation engine closes an element by smoothly reducing it's height to zero
var closerElement = null; // Element being closed; null if none
var closerStartHeight; // Starting height
var closerProgress; // 0..1 of how far we are
var closerStep; // 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)
		doZoomer();
	if(closerElement)
		doCloser();
}

// Start moving the element 'e' from the position of the element 'start' to the position of the element 'target'
function startZoomer(e,start,target,slowly)
{
	stopZoomer();
	zoomerElement = e;
	zoomerStart = start;
	zoomerStartScroll = findScrollY();
	zoomerTargetScroll = ensureVisible(target);
	zoomerTarget = target;
	zoomerProgress = 0;
	zoomerStep = slowly ? 0.01 : 0.12;
	startAnimating();
}

// Stop any ongoing zoomer animation
function stopZoomer()
{
	if(zoomerElement)
		{
		stopAnimating();
		//Rakusai FireFoxでズーム後に線が残るバグ修正
		//透過度を変化させる処理は重いのでコメントアウト
		zoomerElement.style.width = "0";
		zoomerElement.style.left = "-100";
		zoomerElement.style.visibility = "hidden";
//		  zoomerTarget.style.opacity = 1;
		window.scrollTo(0,zoomerTargetScroll);
		zoomerElement = null;
		}
}

// Perform a tick of the zoomer animation
function doZoomer()
{
	zoomerProgress += zoomerStep;
	if(zoomerProgress >= 1.0)
		stopZoomer();
	else
		{
		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";
//		  zoomerTarget.style.opacity = zoomerProgress;
		window.scrollTo(0,zoomerStartScroll + (zoomerTargetScroll-zoomerStartScroll) * f);
		}
}

// Start closing an element
function startCloser(e,slowly)
{
	// Stop any existing close operation
	stopCloser();
	// Create the wrapper div that doesn't have any padding or margins or anything to confuse offsetWidth vs. style.width
	
	var wrapper = createTiddlyElement(e.parentNode,"div",null,null);
	e.parentNode.insertBefore(wrapper,e);
	wrapper.appendChild(e);
	closerElement = wrapper;
	// Save the information for the close operation
	closerStartHeight = closerElement.offsetHeight;
	closerProgress = 0;
	closerStep = slowly ? 0.01 : 0.12;
	closerElement.style.overflow = "hidden";
	startAnimating();
}

// Stop closing the current element
function stopCloser()
{
	if(closerElement)
		{
		stopAnimating();
		closerElement.parentNode.removeChild(closerElement);
		closerElement = null;
		}
}

// Perform a tick of the closer animation
function doCloser()
{
	closerProgress += closerStep;
	if(closerProgress > 1.0)
		stopCloser();
	else
		{
		var f = slowInSlowOut(closerProgress);
		var h = closerStartHeight * (1-f)
		closerElement.style.height = (h <= 2) ? 2 : h;
//		  closerElement.style.opacity = (1-f);
		}
}

// ---------------------------------------------------------------------------------
// Standalone utility functions
// ---------------------------------------------------------------------------------

// Escape a string to ensure it doesn't have any RegExp special characters
function escapeRegExp(s)
{
	// Escape any special characters with that character preceded by a backslashes
	return(s.replace(new RegExp("[\\\\\\^\\$\\*\\+\\?\\(\\)\\=\\!\\|\\,\\{\\}\\[\\]\\.]","g"),"\\$&"));
}

// Return a date in UTC YYYYMMDDHHMM format
function ConvertToYYYYMMDDHHMM(d)
{
	return(d.getFullYear() + '' + (d.getMonth() <= 8 ? '0' : '') + (d.getMonth() + 1) + '' + (d.getDate() <= 9 ? '0' : '') + d.getDate() + (d.getHours() <= 9 ? '0' : '') + d.getHours() + (d.getMinutes() <= 9 ? '0' : '') + d.getMinutes());
}

// Return a date in UTC YYYYMMDDHHMM format
function ConvertToYYYYMMDD(d)
{
	return(d.getFullYear() + '.' + (d.getMonth() <= 8 ? '0' : '') + (d.getMonth() + 1) + '.' + (d.getDate() <= 9 ? '0' : '') + d.getDate());
}

// Convert a date in UTC YYYYMMDDHHMM format to date type
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);
}


//Rakusai Convert a date in local format to string
function LocalDateStringFromYYYYMMDDHHMM(d)
{
	var theLocal = d.substr(0,4) + "年" + 	d.substr(4,2) + "月" + d.substr(6,2) + "日";
	return(theLocal);
}

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

// Get the scroll position for window.scrollTo necessary to scroll a given element into view
function ensureVisible(e)
{
	var posTop = findPosY(e);
	var posBot = posTop + e.offsetHeight;
	var winTop = findScrollY();
	var winHeight = findWindowHeight();
	var winBot = winTop + winHeight;
	if(posTop < winTop)
		return(posTop);
	else if(posBot > winBot)
		{
		if(e.offsetHeight < winHeight)
			return(posTop - (winHeight - e.offsetHeight));
		else
			return(posTop);
		}
	else
		return(winTop);
}

function findWindowHeight()
{
	return(window.innerHeight ? window.innerHeight : document.body.clientHeight);
}

function findScrollY()
{
	return(window.scrollY ? window.scrollY : document.body.scrollTop);
}

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

// Create a non-breaking space
function insertSpacer(place)
{
	place.appendChild(document.createTextNode(String.fromCharCode(160)));
}


//
// addLoadEvent()
// Adds event to window.onload without overwriting currently assigned onload functions.
// Function found at Simon Willison's weblog - http://simon.incutio.com/
//
function addLoadEvent(func)
{	
	var oldonload = window.onload;
	if (typeof window.onload != 'function'){
    	window.onload = func;
	} else {
		window.onload = function(){
		oldonload();
		func();
		}
	}

}

addLoadEvent(main);	// run initLightbox onLoad
