diff --git a/exec/load/chateng.js b/exec/load/chateng.js
index 9ea6a60e5f83a54406b8dc3b2357998d8bc1dde4..78218d3538d5f22b137ae3dd2ca18a11762d53c2 100644
--- a/exec/load/chateng.js
+++ b/exec/load/chateng.js
@@ -23,6 +23,7 @@ function Chat(key,engine)
 		engine=new ChatEngine();
 		engine.init();
 	}
+	engine.redraw();
 	if(key)
 	{
 		engine.processKey(key);
@@ -61,76 +62,47 @@ load("scrollbar.js");
 load("graphic.js");
 load("str_cmds.js");
 load("nodedefs.js");
+load("msgwndw.js");
+
 bbs.sys_status |= SS_PAUSEOFF;	
 oldpass=console.ctrl_key_passthru;
 
-function ChatEngine(root)
-{
-	const flag_normal=			"#";
-	const flag_global=			"!";
-	const flag_private=		"%";
-	const flag_alert=			2;
-	const flag_notice=			1;
-	const flag_message=		0;
-	
-	const local_color=			"\1n\1g";
-	const remote_color=		"\1n\1c";
-	const alert_color=			"\1r\1h";
-	const notice_color=		"\1n\1y";
-	const input_color=			"\1n";
-	const private_color=		"\1y\1h";
-	const global_color=		"\1n\1m";
+const flag_normal=			"#";
+const flag_global=			"!";
+const flag_private=		"%";
+const flag_alert=			2;
+const flag_notice=			1;
+const flag_message=		0;
+
+const local_color=			"\1n\1g";
+const remote_color=		"\1n\1c";
+const alert_color=			"\1r\1h";
+const notice_color=		"\1n\1y";
+const input_color=			"\1n";
+const private_color=		"\1n\1w";
+const global_color=		"\1n\1m";
 	
+function ChatEngine(root)
+{	
 	//TODO: the only time this will be used is for storing chat history
 	//maybe give ALL chat history files their own home, independent of the parent script
 	var root_dir=(root?root:js.exec_dir);
 	var stream=new ServiceConnection("chat");
-	var scope=flag_normal;
-	var window=false;
-	var message=new Object();
-	var fullscreen=true;
-	var scrollbar=false;
-	var background="\0010";
-	var ignore=[];
-	
-	this.buffer="";
-	this.room="main";	
-	this.columns=console.screen_columns;
-	this.rows=console.screen_rows;
-	this.x=1;
-	this.y=1;
-	this.input_line;
+	this.input_line=new InputLine();
+	this.chat_room=new ChatRoom();
 	
 	// USEFUL METHODS 
-	this.init=function(room,c,r,x,y,bg,ix,iy,iw) //NOTE: DESTROYS BUFFER AND MESSAGE LIST
+	this.init=function(room,c,r,x,y,ix,iy,iw,bg) //NOTE: DESTROYS BUFFER AND MESSAGE LIST
 	{
-		if(x) this.x=x;				//top left corner x
-		if(y) this.y=y;				//top left corner y
-		if(room) this.room=room;		//room name (for lobby style interface)
-		if(bg) background=bg;
-		this.buffer="";
-
-		if(c && r) {
-			fullscreen=false;
-			this.columns=c;
-			this.rows=r;
-			if(this.columns>=console.screen_columns) this.columns=console.screen_columns-1;
-			scrollbar=new Scrollbar(this.x+this.columns,this.y,this.rows,"vertical","\1k\1h"); 
-		}
- 		if(ix && iy) {
-			this.input_line=new InputLine(ix,iy,iw?iw:this.columns,bg);
-			fullscreen=false;
-		} else if(ix || iy) {
-			log("invalid input line parameters",LOG_WARNING);
-			return false;
-		}
-		if(!fullscreen) {
-			console.ctrlkey_passthru="+ACGKLOPQRTUVWXYZ_";
-			bbs.sys_status|=SS_MOFF;
-			window=new Graphic(this.columns,this.rows,undefined,undefined,true);
-		}
+		this.input_line=new InputLine();
+		this.input_line.init(ix,iy,iw,bg);
+		this.chat_room.init(room,x,y,c,r,bg);
 		this.entryMessage();
-		return true;
+	}
+	this.initbox=function()
+	{
+		this.chat_room.initbox();
+		this.input_line.initbox();
 	}
 	this.exit=function()
 	{
@@ -144,47 +116,21 @@ function ChatEngine(root)
 	this.exitMessage=function()
 	{
 		if(user.compare_ars("QUIET")) return false;
-		var message=user.alias + " disconnected";
-		this.send(message,flag_notice);
+		var message=new Message(user.alias + " disconnected",flag_notice);
+		this.send(message);
 	}
 	this.entryMessage=function()
 	{
 		if(user.compare_ars("QUIET")) return false;
-		var message=user.alias + " connected";
-		this.send(message,flag_notice);
+		var message=new Message(user.alias + " connected",flag_notice);
+		this.send(message);
 	}
 	this.resize=function(x,y,c,r,ix,iy,iw) //NOTE: DOES NOT DESTROY BUFFER OR MESSAGE LIST
 	{
-		this.clearChatWindow();
-		if(x) this.x=x;
-		if(y) this.y=y;
-		if(c) this.columns=columns;
-		if(r) this.rows=rows;
-		if(ix && iy) {
-			if(!this.input_line) {
-				this.input_line=new InputLine(ix,iy,iw?iw:this.columns,background);
-			} else {
-				this.input_line.x=ix;
-				this.input_line.y=iy;
-				this.input_line.width=iw?iw:this.columns;
-			}
-		} else {
-			if(this.input_line)	{
-				this.input_line.x=this.x;
-				this.input_line.y=this.y+this.rows+1;
-				this.input_line.width=this.columns;
-			}
-		}
-		if(this.input_line) {
-			scrollbar=new Scrollbar(this.x+this.columns-1,this.y,this.rows,"vertical",DARKGRAY); 
-		}
+		this.input_line.init(ix,iy,iw);
+		this.chat_room.init(x,y,c,r);
 		this.redraw();
 	}
-	this.ignoreUser=function(alias)
-	{
-		if(ignore[alias]==true) ignore[alias]=false;
-		else ignore[alias]==true;
-	}
 	this.getUserList=function()
 	{
 		
@@ -192,47 +138,36 @@ function ChatEngine(root)
 	this.findUser=function(id)
 	{
 		//return stream.findUser(id);
-	}
-	this.clearChatWindow=function() //CLEARS THE ENTIRE CHAT WINDOW
-	{
-		clearBlock(this.x,this.y,this.columns,this.rows);
-	}
-	this.list=function(array,color) //DISPLAYS A TEMPORARY MESSAGE IN THE CHAT WINDOW (NOT STORED)
-	{
-		for(var i=0;i<array.length;i++) this.display(array[i],color);
-	}
-	this.notice=function(msg)
-	{
-		this.display(msg,notice_color);
-	}
-	this.alert=function(msg)
-	{
-		this.display(msg,alert_color);
+		return true;
 	}
 	this.redraw=function()
 	{
-		this.clearChatWindow();
-		this.display();
-		this.printBuffer();
+		this.chat_room.draw();
+		this.input_line.draw();
 	}
-	this.send=function(txt,level,scope,source,target)
+	this.send=function(message)
 	{
-		if(!level) level=flag_message;
-		if(!scope) scope=flag_normal;
-		var packet=new Message(txt,level,scope,source,target);
-		if(!stream.send(packet)) {
+		if(!message.level) message.level=flag_message;
+		if(!message.scope) message.scope=flag_normal;
+		if(!stream.send(message)) {
 			this.alert("message not sent.");
 		}
 	}
 	this.receive=function()
 	{
+		var notices=stream.getNotices();
+		while(notices.length) {
+			this.chat_room.notice(notices.shift());
+		}
 		var packet=stream.receive();
+		this.processData(packet);
+	}
+	this.processData=function(packet)
+	{
 		if(packet && packet.txt) {
-			this.processData(packet);
+			this.chat_room.process(packet);
 		}
 	}
-	
-	//INTERNAL METHODS
 	this.processKey=function(key) //PROCESS ALL INCOMING KEYSTROKES
 	{
 		switch(key.toUpperCase())
@@ -267,233 +202,189 @@ function ChatEngine(root)
 		case KEY_DOWN:
 		case '\x02':	/* CTRL-B KEY_HOME */
 		case '\x05':	/* CTRL-E KEY_END */
-			this.showHistory(key);
+			this.chat_room.scroll(key);
 			break;
 		case '\b':
-			this.backSpace();
+			this.input_line.backspace();
 			break;
 		case '\r':
 		case '\n':
-			this.submitMessage();
+			this.submit();
 			break;
 		case '\x09':	/* CTRL-I TAB */
-			if(message.scope==flag_global) message.scope=flag_normal;
-			else message.scope=flag_global;
-			this.printBuffer();
-			break;
-		case ';':
-			this.bufferKey(key);
-			if(this.buffer.length==1 && fullscreen && user.compare_ars("SYSOP")) {
-				this.getStringCommand()
-				this.resetInput();
-				break;
-			}
+			this.input_line.toggle();
 			break;
 		case '@':
 			if(!user.compare_ars("SYSOP") && !(bbs.sys_status&SS_TMPSYSOP)) break;
 		default:
-			this.bufferKey(key);
+			this.input_line.bufferKey(key);
 			break;
 		}
 		return true;
 	}
-	this.getStringCommand=function()
+	this.submit=function()
 	{
-		var cmd=console.getstr();
-		if(cmd.length>0) {
-			str_cmds(cmd);
-			return true;
-		} else return false;
+		var message=this.input_line.submit();
+		if(message) {
+			this.send(message);
+			this.chat_room.process(message);
+		}
 	}
-	this.submitMessage=function()
+}
+function ChatRoom()
+{
+	this.columns=console.screen_columns;
+	this.rows=console.screen_rows;
+	this.x=1;
+	this.y=1;
+	this.window;
+	this.scrollbar;
+	this.fullscreen=true;
+	this.ignored=[];
+	this.box=false;
+	this.room="Main";
+	this.bg="";
+	
+	this.init=function(room,x,y,c,r,bg)
 	{
-		if(strlen(this.buffer)>0) {
-			this.send(this.buffer,message.level,message.scope,user.alias,message.target);
-			switch(message.scope) 
-			{
-			case flag_global:
-				this.display(this.buffer,global_color,user.alias);	
-				break;
-			case flag_private:
-				this.display(this.buffer,private_color,user.alias,message.target);
-				break;
-			case flag_normal:
-			default:
-				this.display(this.buffer,local_color,user.alias);
-				break;
-			}
+		if(!(x || y || c || r)) {
+			this.fullscreen=true;
+			console.ctrlkey_passthru=oldpass;
+			bbs.sys_status&=~SS_MOFF;
+			return;
+		}
+		if(x && y) {
+			this.x=x;
+			this.y=y;
+			this.fullscreen=false;
+		} 
+		if(c && r) {
+			this.columns=c;
+			this.rows=r;
+			this.fullscreen=false;
 		}
-		this.resetInput();
+		if(bg) this.bg=bg;
+		if(room) this.room=room;
+		
+		if(this.box) this.initbox();
+		if(this.columns>=console.screen_columns) this.columns=console.screen_columns-1;
+		this.scrollbar=new Scrollbar(this.x+this.columns,this.y,this.rows,"vertical","\1k\1h"); 
+		this.window=new Graphic(this.columns,this.rows,getColor(this.bg));
+		console.ctrlkey_passthru="+ACGKLOPQRTUVWXYZ_";
+		bbs.sys_status|=SS_MOFF;
 	}
-	this.backSpace=function()
+	this.initbox=function()
 	{
-		if(this.buffer.length>0) {
-			if(fullscreen) {
-				console.left();
-				console.cleartoeol();
-				this.buffer=this.buffer.substr(0,this.buffer.length-1);
-			} else if(this.buffer.length<=this.input_line.width) {
-				this.getInputPosition();
-				console.left();
-				console.putmsg(" ",P_SAVEATR);
-				this.buffer=this.buffer.substr(0,this.buffer.length-1);
-			} else {
-				this.buffer=this.buffer.substr(0,this.buffer.length-1);
-				this.printBuffer();
-			}
-			return true;
-		} else {
-			return false;
-		}
+		this.box=new Window(this.x-1,this.y-1,this.columns+2,this.rows+2);
+		this.box.init("\1n\1cCHAT","\1n\1c" + this.room);
 	}
-	this.getInputPosition=function()
+	this.ignore=function(alias)
 	{
-		console.gotoxy(this.input_line.x+this.buffer.length,this.input_line.y);
+		if(this.ignored[alias]==true) this.ignored[alias]=false;
+		else ignored[alias]==true;
 	}
-	this.printBuffer=function()
+	this.process=function(data)
 	{
-		if(!this.buffer.length) return false;
-		var color="";
-		switch(message.scope) {
-		case flag_global:
-			color=background + global_color;
-			break;
+		if(this.ignored[data.source]==true) return false;
+		switch(data.scope)
+		{
 		case flag_private:
-			color=background + private_color;
-			break;
-		case flag_normal:
-		default:	
-			color=background + input_color;
-			break;
-		}
-		if(fullscreen) {
-			console.putmsg(color,P_SAVEATR);
-			console.write("\r" + this.buffer);
-			console.cleartoeol();
-		} else {
-			console.gotoxy(this.input_line);
-			console.putmsg(color,P_SAVEATR);
-			if(this.buffer.length>this.input_line.width) {
-				var overrun=(this.buffer.length-this.columns);
-				var truncated=this.buffer.substr(overrun);
-				var disp=truncated;
-				if(disp.indexOf('@')>=0) disp=disp.replace(/@/g,"?");
-				console.write(disp);
-			} else {
-				console.write(this.buffer);
+			if(data.target) {
+				if(data.target==user.alias) this.post(data.txt,private_color + "\1h",data.source);
+				else this.post(data.txt,private_color,data.source + "\1h-" +private_color+ data.target);
 			}
-		}
-	}
-	this.bufferKey=function(key) //ADD A KEY TO THE USER INPUT BUFFER
-	{
-		if(!key) return false;
-		var color="";
-		switch(message.scope) {
-		case flag_global:
-			color=background + global_color;
 			break;
-		case flag_private:
-			color=background + private_color;
+		case flag_global:
+			switch(data.source) {
+				case user.alias:
+					this.post(data.txt,global_color,data.source);
+					break;
+				default:
+					this.post(data.txt,global_color + "\1h",data.source);
+					break;
+			}
 			break;
 		case flag_normal:
-		default:	
-			color=background + input_color;
+		default:
+			if(!data.source) {
+				switch(data.level) 
+				{
+				case flag_alert:
+					this.post(data.txt,alert_color);
+					break;
+				case flag_notice:
+				default:
+					this.post(data.txt,notice_color);
+					break;
+				}
+			} else	if(data.source==user.alias) {
+				this.post(data.txt,local_color,data.source);
+			} else this.post(data.txt,remote_color,data.source);
 			break;
 		}
-		if(!fullscreen) {
-			if(this.buffer.length>=this.input_line.width) {
-				this.buffer+=key;
-				this.printBuffer();
-				return;
-			} else {
-				this.getInputPosition();
-			}
-		} 
-		this.buffer+=key;
-		console.putmsg(color,P_SAVEATR);
-		console.write(key);
 	}
-	this.showHistory=function(key) //ACTIVATE MESSAGE HISTORY SCROLLBACK
+	this.scroll=function(key) 
 	{
-		if(!fullscreen && window.length>window.height) {
+		if(!this.fullscreen && this.window.length>this.window.height) {
 			switch(key)
 			{
 				case '\x02':	/* CTRL-B KEY_HOME */
-					window.home();
+					this.window.home();
 					break;
 				case '\x05':	/* CTRL-E KEY_END */
-					window.end();
+					this.window.end();
 					break;
 				case KEY_DOWN:
-					window.scroll(1);
+					this.window.scroll(1);
 					break;
 				case KEY_UP:
-					window.scroll(-1);
+					this.window.scroll(-1);
 					break;
 			}
-			window.draw(this.x,this.y);
-			scrollbar.draw(window.index,window.length,window.height);
+			this.window.draw(this.x,this.y);
+			this.scrollbar.draw(this.window.index,this.window.length,this.window.height);
 		}
 	}
-	this.display=function(text,color,source,target)
+	this.post=function(text,color,source,target)
 	{
+		if(text.indexOf('@')>=0) {
+			if(user.compare_ars("SYSOP") || bbs.sys_status&SS_TMPSYSOP) {
+				text=text.replace(/@/g,"?");
+			}
+		}
 		var msg;
 		if(source) {
-			msg="\r" + color + source + "\1h: " + color + text + "\r\n";
+			msg="\r" + color + this.bg + source + "\1h: " + color + this.bg + text + "\r\n";
 		} else {
-			msg="\r" + color + text + "\r\n";
+			msg="\r" + color + this.bg + text + "\r\n";
 		}
-		if(fullscreen) {
+		if(this.fullscreen) {
 			console.putmsg(msg,P_SAVEATR); 
-			this.printBuffer();
 		} else {
-			window.putmsg(false,false,msg,undefined,true); 
-			window.draw(this.x,this.y);
-			if(window.length>window.height) scrollbar.draw(window.index,window.length,window.height);
-
+			this.window.putmsg(false,false,msg,undefined,true); 
+			this.draw();
 		}
 	}
-	this.processData=function(data)
+	this.list=function(array,color) //DISPLAYS A TEMPORARY MESSAGE IN THE CHAT WINDOW (NOT STORED)
 	{
-		if(ignore[data.source]==true) return false;
-		switch(data.scope)
-		{
-		case flag_private:
-			if(data.target==user.alias) this.display(data.txt,private_color + "\1h",data.source);
-			break;
-		case flag_global:
-			this.display(data.txt,global_color + "\1h",data.source);
-			break;
-		case flag_normal:
-		default:
-			if(!data.source) {
-				switch(data.level) 
-				{
-				case flag_alert:
-					this.display(data.txt,alert_color);
-					break;
-				case flag_notice:
-				default:
-					this.display(data.txt,notice_color);
-					break;
-				}
-			} else	if(data.source==user.alias) {
-				this.display(data.txt,local_color,data.source);
-			} else this.display(data.txt,remote_color,data.source);
-			break;
-		}
+		for(var i=0;i<array.length;i++) this.post(array[i],color);
+	}
+	this.notice=function(msg)
+	{
+		this.post(msg,notice_color);
 	}
-	this.resetInput=function()
+	this.alert=function(msg)
 	{
-		if(!fullscreen) this.input_line.clear();
-		else {
-			console.left(this.buffer.length);
-			console.cleartoeol();
+		this.post(msg,alert_color);
+	}
+	this.draw=function()
+	{
+		if(!this.fullscreen) {
+			if(this.box) this.box.draw();
+			this.window.draw(this.x,this.y);
+			if(this.window.length>this.window.height) this.scrollbar.draw(this.window.index,this.window.length,this.window.height);
 		}
-		this.buffer="";
-		message=new Message();
 	}
-	
 }
 function Message(txt,level,scope,source,target)
 {
@@ -503,42 +394,190 @@ function Message(txt,level,scope,source,target)
 	this.source=source;
 	this.target=target;
 }
-function InputLine(x,y,width,bg)
+function InputLine()
 {
-	this.x=x;
-	this.y=y;
-	this.width=width;
-	this.bg=bg;
-	this.position;
-	this.window;
+	this.x=1;
+	this.y=1;
+	this.width;
+	this.bg="";
+	this.fg=input_color;
 	this.buffer="";
+	this.scope=flag_normal;
+	this.target="";
+	this.box=false;
 	
-	this.clear=function(bg) 
+	this.clear=function() 
 	{
-		clearLine(this.width,this.x,this.y,bg?bg:this.bg);
+		if(this.width) {
+			console.gotoxy(this);
+			console.putmsg(this.bg+format("%*s",this.width,""),P_SAVEATR);
+		} else {
+			console.write("\r");
+			console.cleartoeol();
+		}
 		console.gotoxy(this);
 	}
-	this.init=function(x,y,w,bg,window) 
-	{
-		this.x=x;
-		this.y=y;
-		this.width=w;
-		this.bg=bg;
-		if(window) {
-			this.window=new Window(x,y,w,3);
-			this.window.init("\1nINPUT");
-			this.x+=1;
-			this.y+=1;
-			this.width-=2;
+	this.init=function(x,y,w,bg,fg) 
+	{
+		if(x) this.x=x;
+		if(y) this.y=y;
+		if(w) this.width=w;
+		if(bg) this.bg=bg;
+		if(fg) this.fg=fg;
+		if(this.box) this.initbox();
+		this.reset();
+	}
+	this.initbox=function()
+	{
+		this.box=new Window(this.x-1,this.y-1,this.width+2,3);
+		var color=this.fg;
+		var subtitle=false;
+		switch(this.scope) {
+			case flag_global:
+				color=global_color;
+				subtitle="GLOBAL";
+				break;
+			case flag_private:
+				color=private_color;
+				subtitle="PRIVATE";
+				break;
+			case flag_normal:
+			default:
+				break;
 		}
+		this.box.init("\1n\1cINPUT",subtitle?color + subtitle:false);
+	}
+	this.submit=function()
+	{
+		if(strlen(this.buffer)<1) return false;
+		switch(this.buffer.charAt(0))
+		{
+			case ';':
+				if(this.buffer.length>1 && (user.compare_ars("SYSOP") || bbs.sys_status&SS_TMPSYSOP)) {
+					str_cmds(this.buffer.substr(1));
+					this.reset();
+					return false;
+				} else {
+					break;
+				}
+			case '/':
+				var target=getFirstWord(this.buffer.substr(1));
+				if(target.length>0) {
+					this.target=target;
+					this.buffer=removeSpaces(this.buffer.substr(target.length+1));
+					this.scope=flag_private;
+				}
+			default:
+				break;
+		}
+		var msg=new Message(this.buffer,flag_message,this.scope,user.alias,this.target);
+		this.reset();
+		this.clear();
+		return msg;
+	}
+	this.toggle=function()
+	{
+		if(this.scope==flag_global) this.scope=flag_normal;
+		else this.scope=flag_global;
+		if(this.box) this.initbox();
+		this.draw();
+	}
+	this.backspace=function()
+	{
+		if(this.buffer.length>0) {
+			if(!this.width>0) {
+				console.left();
+				console.cleartoeol();
+				this.buffer=this.buffer.substr(0,this.buffer.length-1);
+			} else if(this.buffer.length<=this.width) {
+				this.getxy();
+				console.left();
+				console.putmsg(" ",P_SAVEATR);
+				this.buffer=this.buffer.substr(0,this.buffer.length-1);
+			} else {
+				this.buffer=this.buffer.substr(0,this.buffer.length-1);
+				this.draw();
+			}
+			return true;
+		} else {
+			return false;
+		}
+	}
+	this.reset=function()
+	{
+		this.buffer="";
+		if(this.scope==flag_private) {
+			this.scope=flag_normal;
+			this.initbox();
+		}
+		this.target="";
+	}
+	this.getxy=function()
+	{
+		console.gotoxy(this.x+this.buffer.length,this.y);
+	}
+	this.bufferKey=function(key)
+	{
+		if(!key) return false;
+		if(this.width>0) {
+			if(this.buffer.length>=this.width) {
+				this.buffer+=key;
+				this.draw();
+				return;
+			} else {
+				this.getxy();
+			}
+		}
+		this.buffer+=key;
+		var color=this.fg;
+		switch(this.scope) {
+			case flag_global:
+				color=global_color;
+				break;
+			case flag_private:
+				color=private_color;
+				break;
+			case flag_normal:
+			default:	
+				break;
+		}
+		console.putmsg(color+this.bg,P_SAVEATR);
+		console.write(key);
 	}
 	this.draw=function()
 	{
-		if(this.window) {
-			this.window.draw();
+		if(this.box) this.box.draw();
+		if(this.buffer.length<1) {
+			this.clear();
+			return;
+		}
+		var color=this.fg;
+		switch(this.scope) {
+			case flag_global:
+				color=global_color;
+				break;
+			case flag_private:
+				color=private_color;
+				break;
+			case flag_normal:
+			default:
+				break;
+		}
+		if(this.width>0) {
+			console.putmsg(color + this.bg,P_SAVEATR);
+			console.gotoxy(this);
+			if(this.buffer.length>this.width) {
+				var overrun=(this.buffer.length-this.width);
+				var truncated=this.buffer.substr(overrun);
+				if(truncated.indexOf('@')>=0) truncated=truncated.replace(/@/g,"?");
+				console.write(truncated);
+			} else {
+				console.write(printPadded(this.buffer,this.width));
+			}
+		} else {
+			console.putmsg("\r" + color + this.bg,P_SAVEATR);
+			console.write(this.buffer);
 		}
-		console.gotoxy(this.x,this.y);
-		console.write(printPadded(this.color + this.buffer,this.width));
 	}
 }