diff --git a/src/sbbs3/js_global.c b/src/sbbs3/js_global.c
index 6fd4e80e92419eb2bfe38506e9707b695a59a1fb..405c41d013a1ea7aa8717453dfa14e1aafcb1544 100644
--- a/src/sbbs3/js_global.c
+++ b/src/sbbs3/js_global.c
@@ -614,12 +614,16 @@ js_word_wrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 	int32		l,len=79;
 	int32		oldlen=79;
 	int32		crcount;
+	JSBool		handle_quotes=JS_TRUE;
 	ulong		i,k,t;
 	int			ocol=1;
 	int			icol=1;
 	uchar*		inbuf;
 	char*		outbuf;
 	char*		linebuf;
+	char*		prefix=NULL;
+	int			prefix_len=0;
+	int			prefix_bytes=0;
 	JSString*	js_str;
 
 	if(JSVAL_IS_VOID(argv[0]))
@@ -637,9 +641,17 @@ js_word_wrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 	if(argc>2)
 		JS_ValueToInt32(cx,argv[2],&oldlen);
 
+	if(argc>3 && JSVAL_IS_BOOLEAN(argv[3]))
+		handle_quotes=JSVAL_TO_BOOLEAN(argv[3]);
+
 	if((linebuf=(char*)malloc((len*2)+2))==NULL) /* room for ^A codes */
 		return(JS_FALSE);
 
+	if(handle_quotes) {
+		if((prefix=(char *)malloc((len*2)+2))==NULL) /* room for ^A codes */
+			return(JS_FALSE);
+	}
+
 	outbuf[0]=0;
 	for(i=l=0; inbuf[i]; i++) {
 		switch(inbuf[i]) {
@@ -654,12 +666,97 @@ js_word_wrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 					l=0;
 					ocol=1;
 				}
-				else if(isspace(inbuf[i+1])) {	/* Next line starts with whitespace.  This is a "hard" CR. */
-					linebuf[l++]='\r';
-					linebuf[l++]='\n';
-					strncat(outbuf, linebuf, l);
-					l=0;
-					ocol=1;
+				else if(isspace(inbuf[i+1])) {	/* Next line starts with whitespace.  This is a "hard" CR. or quoted */
+					if(handle_quotes) {
+						/* k will be the new prefix_bytes */
+						t=1;
+						k=prefix_bytes;
+						prefix_bytes=0;
+						prefix_len=0;
+						while(t && t<6) {
+							prefix_bytes++;
+							/* Skip CTRL-A codes */
+							while(inbuf[i+prefix_bytes]=='\x01') {
+								k++;
+								if(inbuf[i+prefix_bytes]=='\x01')
+									break;
+								k++;
+							}
+							prefix_len++;
+							switch(t) {
+								case 1:		/* At start of possible quote (Next char should be space) */
+									if(inbuf[i+prefix_bytes]!=' ') {
+										if(prefix_bytes>1)
+										t=6;
+									}
+									else
+										t++;
+									break;
+								case 2:		/* At start of nick (next char should be alphanum or '>') */
+									if(inbuf[i+prefix_bytes]==' ')
+										t=0;
+									else {
+										if(inbuf[i+prefix_bytes]=='>')
+											t=5;
+										else
+											t++;
+									}
+									break;
+								case 3:		/* At second nick initial (next char should be alphanum or '>') */
+									if(inbuf[i+prefix_bytes]==' ')
+										t=0;
+									else {
+										if(inbuf[i+prefix_bytes]=='>')
+											t=5;
+										else
+											t++;
+									}
+									break;
+								case 4:		/* After two regular chars, next should HAS to be a '>') */
+									if(inbuf[i+prefix_bytes]!='>')
+										t=0;
+									else
+										t++;
+									break;
+								case 5:		/* At '>' next char must be a space */
+									if(inbuf[i+prefix_bytes]!=' ')
+										t=0;
+									else
+										t++;
+									break;
+							}
+						}
+						if(!t) {
+							prefix_bytes=0;
+							prefix_len=0;
+							prefix[0]=0;
+						}
+						else {
+							/* New prefix (different len, or different text)?  Force a new line and copy the new prefix */
+							if(prefix_bytes != k || (!memcmp(prefix,inbuf+i+1,prefix_bytes+1))) {
+								memcpy(prefix,inbuf+i+1,prefix_bytes);
+								prefix[prefix_bytes]=0;
+								linebuf[l++]='\r';
+								linebuf[l++]='\n';
+								strncat(outbuf, linebuf, l);
+								if(prefix)
+									strcpy(linebuf,prefix);
+								l=prefix_bytes;
+								ocol=prefix_len;
+								i++;	/* Do not keep the space (ie: cheat) for the first line of this quote block */
+							}
+							i+=prefix_bytes-1;	/* Keep the space (kinda a cheat... don't tell anyone) */
+						}
+					}
+					else {
+						linebuf[l++]='\r';
+						linebuf[l++]='\n';
+						strncat(outbuf, linebuf, l);
+						if(prefix)
+							strcpy(linebuf,prefix);
+						l=prefix_bytes;
+						ocol=prefix_len;
+					}
 				}
 				else {
 					if(icol < oldlen) {			/* If this line is overly long, It's impossible for the next word to fit */
@@ -669,8 +766,10 @@ js_word_wrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 							linebuf[l++]='\r';
 							linebuf[l++]='\n';
 							strncat(outbuf, linebuf, l);
-							l=0;
-							ocol=1;
+							if(prefix)
+								strcpy(linebuf,prefix);
+							l=prefix_bytes;
+							ocol=prefix_len;
 						}
 						else {		/* Not a hard CR... add space if needed */
 							if(!isspace(linebuf[l-1]))
@@ -743,11 +842,15 @@ js_word_wrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 					strncat(outbuf, linebuf, l+1);
 					strcat(outbuf, "\r\n");
 					/* Move trailing words to start of buffer. */
+					l=prefix_bytes;
 					if(k-t>0)							/* k-1 is the last char position.  t is the start of the next line position */
-						memmove(linebuf, linebuf+t, k-t);
-					l=k-t;
+						memmove(linebuf+l, linebuf+t, k-t);
+					if(prefix)
+						memcpy(linebuf,prefix,prefix_bytes);
+					ocol=prefix_len;
+					l+=k-t;
 					/* Find new ocol */
-					for(ocol=1,t=0; t<l; t++) {
+					for(ocol=prefix_len+1,t=0; t<l; t++) {
 						switch(linebuf[t]) {
 							case '\x01':	/* CTRL-A */
 								if(linebuf[t+1]!='\x01')
@@ -777,6 +880,8 @@ js_word_wrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
 	js_str = JS_NewStringCopyZ(cx, outbuf);
 	free(outbuf);
+	if(prefix)
+		free(prefix);
 	if(js_str==NULL)
 		return(JS_FALSE);
 
@@ -2887,8 +2992,10 @@ static jsSyncMethodSpec js_global_functions[] = {
 	,JSDOCSTR("return a decoded HTML-encoded text string")
 	,311
 	},
-	{"word_wrap",		js_word_wrap,		1,	JSTYPE_STRING,	JSDOCSTR("text [,line_length=<tt>79</tt>]")
-	,JSDOCSTR("returns a word-wrapped version of the text string argument, <i>line_length</i> defaults to <i>79</i>")
+	{"word_wrap",		js_word_wrap,		1,	JSTYPE_STRING,	JSDOCSTR("text [,line_length=<tt>79</tt> [, orig_line_length=<tt>79</tt> [, handle_quotes=<tt>true</tt>]]]]")
+	,JSDOCSTR("returns a word-wrapped version of the text string argument optionally handing quotes magically, "
+		"<i>line_length</i> defaults to <i>79</i> <i>orig_line_length</i> defaults to <i>79</i> "
+		"and <i>handle_quotes</i> defaults to <i>true</i>")
 	,311
 	},
 	{"quote_msg",		js_quote_msg,		1,	JSTYPE_STRING,	JSDOCSTR("text [,line_length=<tt>79</tt>] [,prefix=<tt>\" > \"</tt>]")