var p = 0;
var text = "";
var niv = 0;
var pre = '<TABLE CLASS="term" BORDER="0" CELLPADDING="0" CELLSPACING="0"><TR>';
var post = '</TR></TABLE>';
var textToTest = "";
var syntax = "CF";
var curSelection = null;
var curFS = null;
var selectLocked = false;
var autoVerify = true;
var curTR, curEL, curStart, curEnd;
var globalSub = "";
var globalError = false;
var stack = new Array("");
var curStack = 0;
function parseNewRE()
	{
	getSelection();
	if (stack[curStack] != expressionArea.value)
		{
		maxStack = ++curStack;
		stack[curStack] = expressionArea.value;
		document.frames['expressionFrame'].disable("redo");
		}
	parseRE();
	}

function redo()
	{
	document.frames['expressionFrame'].document.formul.expressionArea.value = stack[++curStack];
	if (curStack >= maxStack) document.frames['expressionFrame'].disable("redo");
	parseRE();
	}
function undo()
	{
	document.frames['expressionFrame'].document.formul.expressionArea.value = stack[--curStack];
	if (curStack <= 0) document.frames['expressionFrame'].disable("undo");
	document.frames['expressionFrame'].enable("redo");
	parseRE();
	}
	
function parseRE()
	{
	text = expressionArea.value;
	if (curStack > 0) document.frames['expressionFrame'].enable ("undo");
	var title = '<TABLE BORDER="0" HEIGHT="100%" WIDTH="100%"><TR><TD ALIGN="center" VALIGN="middle">';
	p = 0;
	out = parseTerm("");
	diagramDiv.innerHTML = title + out + '</TD></TR></TABLE>';
	var inp = diagramDoc.getElementsByName("input");
	for (var i = 0; i < inp.length; i++)	// restore escaped quotes
		{
		inp[i].value = inp[i].value.replace(/;::;/g, '"');
		}
	if (autoVerify) verify()
	}
function parseTerm(value)
	{
	var localSub = value;
	var localError = false;
	var start = p-1;
	var out = '';
	leg = "0 or more,1 or more,0 or 1,alternative".split(",");
	var car;
	//var opening = text.substr(p++,1);//alert("parse p = " + p + ", opening = " + opening);
	var reverse = 0;
	var done = false;
	//out += '<FIELDSET>';
	//var insertLegend = out.length;
	while (p < text.length)
		{
		car = text.substr(p++,1);//alert("car = " + car);
		localSub += car;
		switch(car)
			{
			case "\\": 
				{
				k = text.substr(p++,1);
				localSub += k;
				singleCar(car+k);
				//value += text.substr(p++,1);
				break;
				}
			case "[":
				{
				if (value.length > 0)
					{
					out += '<TD>' + createInput(value, p) + '</TD>';
					value = "";
					}
				out  += '<TD>' + parseRange() + '</TD>'; 
				localSub += globalSub;
				localError = globalError;
				break;
				}
			case "(":
				{
				if (value.length > 0)
					{
					out += '<TD>' + createInput(value, p) + '</TD>';
					value = "";
					}
				out  += '<TD>' + parseTerm("") + '</TD>';
				localSub += globalSub;
				break;
				}
			case "*":
			case "+":
			case "?":
			case "{":
				{
				p--;
				out = closePar(out, value, start, p+1, "");
				out = pre + out + post;
				return out;
				}
			case ")":
				{
				var REerror = "";
				try	
					{
					//alert ("test : (" + localSub);
					//alert(globalError)
					var RE = new RegExp("(" + localSub, "g");
					}
				catch(e) 
					{
					if (!globalError) REerror = error (e.message);
					localError = true;
					}
				out = closePar(out, value, start, p, REerror);
				out = pre + out + post;
				globalSub = localSub;
				globalError = localError;
				return out;
				}
			case "|":
				{
				out = closeOR(out, value);
				value = "";
				break;
				}
			default:
				{
				singleCar (car);
				break;
				}
			}
		}
	if (value.length > 0)
		out += '<TD>' + createInput(value, p+1) + '</TD>';
	return pre + out + post;
	function singleCar (c)
		{
		var next = text.substr(p,1);
		if (p < text.length && "*+?{".indexOf(next) >=0)
			{
			if (value.length > 0)
				{
				out += '<TD>' + createInput(value, p) + '</TD>';
				value = "";
				}
			out  += '<TD>' + parseTerm(c) + '</TD>'; 
			}
		else value += c;
		}
	}
function parseRange()
	{
	var localSub = "";
	var start = p-1;
	var out = '';
	var car, value = "";
	var reverse = 0;
	var done = false;
	while (p < text.length)
		{
		car = text.substr(p++,1);//alert("p = " + p);alert("car = " + car);
		localSub += car;
		switch(car)
			{
			case "\\": 
				{
				k = text.substr(p++,1);
				value += car + k;
				localSub += k;
				break;
				}
			case "^": 
				{
				if (value == "") reverse = 1;
				value += car;
				break;
				}
			case "]":
				{
				if (value.length == 0 | text.substr(p,1) == "]")
					{
					value += car;
					break;
					}
				out = closeRange(value, reverse, start);
				value = "";
				out = closePar(out, "", start, p, "");
				out = pre + out + post;
				globalSub = localSub;
				return out;
				}
			default:
				{
				value += car;
				break;
				}
			}
		}
	if (value.length > 0)
		out += '<TD>' + createInput(value, p) + '</TD>';
	niv--;
	return pre + out + post;
	}
function closePar (out, value, start, pos, REerror)
	{
	var startFS = start;
	var temp = '';
	var k, legend = "", startCurly = p;
	if (REerror == "")
		{
		if (p < text.length) 
			{
			k = text.substr(p,1);
			if (k == "{") 
				{
				p++;
				legend = parseCurly ();//alert(legend)
				}
			else 
				{
				k = "*+?".indexOf(k)
				if (k >=0)
					{
					legend = leg[k];
					p++;
					}
				}
			}
		}
	else legend = REerror;
	if (legend != "")
		{
		temp += '<LEGEND' + createOnMouse(startCurly,p) + '>' + legend + '</LEGEND>';
		}
	if (value.length > 0)
		{
		out += '<TD>' + createInput(value, pos) + '</TD>';
		}
	temp += pre + out + post;
	return createFieldset(temp, startFS, p);
	}
function parseCurly ()
	{
	var n = -1;
	var m = -1;
	var cur = 0;
	var v, curVal = 0;
	var done = false;
	while (p < text.length && !done)
		{
		car = text.substr(p++,1);
		switch(car)
			{
			case ",": 
				{
				if (cur) return error("Closing } expected");
				cur = 1;
				curVal = 0;
				break;
				}
			case "}": 
				{
				done = true;
				break;
				}
			default:
				{
				if (isNaN(car)) return error("Number expected");
				curVal = curVal*10 + parseInt(car);
				if (cur) n = curVal;
				else m = curVal;
				}
			}
		}
	if (m < 0 && p == text.length) return "";
	if (m < 0) 
		{
		globalError = true;
		return error("invalid repeat spec.");
		}
	if (n <= 0 && !cur) return "" + m + " times";
	if (n <= 0) return "at least " + m;
	if (n == m)return "" + m + " times";
	if (m < n) return "" + m + " to " + n;
	globalError = true;
	return error("invalid"); 
	}
function error (msg)
	{
	return '<FONT COLOR="#FF0000">' + msg + '</FONT>';
	}
function closeOR (out, value)
	{
	if (value.length > 0)
		{
		out += '<TD>' + createInput(value, p) + '</TD>';
		}
	out += '<TD' + createOnMouse(p-1,p) + '>or</TD>' + post + '<BR>' + pre;
	return out;
	}
function closeRange (value, reverse, start)
	{
	var leg = "any in,any not in".split(",");
	var temp,legend = leg[reverse];
	try	
		{
		var RE = new RegExp("[" + value + "]", "g");
		}
	catch(e) 
		{
		legend = error (e.message)
		}
	temp = '<LEGEND' + createOnMouse(p-2-value.length,p) + '>' + legend + '</LEGEND>';
	if (value.length > 0)
		{
		temp += createInput(value, p);
		}
	return createFieldset(temp, start, p);
	}
function verify()
	{
	var temp
	if (text == "") temp = textToTest;
	else
		{
		if (syntax == "CF") text = toJS(text);
		try	
			{
			var RE = new RegExp(text, "g");
			temp = textToTest.replace (RE, ';::;$&:;;:');
			}
		catch(e) 
			{
			temp = textToTest;
			}
		}
	temp = temp.replace(/</g, "&lt;");
	temp = temp.replace(/>/g, "&gt;");
	temp = temp.replace (/;::;/g, '<b><FONT COLOR="#00A000">');
	temp = temp.replace (/:;;:/g, '</FONT></b>');
	temp = temp.replace(/\r/, "<BR>");
	document.frames['testFrame'].testAreaDiv.innerHTML = temp;
	document.frames['testFrame'].testAreaDiv.style.visibility="visible";
	}
function autoVtoggle()
	{
	if (autoVerify = !autoVerify) verify ();
	else document.frames['testFrame'].testAreaDiv.innerHTML = textToTest;
	}
function hLight(el, start, end)
	{
	if (selectLocked) return;
	el.onmouseout = elMouseOut;
	el.onclick = elClick;
	el.beg = start;
	el.end = end;
	highLightExpr(start, end);
	curEL = el;
	}
function highLightExpr(start, end)
	{
	if (selectLocked) return;
	curTR = expressionArea.createTextRange();
	curTR.moveEnd("character", end-curTR.text.length);
	curTR.moveStart("character", start);
	curTR.select();
	curStart = start;
	curEnd = end;
	}
function enterFS(FS, start, end)
	{
	if (selectLocked) return;
	FS.prev = curFS;
	if (curFS)
		{
		curFS.style.borderColor = "#FFC000"
		curFS.style.backgroundColor = ""
		}
	FS.start = start;
	FS.end = end;
	activeFS (FS)
	}
function activeFS(FS)
	{
	if (selectLocked) return;
	curFS = FS;
	curFS.style.borderColor = "#0000FF";
	curFS.style.backgroundColor = "#E0E0FF";
	curFS.onmouseleave = leaveFS;
	curFS.onclick = FSClick;
	highLightExpr(curFS.start, curFS.end);
	}
function leaveFS()
	{
	if (selectLocked) return;
	curFS.style.borderColor = "#FFC000"
	curFS.style.backgroundColor = ""
	if (curFS.prev)activeFS(curFS.prev)
	else curFS = null;
	}
function elMouseOut()
	{
	if (selectLocked) return;
	if (curFS)highLightExpr(curFS.start, curFS.end);
	}
function elClick()
	{
	selectLocked = true;
	document.onmouseout=docOut;
	curEL.onmouseout = null;
	curEL.onchange = elChange;
	curStart = curEL.beg;
	curEnd = curEL.end;
	return false;
	}
function FSClick()
	{
	if (selectLocked) return;
	selectLocked = true;
	document.onmouseout=docOut;
	curEL.onmouseout = null;
	curTR.select();
	}
function docOut()
	{
	selectLocked = false;
	}
function elChange()
	{
	expressionArea.value = text.substr(0, curStart)
		+ curEL.value + text.substr(curEnd);
	parseRE();
	selectLocked = false;
	}
function createInput (value, pos)
	{
	var l = 1 + value.length;
	var val = value.replace (/"/g, ";::;");	// escape quotes
	return '<INPUT NAME="input" CLASS="diag" TYPE="text" SIZE="' + l + '" VALUE="'	+ val + '"' 
		+ createOnMouse(pos-l,pos-1) + '>';
	}
function createOnMouse(start,end)
	{
	return ' onmouseover="parent.hLight(this,' + start + ',' + end + ')"';
	}
function createFieldset(text, start, end)
	{
	return '<TD><FIELDSET onmouseenter="parent.enterFS(this,' + start + "," + end
		+ ')">' + text + '</FIELDSET></TD>';
	}
function dump(t,n)
	{
	document.formul.testAreaDiv.value += "\n\n" + niv + ", " + n + " : " + t;
	}
function setSyntax(s)
	{
	syntax = s;
	if (expressionArea)	
		{
		text = expressionArea.value;
		if (syntax == "CF")
			{
			text = text.replace(/\[\^(A-Za-z|a-zA-Z)\]/g,"[^[:alpha:]]");
			text = text.replace(/\[\^A-Z\]/g,"[^[:upper:]]");
			text = text.replace(/\[\^a-z\]/g,"[^[:lower:]]");
			text = text.replace(/\[\^0-9\]/g,"[^[:digit:]]");
			text = text.replace(/\[\\D\]/g,"[^[:digit:]]");
			text = text.replace(/\[\^A-Za-z0-9\]/g,"[^[:alnum:]]");
			text = text.replace(/\[\^A-Fa-f0-9\]/g,"[^[:xdigit:]]");
			text = text.replace(/\[\\S\]/g,"[^[:space:]]");
			text = text.replace(/\[(A-Za-z|a-zA-Z)\]/g,"[[:alpha:]]");
			text = text.replace(/\[A-Z\]/g,"[[:upper:]]");
			text = text.replace(/\[a-z\]/g,"[[:lower:]]");
			text = text.replace(/\[0-9\]/g,"[[:digit:]]");
			text = text.replace(/\[\\d\]/g,"[[:digit:]]");
			text = text.replace(/\[A-Za-z0-9\]/g,"[[:alnum:]]");
			text = text.replace(/\[A-Fa-f0-9\]/g,"[[:xdigit:]]");
			text = text.replace(/\[\\s\]/g,"[[:space:]]");
			//text = text.replace(/(^|[^\\])\\d/g,"$1[[:digit:]]");
			//text = text.replace(/(^|[^\\])\\s/g,"$1[[:space:]]");
			}
		else text = toJS(text);
		expressionArea.value = text;
		parseRE();
		}
	}
function toJS(text)
	{
	text = text.replace(/\[\[:alpha:\]\]/g,"[A-Za-z]");
	text = text.replace(/\[\[:upper:\]\]/g,"[A-Z]");
	text = text.replace(/\[\[:lower:\]\]/g,"[a-z]");
	text = text.replace(/\[\[:digit:\]\]/g,"[\\d]");
	text = text.replace(/\[\[:alnum:\]\]/g,"[A-Za-z0-9]");
	text = text.replace(/\[\[:xdigit:\]\]/g,"[A-Fa-f0-9]");
	text = text.replace(/\[\[:space:\]\]/g,"[\\s]");
	text = text.replace(/\[\^\[:alpha:\]\]/g,"[^A-Za-z]");
	text = text.replace(/\[\^\[:upper:\]\]/g,"[^A-Z]");
	text = text.replace(/\[\^\[:lower:\]\]/g,"[^a-z]");
	text = text.replace(/\[\^\[:digit:\]\]/g,"[\\D]");
	text = text.replace(/\[\^\[:alnum:\]\]/g,"[^A-Za-z0-9]");
	text = text.replace(/\[\^\[:xdigit:\]\]/g,"[^A-Fa-f0-9]");
	text = text.replace(/\[\^\[:space:\]\]/g,"[\\S]");
	text = text.replace(/\[:alpha:\]/g,"A-Za-z");
	text = text.replace(/\[:upper:\]/g,"A-Z");
	text = text.replace(/\[:lower:\]/g,"a-z");
	text = text.replace(/\[:digit:\]/g,"\\d");
	text = text.replace(/\[:alnum:\]/g,"A-Za-z0-9");
	text = text.replace(/\[:xdigit:\]/g,"A-Fa-f0-9");
	text = text.replace(/\[:space:\]/g,"\\s");
	return text;
	}
function getSelection()
	{
	curSelection = null;
	var sel = document.frames['expressionFrame'].document.selection;
	var rng = sel.createRange();
	var el = rng.parentElement();
	if (!el) return;
	if (el.name == "expressionArea")	curSelection = rng;
	window.status = rng.text;
	}

