/* ----------------------------------------------------------------------
 * File:        webmud.js
 * Description: Javascript part of the UNItopia Webmud Client.
 * Author:      Andreas Klauer (Andreas.Klauer@metamorpher.de)
 *              Alexander Motzkau (gnomi@unitopia.de>
 * Version:     0.3 (2008-03-08)
 * License:     GPL
 * ----------------------------------------------------------------------
 * This file is part of Webmud Client.
 * 
 * Webmud Client is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Webmud Client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Webmud Client; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 * ----------------------------------------------------------------------
 */

var term;
var beeps = 0;
var default_beep = "standard";
var beep_muted = 0;
var locked;

var cmds = new Array();  // List of the last entered commands.
var cmd_pos = -1;        // Our position into that list. 0 = last command

function add_class(elem, eclass)
{
    if(elem.className && elem.className.length>0)
	elem.className += " " + eclass;
    else
	elem.className = eclass;
}

function del_class(elem, eclass)
{
    if(elem.className && elem.className.length>0)
    {
	var newclass = "";
	var classes = elem.className.split(/\s+/);
        for (var oldclass = 0; oldclass < classes.length; oldclass++)
	    if(classes[oldclass] != eclass)
		newclass += " " + classes[oldclass];
	
	elem.className = newclass;
    }
}

function has_class(elem, eclass)
{
    if(elem.className && elem.className.length>0)
    {
        var classes = elem.className.split(/\s+/);
        for (var i = 0; i < classes.length; i++)
            if(classes[i] == eclass)
                return true;
    }
    return false;
}

function finish_beep(num)
{
    switch(num)
    {
	case 0:
	    del_class(term,"beep");
	    setTimeout("finish_beep(1);", 100);
	    return;
	
	case 1:
	    add_class(term,"beep");
	    setTimeout("finish_beep(2);", 200);
	    return;

	case 2:
	    del_class(term,"beep");
	    setTimeout("finish_beep(3);", 500);
	    return;

	case 3:
	    beeps--;
	    if(beeps)
	    {
	        add_class(term,"beep");
		setTimeout("finish_beep(0);", 200);
	    }
	    return;
    }
}

function do_beep(type, num)
{
    try
    {
        var snd;

        if(!beep_muted)
            snd = document.getElementById("beep_" + type);

        if(snd && snd.play)
        {
            try { snd.currentTime = 0; } catch(e) {}
            snd.play();
            return;
        }
    }
    catch(e)
    {
    }

    {
        /* Fall back to visual beep. */

        if(!beeps)
        {
            add_class(term,"beep");
            setTimeout("finish_beep(0);", 200);
        }

        beeps += num;
    }
}

function highlight_div(elem, eclass)
{
    switch(elem.beeping)
    {
        case 1:
            del_class(elem, eclass);
            elem.beeping = 2;
            setTimeout(function() { highlight_div(elem, eclass); }, 150);
            return;
        case 2:
            add_class(elem, eclass);
            elem.beeping = 3;
            setTimeout(function() { highlight_div(elem, eclass); }, 300);
            return;
        case 3:
            del_class(elem, eclass);
            elem.beeping = 4;
            setTimeout(function() { highlight_div(elem, eclass); }, 500);
            return;
        case 4:
            elem.beeping = undefined;
            return;
    }
}

function make_highlight_div(elem, eclass)
{
    return function() { highlight_div(elem, eclass); };
}

function maketip(elem, e, desc)
{
    var mx, my;

    mx = e.clientX;
    my = e.clientY;

    mx += document.body.scrollLeft;
    my += document.body.scrollTop;

    if(elem.tip)
    {
        elem.tip.style.left = mx+"px";
        elem.tip.style.top = my+"px";
    }
    else
    {
        var div = document.createElement("div");
        div.className = "invdesctip";

        div.appendChild(desc);
        div.style.position = "absolute";
        div.style.left = mx+"px";
        div.style.top = my+"px";
        div.style.zIndex = 10;
        div.style.margin = "10px";
        div.style.opacity = 0;
        div.fadepos = 0;
        div.fadein = setInterval(function()
        {
          if(div.style.opacity >= 1)
              clearInterval(div.fadein);
          else
          {
              div.fadepos++;
              div.style.opacity = div.fadepos / 10;
          }
        }, 10);
        document.body.appendChild(div);

        elem.tip = div
    }
}

function loadtip(elem)
{
    funcall("invdesc", elem.id.substr(4), function(xmltext)
    {
        var desc = xmltext.getElementById("invdesc");
        if(!desc)
            return;

        desc.id = desc.id + "." + elem.id;
        maketip(elem, elem.waiting, desc);
        elem.waiting = undefined;
    }, true);

}

function createtip(elem)
{
    return function(e)
    {
        if(!elem.tip)
        {
            if(elem.waiting)
                elem.waiting = e;
            else
            {
                elem.waiting = e;
                setTimeout(function() { if(elem.waiting) { loadtip(elem); } }, 400);
            }
        }
        else
            maketip(elem, e, null);
    }
}

function removetip(elem)
{
    return function(e)
    {
        if(elem.tip)
        {
            var body = document.getElementsByTagName("body")[0];
            body.removeChild(elem.tip);
            elem.tip = undefined;
        }
        elem.waiting = undefined;
    }
}

function inittips(doc, tag, eclass)
{
    var elems = doc.getElementsByTagName(tag);
    for(var i = 0; i < elems.length; i++)
    {
        if(has_class(elems[i], eclass))
        {
            elems[i].onmousemove = createtip(elems[i]);
            elems[i].onmouseout = removetip(elems[i]);
        }
    }
}

function setstyle(style)
{
    var links = document.getElementsByTagName("link");
    for(var i = 0; i < links.length; i++)
    {
        var elem = links[i];
        if(elem.getAttribute("rel").indexOf("style") >= 0 && elem.getAttribute("title"))
        {
            elem.disabled = (elem.id != style);
        }
    }
}

function getheight()
{
    if(!term)
        term = document.getElementById("terminal");

    var line = document.getElementById("firstbr");
    var height =  Math.floor(term.clientHeight/(line.scrollHeight||line.clientHeight)-1);
    if(height<2 || height==Number.NaN || height == Number.POSITIVE_INFINITY)
	return "25";
    else
	return Number(height).toString()
}

function updateinfoheight()
{
    if(!term)
        term = document.getElementById("terminal");
    var infodiv = document.getElementById("clientinfodiv");
    var infotools = document.getElementById("clienttooldiv");
    if(infodiv && infotools)
       infodiv.style.height = (term.offsetHeight - infotools.offsetHeight - 4) + "px";
}

function checkscroll(fun)
{
    if(!term)
        term = document.getElementById("terminal");

    /* Determine Autoscroll */
    var scroll;
    var height = term.scrollHeight || term.offsetHeight;
    
    if (height - term.scrollTop - (term.style.pixelHeight || term.offsetHeight) < 10)
	scroll = true;
    else
	scroll = false;
	
    fun();
    
    /* Scroll to bottom if necessary. */
    if(scroll)
    {
	term.scrollTop = term.scrollHeight || term.offsetHeight;
	/* And a second time for the lazy IE6. */
	term.scrollTop = term.scrollHeight || term.offsetHeight;
    }
}

function addspan(elem)
{
    checkscroll(function()
    {
	term.appendChild(elem);
    });
}

function clearscreen()
{
    if(!term)
        term = document.getElementById("terminal");

    var notthisone = document.getElementById("firstbr");
    var child = term.firstChild;
    while(child)
    {
	var nextchild = child.nextSibling;
	if(child != notthisone)
	    term.removeChild(child);
	
	child = nextchild;
    }
}

function addtext(spanelem, text)
{
    var zeilen = text.split("\n");
    for(var i=0;i<zeilen.length;i++)
    {
	if(i)
	{
	    /* IE-Bug: Make sure, there is a non-empty line before. */
	    var previous;
	    if(spanelem.lastChild)
		previous = spanelem.lastChild;
	    else if(spanelem.previousSibling)
		previous = spanelem.previousSibling.lastChild;
	    else if(term.lastChild)
		previous = term.lastChild.lastChild;
	
	    if(!previous || previous.nodeType != 3 || !previous.nodeValue.length)
	    {
		var dummytext = document.createTextNode(" ");
		spanelem.appendChild(dummytext);
	    }
	    
	    var brelem = document.createElement("br");
	    var textelem = document.createTextNode(zeilen[i]);
	    spanelem.appendChild(brelem);
	    spanelem.appendChild(textelem);
	}
	else if(spanelem.lastChild && spanelem.lastChild.nodeType == 3)
	{
	    spanelem.lastChild.appendData(zeilen[i]);
	}
	else if(zeilen[i].length>0)
	{
	    var textelem = document.createTextNode(zeilen[i]);
	    spanelem.appendChild(textelem);
	}
    }
}

var exitscmd;

function sendtextfun(text)
{
    return function()
    {
        return webmud_send(text);
    };
}


/* 
 * The ANSI Object takes care of terminal output conversion to HTML.
 */
function ANSI()
{
    /* set ANSI color back to normal: */
    this.norm = function()
    {
	this.foreground = undefined;
	this.background = undefined;
	this.start256 = 0;
	this.bold = false;
        this.faint = false;
	this.italic = false;
        this.underlined = false;
	this.blink = false;
        this.reverse = false;
	this.concealed = false;
    };
    
    /* Initialize this object in the normal state: */
    this.norm();
    

    /* Add text. */
    this.add = function(text)
    {
	text = text.replace(/\r\n?/g,"\n");
	
	if(!text.length)
	    return;
	
	if(!this.span)
	{
	    var spanclass = "";
	    var fg, bg;
	    
	    if(this.reverse)
	    {
		fg = this.background;
		bg = this.foreground;
	    }
	    else
	    {
		fg = this.foreground;
		bg = this.background;
	    }
	    
	    if(this.concealed)
		fg = bg;
	    
	    if(fg == undefined)
	    {
		if(this.reverse != this.concealed)
		    spanclass += " freverse"+(this.faint?"f":"");
		else
		    spanclass += " fdefault"+(this.faint?"f":"");
	    }
	    else
		spanclass += " f"+("0123456789abcdef".charAt(fg>>4))+("0123456789abcdef".charAt(fg&15))+(this.faint?"f":"");
	    if(bg == undefined)
	    {
		if(this.reverse)
		    spanclass += " breverse"+(this.faint?"f":"");
		else
		    spanclass += " bdefault"+(this.faint?"f":"");
	    }
	    else
		spanclass += " b"+("0123456789abcdef".charAt(bg>>4))+("0123456789abcdef".charAt(bg&15))+(this.faint?"f":"");
	    
	    if(this.bold)
		spanclass += " bold";
	
	    if(this.italic)
		spanclass += " italic";
	
	    if(this.underlined)
		spanclass += " underline";
	        
	    if(this.blink)
		spanclass += " blink";
	    	    
	    this.span = document.createElement("span");
	    if(spanclass.length)
		this.span.className = spanclass.substr(1);
	    addtext(this.span, text);
	    addspan(this.span);
	}
	else
	{
	    var textelem = this.span;
	    checkscroll(function() { addtext(textelem, text); });
	}
    };
    
    this.flush = function()
    {
	this.span = undefined;
    };
    
    this.echo = true;
    this.process_apc = function(fullcmd)
    {
        var args = fullcmd.split(":");
        var cmd = args[0];

	switch(cmd)
	{
	    case "request":
		funcall("receive", null);
		break;
	
	    case "noecho":
		this.echo = false;
		var inputline = document.getElementById("command");
		try
		{ 
		    inputline.setAttribute("type", "password");
		    inputline.focus()
		}
                catch(e)
		{
		    /* Internet Explorer does not support this command. */
		}
		break;
	
	    case "echo":
		this.echo = true;
		var inputline = document.getElementById("command");
		try
		{ 
		    inputline.setAttribute("type", "text");
		    inputline.focus()
		}
                catch(e)
		{
		    /* Internet Explorer does not support this command. */
		}
		break;
	
	    case "unlock":
		if(locked)
		{
		    clearInterval(locked);
		    locked = undefined;
		}
		window.onbeforeunload = undefined;
		break;
	
	    case "lock":
		if(!locked)
		{
		    if(window.location.hash == "#locked")
			window.location.hash = "#unlocked";
		
		    window.location.hash = "#locked";
		    locked = setInterval(function()
		    {
			if(window.location.hash != "#locked")
			    window.location.hash = "#locked";
		    }, 500);
		    
		    window.onbeforeunload = function()
		    {
		        return "Wenn Sie diese Seite neuladen, wird die Verbindung ins MUD geschlossen.\nVerbindung wirklich beenden?";
		    };
		}
		break;
	
	    case "style":
		if(args.length>1)
		    checkscroll(function() { setstyle("style."+args[1]); });
		break;
	
	    case "beep":
		if(args.length>1)
		    do_beep(args[1], 1);
		break;
	
	    case "defaultbeep":
		if(args.length>1)
		    default_beep = args[1];
		break;
	
	    case "mutebeep":
		beep_muted = 1;
		break;
	
	    case "unmutebeep":
		beep_muted = 0;
		break;
	
	    case "points":
	       var pointsdiv = document.getElementById("points");
	       if(!pointsdiv)
	           break;
	
	       for(var olddiv = pointsdiv.firstChild; olddiv; olddiv = olddiv.nextSibling)
	           olddiv.forremoval = true;
	       
	       for(var i=1; i<args.length; i++)
	       {
	           var subargs = args[i].split(",");
	           if(subargs.length < 3)
	               continue;
	
	           var pid = "point."+subargs[0].toLowerCase();
	           var opid = "outbar."+subargs[0].toLowerCase();
	           var ipid = "inbar."+subargs[0].toLowerCase();
	           var curp = parseInt(subargs[1]);
	           var maxp = parseInt(subargs[2]);
	
	           var pdiv = document.getElementById(pid);
	           if(!pdiv)
	           {
	               pdiv = document.createElement("div");
	               pdiv.setAttribute("id", pid);
	               pdiv.className = "point";
	
	               var ptext = document.createTextNode(subargs[0]+": "+curp+"/"+maxp);
	               pdiv.appendChild(ptext);
	               
	               var odiv = document.createElement("div");
	               odiv.setAttribute("id", opid);
	               odiv.className =  "outbar outbar" + Math.round(curp*8/maxp);
	               var idiv = document.createElement("div");
	               idiv.setAttribute("id", ipid);
	               idiv.className =  "inbar inbar" + Math.round(curp*8/maxp);
	               idiv.style.width = (curp*10/maxp)+"ex";
	               odiv.appendChild(idiv);
	               pdiv.appendChild(odiv);
	               
	               pointsdiv.appendChild(pdiv);

	               pdiv.beeping = 1;
	               pdiv.curpoints = curp;
	               pdiv.maxpoints = maxp;
	               add_class(pdiv, "changedpoint");

	               setTimeout(make_highlight_div(pdiv,"changedpoint"), 500);
	           }
	           else if(pdiv.curpoints != curp || pdiv.maxpoints != maxp)
	           {
	               var ptext = document.createTextNode(subargs[0]+": "+curp+"/"+maxp);
	               pdiv.replaceChild(ptext, pdiv.firstChild);
	               pdiv.forremoval = undefined;

                       pdiv.curpoints = curp;
	               pdiv.maxpoints = maxp;

	               var odiv = document.getElementById(opid);
	               var idiv = document.getElementById(ipid);
	               odiv.className =  "outbar outbar" + Math.round(curp*8/maxp);
	               idiv.className =  "inbar inbar" + Math.round(curp*8/maxp);
	               idiv.style.width = (curp*10/maxp)+"ex";
	
                       if(!pdiv.beeping)
                       {
                           pdiv.beeping = 1;
	                   add_class(pdiv, "changedpoint");
	                   setTimeout(make_highlight_div(pdiv,"changedpoint"), 500);
	               }
	               else
	                   pdiv.beeping = 1;
	           }
	           else
	               pdiv.forremoval = undefined;
	       }

               var removediv = pointsdiv.firstChild;
               while(removediv)
	           if(removediv.forremoval)
	           {
	               var tempdiv = removediv;
	               removediv = removediv.nextSibling;
	
	               pointsdiv.removeChild(tempdiv);
	           }
	           else
	               removediv = removediv.nextSibling;
		updateinfoheight();
		break;

	    case "exits":
		var exits = new Array("h","n","no","nw","o","r","s","so","sw","w","xn","xno","xnw","xo","xs","xso","xsw","xw");
		var himmel = false;
		args.shift();
		args.sort();
		
		var arg = 0;
		
		for(var i = 0; i < exits.length; i++)
		{
		    if(i >= 10 && himmel)
		        break;
		
		    var button = document.getElementById("button."+(i<10 ? exits[i] : exits[i].substr(1)));
		    if(!button)
			continue;
		
		    if(arg < args.length && args[arg] == exits[i])
		    {
		        del_class(button, "inactivebutton");
		        arg++;
		
		        if(i != 0 && i != 5 && i < 10)
		            himmel = true;
		        if(i >= 10 && !exitscmd)
		        {
		            exitscmd = new Array("hoch","norden","nordosten","nordwesten","osten","runter","sueden","suedosten","suedwesten","westen","geradeaus","schraegrechts","schraeglinks","rechts","zurueck","scharfrechts","scharflinks","links");
		            i = 9;
		            continue;
		        }
		    }
		    else
		        add_class(button, "inactivebutton");
		
		    if(exitscmd)
		        button.onclick = sendtextfun(exitscmd[i]);
		}
		break;
	
	    case "update":
		if(args.length<2)
		    break;
		switch(args[1])
		{
		    case "inv":
		        funcall("inventory", null, function(xmltext)
		        {
		            var info = document.getElementById("clientinfodiv");
		            if(!info)
		                return;
		            var oldinv = document.getElementById("inventory");
		            var newinv = xmltext.getElementById("inventory");
		
		            if(oldinv)
		                info.replaceChild(newinv, oldinv);
		            else
		                info.appendChild(newinv);
		
		            inittips(document, "li", "invitem");
		
		        }, true);
		}
		break;
	
	    case "edit":
	    {
	        if(args.length<4)
	            break;
	
	        var editid = args[1];
	        var editname = args.slice(3).join(":");
	
	        var editfun = function(text)
	        {
	            edit(editname, text, function(result)
	            {
	                funcall("editsave", result ? editid+"\n"+result : editid, function dummy(arg) {}, false);
	            });
	        };
	
	        if(parseInt(args[2]))
	            funcall("editload", editid, editfun, false);
	        else
	            editfun("");
	    }
	}
    };

    this.process_csi = function(cmd, opt)
    {
	if(cmd == "m")
	{
	    var opts = opt.split(";");
	    
	    this.span = undefined;
	    
	    for(var i=0; i<opts.length; i++)
	    {
		var sopt = Number(opts[i]);
		
		if(this.start256&4)
		{
		    if(this.start256&1)
			this.background = sopt;
		    else
			this.foreground = sopt;
		
		    this.start256 = 0;
		    continue;
		}
		
		switch(sopt)
		{
		    case 0:
            		this.norm();
			break;
        	    case 1:
			this.bold = true;
			break;
		    case 2:
			this.faint = true;
			break;
		    case 3:
			this.italic = true;
			break;
		    case 4:
			this.underlined = true;
			break;
		    case 5:
			if(this.start256&2)
			{
			    this.start256|=4;
			    break;
			}
			/* Fallthrough */
		    case 6:
			this.blink = true;
			break;
		    case 7:
			this.reverse = true;
			break;
		    case 8:
			this.concealed = true;
			break;

		    case 38:
			this.start256 = 2;
			/* Fallthrough */
		    case 39:
			this.foreground = undefined;
			break;
		    case 48:
			this.start256 = 3;
			/* Fallthrough */
		    case 49:
			this.background = undefined;
			break;
			
		    default:
			if(sopt>=30 && sopt<38)
			    this.foreground = sopt-30;
			else if(sopt>=40 && sopt<48)
			    this.background = sopt-40;
			break;
		}
	    }
	}
	else if(cmd == "J")
	{
	    var num = Number(opt);
	    if(num==1 || num==2 || (opt.length==0 && this.home))
	    {
		this.span = undefined;
		clearscreen();
	    }
	}
	else if(cmd == "H" && opt.length==0)
	    this.home = true;
	else
	    this.home = undefined;
    };

    /* Process raw text. */
    this.buffer = "";
    this.escbuffer = "";
    
    this.process_text = function(text)
    {
	var pos;
	
	text = this.buffer + text;
	this.buffer = "";
	
	if((pos = text.indexOf("\007"))>=0)
	{
	    var num_beeps = 0;
	
	    do
	    {
		text = text.substr(0, pos) + text.substr(pos+1);
		num_beeps++;
	    }
	    while((pos = text.indexOf("\007", pos))>=0);
	
	    do_beep(default_beep, num_beeps);
	}

	while((pos = text.indexOf("\033"))>=0)
	{
	    var len = text.length;
	    var cmd, opt;

	    this.add(text.substr(0,pos));
	    
	    
	    if(pos+1 == len)
	    {
		this.buffer = text.substr(pos);
		return;
	    }
	    
	    switch(text.charAt(pos+1))
	    {
		case "[": /* ECMA-48 CSI */
		{
		    var i = pos+2;
		    var c;

		    while(i < len)
	    	    {
			c = text.charCodeAt(i);
			if((c>96 && c<=122) || (c>64 && c<=90) || c==27)
			    break;
			i++;
	    	    }
		    
		    if(i == len || (c==27 && i+1 == len))
		    {
			this.buffer = text.substr(pos);
			return;
		    }
		    
		    if(c==27)
		    {
			this.escbuffer = text.substr(pos, i-pos);
			text = text.substr(i);
			continue;
		    }
	    
		    this.process_csi(text.substr(i,1),text.substr(pos+2,i-pos-2));
		    text = text.substr(i+1);
		    break;
		}
		
		case "c":
		    this.norm();
		    break;
		
		case "_": /* ECMA-48 APC */
		{
		    var i = text.indexOf("\033\\", pos+2);
		    if(i < 0)
		    {
			this.buffer = text.substr(pos);
			return;
		    }
		    
		    this.process_apc(text.substr(pos+2, i-pos-2));
		    text = text.substr(i+2);
		    break;
		}
		
		case "\033":
		    this.escbuffer="\033";
		    text = text.substr(pos+1);
		    continue;
		
		default:
		    text = text.substr(pos+2);
		    break;
	    }
	    
	    text = this.escbuffer + text;
	    this.escbuffer = "";
	}
	
	this.add(text);
    };
}

var ansi = new ANSI();

var windowlist = new function()
{
    this.list = new Array();
    this.add = function(win)
    {
        win.winpos = this.list.length;
        this.list.push(win);
        win.div.style.zIndex = 10 + win.winpos;
    };

    this.focus = function(win)
    {
        for(var i = win.winpos; i < this.list.length - 1; i++)
        {
            var dwin = this.list[i+1];
            this.list[i] = dwin;
            dwin.div.style.zIndex = 10 + i;
            dwin.winpos = i;
        }

        this.list[this.list.length-1] = win;
        win.div.style.zIndex = 9 + this.list.length;
        win.winpos = this.list.length-1;
    };
};

function FWindow(titel, content, closecb)
{
    this.dragging = false;
    this.resizing = 0;

    this.posx = 100;
    this.posy = 100;
    this.sizex = 250;
    this.sizey = 350;
    this.closefun = closecb;

    var correctheight = false;
    var correctpos = false;

    if(content && content.parentNode)
    {
        correctpos = true;
        this.sizex = content.offsetWidth + 6;
        this.sizey = content.offsetHeight + 6;

        this.posx = content.offsetLeft - 3;
        this.posy = content.offsetTop - 3;
        var par = content.offsetParent;
        while(par)
        {
            this.posx += par.offsetLeft;
            this.posy += par.offsetTop;
            par = par.offsetParent;
        }
    }
    else if(content)
    {
        if(content.suggestwidth)
            this.sizex = content.suggestwidth + 6;
        if(content.suggestheight)
        {
            this.sizey = content.suggestheight + 6;
            correctheight = true;
        }
        if(content.suggestposx)
            this.posx = content.suggestposx;
        if(content.suggestposy)
            this.posy = content.suggestposy;
    }

    this.drag = function(e)
    {
        if(!this.dragging)
        {
            document.onmousemove = null;
            return true;
        }

        var mx, my;

        mx = e.clientX + document.body.scrollLeft;
        my = e.clientY + document.body.scrollTop;

        this.posx += mx - this.mx;
        this.posy += my - this.my;

        if(this.posx < 0)
        {
            mx -= this.posx;
            this.posx = 0;
        }

        if(this.posy < 0)
        {
            my -= this.posy;
            this.posy = 0;
        }

        this.mx = mx;
        this.my = my;

        this.div.style.left = this.posx+"px";
        this.div.style.top = this.posy+"px";

        return false;
    };

    this.dragstop = function(e)
    {
        document.onmouseup = null;
        if(!this.dragging)
            return true;

        document.onmousemove = null;
        this.dragging = false;

        return false;
    };

    this.resize = function(e)
    {
        if(!this.resizing)
        {
            document.onmousemove = null;
            return true;
        }

        var mx, my, diffx, diffy;

        mx = e.clientX + document.body.scrollLeft;
        my = e.clientY + document.body.scrollTop;

        diffx = mx - this.mx;
        diffy = my - this.my;

        if(this.resizing & 1)
        {
            this.sizex -= diffx;
            if (this.sizex < 100)
            {
                mx -= 100 - this.sizex;
                diffx -= 100 - this.sizex;
                this.sizex = 100;
            }
            this.posx += diffx;
        }

        if(this.resizing & 2)
        {
            this.sizey -= diffy;
            if (this.sizey < 50)
            {
                my -= 50 - this.sizey;
                diffy -= 50 - this.sizey;
                this.sizey = 50;
            }
            this.posy += diffy;
        }

        if(this.resizing & 4)
        {
            this.sizex += diffx;
            if (this.sizex < 100)
            {
                mx += 100 - this.sizex;
                this.sizex = 100;
            }
        }

        if(this.resizing & 8)
        {
            this.sizey += diffy;
            if (this.sizey < 50)
            {
                my += 50 - this.sizey;
                this.sizey = 50;
            }
        }

        this.mx = mx;
        this.my = my;

        this.div.style.left = this.posx+"px";
        this.div.style.top = this.posy+"px";
        this.div.style.width=this.sizex+"px";
        this.div.style.height=this.sizey+"px";

        if(this.onresize)
            this.onresize();

        return false;
    };

    this.resizestop = function(e)
    {
        document.onmouseup = null;
        if(!this.resizing)
            return true;

        document.onmousemove = null;
        this.resizing = 0;

        return false;
    };

    this.close = function()
    {
        document.body.removeChild(this.div);
        if(this.closefun)
            this.closefun();
        return false;
    };


    this.div = document.createElement("div");
    this.div.className = "fwindow";

    var self = this;

    var tbl = document.createElement("table");
    var lineclass = new Array("fup", "ftitle", "fmiddle", "fdown");
    var colclass = new Array("fleft", "fcenter", "fright");
    var lineresize = new Array(2, 0, 0, 8);
    var colresize = new Array(1, 0, 4);
    var titlediv;

    for(var l=0; l<4; l++)
    {
        var line = tbl.insertRow(-1);
        line.className = lineclass[l];

        for(var c=0; c<3; c++)
        {
            var col = line.insertCell(-1);
            col.className = colclass[c];

            var innerdiv;
            innerdiv = document.createElement("div");
            if(l == 2 && c == 1 && content)
                innerdiv.appendChild(content);

            if(l == 1 && c == 1)
            {
                var titletext = document.createTextNode(titel);
                innerdiv.appendChild(titletext);
                var closediv = document.createElement("div");
                closediv.className = "fclose";
                var closetext = document.createTextNode("\u2217");
                closediv.appendChild(closetext);
                innerdiv.appendChild(closediv);

                closediv.onclick = function(e) { return self.close(); }

                innerdiv.onmousedown = function(e)
                {
                    self.dragging = true;
                    self.mx = e.clientX + document.body.scrollLeft;
                    self.my = e.clientY + document.body.scrollTop;

                    document.onmousemove = function(e) { return self.drag(e); }
                    document.onmouseup = function(e) { return self.dragstop(e); }
                    windowlist.focus(self);

                    return false;
                };

                titlediv = line;
            }
            var res = lineresize[l] + colresize[c];
            if(res)
            {
                col.resizeflag = res;
                col.onmousedown = function(e)
                {
                    self.resizing = this.resizeflag;
                    self.mx = e.clientX + document.body.scrollLeft;
                    self.my = e.clientY + document.body.scrollTop;

                    document.onmousemove = function(e) { return self.resize(e); }
                    document.onmouseup = function(e) { return self.resizestop(e); }
                    windowlist.focus(self);

                    return false;
                };
            }
            col.appendChild(innerdiv);
        }
    }
    this.div.appendChild(tbl);

    this.div.onmousedown = function(e)
    {
        windowlist.focus(self);
        return true;
    };

    if(this.posx < 0)
        this.posx = 0;
    if(this.posy < 0)
        this.posy = 0;

    this.div.style.width=this.sizex+"px";
    this.div.style.height=this.sizey+"px";

    this.div.style.position = "absolute";
    this.div.style.left = this.posx+"px";
    this.div.style.top = this.posy+"px";

    windowlist.add(this);
    document.body.appendChild(this.div);

    if(content)
    {
        if(correctpos)
        {
            this.posy -= titlediv.offsetHeight;
            this.div.style.top = this.posy+"px";
        }
        if(correctheight)
        {
            this.sizey += titlediv.offsetHeight;
            this.div.style.height=this.sizey+"px";
        }
    }
}

function edit(name, text, cb)
{
    var win;
    var cancel = true;

    var editor = document.createElement("table");
    editor.className = "editor";

    var commands = editor.insertRow(-1);
    commands.className = "editcommands";

    var bcell = commands.insertCell(-1);
    var button = document.createElement("input");
    button.className = "editbutton";
    button.type = "button";
    button.value = "Speichern";
    button.onclick = function(e)
    {
        cancel = false;
        win.close();
    };
    bcell.appendChild(button);

    bcell = commands.insertCell(-1);
    button = document.createElement("input");
    button.className = "editbutton";
    button.type = "button";
    button.value = "Abbrechen";
    button.onclick = function(e)
    {
        cancel = true;
        win.close();
    };
    bcell.appendChild(button);

    var arow = editor.insertRow(-1);
    arow.className = "editrow";
    var acell = arow.insertCell(-1);
    acell.colSpan = 2;

    var area = document.createElement("textarea");
    area.className = "editarea";
    area.cols = 80;
    area.rows = 2;
    area.setAttribute("wrap","off");
    area.onkeypress = function(e)
    {
       if(e.keyCode == 13 && !e.shiftKey && !e.altKey && !e.ctrlKey)
       {
           /* Automatische Einrueckung */
           area.keyinfo = e;
           var txt = area.value;
           var sel = area.selectionStart;
           var oldheight = area.scrollHeight;
           var oldpos = area.scrollTop;

           var numlines = txt.split('\n').length-1;
           var posline = txt.substring(0, area.selectionStart).split('\n').length;
           var bottom = (area.scrollTop + area.clientHeight < oldheight * posline / numlines);

           var zbegin = txt.lastIndexOf('\n', sel-1) + 1;
           var zend;
           for(zend = zbegin; zend < sel && (txt.charAt(zend) == ' ' || txt.charAt(zend) == '\t'); zend++);
           area.value = txt.substring(0, area.selectionStart) + "\n" + txt.substring(zbegin, zend) + txt.substring(area.selectionEnd);

           /* Check if they are counting "\n" twice. */
           if(area.textLength > txt.length + 1 + zend - zbegin)
               sel++;
           area.selectionStart = sel + 1 + zend - zbegin;
           area.selectionEnd = area.selectionStart;
           if(bottom)
               area.scrollTop = oldpos + area.scrollHeight - oldheight;
           else
               area.scrollTop = oldpos;
           return false;
       }

       if(e.keyCode == 9)
       {
           /* Tab-Unterstuetzung */
           var sel = area.selectionStart;
           var txt = area.value;
           var oldpos = area.scrollTop;
           area.value = txt.substring(0, area.selectionStart) + "\t" + txt.substring(area.selectionEnd);
           area.selectionStart = sel + 1;
           area.selectionEnd = area.selectionStart;
           area.scrollTop = oldpos;
           return false;
       }
    };
    var textnode = document.createTextNode(text);
    area.appendChild(textnode);

    acell.appendChild(area);
    editor.suggestwidth = term.clientWidth;
    editor.suggestheight = Math.ceil(term.clientWidth*3/4);
    editor.suggestposx = Math.ceil(term.clientWidth/2);

    win = new FWindow(name, editor, function(win)
    {
        cb(cancel ? null : area.value);
    });

    win.onresize = function()
    {
        area.style.display = "none";
        area.style.height = (area.parentNode.clientHeight-7)+"px";
        area.style.width = (area.parentNode.clientWidth-2)+"px";
        area.style.display = "block";
    };

    win.onresize();
}

function funcall(fun, data, cb, xml, retry)
{
    var url = window.location.protocol + "//" + window.location.host + "/webmud2/webmud.wsgi/" + fun+"/"+connid;
    var src = window.location.search;

    if(src != "")
    {
        if(src.charAt(0)=="?")
            src = src.substr(1);

        var srcparts = src.split('&');
        for(var i=0; i<srcparts.length; i++)
            if(srcparts[i].indexOf('SESSIONID=')==0)
            {
                url += "?" + srcparts[i];
                break;
            }
    }

    if(!cb && !xml)
    {
        cb = function(txt) { ansi.process_text(txt); }
        retry = function() { funcall(fun, data); }
    }

    var request;
    if (window.ActiveXObject) // MSIE
        request = new ActiveXObject("Microsoft.XMLHTTP");
    else
        request = new XMLHttpRequest();

    request.onreadystatechange = function()
        {
            if(request.readyState != 4)
                return;

            if(request.status != 200)
            {
                if(retry)
                    setTimeout(retry, 200);
                return;
            }

            cb(xml ? request.responseXML : request.responseText);
        };
    request.open(data ? 'POST' : 'GET', url, true);
    request.setRequestHeader("X-Requested-With", "XMLHttpRequest"); 
    request.setRequestHeader("Accept", xml ? "text/xml" : "text/plain");
    if(data)
        request.setRequestHeader("Content-Type", "text/plain; charset=utf-8");
    request.send(data);
}

function webmud_send(text)
{
    ansi.flush();
    
    var spanem = document.createElement("span");
    var textem = document.createTextNode((ansi.echo?text:""));
    var brem = document.createElement("br");
    spanem.className = "cmd";
    spanem.appendChild(textem);
    spanem.appendChild(brem);
    addspan(spanem);

    funcall("send", text+"\n");
    return false;

}

function webmud_submit()
{
    var inputline = document.getElementById("command");
    var text = inputline.value;
    inputline.value = "";

    if(text.length > ((text.substring(0,1) == "!")?3:2) && inputline.getAttribute("type") != "password")
    {
        if(cmd_pos >= 0)
            cmds[cmds.length-1] = text;
        else
            cmds.push(text);

        cmd_pos = -1;

        if(cmds.length > 20)
            cmds.shift();
    }

    return webmud_send(text);
}

function webmud_resize()
{
    if(!term)
        term = document.getElementById("terminal");

    var height
    if(window.innerHeight)
        height = Math.floor(window.innerHeight*0.98);
    else if(document.documentElement && document.documentElement.clientHeight)
        height = document.documentElement.clientHeight;
    else
    {
	funcall("height", getheight());
	return true;
    }

    var ob = term;
    while(ob)
    {
	height -= ob.offsetTop;
	ob = ob.offsetParent;
    }
    ob = document.getElementById("control");
    height -= ob.offsetHeight;
    ob = document.getElementById("mainfooter");
    height -= ob.offsetHeight;
    height -= 2*term.offsetTop;
    height -= 20; // Cellpadding
    height -= 10; // Terminal-Border

    checkscroll(function()
    {	
	term.style.height = height+"px";
    });
    
    funcall("height", getheight());
    updateinfoheight();
    
    return true;
}

function webmud_connect()
{
    funcall("connect", getheight());
    return true;
}

function webmud_disconnect()
{
    funcall("disconnect", null);
    return true;
}

function webmud_keypress(ev)
{
    switch(ev.keyCode)
    {
        case 38: // Hoch
            if(cmds.length)
            {
                var inputline = document.getElementById("command");
                var text = inputline.value;

                if(cmd_pos < 0)
                {
                    cmds.push(text);
                    cmd_pos = 1;

                    inputline.value = cmds[cmds.length-2];
                }
                else if(cmd_pos < cmds.length-1)
                {
                    cmd_pos++;
                    inputline.value = cmds[cmds.length-1-cmd_pos];
                }
            }
            return false;

        case 40: // Runter
            if(cmd_pos > 0)
            {
                var inputline = document.getElementById("command");

                if(cmd_pos > 1)
                {
                    cmd_pos--;
                    inputline.value = cmds[cmds.length-1-cmd_pos];
                }
                else
                {
                    inputline.value = cmds.pop();
                    cmd_pos = -1;
                }
            }
            return false;
    }
    return true;
}
